Add reflection APIs for wgpu::Texture.

This requires adding a custom implementation of DeviceCreateTexture and
the Texture object in the wire client.

In dawn::native this requires the format enum separately from mFormat
which is a `const Format&` so that garbage format values can be
reflected correctly for error textures.

Bug: dawn:1451
Change-Id: I75b5635f36647f6f04dae54e92154f2b552beb64
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/92661
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index a5b0dcc..8304b73 100644
--- a/dawn.json
+++ b/dawn.json
@@ -2520,6 +2520,38 @@
                 ]
             },
             {
+                "name": "get width",
+                "returns": "uint32_t"
+            },
+            {
+                "name": "get height",
+                "returns": "uint32_t"
+            },
+            {
+                "name": "get depth or array layers",
+                "returns": "uint32_t"
+            },
+            {
+                "name": "get mip level count",
+                "returns": "uint32_t"
+            },
+            {
+                "name": "get sample count",
+                "returns": "uint32_t"
+            },
+            {
+                "name": "get dimension",
+                "returns": "texture dimension"
+            },
+            {
+                "name": "get format",
+                "returns": "texture format"
+            },
+            {
+                "name": "get usage",
+                "returns": "texture usage"
+            },
+            {
                 "name": "destroy"
             }
         ]
diff --git a/dawn_wire.json b/dawn_wire.json
index 8ce3e90..cb0db16 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -205,12 +205,21 @@
             "ShaderModuleGetCompilationInfo",
             "QueueOnSubmittedWorkDone",
             "QueueWriteBuffer",
-            "QueueWriteTexture"
+            "QueueWriteTexture",
+            "TextureGetWidth",
+            "TextureGetHeight",
+            "TextureGetDepthOrArrayLayers",
+            "TextureGetMipLevelCount",
+            "TextureGetSampleCount",
+            "TextureGetDimension",
+            "TextureGetFormat",
+            "TextureGetUsage"
         ],
         "client_handwritten_commands": [
             "BufferDestroy",
             "BufferUnmap",
             "DeviceCreateErrorBuffer",
+            "DeviceCreateTexture",
             "DeviceGetQueue",
             "DeviceInjectError"
         ],
@@ -220,7 +229,8 @@
             "Device",
             "Instance",
             "Queue",
-            "ShaderModule"
+            "ShaderModule",
+            "Texture"
         ],
         "server_custom_pre_handler_commands": [
             "BufferDestroy",
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 420230b..e933b15 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -1209,7 +1209,7 @@
     Ref<TextureBase> result;
     if (ConsumedError(CreateTexture(descriptor), &result, "calling %s.CreateTexture(%s).", this,
                       descriptor)) {
-        return TextureBase::MakeError(this);
+        return TextureBase::MakeError(this, descriptor);
     }
     return result.Detach();
 }
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 75f8866..843b6cf 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -529,7 +529,8 @@
       mSampleCount(descriptor->sampleCount),
       mUsage(descriptor->usage),
       mInternalUsage(mUsage),
-      mState(state) {
+      mState(state),
+      mFormatEnumForReflection(descriptor->format) {
     uint32_t subresourceCount = mMipLevelCount * GetArrayLayers() * GetAspectCount(mFormat.aspects);
     mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false);
 
@@ -559,16 +560,25 @@
     TrackInDevice();
 }
 
-TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag)
-    : ApiObjectBase(device, tag), mFormat(kUnusedFormat) {}
+TextureBase::TextureBase(DeviceBase* device,
+                         const TextureDescriptor* descriptor,
+                         ObjectBase::ErrorTag tag)
+    : ApiObjectBase(device, tag),
+      mDimension(descriptor->dimension),
+      mFormat(kUnusedFormat),
+      mSize(descriptor->size),
+      mMipLevelCount(descriptor->mipLevelCount),
+      mSampleCount(descriptor->sampleCount),
+      mUsage(descriptor->usage),
+      mFormatEnumForReflection(descriptor->format) {}
 
 void TextureBase::DestroyImpl() {
     mState = TextureState::Destroyed;
 }
 
 // static
-TextureBase* TextureBase::MakeError(DeviceBase* device) {
-    return new TextureBase(device, ObjectBase::kError);
+TextureBase* TextureBase::MakeError(DeviceBase* device, const TextureDescriptor* descriptor) {
+    return new TextureBase(device, descriptor, ObjectBase::kError);
 }
 
 ObjectType TextureBase::GetType() const {
@@ -767,6 +777,37 @@
     Destroy();
 }
 
+uint32_t TextureBase::APIGetWidth() const {
+    return mSize.width;
+}
+
+uint32_t TextureBase::APIGetHeight() const {
+    return mSize.height;
+}
+uint32_t TextureBase::APIGetDepthOrArrayLayers() const {
+    return mSize.depthOrArrayLayers;
+}
+
+uint32_t TextureBase::APIGetMipLevelCount() const {
+    return mMipLevelCount;
+}
+
+uint32_t TextureBase::APIGetSampleCount() const {
+    return mSampleCount;
+}
+
+wgpu::TextureDimension TextureBase::APIGetDimension() const {
+    return mDimension;
+}
+
+wgpu::TextureFormat TextureBase::APIGetFormat() const {
+    return mFormatEnumForReflection;
+}
+
+wgpu::TextureUsage TextureBase::APIGetUsage() const {
+    return mUsage;
+}
+
 MaybeError TextureBase::ValidateDestroy() const {
     DAWN_TRY(GetDevice()->ValidateObject(this));
     return {};
diff --git a/src/dawn/native/Texture.h b/src/dawn/native/Texture.h
index dff5dcb..5958094 100644
--- a/src/dawn/native/Texture.h
+++ b/src/dawn/native/Texture.h
@@ -47,7 +47,7 @@
     enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
     enum class ClearValue { Zero, NonZero };
 
-    static TextureBase* MakeError(DeviceBase* device);
+    static TextureBase* MakeError(DeviceBase* device, const TextureDescriptor* descriptor);
 
     ObjectType GetType() const override;
 
@@ -96,6 +96,14 @@
     // Dawn API
     TextureViewBase* APICreateView(const TextureViewDescriptor* descriptor = nullptr);
     void APIDestroy();
+    uint32_t APIGetWidth() const;
+    uint32_t APIGetHeight() const;
+    uint32_t APIGetDepthOrArrayLayers() const;
+    uint32_t APIGetMipLevelCount() const;
+    uint32_t APIGetSampleCount() const;
+    wgpu::TextureDimension APIGetDimension() const;
+    wgpu::TextureFormat APIGetFormat() const;
+    wgpu::TextureUsage APIGetUsage() const;
 
   protected:
     TextureBase(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state);
@@ -106,7 +114,7 @@
     void DestroyImpl() override;
 
   private:
-    TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag);
+    TextureBase(DeviceBase* device, const TextureDescriptor* descriptor, ObjectBase::ErrorTag tag);
 
     MaybeError ValidateDestroy() const;
     wgpu::TextureDimension mDimension;
@@ -118,6 +126,7 @@
     wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
     wgpu::TextureUsage mInternalUsage = wgpu::TextureUsage::None;
     TextureState mState;
+    wgpu::TextureFormat mFormatEnumForReflection;
 
     // TODO(crbug.com/dawn/845): Use a more optimized data structure to save space
     std::vector<bool> mIsSubresourceContentInitializedAtIndex;
diff --git a/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp b/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
index f9a25ab..03a70d2 100644
--- a/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/InternalUsageValidationTests.cpp
@@ -204,6 +204,24 @@
     }
 }
 
+// Test that the internal usages aren't reflected with wgpu::Texture::GetUsage.
+TEST_F(TextureInternalUsageValidationTest, InternalUsagesAreNotReflected) {
+    wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
+    internalDesc.internalUsage = wgpu::TextureUsage::StorageBinding;
+
+    wgpu::TextureDescriptor textureDesc = {};
+    textureDesc.size = {1, 1};
+    textureDesc.usage = wgpu::TextureUsage::CopySrc;
+    textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
+    textureDesc.nextInChain = &internalDesc;
+
+    wgpu::Texture texture = device.CreateTexture(&textureDesc);
+    ASSERT_EQ(texture.GetUsage(), wgpu::TextureUsage::CopySrc);
+}
+
+
+// Test the validation of internal usages against command encoders with and without
+// useInternalUsages.
 TEST_F(TextureInternalUsageValidationTest, CommandValidation) {
     wgpu::TextureDescriptor textureDesc = {};
     textureDesc.size = {1, 1};
diff --git a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
index ffac5ad..253354b 100644
--- a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
@@ -906,4 +906,64 @@
     }
 }
 
+static void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
+                                          const wgpu::TextureDescriptor& desc) {
+    EXPECT_EQ(desc.size.width, tex.GetWidth());
+    EXPECT_EQ(desc.size.height, tex.GetHeight());
+    EXPECT_EQ(desc.size.depthOrArrayLayers, tex.GetDepthOrArrayLayers());
+    EXPECT_EQ(desc.mipLevelCount, tex.GetMipLevelCount());
+    EXPECT_EQ(desc.sampleCount, tex.GetSampleCount());
+    EXPECT_EQ(desc.dimension, tex.GetDimension());
+    EXPECT_EQ(desc.usage, tex.GetUsage());
+    EXPECT_EQ(desc.format, tex.GetFormat());
+}
+
+// Test that the texture creation parameters are correctly reflected for succesfully created
+// textures.
+TEST_F(TextureValidationTest, CreationParameterReflectionForValidTextures) {
+    // Test reflection on two succesfully created but different textures.
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {3, 2, 1};
+        desc.mipLevelCount = 1;
+        desc.sampleCount = 4;
+        desc.dimension = wgpu::TextureDimension::e2D;
+        desc.usage = wgpu::TextureUsage::RenderAttachment;
+        desc.format = wgpu::TextureFormat::RGBA8Unorm;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        CheckTextureMatchesDescriptor(tex, desc);
+    }
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {47, 32, 19};
+        desc.mipLevelCount = 3;
+        desc.sampleCount = 1;
+        desc.dimension = wgpu::TextureDimension::e3D;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        desc.format = wgpu::TextureFormat::R32Float;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        CheckTextureMatchesDescriptor(tex, desc);
+    }
+}
+
+// Test that the texture creation parameters are correctly reflected for error textures.
+TEST_F(TextureValidationTest, CreationParameterReflectionForErrorTextures) {
+    // Fill a descriptor with a bunch of garbage values.
+    wgpu::TextureDescriptor desc;
+    desc.size = {0, 0xFFFF'FFFF, 1};
+    desc.mipLevelCount = 0;
+    desc.sampleCount = 42;
+    desc.dimension = static_cast<wgpu::TextureDimension>(0xFFFF'FF00);
+    desc.usage = static_cast<wgpu::TextureUsage>(0xFFFF'FFFF);
+    desc.format = static_cast<wgpu::TextureFormat>(0xFFFF'FFF0);
+
+    // Error! Because the texture width is 0.
+    wgpu::Texture tex;
+    ASSERT_DEVICE_ERROR(tex = device.CreateTexture(&desc));
+
+    CheckTextureMatchesDescriptor(tex, desc);
+}
+
 }  // namespace
diff --git a/src/dawn/wire/BUILD.gn b/src/dawn/wire/BUILD.gn
index 4c20cd8..8ca560b 100644
--- a/src/dawn/wire/BUILD.gn
+++ b/src/dawn/wire/BUILD.gn
@@ -96,6 +96,8 @@
     "client/RequestTracker.h",
     "client/ShaderModule.cpp",
     "client/ShaderModule.h",
+    "client/Texture.cpp",
+    "client/Texture.h",
     "server/ObjectStorage.h",
     "server/Server.cpp",
     "server/Server.h",
diff --git a/src/dawn/wire/CMakeLists.txt b/src/dawn/wire/CMakeLists.txt
index b69eea1..4698689 100644
--- a/src/dawn/wire/CMakeLists.txt
+++ b/src/dawn/wire/CMakeLists.txt
@@ -69,6 +69,8 @@
     "client/RequestTracker.h"
     "client/ShaderModule.cpp"
     "client/ShaderModule.h"
+    "client/Texture.cpp"
+    "client/Texture.h"
     "server/ObjectStorage.h"
     "server/Server.cpp"
     "server/Server.h"
diff --git a/src/dawn/wire/client/ApiObjects.h b/src/dawn/wire/client/ApiObjects.h
index 1998d54..14b6b7e 100644
--- a/src/dawn/wire/client/ApiObjects.h
+++ b/src/dawn/wire/client/ApiObjects.h
@@ -23,6 +23,7 @@
 #include "dawn/wire/client/Instance.h"
 #include "dawn/wire/client/Queue.h"
 #include "dawn/wire/client/ShaderModule.h"
+#include "dawn/wire/client/Texture.h"
 
 #include "dawn/wire/client/ApiObjects_autogen.h"
 
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index f4b2460..940764a 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -204,6 +204,10 @@
     return Buffer::CreateError(this, &fakeDescriptor);
 }
 
+WGPUTexture Device::CreateTexture(const WGPUTextureDescriptor* descriptor) {
+    return Texture::Create(this, descriptor);
+}
+
 WGPUQueue Device::GetQueue() {
     // The queue is lazily created because if a Device is created by
     // Reserve/Inject, we cannot send the GetQueue message until
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index f613e6a..8a76cb9 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -50,6 +50,7 @@
     void CreateRenderPipelineAsync(WGPURenderPipelineDescriptor const* descriptor,
                                    WGPUCreateRenderPipelineAsyncCallback callback,
                                    void* userdata);
+    WGPUTexture CreateTexture(const WGPUTextureDescriptor* descriptor);
 
     void HandleError(WGPUErrorType errorType, const char* message);
     void HandleLogging(WGPULoggingType loggingType, const char* message);
diff --git a/src/dawn/wire/client/Texture.cpp b/src/dawn/wire/client/Texture.cpp
new file mode 100644
index 0000000..f4c0b97
--- /dev/null
+++ b/src/dawn/wire/client/Texture.cpp
@@ -0,0 +1,82 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn/wire/client/Texture.h"
+
+#include "dawn/wire/client/Client.h"
+#include "dawn/wire/client/Device.h"
+
+namespace dawn::wire::client {
+
+// static
+WGPUTexture Texture::Create(Device* device, const WGPUTextureDescriptor* descriptor) {
+    Client* wireClient = device->client;
+    auto* textureObjectAndSerial = wireClient->TextureAllocator().New(wireClient);
+
+    // Copy over descriptor data for reflection.
+    Texture* texture = textureObjectAndSerial->object.get();
+    texture->mSize = descriptor->size;
+    texture->mMipLevelCount = descriptor->mipLevelCount;
+    texture->mSampleCount = descriptor->sampleCount;
+    texture->mDimension = descriptor->dimension;
+    texture->mFormat = descriptor->format;
+    texture->mUsage = static_cast<WGPUTextureUsage>(descriptor->usage);
+
+    // Send the Device::CreateTexture command without modifications.
+    DeviceCreateTextureCmd cmd;
+    cmd.self = ToAPI(device);
+    cmd.selfId = device->id;
+    cmd.descriptor = descriptor;
+    cmd.result = ObjectHandle{texture->id, textureObjectAndSerial->generation};
+    wireClient->SerializeCommand(cmd);
+
+    return ToAPI(texture);
+}
+
+Texture::Texture(Client* c, uint32_t r, uint32_t i) : ObjectBase(c, r, i) {}
+Texture::~Texture() = default;
+
+uint32_t Texture::GetWidth() const {
+    return mSize.width;
+}
+
+uint32_t Texture::GetHeight() const {
+    return mSize.height;
+}
+
+uint32_t Texture::GetDepthOrArrayLayers() const {
+    return mSize.depthOrArrayLayers;
+}
+
+uint32_t Texture::GetMipLevelCount() const {
+    return mMipLevelCount;
+}
+
+uint32_t Texture::GetSampleCount() const {
+    return mSampleCount;
+}
+
+WGPUTextureDimension Texture::GetDimension() const {
+    return mDimension;
+}
+
+WGPUTextureFormat Texture::GetFormat() const {
+    return mFormat;
+}
+
+WGPUTextureUsage Texture::GetUsage() const {
+    return mUsage;
+}
+
+}  // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/Texture.h b/src/dawn/wire/client/Texture.h
new file mode 100644
index 0000000..0bce2ea
--- /dev/null
+++ b/src/dawn/wire/client/Texture.h
@@ -0,0 +1,54 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_WIRE_CLIENT_TEXTURE_H_
+#define SRC_DAWN_WIRE_CLIENT_TEXTURE_H_
+
+#include "dawn/webgpu.h"
+
+#include "dawn/wire/client/ObjectBase.h"
+
+namespace dawn::wire::client {
+
+class Device;
+
+class Texture final : public ObjectBase {
+  public:
+    static WGPUTexture Create(Device* device, const WGPUTextureDescriptor* descriptor);
+
+    Texture(Client* client, uint32_t refcount, uint32_t id);
+    ~Texture();
+
+    // Note that these values can be arbitrary since they aren't validated in the wire client.
+    uint32_t GetWidth() const;
+    uint32_t GetHeight() const;
+    uint32_t GetDepthOrArrayLayers() const;
+    uint32_t GetMipLevelCount() const;
+    uint32_t GetSampleCount() const;
+    WGPUTextureDimension GetDimension() const;
+    WGPUTextureFormat GetFormat() const;
+    WGPUTextureUsage GetUsage() const;
+
+  private:
+    WGPUExtent3D mSize;
+    uint32_t mMipLevelCount;
+    uint32_t mSampleCount;
+    WGPUTextureDimension mDimension;
+    WGPUTextureFormat mFormat;
+    WGPUTextureUsage mUsage;
+};
+
+}  // namespace dawn::wire::client
+
+#endif  // SRC_DAWN_WIRE_CLIENT_TEXTURE_H_