// 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 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) { wgpu::TextureView color0 = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); wgpu::TextureView color1 = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); wgpu::TextureView color2 = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); wgpu::TextureView color3 = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); // For setting the color attachment, control case { utils::ComboRenderPassDescriptor renderPass({color0, color1, color2, color3}); AssertBeginRenderPassSuccess(&renderPass); } // For setting the color attachment, OOB { // We cannot use utils::ComboRenderPassDescriptor here because it only supports at most // kMaxColorAttachments(4) color attachments. std::array colorAttachments; colorAttachments[0].view = color0; colorAttachments[0].resolveTarget = nullptr; colorAttachments[0].clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; colorAttachments[0].loadOp = wgpu::LoadOp::Clear; colorAttachments[0].storeOp = wgpu::StoreOp::Store; colorAttachments[1] = colorAttachments[0]; colorAttachments[1].view = color1; colorAttachments[2] = colorAttachments[0]; colorAttachments[2].view = color2; colorAttachments[3] = colorAttachments[0]; colorAttachments[3].view = color3; colorAttachments[4] = colorAttachments[0]; colorAttachments[4].view = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); wgpu::RenderPassDescriptor renderPass; renderPass.colorAttachmentCount = 5; 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::TextureView resolveTextureView = resolveTexture.CreateView(); 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