1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "utils/ComboRenderBundleEncoderDescriptor.h" 16 #include "utils/ComboRenderPipelineDescriptor.h" 17 #include "utils/WGPUHelpers.h" 18 19 #include "tests/unittests/validation/ValidationTest.h" 20 21 constexpr static uint32_t kSize = 4; 22 // Note that format Depth24PlusStencil8 has both depth and stencil aspects, so parameters 23 // depthReadOnly and stencilReadOnly should be the same in render pass and render bundle. 24 wgpu::TextureFormat kFormat = wgpu::TextureFormat::Depth24PlusStencil8; 25 26 namespace { 27 28 class RenderPipelineAndPassCompatibilityTests : public ValidationTest { 29 public: CreatePipeline(wgpu::TextureFormat format,bool enableDepthWrite,bool enableStencilWrite)30 wgpu::RenderPipeline CreatePipeline(wgpu::TextureFormat format, 31 bool enableDepthWrite, 32 bool enableStencilWrite) { 33 // Create a NoOp pipeline 34 utils::ComboRenderPipelineDescriptor pipelineDescriptor; 35 pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"( 36 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> { 37 return vec4<f32>(); 38 })"); 39 pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"( 40 [[stage(fragment)]] fn main() { 41 })"); 42 pipelineDescriptor.cFragment.targets = nullptr; 43 pipelineDescriptor.cFragment.targetCount = 0; 44 45 // Enable depth/stencil write if needed 46 wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(format); 47 if (enableDepthWrite) { 48 depthStencil->depthWriteEnabled = true; 49 } 50 if (enableStencilWrite) { 51 depthStencil->stencilFront.failOp = wgpu::StencilOperation::Replace; 52 } 53 return device.CreateRenderPipeline(&pipelineDescriptor); 54 } 55 CreateRenderPassDescriptor(wgpu::TextureFormat format,bool depthReadOnly,bool stencilReadOnly)56 utils::ComboRenderPassDescriptor CreateRenderPassDescriptor(wgpu::TextureFormat format, 57 bool depthReadOnly, 58 bool stencilReadOnly) { 59 wgpu::TextureDescriptor textureDescriptor = {}; 60 textureDescriptor.size = {kSize, kSize, 1}; 61 textureDescriptor.format = format; 62 textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment; 63 wgpu::Texture depthStencilTexture = device.CreateTexture(&textureDescriptor); 64 65 utils::ComboRenderPassDescriptor passDescriptor({}, depthStencilTexture.CreateView()); 66 if (depthReadOnly) { 67 passDescriptor.cDepthStencilAttachmentInfo.depthReadOnly = true; 68 passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; 69 passDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; 70 } 71 72 if (stencilReadOnly) { 73 passDescriptor.cDepthStencilAttachmentInfo.stencilReadOnly = true; 74 passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; 75 passDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; 76 } 77 78 return passDescriptor; 79 } 80 }; 81 82 // Test depthWrite/stencilWrite in DepthStencilState in render pipeline vs 83 // depthReadOnly/stencilReadOnly in DepthStencilAttachment in render pass. TEST_F(RenderPipelineAndPassCompatibilityTests,WriteAndReadOnlyConflictForDepthStencil)84 TEST_F(RenderPipelineAndPassCompatibilityTests, WriteAndReadOnlyConflictForDepthStencil) { 85 for (bool depthStencilReadOnlyInPass : {true, false}) { 86 for (bool depthWriteInPipeline : {true, false}) { 87 for (bool stencilWriteInPipeline : {true, false}) { 88 wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); 89 utils::ComboRenderPassDescriptor passDescriptor = CreateRenderPassDescriptor( 90 kFormat, depthStencilReadOnlyInPass, depthStencilReadOnlyInPass); 91 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor); 92 wgpu::RenderPipeline pipeline = 93 CreatePipeline(kFormat, depthWriteInPipeline, stencilWriteInPipeline); 94 pass.SetPipeline(pipeline); 95 pass.Draw(3); 96 pass.EndPass(); 97 if (depthStencilReadOnlyInPass && 98 (depthWriteInPipeline || stencilWriteInPipeline)) { 99 ASSERT_DEVICE_ERROR(encoder.Finish()); 100 } else { 101 encoder.Finish(); 102 } 103 } 104 } 105 } 106 } 107 108 // Test depthWrite/stencilWrite in DepthStencilState in render pipeline vs 109 // depthReadOnly/stencilReadOnly in RenderBundleEncoderDescriptor in render bundle. TEST_F(RenderPipelineAndPassCompatibilityTests,WriteAndReadOnlyConflictForDepthStencilBetweenPipelineAndBundle)110 TEST_F(RenderPipelineAndPassCompatibilityTests, 111 WriteAndReadOnlyConflictForDepthStencilBetweenPipelineAndBundle) { 112 for (bool depthStencilReadOnlyInBundle : {true, false}) { 113 utils::ComboRenderBundleEncoderDescriptor desc = {}; 114 desc.depthStencilFormat = kFormat; 115 desc.depthReadOnly = depthStencilReadOnlyInBundle; 116 desc.stencilReadOnly = depthStencilReadOnlyInBundle; 117 118 for (bool depthWriteInPipeline : {true, false}) { 119 for (bool stencilWriteInPipeline : {true, false}) { 120 wgpu::RenderBundleEncoder renderBundleEncoder = 121 device.CreateRenderBundleEncoder(&desc); 122 wgpu::RenderPipeline pipeline = 123 CreatePipeline(kFormat, depthWriteInPipeline, stencilWriteInPipeline); 124 renderBundleEncoder.SetPipeline(pipeline); 125 renderBundleEncoder.Draw(3); 126 if (depthStencilReadOnlyInBundle && 127 (depthWriteInPipeline || stencilWriteInPipeline)) { 128 ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); 129 } else { 130 renderBundleEncoder.Finish(); 131 } 132 } 133 } 134 } 135 } 136 137 // Test depthReadOnly/stencilReadOnly in RenderBundleEncoderDescriptor in render bundle vs 138 // depthReadOnly/stencilReadOnly in DepthStencilAttachment in render pass. TEST_F(RenderPipelineAndPassCompatibilityTests,WriteAndReadOnlyConflictForDepthStencilBetweenBundleAndPass)139 TEST_F(RenderPipelineAndPassCompatibilityTests, 140 WriteAndReadOnlyConflictForDepthStencilBetweenBundleAndPass) { 141 for (bool depthStencilReadOnlyInPass : {true, false}) { 142 for (bool depthStencilReadOnlyInBundle : {true, false}) { 143 for (bool emptyBundle : {true, false}) { 144 // Create render bundle, with or without a pipeline 145 utils::ComboRenderBundleEncoderDescriptor desc = {}; 146 desc.depthStencilFormat = kFormat; 147 desc.depthReadOnly = depthStencilReadOnlyInBundle; 148 desc.stencilReadOnly = depthStencilReadOnlyInBundle; 149 wgpu::RenderBundleEncoder renderBundleEncoder = 150 device.CreateRenderBundleEncoder(&desc); 151 if (!emptyBundle) { 152 wgpu::RenderPipeline pipeline = CreatePipeline( 153 kFormat, !depthStencilReadOnlyInBundle, !depthStencilReadOnlyInBundle); 154 renderBundleEncoder.SetPipeline(pipeline); 155 renderBundleEncoder.Draw(3); 156 } 157 wgpu::RenderBundle bundle = renderBundleEncoder.Finish(); 158 159 // Create render pass and call ExecuteBundles() 160 wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); 161 utils::ComboRenderPassDescriptor passDescriptor = CreateRenderPassDescriptor( 162 kFormat, depthStencilReadOnlyInPass, depthStencilReadOnlyInPass); 163 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDescriptor); 164 pass.ExecuteBundles(1, &bundle); 165 pass.EndPass(); 166 if (!depthStencilReadOnlyInPass || depthStencilReadOnlyInBundle) { 167 encoder.Finish(); 168 } else { 169 ASSERT_DEVICE_ERROR(encoder.Finish()); 170 } 171 } 172 } 173 } 174 } 175 176 // TODO(dawn:485): add more tests. For example: 177 // - depth/stencil attachment should be designated if depth/stencil test is enabled. 178 // - pipeline and pass compatibility tests for color attachment(s). 179 // - pipeline and pass compatibility tests for compute. 180 181 } // anonymous namespace 182