// Copyright 2021 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 "tests/unittests/validation/ValidationTest.h" #include "utils/ComboRenderPipelineDescriptor.h" #include "utils/WGPUHelpers.h" namespace { class ExternalTextureTest : public ValidationTest { public: wgpu::TextureDescriptor CreateDefaultTextureDescriptor() { wgpu::TextureDescriptor descriptor; descriptor.size.width = kWidth; descriptor.size.height = kHeight; descriptor.size.depthOrArrayLayers = kDefaultDepth; descriptor.mipLevelCount = kDefaultMipLevels; descriptor.sampleCount = kDefaultSampleCount; descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.format = kDefaultTextureFormat; descriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::RenderAttachment; return descriptor; } protected: void SetUp() override { ValidationTest::SetUp(); queue = device.GetQueue(); } static constexpr uint32_t kWidth = 32; static constexpr uint32_t kHeight = 32; static constexpr uint32_t kDefaultDepth = 1; static constexpr uint32_t kDefaultMipLevels = 1; static constexpr uint32_t kDefaultSampleCount = 1; static constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm; wgpu::Queue queue; }; TEST_F(ExternalTextureTest, CreateExternalTextureValidation) { wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor(); wgpu::ExternalTextureDescriptor externalDesc; externalDesc.format = kDefaultTextureFormat; // Creating an external texture from a 2D, single-subresource texture should succeed. { wgpu::Texture texture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = texture.CreateView(); device.CreateExternalTexture(&externalDesc); } // Creating an external texture with a mismatched texture view format should fail. { textureDescriptor.format = wgpu::TextureFormat::RGBA8Uint; wgpu::Texture texture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = texture.CreateView(); ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } // Creating an external texture from a non-2D texture should fail. { textureDescriptor.dimension = wgpu::TextureDimension::e3D; wgpu::Texture internalTexture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = internalTexture.CreateView(); ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } // Creating an external texture from a texture with mip count > 1 should fail. { textureDescriptor.mipLevelCount = 2; wgpu::Texture internalTexture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = internalTexture.CreateView(); ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } // Creating an external texture from a texture without TextureUsage::Sampled should fail. { textureDescriptor.mipLevelCount = 2; wgpu::Texture internalTexture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = internalTexture.CreateView(); ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } // Creating an external texture with an unsupported format should fail. { constexpr wgpu::TextureFormat kUnsupportedFormat = wgpu::TextureFormat::R8Uint; textureDescriptor.format = kUnsupportedFormat; wgpu::Texture internalTexture = device.CreateTexture(&textureDescriptor); externalDesc.plane0 = internalTexture.CreateView(); externalDesc.format = kUnsupportedFormat; ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } // Creating an external texture with an error texture view should fail. { wgpu::Texture internalTexture = device.CreateTexture(&textureDescriptor); wgpu::TextureViewDescriptor errorViewDescriptor; errorViewDescriptor.format = kDefaultTextureFormat; errorViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; errorViewDescriptor.mipLevelCount = 1; errorViewDescriptor.arrayLayerCount = 2; ASSERT_DEVICE_ERROR(wgpu::TextureView errorTextureView = internalTexture.CreateView(&errorViewDescriptor)); externalDesc.plane0 = errorTextureView; externalDesc.format = kDefaultTextureFormat; ASSERT_DEVICE_ERROR(device.CreateExternalTexture(&externalDesc)); } } // Test that submitting a render pass that contains a destroyed external texture results in // an error. TEST_F(ExternalTextureTest, SubmitDestroyedExternalTextureInRenderPass) { wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor(); wgpu::Texture texture = device.CreateTexture(&textureDescriptor); wgpu::ExternalTextureDescriptor externalDesc; externalDesc.format = kDefaultTextureFormat; 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 = CreateDefaultTextureDescriptor(); 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.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } // Destroying the external texture should result in an error. { externalTexture.Destroy(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } } // Test that submitting a render pass that contains a destroyed external texture plane // results in an error. TEST_F(ExternalTextureTest, SubmitDestroyedExternalTexturePlaneInRenderPass) { wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor(); wgpu::Texture texture = device.CreateTexture(&textureDescriptor); wgpu::ExternalTextureDescriptor externalDesc; externalDesc.format = kDefaultTextureFormat; 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 = CreateDefaultTextureDescriptor(); 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.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } // Destroying an external texture underlying plane should result in an error. { texture.Destroy(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } } // Test that submitting a compute pass that contains a destroyed external texture results in // an error. TEST_F(ExternalTextureTest, SubmitDestroyedExternalTextureInComputePass) { wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor(); wgpu::Texture texture = device.CreateTexture(&textureDescriptor); wgpu::ExternalTextureDescriptor externalDesc; externalDesc.format = kDefaultTextureFormat; 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}}); wgpu::ComputePassDescriptor computePass; // Control case should succeed. { wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&computePass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } // Destroying the external texture should result in an error. { externalTexture.Destroy(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&computePass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } } // Test that submitting a compute pass that contains a destroyed external texture plane // results in an error. TEST_F(ExternalTextureTest, SubmitDestroyedExternalTexturePlaneInComputePass) { wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor(); wgpu::Texture texture = device.CreateTexture(&textureDescriptor); wgpu::ExternalTextureDescriptor externalDesc; externalDesc.format = kDefaultTextureFormat; 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}}); wgpu::ComputePassDescriptor computePass; // Control case should succeed. { wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&computePass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } // Destroying an external texture underlying plane should result in an error. { texture.Destroy(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&computePass); { pass.SetBindGroup(0, bindGroup); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } } } // namespace