Add visibleRect to ExternalTextureDescriptor

Add visibleRect in ExternalTextureDescriptor to create ExternalTexture.
This helps ExternalTexture present the content correctly if needed.

Bug: chromium:1361363
Change-Id: I54b1912305080943babd7558ef40bca8528c932c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106181
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/dawn.json b/dawn.json
index 2bcd047..4b2f150 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1330,6 +1330,14 @@
             {"value": 3, "name": "error"}
         ]
     },
+    "extent 2D": {
+        "category": "structure",
+        "_TODO": "crbug.com/1316671: Remove default value of 'width' after chromium side chagnes landed",
+        "members": [
+            {"name": "width", "type": "uint32_t", "default": 0},
+            {"name": "height", "type": "uint32_t", "default": 1}
+        ]
+    },
     "extent 3D": {
         "category": "structure",
         "members": [
@@ -1359,10 +1367,12 @@
         "category": "structure",
         "extensible": "in",
         "tags": ["dawn"],
+        "_TODO": "crbug.com/1316671: Mark 'visible rect' as must have after chromium side changes landed",
         "members": [
             {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
             {"name": "plane 0", "type": "texture view"},
             {"name": "plane 1", "type": "texture view", "optional": true},
+            {"name": "visible rect", "type": "extent 2D"},
             {"name": "do yuv to rgb conversion only", "type": "bool", "default": "false"},
             {"name": "yuv to rgb conversion matrix", "type": "float", "annotation": "const*",
                 "length": 12, "optional": true},
diff --git a/src/dawn/native/ExternalTexture.cpp b/src/dawn/native/ExternalTexture.cpp
index ab40470..14ea84f 100644
--- a/src/dawn/native/ExternalTexture.cpp
+++ b/src/dawn/native/ExternalTexture.cpp
@@ -99,6 +99,22 @@
         }
     }
 
+    // TODO(crbug.com/1316671): visibleRect must have valid value after chromium side changes
+    // landed.
+    if (descriptor->visibleRect.width > 0) {
+        DAWN_INVALID_IF(descriptor->visibleRect.width == 0 || descriptor->visibleRect.height == 0,
+                        "VisibleRect(%u, %u) have 0 on width or height.",
+                        descriptor->visibleRect.width, descriptor->visibleRect.height);
+
+        Extent3D maxVisibleRectSize = descriptor->plane0->GetTexture()->GetSize();
+        DAWN_INVALID_IF(descriptor->visibleRect.width > maxVisibleRectSize.width ||
+                            descriptor->visibleRect.height > maxVisibleRectSize.height,
+                        "VisibleRect(%u, %u) is exceed the max visible rect size, defined by "
+                        "Plane0 size (%u, %u).",
+                        descriptor->visibleRect.width, descriptor->visibleRect.height,
+                        maxVisibleRectSize.width, maxVisibleRectSize.height);
+    }
+
     return {};
 }
 
@@ -114,7 +130,9 @@
 
 ExternalTextureBase::ExternalTextureBase(DeviceBase* device,
                                          const ExternalTextureDescriptor* descriptor)
-    : ApiObjectBase(device, descriptor->label), mState(ExternalTextureState::Alive) {
+    : ApiObjectBase(device, descriptor->label),
+      mVisibleRect(descriptor->visibleRect),
+      mState(ExternalTextureState::Alive) {
     GetObjectTrackingList()->Track(this);
 }
 
@@ -200,6 +218,13 @@
     ASSERT(!IsError());
     DAWN_INVALID_IF(mState == ExternalTextureState::Destroyed,
                     "Destroyed external texture %s is used in a submit.", this);
+
+    for (uint32_t i = 0; i < kMaxPlanesPerFormat; ++i) {
+        if (mTextureViews[i] != nullptr) {
+            DAWN_TRY_CONTEXT(mTextureViews[i]->GetTexture()->ValidateCanUseInSubmitNow(),
+                             "Validate plane %u of %s can be used in a submit.", i, this);
+        }
+    }
     return {};
 }
 
@@ -227,4 +252,9 @@
     return ObjectType::ExternalTexture;
 }
 
+const Extent2D& ExternalTextureBase::GetVisibleRect() const {
+    ASSERT(!IsError());
+    return mVisibleRect;
+}
+
 }  // namespace dawn::native
diff --git a/src/dawn/native/ExternalTexture.h b/src/dawn/native/ExternalTexture.h
index 509a7ee..6ea47e8 100644
--- a/src/dawn/native/ExternalTexture.h
+++ b/src/dawn/native/ExternalTexture.h
@@ -49,6 +49,7 @@
     BufferBase* GetParamsBuffer() const;
     const std::array<Ref<TextureViewBase>, kMaxPlanesPerFormat>& GetTextureViews() const;
     ObjectType GetType() const override;
+    const Extent2D& GetVisibleRect() const;
 
     MaybeError ValidateCanUseInSubmitNow() const;
     static ExternalTextureBase* MakeError(DeviceBase* device);
@@ -73,6 +74,10 @@
     Ref<BufferBase> mParamsBuffer;
     std::array<Ref<TextureViewBase>, kMaxPlanesPerFormat> mTextureViews;
 
+    // TODO(dawn:1082) Use the visible rect in the external texture shader code to sample
+    // video content.
+    Extent2D mVisibleRect;
+
     ExternalTextureState mState;
 };
 }  // namespace dawn::native
diff --git a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
index 6f6aebc..55d9499 100644
--- a/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
+++ b/src/dawn/tests/unittests/validation/ExternalTextureTests.cpp
@@ -581,4 +581,90 @@
     }
 }
 
+// Test create external texture with too large visible rect results in error.
+TEST_F(ExternalTextureTest, CreateExternalTextureWithErrorVisibleRect) {
+    // Control case should succeed.
+    {
+        wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor();
+        wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
+
+        wgpu::ExternalTextureDescriptor externalDesc = CreateDefaultExternalTextureDescriptor();
+        externalDesc.plane0 = texture.CreateView();
+        externalDesc.visibleRect = {texture.GetWidth(), texture.GetHeight()};
+        device.CreateExternalTexture(&externalDesc);
+    }
+
+    // VisibleRect is OOB on width
+    {
+        wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor();
+        wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
+
+        wgpu::ExternalTextureDescriptor externalDesc = CreateDefaultExternalTextureDescriptor();
+        externalDesc.plane0 = texture.CreateView();
+        externalDesc.visibleRect = {texture.GetWidth() + 1, texture.GetHeight()};
+        ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc));
+    }
+
+    // VisibleRect is OOB on height
+    {
+        wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor();
+        wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
+
+        wgpu::ExternalTextureDescriptor externalDesc = CreateDefaultExternalTextureDescriptor();
+        externalDesc.plane0 = texture.CreateView();
+        externalDesc.visibleRect = {texture.GetWidth(), texture.GetHeight() + 1};
+        ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc));
+    }
+}
+
+// Test that submitting an external texture with a plane that is not submittable results in error.
+TEST_F(ExternalTextureTest, SubmitExternalTextureWithDestroyedPlane) {
+    wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor();
+    wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
+
+    wgpu::ExternalTextureDescriptor externalDesc = CreateDefaultExternalTextureDescriptor();
+    externalDesc.plane0 = texture.CreateView();
+    wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
+
+    // Create a bind group that contains the external texture.
+    wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+        device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
+    wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, externalTexture}});
+
+    // Create another texture to use as a color attachment.
+    wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor();
+    wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
+    wgpu::TextureView renderView = renderTexture.CreateView();
+
+    utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr);
+
+    // Control case should succeed.
+    {
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+        {
+            pass.SetBindGroup(0, bindGroup);
+            pass.End();
+        }
+
+        wgpu::CommandBuffer commands = encoder.Finish();
+
+        queue.Submit(1, &commands);
+    }
+
+    // Destroying the plane0 backed texture should result in an error.
+    {
+        texture.Destroy();
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+        {
+            pass.SetBindGroup(0, bindGroup);
+            pass.End();
+        }
+
+        wgpu::CommandBuffer commands = encoder.Finish();
+        ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
+    }
+}
+
 }  // namespace