| // Copyright 2018 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <cmath> |
| #include <string> |
| #include <vector> |
| |
| #include "dawn/common/Constants.h" |
| #include "dawn/tests/unittests/validation/ValidationTest.h" |
| #include "dawn/utils/ComboRenderBundleEncoderDescriptor.h" |
| #include "dawn/utils/ComboRenderPipelineDescriptor.h" |
| #include "dawn/utils/WGPUHelpers.h" |
| |
| namespace dawn { |
| namespace { |
| |
| class RenderPassDescriptorValidationTest : public ValidationTest { |
| public: |
| void AssertBeginRenderPassSuccess(const wgpu::RenderPassDescriptor* descriptor) { |
| wgpu::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); |
| commandEncoder.Finish(); |
| } |
| void AssertBeginRenderPassError(const wgpu::RenderPassDescriptor* descriptor) { |
| wgpu::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); |
| ASSERT_DEVICE_ERROR(commandEncoder.Finish()); |
| } |
| |
| void AssertBeginRenderPassError(const wgpu::RenderPassDescriptor* descriptor, |
| testing::Matcher<std::string> errorMatcher) { |
| wgpu::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); |
| ASSERT_DEVICE_ERROR(commandEncoder.Finish(), errorMatcher); |
| } |
| |
| private: |
| wgpu::CommandEncoder TestBeginRenderPass(const wgpu::RenderPassDescriptor* descriptor) { |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(descriptor); |
| renderPassEncoder.End(); |
| return commandEncoder; |
| } |
| }; |
| |
| wgpu::Texture CreateTexture(wgpu::Device& device, |
| wgpu::TextureDimension dimension, |
| wgpu::TextureFormat format, |
| uint32_t width, |
| uint32_t height, |
| uint32_t arrayLayerCount, |
| uint32_t mipLevelCount, |
| uint32_t sampleCount = 1, |
| wgpu::TextureUsage usage = wgpu::TextureUsage::RenderAttachment) { |
| wgpu::TextureDescriptor descriptor; |
| descriptor.dimension = dimension; |
| descriptor.size.width = width; |
| descriptor.size.height = height; |
| descriptor.size.depthOrArrayLayers = arrayLayerCount; |
| descriptor.sampleCount = sampleCount; |
| descriptor.format = format; |
| descriptor.mipLevelCount = mipLevelCount; |
| descriptor.usage = usage; |
| |
| return device.CreateTexture(&descriptor); |
| } |
| |
| wgpu::TextureView Create2DAttachment(wgpu::Device& device, |
| uint32_t width, |
| uint32_t height, |
| wgpu::TextureFormat format) { |
| wgpu::Texture texture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, format, width, height, 1, 1); |
| return texture.CreateView(); |
| } |
| |
| // Using BeginRenderPass with no attachments isn't valid |
| TEST_F(RenderPassDescriptorValidationTest, Empty) { |
| utils::ComboRenderPassDescriptor renderPass({}, nullptr); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // A render pass with only one color or one depth attachment is ok |
| TEST_F(RenderPassDescriptorValidationTest, OneAttachment) { |
| // One color attachment |
| { |
| wgpu::TextureView color = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| utils::ComboRenderPassDescriptor renderPass({color}); |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| // One depth-stencil attachment |
| { |
| wgpu::TextureView depthStencil = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencil); |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Regression test for chromium:1487788 were cached attachment states used in a pass encoder created |
| // from an error command encoder are not cleaned up if the device's last reference is dropped before |
| // the pass. |
| TEST_F(RenderPassDescriptorValidationTest, ErrorEncoderLingeringAttachmentState) { |
| utils::ComboRenderPassDescriptor descriptor( |
| {Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm)}); |
| |
| // Purposely add a bad chain to the command encoder to force an error command encoder. |
| wgpu::CommandEncoderDescriptor commandEncoderDesc = {}; |
| wgpu::ChainedStruct chain = {}; |
| commandEncoderDesc.nextInChain = &chain; |
| |
| wgpu::CommandEncoder commandEncoder; |
| ASSERT_DEVICE_ERROR(commandEncoder = device.CreateCommandEncoder(&commandEncoderDesc)); |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&descriptor); |
| |
| ExpectDeviceDestruction(); |
| device = nullptr; |
| } |
| |
| // Test OOB color attachment indices are handled |
| TEST_F(RenderPassDescriptorValidationTest, ColorAttachmentOutOfBounds) { |
| std::array<wgpu::RenderPassColorAttachment, kMaxColorAttachments + 1> colorAttachments; |
| for (uint32_t i = 0; i < colorAttachments.size(); i++) { |
| colorAttachments[i].view = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::R8Unorm); |
| colorAttachments[i].resolveTarget = nullptr; |
| colorAttachments[i].clearValue = {0.0f, 0.0f, 0.0f, 0.0f}; |
| colorAttachments[i].loadOp = wgpu::LoadOp::Clear; |
| colorAttachments[i].storeOp = wgpu::StoreOp::Store; |
| } |
| |
| // Control case: kMaxColorAttachments is valid. |
| { |
| wgpu::RenderPassDescriptor renderPass; |
| renderPass.colorAttachmentCount = kMaxColorAttachments; |
| renderPass.colorAttachments = colorAttachments.data(); |
| renderPass.depthStencilAttachment = nullptr; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Error case: kMaxColorAttachments + 1 is an error. |
| { |
| wgpu::RenderPassDescriptor renderPass; |
| renderPass.colorAttachmentCount = kMaxColorAttachments + 1; |
| renderPass.colorAttachments = colorAttachments.data(); |
| renderPass.depthStencilAttachment = nullptr; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Test sparse color attachment validations |
| TEST_F(RenderPassDescriptorValidationTest, SparseColorAttachment) { |
| // Having sparse color attachment is valid. |
| { |
| std::array<wgpu::RenderPassColorAttachment, 2> colorAttachments; |
| colorAttachments[0].view = nullptr; |
| |
| colorAttachments[1].view = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| colorAttachments[1].loadOp = wgpu::LoadOp::Load; |
| colorAttachments[1].storeOp = wgpu::StoreOp::Store; |
| |
| wgpu::RenderPassDescriptor renderPass; |
| renderPass.colorAttachmentCount = colorAttachments.size(); |
| renderPass.colorAttachments = colorAttachments.data(); |
| renderPass.depthStencilAttachment = nullptr; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // When all color attachments are null |
| { |
| std::array<wgpu::RenderPassColorAttachment, 2> colorAttachments; |
| colorAttachments[0].view = nullptr; |
| colorAttachments[1].view = nullptr; |
| |
| // Control case: depth stencil attachment is not null is valid. |
| { |
| wgpu::TextureView depthStencilView = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); |
| wgpu::RenderPassDepthStencilAttachment depthStencilAttachment; |
| depthStencilAttachment.view = depthStencilView; |
| depthStencilAttachment.depthClearValue = 1.0f; |
| depthStencilAttachment.stencilClearValue = 0; |
| depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Clear; |
| depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store; |
| depthStencilAttachment.stencilLoadOp = wgpu::LoadOp::Clear; |
| depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Store; |
| |
| wgpu::RenderPassDescriptor renderPass; |
| renderPass.colorAttachmentCount = colorAttachments.size(); |
| renderPass.colorAttachments = colorAttachments.data(); |
| renderPass.depthStencilAttachment = &depthStencilAttachment; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Error case: depth stencil attachment being null is invalid. |
| { |
| wgpu::RenderPassDescriptor renderPass; |
| renderPass.colorAttachmentCount = colorAttachments.size(); |
| renderPass.colorAttachments = colorAttachments.data(); |
| renderPass.depthStencilAttachment = nullptr; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| } |
| |
| // Check that the render pass color attachment must have the RenderAttachment usage. |
| TEST_F(RenderPassDescriptorValidationTest, ColorAttachmentInvalidUsage) { |
| // Control case: using a texture with RenderAttachment is valid. |
| { |
| wgpu::TextureView renderView = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| utils::ComboRenderPassDescriptor renderPass({renderView}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Error case: using a texture with Sampled is invalid. |
| { |
| wgpu::TextureDescriptor texDesc; |
| texDesc.usage = wgpu::TextureUsage::TextureBinding; |
| texDesc.size = {1, 1, 1}; |
| texDesc.format = wgpu::TextureFormat::RGBA8Unorm; |
| wgpu::Texture sampledTex = device.CreateTexture(&texDesc); |
| |
| utils::ComboRenderPassDescriptor renderPass({sampledTex.CreateView()}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Attachments must have the same size |
| TEST_F(RenderPassDescriptorValidationTest, SizeMustMatch) { |
| wgpu::TextureView color1x1A = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| wgpu::TextureView color1x1B = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| wgpu::TextureView color2x2 = Create2DAttachment(device, 2, 2, wgpu::TextureFormat::RGBA8Unorm); |
| |
| wgpu::TextureView depthStencil1x1 = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); |
| wgpu::TextureView depthStencil2x2 = |
| Create2DAttachment(device, 2, 2, wgpu::TextureFormat::Depth24PlusStencil8); |
| |
| // Control case: all the same size (1x1) |
| { |
| utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil1x1); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // One of the color attachments has a different size |
| { |
| utils::ComboRenderPassDescriptor renderPass({color1x1A, color2x2}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // The depth stencil attachment has a different size |
| { |
| utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil2x2); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Attachments formats must match whether they are used for color or depth-stencil |
| TEST_F(RenderPassDescriptorValidationTest, FormatMismatch) { |
| wgpu::TextureView color = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| wgpu::TextureView depthStencil = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); |
| |
| // Using depth-stencil for color |
| { |
| utils::ComboRenderPassDescriptor renderPass({depthStencil}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using color for depth-stencil |
| { |
| utils::ComboRenderPassDescriptor renderPass({}, color); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Depth and stencil storeOps can be different |
| TEST_F(RenderPassDescriptorValidationTest, DepthStencilStoreOpMismatch) { |
| constexpr uint32_t kArrayLayers = 1; |
| constexpr uint32_t kLevelCount = 1; |
| constexpr uint32_t kSize = 32; |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; |
| |
| wgpu::Texture colorTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers, kLevelCount); |
| wgpu::Texture depthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, |
| kArrayLayers, kLevelCount); |
| |
| wgpu::TextureViewDescriptor descriptor; |
| descriptor.dimension = wgpu::TextureViewDimension::e2D; |
| descriptor.baseArrayLayer = 0; |
| descriptor.arrayLayerCount = kArrayLayers; |
| descriptor.baseMipLevel = 0; |
| descriptor.mipLevelCount = kLevelCount; |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| |
| // Base case: StoreOps match so render pass is a success |
| { |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Base case: StoreOps match so render pass is a success |
| { |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Discard; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Discard; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // StoreOps mismatch still is a success |
| { |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Discard; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Currently only texture views with arrayLayerCount == 1 are allowed to be color and depth |
| // stencil attachments |
| TEST_F(RenderPassDescriptorValidationTest, TextureViewLayerCountForColorAndDepthStencil) { |
| constexpr uint32_t kLevelCount = 1; |
| constexpr uint32_t kSize = 32; |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; |
| |
| constexpr uint32_t kArrayLayers = 10; |
| |
| wgpu::Texture colorTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers, kLevelCount); |
| wgpu::Texture depthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, |
| kArrayLayers, kLevelCount); |
| |
| wgpu::TextureViewDescriptor baseDescriptor; |
| baseDescriptor.dimension = wgpu::TextureViewDimension::e2DArray; |
| baseDescriptor.baseArrayLayer = 0; |
| baseDescriptor.arrayLayerCount = kArrayLayers; |
| baseDescriptor.baseMipLevel = 0; |
| baseDescriptor.mipLevelCount = kLevelCount; |
| |
| // Using 2D array texture view with arrayLayerCount > 1 is not allowed for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.arrayLayerCount = 5; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using 2D array texture view with arrayLayerCount > 1 is not allowed for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.arrayLayerCount = 5; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using 2D array texture view that covers the first layer of the texture is OK for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.baseArrayLayer = 0; |
| descriptor.arrayLayerCount = 1; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D array texture view that covers the first layer is OK for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.baseArrayLayer = 0; |
| descriptor.arrayLayerCount = 1; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D array texture view that covers the last layer is OK for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.baseArrayLayer = kArrayLayers - 1; |
| descriptor.arrayLayerCount = 1; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D array texture view that covers the last layer is OK for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.baseArrayLayer = kArrayLayers - 1; |
| descriptor.arrayLayerCount = 1; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Check that depthSlice must be set correctly for 3D color attachments and must not be set for |
| // non-3D color attachments. |
| TEST_F(RenderPassDescriptorValidationTest, TextureViewDepthSliceForColor) { |
| constexpr uint32_t kSize = 8; |
| constexpr uint32_t kDepthOrArrayLayers = 4; |
| constexpr uint32_t kMipLevelCounts = 4; |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| |
| wgpu::Texture colorTexture3D = |
| CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize, |
| kDepthOrArrayLayers, kMipLevelCounts); |
| |
| wgpu::TextureView colorView2D = Create2DAttachment(device, kSize, kSize, kColorFormat); |
| |
| wgpu::TextureViewDescriptor baseDescriptor; |
| baseDescriptor.dimension = wgpu::TextureViewDimension::e3D; |
| baseDescriptor.baseArrayLayer = 0; |
| baseDescriptor.arrayLayerCount = 1; |
| baseDescriptor.baseMipLevel = 0; |
| baseDescriptor.mipLevelCount = 1; |
| |
| // Control case: It's valid if depthSlice is set within the depth range of a 3D color |
| // attachment. |
| { |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| utils::ComboRenderPassDescriptor renderPass({view}); |
| renderPass.cColorAttachments[0].depthSlice = kDepthOrArrayLayers - 1; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It's invalid if depthSlice is not set for a 3D color attachment. |
| { |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| utils::ComboRenderPassDescriptor renderPass({view}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It's invalid if depthSlice is out of the depth range of a 3D color attachment. |
| { |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| utils::ComboRenderPassDescriptor renderPass({view}); |
| renderPass.cColorAttachments[0].depthSlice = kDepthOrArrayLayers; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It's invalid if depthSlice is out of the depth range of a 3D color attachment with non-zero |
| // mip level. |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.baseMipLevel = 2; |
| wgpu::TextureView view = colorTexture3D.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({view}); |
| renderPass.cColorAttachments[0].depthSlice = kDepthOrArrayLayers >> 2; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Control case: It's valid if depthSlice is unset for a non-3D color attachment. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView2D}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It's invalid if depthSlice is set for a non-3D color attachment. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView2D}); |
| renderPass.cColorAttachments[0].depthSlice = 0; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Check that the depth slices of a 3D color attachment cannot overlap in same render pass. |
| TEST_F(RenderPassDescriptorValidationTest, TextureViewDepthSliceOverlaps) { |
| constexpr uint32_t kSize = 8; |
| constexpr uint32_t kDepthOrArrayLayers = 2; |
| constexpr uint32_t kMipLevelCounts = 2; |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| |
| wgpu::Texture colorTexture3D = |
| CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize, |
| kDepthOrArrayLayers, kMipLevelCounts); |
| |
| wgpu::TextureViewDescriptor baseDescriptor; |
| baseDescriptor.dimension = wgpu::TextureViewDimension::e3D; |
| baseDescriptor.baseArrayLayer = 0; |
| baseDescriptor.arrayLayerCount = 1; |
| baseDescriptor.baseMipLevel = 0; |
| baseDescriptor.mipLevelCount = 1; |
| |
| // Control case: It's valid if different depth slices of a texture are set in a render pass. |
| { |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| utils::ComboRenderPassDescriptor renderPass({view, view}); |
| renderPass.cColorAttachments[0].depthSlice = 0; |
| renderPass.cColorAttachments[1].depthSlice = 1; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It's valid if same depth slice of different mip levels from a texture with size [1, 1, n] is |
| // set in a render pass. |
| { |
| wgpu::Texture texture = CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, 1, |
| 1, kDepthOrArrayLayers, kMipLevelCounts); |
| wgpu::TextureView view = texture.CreateView(&baseDescriptor); |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.baseMipLevel = 1; |
| wgpu::TextureView view2 = texture.CreateView(&descriptor); |
| |
| utils::ComboRenderPassDescriptor renderPass({view, view2}); |
| renderPass.cColorAttachments[0].depthSlice = 0; |
| renderPass.cColorAttachments[1].depthSlice = 0; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It's valid if same depth slice of different textures is set in a render pass. |
| { |
| wgpu::Texture otherColorTexture3D = |
| CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize, |
| kDepthOrArrayLayers, kMipLevelCounts); |
| |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| wgpu::TextureView view2 = otherColorTexture3D.CreateView(&baseDescriptor); |
| |
| utils::ComboRenderPassDescriptor renderPass({view, view2}); |
| renderPass.cColorAttachments[0].depthSlice = 0; |
| renderPass.cColorAttachments[1].depthSlice = 0; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It's invalid if same depth slice of a texture is set twice in a render pass. |
| { |
| wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor); |
| utils::ComboRenderPassDescriptor renderPass({view, view}); |
| renderPass.cColorAttachments[0].depthSlice = 0; |
| renderPass.cColorAttachments[1].depthSlice = 0; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Check that the render pass depth attachment must have the RenderAttachment usage. |
| TEST_F(RenderPassDescriptorValidationTest, DepthAttachmentInvalidUsage) { |
| // Control case: using a texture with RenderAttachment is valid. |
| { |
| wgpu::TextureView renderView = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth32Float); |
| utils::ComboRenderPassDescriptor renderPass({}, renderView); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Error case: using a texture with Sampled is invalid. |
| { |
| wgpu::TextureDescriptor texDesc; |
| texDesc.usage = wgpu::TextureUsage::TextureBinding; |
| texDesc.size = {1, 1, 1}; |
| texDesc.format = wgpu::TextureFormat::Depth32Float; |
| wgpu::Texture sampledTex = device.CreateTexture(&texDesc); |
| wgpu::TextureView sampledView = sampledTex.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass({}, sampledView); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Only 2D texture views with mipLevelCount == 1 are allowed to be color attachments |
| TEST_F(RenderPassDescriptorValidationTest, TextureViewLevelCountForColorAndDepthStencil) { |
| constexpr uint32_t kArrayLayers = 1; |
| constexpr uint32_t kSize = 32; |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; |
| |
| constexpr uint32_t kLevelCount = 4; |
| |
| wgpu::Texture colorTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers, kLevelCount); |
| wgpu::Texture depthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, |
| kArrayLayers, kLevelCount); |
| |
| wgpu::TextureViewDescriptor baseDescriptor; |
| baseDescriptor.dimension = wgpu::TextureViewDimension::e2D; |
| baseDescriptor.baseArrayLayer = 0; |
| baseDescriptor.arrayLayerCount = kArrayLayers; |
| baseDescriptor.baseMipLevel = 0; |
| baseDescriptor.mipLevelCount = kLevelCount; |
| |
| // Using 2D texture view with mipLevelCount > 1 is not allowed for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.mipLevelCount = 2; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using 2D texture view with mipLevelCount > 1 is not allowed for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.mipLevelCount = 2; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using 2D texture view that covers the first level of the texture is OK for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.baseMipLevel = 0; |
| descriptor.mipLevelCount = 1; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D texture view that covers the first level is OK for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.baseMipLevel = 0; |
| descriptor.mipLevelCount = 1; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D texture view that covers the last level is OK for color |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kColorFormat; |
| descriptor.baseMipLevel = kLevelCount - 1; |
| descriptor.mipLevelCount = 1; |
| |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using 2D texture view that covers the last level is OK for depth stencil |
| { |
| wgpu::TextureViewDescriptor descriptor = baseDescriptor; |
| descriptor.format = kDepthStencilFormat; |
| descriptor.baseMipLevel = kLevelCount - 1; |
| descriptor.mipLevelCount = 1; |
| |
| wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // It is not allowed to set resolve target when the color attachment is non-multisampled. |
| TEST_F(RenderPassDescriptorValidationTest, NonMultisampledColorWithResolveTarget) { |
| static constexpr uint32_t kArrayLayers = 1; |
| static constexpr uint32_t kLevelCount = 1; |
| static constexpr uint32_t kSize = 32; |
| static constexpr uint32_t kSampleCount = 1; |
| static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| |
| wgpu::Texture colorTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, kSampleCount); |
| wgpu::Texture resolveTargetTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, kSampleCount); |
| wgpu::TextureView colorTextureView = colorTexture.CreateView(); |
| wgpu::TextureView resolveTargetTextureView = resolveTargetTexture.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTargetTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // drawCount must not exceed maxDrawCount |
| TEST_F(RenderPassDescriptorValidationTest, MaxDrawCount) { |
| constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| constexpr uint64_t kMaxDrawCount = 16; |
| |
| wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( |
| @vertex fn main() -> @builtin(position) vec4f { |
| return vec4f(0.0, 0.0, 0.0, 1.0); |
| })"); |
| |
| wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"( |
| @fragment fn main() -> @location(0) vec4f { |
| return vec4f(0.0, 1.0, 0.0, 1.0); |
| })"); |
| |
| utils::ComboRenderPipelineDescriptor pipelineDescriptor; |
| pipelineDescriptor.vertex.module = vsModule; |
| pipelineDescriptor.cFragment.module = fsModule; |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); |
| |
| wgpu::TextureDescriptor colorTextureDescriptor; |
| colorTextureDescriptor.size = {1, 1}; |
| colorTextureDescriptor.format = kColorFormat; |
| colorTextureDescriptor.usage = wgpu::TextureUsage::RenderAttachment; |
| wgpu::Texture colorTexture = device.CreateTexture(&colorTextureDescriptor); |
| |
| utils::ComboRenderBundleEncoderDescriptor bundleEncoderDescriptor; |
| bundleEncoderDescriptor.colorFormatCount = 1; |
| bundleEncoderDescriptor.cColorFormats[0] = kColorFormat; |
| |
| wgpu::Buffer indexBuffer = |
| utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, {0, 1, 2}); |
| wgpu::Buffer indirectBuffer = |
| utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Indirect, {3, 1, 0, 0}); |
| wgpu::Buffer indexedIndirectBuffer = |
| utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Indirect, {3, 1, 0, 0, 0}); |
| |
| wgpu::RenderPassDescriptorMaxDrawCount maxDrawCount; |
| maxDrawCount.maxDrawCount = kMaxDrawCount; |
| |
| // Valid. drawCount is less than the default maxDrawCount. |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.Draw(3); |
| } |
| |
| renderPass.End(); |
| encoder.Finish(); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| renderPass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndexed(3); |
| } |
| |
| renderPass.End(); |
| encoder.Finish(); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndirect(indirectBuffer, 0); |
| } |
| |
| renderPass.End(); |
| encoder.Finish(); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| renderPass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndexedIndirect(indexedIndirectBuffer, 0); |
| } |
| |
| renderPass.End(); |
| encoder.Finish(); |
| } |
| |
| { |
| wgpu::RenderBundleEncoder renderBundleEncoder = |
| device.CreateRenderBundleEncoder(&bundleEncoderDescriptor); |
| renderBundleEncoder.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderBundleEncoder.Draw(3); |
| } |
| |
| wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); |
| |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.ExecuteBundles(1, &renderBundle); |
| renderPass.End(); |
| encoder.Finish(); |
| } |
| |
| // Invalid. drawCount counts up with draw calls and |
| // it is greater than maxDrawCount. |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| renderPassDescriptor.nextInChain = &maxDrawCount; |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.Draw(3); |
| } |
| |
| renderPass.End(); |
| ASSERT_DEVICE_ERROR(encoder.Finish()); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| renderPassDescriptor.nextInChain = &maxDrawCount; |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| renderPass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndexed(3); |
| } |
| |
| renderPass.End(); |
| ASSERT_DEVICE_ERROR(encoder.Finish()); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| renderPassDescriptor.nextInChain = &maxDrawCount; |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndirect(indirectBuffer, 0); |
| } |
| |
| renderPass.End(); |
| ASSERT_DEVICE_ERROR(encoder.Finish()); |
| } |
| |
| { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| renderPassDescriptor.nextInChain = &maxDrawCount; |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.SetPipeline(pipeline); |
| renderPass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderPass.DrawIndexedIndirect(indexedIndirectBuffer, 0); |
| } |
| |
| renderPass.End(); |
| ASSERT_DEVICE_ERROR(encoder.Finish()); |
| } |
| |
| { |
| wgpu::RenderBundleEncoder renderBundleEncoder = |
| device.CreateRenderBundleEncoder(&bundleEncoderDescriptor); |
| renderBundleEncoder.SetPipeline(pipeline); |
| |
| for (uint64_t i = 0; i <= kMaxDrawCount; i++) { |
| renderBundleEncoder.Draw(3); |
| } |
| |
| wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); |
| |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); |
| renderPassDescriptor.nextInChain = &maxDrawCount; |
| wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); |
| renderPass.ExecuteBundles(1, &renderBundle); |
| renderPass.End(); |
| ASSERT_DEVICE_ERROR(encoder.Finish()); |
| } |
| } |
| |
| class MultisampledRenderPassDescriptorValidationTest : public RenderPassDescriptorValidationTest { |
| public: |
| utils::ComboRenderPassDescriptor CreateMultisampledRenderPass() { |
| return utils::ComboRenderPassDescriptor({CreateMultisampledColorTextureView()}); |
| } |
| |
| wgpu::TextureView CreateMultisampledColorTextureView() { |
| return CreateColorTextureView(kSampleCount); |
| } |
| |
| wgpu::TextureView CreateNonMultisampledColorTextureView() { return CreateColorTextureView(1); } |
| |
| static constexpr uint32_t kArrayLayers = 1; |
| static constexpr uint32_t kLevelCount = 1; |
| static constexpr uint32_t kSize = 32; |
| static constexpr uint32_t kSampleCount = 4; |
| static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; |
| |
| private: |
| wgpu::TextureView CreateColorTextureView(uint32_t sampleCount) { |
| wgpu::Texture colorTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, |
| kArrayLayers, kLevelCount, sampleCount); |
| |
| return colorTexture.CreateView(); |
| } |
| }; |
| |
| // Tests on the use of multisampled textures as color attachments |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorAttachments) { |
| wgpu::TextureView colorTextureView = CreateNonMultisampledColorTextureView(); |
| wgpu::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); |
| wgpu::TextureView multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| |
| // It is allowed to use a multisampled color attachment without setting resolve target. |
| { |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is not allowed to use multiple color attachments with different sample counts. |
| { |
| utils::ComboRenderPassDescriptor renderPass( |
| {multisampledColorTextureView, colorTextureView}); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // It is not allowed to use a multisampled resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledResolveTarget) { |
| wgpu::TextureView multisampledResolveTargetView = CreateMultisampledColorTextureView(); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = multisampledResolveTargetView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Test the view dimension of resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetDimension) { |
| wgpu::Texture resolveTexture2D = CreateTexture( |
| device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, kLevelCount); |
| |
| // It is allowed to use a 2d texture view as resolve target. |
| { |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTexture2D.CreateView(); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is allowed to use a 2d-array texture view as resolve target. |
| { |
| wgpu::TextureViewDescriptor viewDesc; |
| viewDesc.dimension = wgpu::TextureViewDimension::e2DArray; |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTexture2D.CreateView(&viewDesc); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is not allowed to use a 3d texture view as resolve target. |
| { |
| wgpu::Texture resolveTexture3D = |
| CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize, |
| kArrayLayers, kLevelCount); |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTexture3D.CreateView(); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // It is not allowed to use a resolve target with array layer count > 1. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetArrayLayerMoreThanOne) { |
| constexpr uint32_t kArrayLayers2 = 2; |
| wgpu::Texture resolveTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers2, kLevelCount); |
| wgpu::TextureViewDescriptor viewDesc; |
| viewDesc.dimension = wgpu::TextureViewDimension::e2DArray; |
| wgpu::TextureView resolveTextureView = resolveTexture.CreateView(&viewDesc); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It is not allowed to use a resolve target with mipmap level count > 1. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetMipmapLevelMoreThanOne) { |
| constexpr uint32_t kLevelCount2 = 2; |
| wgpu::Texture resolveTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers, kLevelCount2); |
| wgpu::TextureView resolveTextureView = resolveTexture.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It is not allowed to use a resolve target which is created from a texture whose usage does |
| // not include wgpu::TextureUsage::RenderAttachment. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetUsageNoRenderAttachment) { |
| constexpr wgpu::TextureUsage kUsage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; |
| wgpu::Texture nonColorUsageResolveTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, 1, kUsage); |
| wgpu::TextureView nonColorUsageResolveTextureView = nonColorUsageResolveTexture.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = nonColorUsageResolveTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It is not allowed to use a resolve target which is in error state. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetInErrorState) { |
| wgpu::Texture resolveTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize, kSize, kArrayLayers, kLevelCount); |
| wgpu::TextureViewDescriptor errorTextureView; |
| errorTextureView.dimension = wgpu::TextureViewDimension::e2D; |
| errorTextureView.format = kColorFormat; |
| errorTextureView.baseArrayLayer = kArrayLayers + 1; |
| ASSERT_DEVICE_ERROR(wgpu::TextureView errorResolveTarget = |
| resolveTexture.CreateView(&errorTextureView)); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = errorResolveTarget; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It is allowed to use a multisampled color attachment and a non-multisampled resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorWithResolveTarget) { |
| wgpu::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTargetTextureView; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is not allowed to use a resolve target in a format different from the color attachment. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetDifferentFormat) { |
| constexpr wgpu::TextureFormat kColorFormat2 = wgpu::TextureFormat::BGRA8Unorm; |
| wgpu::Texture resolveTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat2, |
| kSize, kSize, kArrayLayers, kLevelCount); |
| wgpu::TextureView resolveTextureView = resolveTexture.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests on the size of the resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, |
| ColorAttachmentResolveTargetDimensionMismatch) { |
| constexpr uint32_t kSize2 = kSize * 2; |
| wgpu::Texture resolveTexture = CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, |
| kSize2, kSize2, kArrayLayers, kLevelCount + 1); |
| |
| wgpu::TextureViewDescriptor textureViewDescriptor; |
| textureViewDescriptor.nextInChain = nullptr; |
| textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; |
| textureViewDescriptor.format = kColorFormat; |
| textureViewDescriptor.mipLevelCount = 1; |
| textureViewDescriptor.baseArrayLayer = 0; |
| textureViewDescriptor.arrayLayerCount = 1; |
| |
| { |
| wgpu::TextureViewDescriptor firstMipLevelDescriptor = textureViewDescriptor; |
| firstMipLevelDescriptor.baseMipLevel = 0; |
| |
| wgpu::TextureView resolveTextureView = resolveTexture.CreateView(&firstMipLevelDescriptor); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| wgpu::TextureViewDescriptor secondMipLevelDescriptor = textureViewDescriptor; |
| secondMipLevelDescriptor.baseMipLevel = 1; |
| |
| wgpu::TextureView resolveTextureView = resolveTexture.CreateView(&secondMipLevelDescriptor); |
| |
| utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Test the overlaps of multiple resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetUsedTwice) { |
| wgpu::TextureView resolveTextureView = CreateNonMultisampledColorTextureView(); |
| wgpu::TextureView colorTextureView1 = CreateMultisampledColorTextureView(); |
| wgpu::TextureView colorTextureView2 = CreateMultisampledColorTextureView(); |
| |
| // It is allowed to use different resolve targets in a render pass. |
| { |
| wgpu::TextureView anotherResolveTextureView = CreateNonMultisampledColorTextureView(); |
| utils::ComboRenderPassDescriptor renderPass = |
| utils::ComboRenderPassDescriptor({colorTextureView1, colorTextureView2}); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| renderPass.cColorAttachments[1].resolveTarget = anotherResolveTextureView; |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is not allowed to use a resolve target twice in a render pass. |
| { |
| utils::ComboRenderPassDescriptor renderPass = |
| utils::ComboRenderPassDescriptor({colorTextureView1, colorTextureView2}); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; |
| renderPass.cColorAttachments[1].resolveTarget = resolveTextureView; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| |
| // Tests the texture format of the resolve target must support being used as resolve target. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetFormat) { |
| for (wgpu::TextureFormat format : utils::kAllTextureFormats) { |
| if (!utils::TextureFormatSupportsMultisampling(device, format) || |
| utils::IsDepthOrStencilFormat(format)) { |
| continue; |
| } |
| |
| wgpu::Texture colorTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, format, kSize, kSize, kArrayLayers, |
| kLevelCount, kSampleCount); |
| wgpu::Texture resolveTarget = CreateTexture(device, wgpu::TextureDimension::e2D, format, |
| kSize, kSize, kArrayLayers, kLevelCount, 1); |
| |
| utils::ComboRenderPassDescriptor renderPass({colorTexture.CreateView()}); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget.CreateView(); |
| if (utils::TextureFormatSupportsResolveTarget(device, format)) { |
| AssertBeginRenderPassSuccess(&renderPass); |
| } else { |
| AssertBeginRenderPassError(&renderPass); |
| } |
| } |
| } |
| |
| // Tests on the sample count of depth stencil attachment. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, DepthStencilAttachmentSampleCount) { |
| constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; |
| wgpu::Texture multisampledDepthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, |
| kArrayLayers, kLevelCount, kSampleCount); |
| wgpu::TextureView multisampledDepthStencilTextureView = |
| multisampledDepthStencilTexture.CreateView(); |
| |
| // It is not allowed to use a depth stencil attachment whose sample count is different from |
| // the one of the color attachment. |
| { |
| wgpu::Texture depthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, |
| kArrayLayers, kLevelCount); |
| wgpu::TextureView depthStencilTextureView = depthStencilTexture.CreateView(); |
| |
| utils::ComboRenderPassDescriptor renderPass({CreateMultisampledColorTextureView()}, |
| depthStencilTextureView); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({CreateNonMultisampledColorTextureView()}, |
| multisampledDepthStencilTextureView); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // It is allowed to use a multisampled depth stencil attachment whose sample count is equal |
| // to the one of the color attachment. |
| { |
| utils::ComboRenderPassDescriptor renderPass({CreateMultisampledColorTextureView()}, |
| multisampledDepthStencilTextureView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // It is allowed to use a multisampled depth stencil attachment while there is no color |
| // attachment. |
| { |
| utils::ComboRenderPassDescriptor renderPass({}, multisampledDepthStencilTextureView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Creating a render pass with DawnRenderPassColorAttachmentRenderToSingleSampled chained struct |
| // without MSAARenderToSingleSampled feature enabled should result in error. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, |
| CreateMSAARenderToSingleSampledRenderPassWithoutFeatureEnabled) { |
| wgpu::TextureView colorTextureView = CreateNonMultisampledColorTextureView(); |
| |
| wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled renderToSingleSampledDesc; |
| renderToSingleSampledDesc.implicitSampleCount = 4; |
| utils::ComboRenderPassDescriptor renderPass({colorTextureView}); |
| renderPass.cColorAttachments[0].nextInChain = &renderToSingleSampledDesc; |
| |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("feature is not enabled")); |
| } |
| |
| // Creating a render pass with LoadOp::ExpandResolveTexture without DawnLoadResolveTexture feature |
| // enabled should result in error. |
| TEST_F(MultisampledRenderPassDescriptorValidationTest, LoadResolveTextureWithoutFeatureEnabled) { |
| auto multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| auto resolveTarget = CreateNonMultisampledColorTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledColorTextureView; |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("is not enabled")); |
| } |
| |
| // Tests that NaN cannot be accepted as a valid color or depth clear value and INFINITY is valid |
| // in both color and depth clear values. |
| TEST_F(RenderPassDescriptorValidationTest, UseNaNOrINFINITYAsColorOrDepthClearValue) { |
| wgpu::TextureView color = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| |
| // Tests that NaN cannot be used in clearColor. |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.r = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.g = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.b = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.a = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that INFINITY can be used in clearColor. |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.r = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.g = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.b = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearValue.a = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Tests that NaN cannot be used in depthClearValue. |
| { |
| wgpu::TextureView depth = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| utils::ComboRenderPassDescriptor renderPass({color}, depth); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that INFINITY cannot be used in depthClearValue. |
| { |
| wgpu::TextureView depth = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| utils::ComboRenderPassDescriptor renderPass({color}, depth); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = INFINITY; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // TODO(https://crbug.com/dawn/666): Add a test case for clearStencil for stencilOnly |
| // once stencil8 is supported. |
| } |
| |
| // Tests that depth clear values mut be between 0 and 1, inclusive. |
| TEST_F(RenderPassDescriptorValidationTest, ValidateDepthClearValueRange) { |
| wgpu::TextureView depth = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| |
| utils::ComboRenderPassDescriptor renderPass({}, depth); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| // 0, 1, and any value in between are be valid. |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0.1; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0.5; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0.82; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 1; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| // Values less than 0 or greater than 1 are invalid. |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = -1; |
| AssertBeginRenderPassError(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 2; |
| AssertBeginRenderPassError(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = -0.001; |
| AssertBeginRenderPassError(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 1.001; |
| AssertBeginRenderPassError(&renderPass); |
| |
| // Clear values are not validated if the depthLoadOp is Load. |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = -1; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 2; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = -0.001; |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = 1.001; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Tests that default depthClearValue is required if attachment has a depth aspect and depthLoadOp |
| // is clear. |
| TEST_F(RenderPassDescriptorValidationTest, DefaultDepthClearValue) { |
| wgpu::TextureView depthView = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| wgpu::TextureView stencilView = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Stencil8); |
| |
| wgpu::RenderPassDepthStencilAttachment depthStencilAttachment; |
| |
| wgpu::RenderPassDescriptor renderPassDescriptor; |
| renderPassDescriptor.colorAttachmentCount = 0; |
| renderPassDescriptor.colorAttachments = nullptr; |
| renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment; |
| |
| // Default depthClearValue should be accepted if attachment doesn't have |
| // a depth aspect. |
| depthStencilAttachment.view = stencilView; |
| depthStencilAttachment.stencilLoadOp = wgpu::LoadOp::Load; |
| depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Store; |
| AssertBeginRenderPassSuccess(&renderPassDescriptor); |
| |
| // Default depthClearValue should be accepted if depthLoadOp is not clear. |
| depthStencilAttachment.view = depthView; |
| depthStencilAttachment.stencilLoadOp = wgpu::LoadOp::Undefined; |
| depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Undefined; |
| depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Load; |
| depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store; |
| AssertBeginRenderPassSuccess(&renderPassDescriptor); |
| |
| // Default depthClearValue should fail the validation |
| // if attachment has a depth aspect and depthLoadOp is clear. |
| depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Clear; |
| AssertBeginRenderPassError(&renderPassDescriptor); |
| |
| // The validation should pass if valid depthClearValue is provided. |
| depthStencilAttachment.depthClearValue = 0.0f; |
| AssertBeginRenderPassSuccess(&renderPassDescriptor); |
| } |
| |
| // Check the validation rules around depth/stencilReadOnly |
| TEST_F(RenderPassDescriptorValidationTest, ValidateDepthStencilReadOnly) { |
| wgpu::TextureView colorView = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| wgpu::TextureView depthStencilView = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); |
| wgpu::TextureView depthStencilViewNoStencil = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| wgpu::TextureView stencilView = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Stencil8); |
| |
| using Aspect = wgpu::TextureAspect; |
| struct TestSpec { |
| wgpu::TextureFormat format; |
| Aspect formatAspects; |
| Aspect testAspect; |
| }; |
| |
| TestSpec specs[] = { |
| {wgpu::TextureFormat::Depth24PlusStencil8, Aspect::All, Aspect::StencilOnly}, |
| {wgpu::TextureFormat::Depth24PlusStencil8, Aspect::All, Aspect::DepthOnly}, |
| {wgpu::TextureFormat::Depth24Plus, Aspect::DepthOnly, Aspect::DepthOnly}, |
| {wgpu::TextureFormat::Stencil8, Aspect::All, Aspect::StencilOnly}, |
| }; |
| for (const auto& spec : specs) { |
| wgpu::TextureView depthStencil = Create2DAttachment(device, 1, 1, spec.format); |
| utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); |
| |
| Aspect testAspect = spec.testAspect; |
| Aspect otherAspect = |
| testAspect == Aspect::DepthOnly ? Aspect::StencilOnly : Aspect::DepthOnly; |
| |
| auto Set = [&](Aspect aspect, wgpu::LoadOp loadOp, wgpu::StoreOp storeOp, bool readonly) { |
| if (aspect == Aspect::DepthOnly) { |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = loadOp; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = storeOp; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = readonly; |
| } else { |
| DAWN_ASSERT(aspect == Aspect::StencilOnly); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = loadOp; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = storeOp; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = readonly; |
| } |
| }; |
| |
| // Tests that a read-only pass with depth/stencilReadOnly both set to true succeeds. |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| // Tests that readOnly with LoadOp not undefined is invalid. |
| Set(testAspect, wgpu::LoadOp::Clear, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| Set(testAspect, wgpu::LoadOp::Load, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| // Tests that readOnly with StoreOp not undefined is invalid. |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Store, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Discard, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| // Test for the aspect's not present in the format, if applicable. |
| if (testAspect != spec.formatAspects) { |
| // Tests that readOnly with LoadOp not undefined is invalid even if the aspect is not in |
| // the format. |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Clear, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Load, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| // Tests that readOnly with StoreOp not undefined is invalid even if the aspect is not |
| // in the format. |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Store, true); |
| AssertBeginRenderPassError(&renderPass); |
| |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Discard, true); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Test that it is allowed to set only one of the aspects readonly. |
| Set(testAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| Set(otherAspect, wgpu::LoadOp::Load, wgpu::StoreOp::Store, false); |
| AssertBeginRenderPassSuccess(&renderPass); |
| |
| Set(testAspect, wgpu::LoadOp::Load, wgpu::StoreOp::Store, false); |
| Set(otherAspect, wgpu::LoadOp::Undefined, wgpu::StoreOp::Undefined, true); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Check that the depth stencil attachment must use all aspects. |
| TEST_F(RenderPassDescriptorValidationTest, ValidateDepthStencilAllAspects) { |
| wgpu::TextureDescriptor texDesc; |
| texDesc.usage = wgpu::TextureUsage::RenderAttachment; |
| texDesc.size = {1, 1, 1}; |
| |
| wgpu::TextureViewDescriptor viewDesc; |
| viewDesc.baseMipLevel = 0; |
| viewDesc.mipLevelCount = 1; |
| viewDesc.baseArrayLayer = 0; |
| viewDesc.arrayLayerCount = 1; |
| |
| // Using all aspects of a depth+stencil texture is allowed. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.format = wgpu::TextureFormat::Undefined; |
| viewDesc.aspect = wgpu::TextureAspect::All; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using only depth of a depth+stencil texture is an error, case without format |
| // reinterpretation. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.format = wgpu::TextureFormat::Undefined; |
| viewDesc.aspect = wgpu::TextureAspect::DepthOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using only depth of a depth+stencil texture is an error, case with format reinterpretation. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.format = wgpu::TextureFormat::Depth24Plus; |
| viewDesc.aspect = wgpu::TextureAspect::DepthOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using only stencil of a depth+stencil texture is an error, case without format |
| // reinterpration. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.format = wgpu::TextureFormat::Undefined; |
| viewDesc.aspect = wgpu::TextureAspect::StencilOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using only stencil of a depth+stencil texture is an error, case with format reinterpretation. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.format = wgpu::TextureFormat::Stencil8; |
| viewDesc.aspect = wgpu::TextureAspect::StencilOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using DepthOnly of a depth only texture is allowed. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24Plus; |
| viewDesc.format = wgpu::TextureFormat::Undefined; |
| viewDesc.aspect = wgpu::TextureAspect::DepthOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using StencilOnly of a stencil only texture is allowed. |
| { |
| texDesc.format = wgpu::TextureFormat::Stencil8; |
| viewDesc.format = wgpu::TextureFormat::Undefined; |
| viewDesc.aspect = wgpu::TextureAspect::StencilOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined; |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| // Tests validation for per-pixel accounting for render targets. The tests currently assume that the |
| // default maxColorAttachmentBytesPerSample limit of 32 is used. |
| TEST_F(RenderPassDescriptorValidationTest, RenderPassColorAttachmentBytesPerSample) { |
| struct TestCase { |
| std::vector<wgpu::TextureFormat> formats; |
| bool success; |
| }; |
| static std::vector<TestCase> kTestCases = { |
| // Simple 1 format cases. |
| |
| // R8Unorm take 1 byte and are aligned to 1 byte so we can have 8 (max). |
| {{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Unorm, |
| wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Unorm, |
| wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Unorm}, |
| true}, |
| // RGBA8Uint takes 4 bytes and are aligned to 1 byte so we can have 8 (max). |
| {{wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Uint, |
| wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Uint, |
| wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Uint, |
| wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Uint}, |
| true}, |
| // RGBA8Unorm takes 8 bytes (special case) and are aligned to 1 byte so only 4 allowed. |
| {{wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Unorm, |
| wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Unorm}, |
| true}, |
| {{wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Unorm, |
| wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Unorm, |
| wgpu::TextureFormat::RGBA8Unorm}, |
| false}, |
| // RGBA32Float takes 16 bytes and are aligned to 4 bytes so only 2 are allowed. |
| {{wgpu::TextureFormat::RGBA32Float, wgpu::TextureFormat::RGBA32Float}, true}, |
| {{wgpu::TextureFormat::RGBA32Float, wgpu::TextureFormat::RGBA32Float, |
| wgpu::TextureFormat::RGBA32Float}, |
| false}, |
| |
| // Different format alignment cases. |
| |
| // Alignment causes the first 1 byte R8Unorm to become 4 bytes. So even though 1+4+8+16+1 < |
| // 32, the 4 byte alignment requirement of R32Float makes the first R8Unorm become 4 and |
| // 4+4+8+16+1 > 32. Re-ordering this so the R8Unorm's are at the end, however is allowed: |
| // 4+8+16+1+1 < 32. |
| {{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R32Float, |
| wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA32Float, |
| wgpu::TextureFormat::R8Unorm}, |
| false}, |
| {{wgpu::TextureFormat::R32Float, wgpu::TextureFormat::RGBA8Unorm, |
| wgpu::TextureFormat::RGBA32Float, wgpu::TextureFormat::R8Unorm, |
| wgpu::TextureFormat::R8Unorm}, |
| true}, |
| }; |
| |
| for (const TestCase& testCase : kTestCases) { |
| std::vector<wgpu::TextureView> colorAttachmentInfo; |
| for (size_t i = 0; i < testCase.formats.size(); i++) { |
| colorAttachmentInfo.push_back(Create2DAttachment(device, 1, 1, testCase.formats.at(i))); |
| } |
| utils::ComboRenderPassDescriptor descriptor(colorAttachmentInfo); |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&descriptor); |
| renderPassEncoder.End(); |
| if (testCase.success) { |
| commandEncoder.Finish(); |
| } else { |
| ASSERT_DEVICE_ERROR(commandEncoder.Finish()); |
| } |
| } |
| } |
| |
| // TODO(cwallez@chromium.org): Constraints on attachment aliasing? |
| |
| class MSAARenderToSingleSampledRenderPassDescriptorValidationTest |
| : public MultisampledRenderPassDescriptorValidationTest { |
| protected: |
| void SetUp() override { |
| MultisampledRenderPassDescriptorValidationTest::SetUp(); |
| |
| mRenderToSingleSampledDesc.implicitSampleCount = kSampleCount; |
| } |
| |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { |
| return {wgpu::FeatureName::MSAARenderToSingleSampled}; |
| } |
| |
| utils::ComboRenderPassDescriptor CreateMultisampledRenderToSingleSampledRenderPass( |
| wgpu::TextureView colorAttachment, |
| wgpu::TextureView depthStencilAttachment = nullptr) { |
| utils::ComboRenderPassDescriptor renderPass({colorAttachment}, depthStencilAttachment); |
| renderPass.cColorAttachments[0].nextInChain = &mRenderToSingleSampledDesc; |
| |
| return renderPass; |
| } |
| |
| // Create a view for a texture that can be used with a MSAA render to single sampled render |
| // pass. |
| wgpu::TextureView CreateCompatibleColorTextureView() { |
| wgpu::Texture colorTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, /*sampleCount=*/1, |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding); |
| |
| return colorTexture.CreateView(); |
| } |
| |
| private: |
| wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled mRenderToSingleSampledDesc; |
| }; |
| |
| // Test that using a valid color attachment with enabled MSAARenderToSingleSampled doesn't raise any |
| // error. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, ColorAttachmentValid) { |
| // Create a texture with sample count = 1. |
| auto textureView = CreateCompatibleColorTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass(textureView); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // When MSAARenderToSingleSampled is enabled for a color attachment, it must be created with |
| // TextureBinding usage. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, ColorAttachmentInvalidUsage) { |
| // Create a texture with sample count = 1. |
| auto texture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, /*sampleCount=*/1, wgpu::TextureUsage::RenderAttachment); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass(texture.CreateView()); |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("usage")); |
| } |
| |
| // When MSAARenderToSingleSampled is enabled for a color attachment, there must be no explicit |
| // resolve target specified for it. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, ErrorSettingResolveTarget) { |
| // Create a texture with sample count = 1. |
| auto textureView1 = CreateCompatibleColorTextureView(); |
| auto textureView2 = CreateCompatibleColorTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass(textureView1); |
| renderPass.cColorAttachments[0].resolveTarget = textureView2; |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("as a resolve target")); |
| } |
| |
| // Using unsupported implicit sample count in DawnRenderPassColorAttachmentRenderToSingleSampled |
| // chained struct should result in error. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, UnsupportedSampleCountError) { |
| // Create a texture with sample count = 1. |
| auto textureView = CreateCompatibleColorTextureView(); |
| |
| // Create a render pass with implicit sample count = 3. Which is not supported. |
| wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled renderToSingleSampledDesc; |
| renderToSingleSampledDesc.implicitSampleCount = 3; |
| utils::ComboRenderPassDescriptor renderPass({textureView}); |
| renderPass.cColorAttachments[0].nextInChain = &renderToSingleSampledDesc; |
| |
| AssertBeginRenderPassError(&renderPass, |
| testing::HasSubstr("implicit sample count (3) is not supported")); |
| |
| // Create a render pass with implicit sample count = 1. Which is also not supported. |
| renderToSingleSampledDesc.implicitSampleCount = 1; |
| renderPass.cColorAttachments[0].nextInChain = &renderToSingleSampledDesc; |
| |
| AssertBeginRenderPassError(&renderPass, |
| testing::HasSubstr("implicit sample count (1) is not supported")); |
| } |
| |
| // When MSAARenderToSingleSampled is enabled in a color attachment, there should be an error if a |
| // color attachment's format doesn't support resolve. Example, RGBA8Sint format. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, UnresolvableColorFormatError) { |
| // Create a texture with sample count = 1. |
| auto texture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::RGBA8Sint, kSize, |
| kSize, kArrayLayers, kLevelCount, /*sampleCount=*/1, |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass(texture.CreateView()); |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("does not support resolve")); |
| } |
| |
| // Depth stencil attachment's sample count must match the one specified in color attachment's |
| // implicitSampleCount. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, DepthStencilSampleCountValid) { |
| // Create a color texture with sample count = 1. |
| auto colorTextureView = CreateCompatibleColorTextureView(); |
| |
| // Create depth stencil texture with sample count = 4. |
| auto depthStencilTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::Depth24PlusStencil8, kSize, kSize, |
| 1, 1, /*sampleCount=*/kSampleCount, wgpu::TextureUsage::RenderAttachment); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass( |
| colorTextureView, depthStencilTexture.CreateView()); |
| |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Using depth stencil attachment with sample count not matching the implicit sample count will |
| // result in error. |
| TEST_F(MSAARenderToSingleSampledRenderPassDescriptorValidationTest, |
| DepthStencilSampleCountNotMatchImplicitSampleCount) { |
| // Create a color texture with sample count = 1. |
| auto colorTextureView = CreateCompatibleColorTextureView(); |
| |
| // Create depth stencil texture with sample count = 1. Which doesn't match implicitSampleCount=4 |
| // specified in mRenderToSingleSampledDesc. |
| auto depthStencilTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::Depth24PlusStencil8, |
| kSize, kSize, 1, 1, /*sampleCount=*/1, wgpu::TextureUsage::RenderAttachment); |
| |
| auto renderPass = CreateMultisampledRenderToSingleSampledRenderPass( |
| colorTextureView, depthStencilTexture.CreateView()); |
| |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("does not match the sample count")); |
| } |
| |
| class DawnLoadResolveTextureValidationTest : public MultisampledRenderPassDescriptorValidationTest { |
| protected: |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { |
| return {wgpu::FeatureName::DawnLoadResolveTexture, wgpu::FeatureName::TransientAttachments}; |
| } |
| |
| // Create a view for a resolve texture that can be used with LoadOp::ExpandResolveTexture. |
| wgpu::TextureView CreateCompatibleResolveTextureView() { |
| wgpu::Texture colorTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, /*sampleCount=*/1, |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding); |
| |
| return colorTexture.CreateView(); |
| } |
| }; |
| |
| // Test that using a valid resolve texture with LoadOp::ExpandResolveTexture doesn't raise |
| // any error. |
| TEST_F(DawnLoadResolveTextureValidationTest, ResolveTargetValid) { |
| auto multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| |
| // Create a resolve texture with sample count = 1. |
| auto resolveTarget = CreateCompatibleResolveTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledColorTextureView; |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Test that LoadOp::ExpandResolveTexture can be used even if the MSAA attachment is transient. |
| TEST_F(DawnLoadResolveTextureValidationTest, CompatibleWithTransientMSAATexture) { |
| auto multisampledColorTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, kLevelCount, |
| /*sampleCount=*/kSampleCount, |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment); |
| auto multisampledColorTextureView = multisampledColorTexture.CreateView(); |
| |
| // Create a resolve texture with sample count = 1. |
| auto resolveTarget = CreateCompatibleResolveTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledColorTextureView; |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // When LoadOp::ExpandResolveTexture is used, a resolve texture view must be set. |
| TEST_F(DawnLoadResolveTextureValidationTest, ResolveTargetMustBeSet) { |
| auto multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| |
| // Error case: texture view is set but resolveTarget is not set. |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledColorTextureView; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("resolve target")); |
| } |
| |
| // When LoadOp::ExpandResolveTexture is used, the attached texture view must be multisampled. |
| TEST_F(DawnLoadResolveTextureValidationTest, ResolveTargetInvalidSampleCount) { |
| // Create a texture with sample count = 1. |
| auto colorTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, /*sampleCount=*/1, wgpu::TextureUsage::RenderAttachment); |
| |
| // Create a resolve texture with sample count = 1. |
| auto resolveTarget = CreateCompatibleResolveTextureView(); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = colorTexture.CreateView(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("sample count")); |
| } |
| |
| // When LoadOp::ExpandResolveTexture is used, the resolve texture must be created with |
| // TextureBinding usage. |
| TEST_F(DawnLoadResolveTextureValidationTest, ResolveTargetInvalidUsage) { |
| auto multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| |
| // Create a texture with sample count = 1. |
| auto resolveTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, |
| kLevelCount, /*sampleCount=*/1, wgpu::TextureUsage::RenderAttachment); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledColorTextureView; |
| renderPass.cColorAttachments[0].resolveTarget = resolveTexture.CreateView(); |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| AssertBeginRenderPassError(&renderPass, testing::HasSubstr("usage")); |
| } |
| |
| // When LoadOp::ExpandResolveTexture is enabled in a color attachment, there should be an error if a |
| // color attachment's format doesn't support resolve. Example, RGBA8Sint format. |
| TEST_F(DawnLoadResolveTextureValidationTest, UnresolvableColorFormatError) { |
| auto multisampledTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::RGBA8Sint, kSize, kSize, |
| kArrayLayers, kLevelCount, /*sampleCount=*/4, wgpu::TextureUsage::RenderAttachment); |
| |
| // Create a texture with sample count = 1. |
| auto resolveTexture = |
| CreateTexture(device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::RGBA8Sint, kSize, |
| kSize, kArrayLayers, kLevelCount, /*sampleCount=*/1, |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding); |
| |
| auto renderPass = CreateMultisampledRenderPass(); |
| renderPass.cColorAttachments[0].view = multisampledTexture.CreateView(); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTexture.CreateView(); |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| AssertBeginRenderPassError(&renderPass, |
| testing::HasSubstr("does not support being used as resolve target")); |
| } |
| |
| // The LoadOp is NOT currently supported on depth/stencil attachment. |
| TEST_F(DawnLoadResolveTextureValidationTest, OnlyLoadingColorAttachmentIsSupported) { |
| auto multisampledColorTextureView = CreateMultisampledColorTextureView(); |
| auto resolveTarget = CreateCompatibleResolveTextureView(); |
| |
| // Error case: Use ExpandResolveTexture on depth/stencil attachment. |
| { |
| // Create depth stencil texture with sample count = 4. |
| auto depthStencilTexture = CreateTexture( |
| device, wgpu::TextureDimension::e2D, wgpu::TextureFormat::Depth24PlusStencil8, kSize, |
| kSize, 1, 1, /*sampleCount=*/kSampleCount, wgpu::TextureUsage::RenderAttachment); |
| |
| auto renderPass = utils::ComboRenderPassDescriptor({multisampledColorTextureView}, |
| depthStencilTexture.CreateView()); |
| renderPass.cColorAttachments[0].resolveTarget = resolveTarget; |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; |
| |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = |
| wgpu::LoadOp::ExpandResolveTexture; |
| |
| AssertBeginRenderPassError(&renderPass, |
| testing::HasSubstr("not supported on depth/stencil attachment")); |
| } |
| } |
| |
| } // anonymous namespace |
| } // namespace dawn |