| // Copyright 2018 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 "common/Constants.h" |
| |
| #include "utils/WGPUHelpers.h" |
| |
| #include <cmath> |
| |
| 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()); |
| } |
| |
| private: |
| wgpu::CommandEncoder TestBeginRenderPass(const wgpu::RenderPassDescriptor* descriptor) { |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(descriptor); |
| renderPassEncoder.EndPass(); |
| 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); |
| } |
| } |
| |
| // Test OOB color attachment indices are handled |
| TEST_F(RenderPassDescriptorValidationTest, ColorAttachmentOutOfBounds) { |
| std::array<wgpu::RenderPassColorAttachmentDescriptor, kMaxColorAttachments + 1> |
| colorAttachments; |
| for (uint32_t i = 0; i < colorAttachments.size(); i++) { |
| colorAttachments[i].view = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); |
| colorAttachments[i].resolveTarget = nullptr; |
| colorAttachments[i].clearColor = {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); |
| } |
| } |
| |
| // 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::Sampled; |
| 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 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); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Error case: using a texture with Sampled is invalid. |
| { |
| wgpu::TextureDescriptor texDesc; |
| texDesc.usage = wgpu::TextureUsage::Sampled; |
| 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); |
| 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); |
| } |
| |
| 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); |
| } |
| |
| // 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, |
| ColorAttachmentResolveTargetCompatibility) { |
| 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); |
| } |
| } |
| |
| // 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); |
| } |
| } |
| |
| // 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].clearColor.r = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.g = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.b = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.a = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that INFINITY can be used in clearColor. |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.r = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.g = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.b = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| { |
| utils::ComboRenderPassDescriptor renderPass({color}, nullptr); |
| renderPass.cColorAttachments[0].clearColor.a = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Tests that NaN cannot be used in clearDepth. |
| { |
| wgpu::TextureView depth = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| utils::ComboRenderPassDescriptor renderPass({color}, depth); |
| renderPass.cDepthStencilAttachmentInfo.clearDepth = NAN; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that INFINITY can be used in clearDepth. |
| { |
| wgpu::TextureView depth = |
| Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); |
| utils::ComboRenderPassDescriptor renderPass({color}, depth); |
| renderPass.cDepthStencilAttachmentInfo.clearDepth = INFINITY; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| } |
| |
| 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); |
| |
| // Tests that a read-only pass with depthReadOnly set to true succeeds. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values passes when |
| // there is no stencil component in the format. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilViewNoStencil); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false; |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values fails when |
| // both depth and stencil components exist. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that a pass with loadOp set to clear and readOnly set to true fails. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Tests that a pass with storeOp set to clear and readOnly set to true fails. |
| { |
| utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Discard; |
| renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; |
| renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; |
| renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Discard; |
| renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; |
| AssertBeginRenderPassError(&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.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. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.aspect = wgpu::TextureAspect::DepthOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using only stencil of a depth+stencil texture is an error. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| viewDesc.aspect = wgpu::TextureAspect::StencilOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| AssertBeginRenderPassError(&renderPass); |
| } |
| |
| // Using DepthOnly of a depth only texture us allowed. |
| { |
| texDesc.format = wgpu::TextureFormat::Depth24Plus; |
| viewDesc.aspect = wgpu::TextureAspect::DepthOnly; |
| |
| wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc); |
| utils::ComboRenderPassDescriptor renderPass({}, view); |
| AssertBeginRenderPassSuccess(&renderPass); |
| } |
| |
| // TODO(https://crbug.com/dawn/666): Add a test case for stencil-only on stencil8 once this |
| // format is supported. |
| } |
| |
| // TODO(cwallez@chromium.org): Constraints on attachment aliasing? |
| |
| } // anonymous namespace |