• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "tests/DawnTest.h"
16 
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/WGPUHelpers.h"
19 
20 constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
21 constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
22 constexpr uint32_t kRTWidth = 4;
23 constexpr uint32_t kRTHeight = 1;
24 
25 class VertexOnlyRenderPipelineTest : public DawnTest {
26   protected:
SetUp()27     void SetUp() override {
28         DawnTest::SetUp();
29 
30         vertexBuffer =
31             utils::CreateBufferFromData<float>(device, wgpu::BufferUsage::Vertex,
32                                                {// The middle back line
33                                                 -0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 1.0f,
34 
35                                                 // The right front line
36                                                 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
37 
38                                                 // The whole in-between line
39                                                 -1.0f, 0.0f, 0.5f, 1.0f, 1.0f, 0.0f, 0.5f, 1.0f});
40 
41         // Create a color texture as render target
42         {
43             wgpu::TextureDescriptor descriptor;
44             descriptor.dimension = wgpu::TextureDimension::e2D;
45             descriptor.size = {kRTWidth, kRTHeight};
46             descriptor.format = kColorFormat;
47             descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
48             renderTargetColor = device.CreateTexture(&descriptor);
49         }
50 
51         // Create a DepthStencilView for vertex-only pipeline to write and full pipeline to read
52         {
53             wgpu::TextureDescriptor descriptor;
54             descriptor.dimension = wgpu::TextureDimension::e2D;
55             descriptor.size = {kRTWidth, kRTHeight};
56             descriptor.format = kDepthStencilFormat;
57             descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
58             depthStencilTexture = device.CreateTexture(&descriptor);
59             depthStencilView = depthStencilTexture.CreateView();
60         }
61 
62         // The vertex-only render pass to modify the depth and stencil
63         renderPassDescNoColor = utils::ComboRenderPassDescriptor({}, depthStencilView);
64         renderPassDescNoColor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
65         renderPassDescNoColor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
66 
67         // The complete render pass to read the depth and stencil and draw to color attachment
68         renderPassDescWithColor =
69             utils::ComboRenderPassDescriptor({renderTargetColor.CreateView()}, depthStencilView);
70         renderPassDescWithColor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
71         renderPassDescWithColor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
72 
73         // Create a vertex-only render pipeline that only modify the depth in DepthStencilView, and
74         // ignore the stencil component
75         depthPipelineNoFragment =
76             CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Keep,
77                                  wgpu::CompareFunction::Always, true, false);
78         depthPipelineWithFragment =
79             CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Keep,
80                                  wgpu::CompareFunction::Always, true, true);
81 
82         // Create a vertex-only render pipeline that only modify the stencil in DepthStencilView,
83         // and ignore the depth component
84         stencilPipelineNoFragment =
85             CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Replace,
86                                  wgpu::CompareFunction::Always, false, false);
87         stencilPipelineWithFragment =
88             CreateRenderPipeline(wgpu::CompareFunction::Always, wgpu::StencilOperation::Replace,
89                                  wgpu::CompareFunction::Always, false, true);
90 
91         // Create a complete render pipeline that do both depth and stencil tests, and draw to color
92         // attachment
93         fullPipeline =
94             CreateRenderPipeline(wgpu::CompareFunction::Equal, wgpu::StencilOperation::Keep,
95                                  wgpu::CompareFunction::GreaterEqual, false, true);
96     }
97 
CreateRenderPipeline(wgpu::CompareFunction stencilCompare=wgpu::CompareFunction::Always,wgpu::StencilOperation stencilPassOp=wgpu::StencilOperation::Keep,wgpu::CompareFunction depthCompare=wgpu::CompareFunction::Always,bool writeDepth=false,bool useFragment=true)98     wgpu::RenderPipeline CreateRenderPipeline(
99         wgpu::CompareFunction stencilCompare = wgpu::CompareFunction::Always,
100         wgpu::StencilOperation stencilPassOp = wgpu::StencilOperation::Keep,
101         wgpu::CompareFunction depthCompare = wgpu::CompareFunction::Always,
102         bool writeDepth = false,
103         bool useFragment = true) {
104         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
105             [[stage(vertex)]]
106             fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
107                 return pos;
108             })");
109 
110         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
111             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
112                 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
113             })");
114 
115         utils::ComboRenderPipelineDescriptor descriptor;
116         descriptor.primitive.topology = wgpu::PrimitiveTopology::LineList;
117 
118         descriptor.vertex.module = vsModule;
119         descriptor.vertex.bufferCount = 1;
120         descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
121         descriptor.cBuffers[0].attributeCount = 1;
122         descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
123 
124         descriptor.cFragment.module = fsModule;
125         descriptor.cTargets[0].format = kColorFormat;
126 
127         wgpu::DepthStencilState* depthStencil = descriptor.EnableDepthStencil(kDepthStencilFormat);
128 
129         depthStencil->stencilFront.compare = stencilCompare;
130         depthStencil->stencilBack.compare = stencilCompare;
131         depthStencil->stencilFront.passOp = stencilPassOp;
132         depthStencil->stencilBack.passOp = stencilPassOp;
133         depthStencil->depthWriteEnabled = writeDepth;
134         depthStencil->depthCompare = depthCompare;
135 
136         if (!useFragment) {
137             descriptor.fragment = nullptr;
138         }
139 
140         return device.CreateRenderPipeline(&descriptor);
141     }
142 
ClearAttachment(const wgpu::CommandEncoder & encoder)143     void ClearAttachment(const wgpu::CommandEncoder& encoder) {
144         utils::ComboRenderPassDescriptor clearPass =
145             utils::ComboRenderPassDescriptor({renderTargetColor.CreateView()}, depthStencilView);
146         clearPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
147         clearPass.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
148         clearPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
149         clearPass.cDepthStencilAttachmentInfo.clearStencil = 0x0;
150         for (auto& t : clearPass.cColorAttachments) {
151             t.loadOp = wgpu::LoadOp::Clear;
152             t.clearColor = {0.0, 0.0, 0.0, 0.0};
153         }
154 
155         auto pass = encoder.BeginRenderPass(&clearPass);
156         pass.EndPass();
157     }
158 
159     // Render resource
160     wgpu::Buffer vertexBuffer;
161     // Render target
162     wgpu::Texture depthStencilTexture;
163     wgpu::TextureView depthStencilView;
164     wgpu::Texture renderTargetColor;
165     // Render result
166     const RGBA8 filled = RGBA8(0, 255, 0, 255);
167     const RGBA8 notFilled = RGBA8(0, 0, 0, 0);
168     // Render pass
169     utils::ComboRenderPassDescriptor renderPassDescNoColor{};
170     utils::ComboRenderPassDescriptor renderPassDescWithColor{};
171     // Render pipeline
172     wgpu::RenderPipeline stencilPipelineNoFragment;
173     wgpu::RenderPipeline stencilPipelineWithFragment;
174     wgpu::RenderPipeline depthPipelineNoFragment;
175     wgpu::RenderPipeline depthPipelineWithFragment;
176     wgpu::RenderPipeline fullPipeline;
177 };
178 
179 // Test that a vertex-only render pipeline modify the stencil attachment as same as a complete
180 // render pipeline do.
TEST_P(VertexOnlyRenderPipelineTest,Stencil)181 TEST_P(VertexOnlyRenderPipelineTest, Stencil) {
182     auto doStencilTest = [&](const wgpu::RenderPassDescriptor* renderPass,
183                              const wgpu::RenderPipeline& pipeline,
184                              const RGBA8& colorExpect) -> void {
185         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
186 
187         ClearAttachment(encoder);
188 
189         {
190             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass);
191             pass.SetPipeline(pipeline);
192             // Set the stencil reference to a arbitrary value
193             pass.SetStencilReference(0x42);
194             pass.SetVertexBuffer(0, vertexBuffer);
195             // Draw the whole line
196             pass.Draw(2, 1, 4, 0);
197             pass.EndPass();
198         }
199 
200         wgpu::CommandBuffer commands = encoder.Finish();
201         queue.Submit(1, &commands);
202 
203         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 0, 0);
204         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 1, 0);
205         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 2, 0);
206         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 3, 0);
207 
208         // Test that the stencil is set to the chosen value
209         ExpectAttachmentStencilTestData(depthStencilTexture, kDepthStencilFormat, 4, 1, 0, 0, 0x42);
210     };
211 
212     doStencilTest(&renderPassDescWithColor, stencilPipelineWithFragment, filled);
213     doStencilTest(&renderPassDescNoColor, stencilPipelineNoFragment, notFilled);
214 }
215 
216 // Test that a vertex-only render pipeline modify the depth attachment as same as a complete render
217 // pipeline do.
TEST_P(VertexOnlyRenderPipelineTest,Depth)218 TEST_P(VertexOnlyRenderPipelineTest, Depth) {
219     auto doStencilTest = [&](const wgpu::RenderPassDescriptor* renderPass,
220                              const wgpu::RenderPipeline& pipeline,
221                              const RGBA8& colorExpect) -> void {
222         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
223 
224         ClearAttachment(encoder);
225 
226         {
227             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(renderPass);
228             pass.SetPipeline(pipeline);
229             pass.SetStencilReference(0x0);
230             pass.SetVertexBuffer(0, vertexBuffer);
231             // Draw the whole line
232             pass.Draw(2, 1, 4, 0);
233             pass.EndPass();
234         }
235 
236         wgpu::CommandBuffer commands = encoder.Finish();
237         queue.Submit(1, &commands);
238 
239         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 0, 0);
240         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 1, 0);
241         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 2, 0);
242         EXPECT_PIXEL_RGBA8_EQ(colorExpect, renderTargetColor, 3, 0);
243 
244         // Test that the stencil is set to the chosen value
245         uint8_t expectedStencil = 0;
246         ExpectAttachmentDepthStencilTestData(depthStencilTexture, kDepthStencilFormat, 4, 1, 0, 0,
247                                              {0.5, 0.5, 0.5, 0.5}, &expectedStencil);
248     };
249 
250     doStencilTest(&renderPassDescWithColor, depthPipelineWithFragment, filled);
251     doStencilTest(&renderPassDescNoColor, depthPipelineNoFragment, notFilled);
252 }
253 
254 // Test that vertex-only render pipelines and complete render pipelines cooperate correctly in a
255 // single encoder, each in a render pass
256 // In this test we first draw with a vertex-only pipeline to set up stencil in a region, than draw
257 // with another vertex-only pipeline to modify depth in another region, and finally draw with a
258 // complete pipeline with depth and stencil tests enabled. We check the color result of the final
259 // draw, and make sure that it correctly use the stencil and depth result set in previous
260 // vertex-only pipelines.
TEST_P(VertexOnlyRenderPipelineTest,MultiplePass)261 TEST_P(VertexOnlyRenderPipelineTest, MultiplePass) {
262     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
263 
264     ClearAttachment(encoder);
265 
266     // Use the stencil pipeline to set the stencil on the middle
267     {
268         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescNoColor);
269         pass.SetStencilReference(0x1);
270         pass.SetPipeline(stencilPipelineNoFragment);
271         pass.SetVertexBuffer(0, vertexBuffer);
272         // Draw the middle line
273         pass.Draw(2, 1, 0, 0);
274         pass.EndPass();
275     }
276 
277     // Use the depth pipeline to set the depth on the right
278     {
279         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescNoColor);
280         pass.SetStencilReference(0x0);
281         pass.SetPipeline(depthPipelineNoFragment);
282         pass.SetVertexBuffer(0, vertexBuffer);
283         // Draw the right line
284         pass.Draw(2, 1, 2, 0);
285         pass.EndPass();
286     }
287 
288     // Use the complete pipeline to draw with depth and stencil tests
289     {
290         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescWithColor);
291         pass.SetStencilReference(0x1);
292         pass.SetPipeline(fullPipeline);
293         pass.SetVertexBuffer(0, vertexBuffer);
294         // Draw the full line with depth and stencil tests
295         pass.Draw(2, 1, 4, 0);
296         pass.EndPass();
297     }
298 
299     wgpu::CommandBuffer commands = encoder.Finish();
300     queue.Submit(1, &commands);
301 
302     // Only the middle left pixel should pass both stencil and depth tests
303     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 0, 0);
304     EXPECT_PIXEL_RGBA8_EQ(filled, renderTargetColor, 1, 0);
305     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 2, 0);
306     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderTargetColor, 3, 0);
307 }
308 
309 DAWN_INSTANTIATE_TEST(VertexOnlyRenderPipelineTest,
310                       D3D12Backend(),
311                       D3D12Backend({"use_dummy_fragment_in_vertex_only_pipeline"}),
312                       MetalBackend(),
313                       MetalBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
314                       OpenGLBackend(),
315                       OpenGLBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
316                       OpenGLESBackend(),
317                       OpenGLESBackend({"use_dummy_fragment_in_vertex_only_pipeline"}),
318                       VulkanBackend(),
319                       VulkanBackend({"use_dummy_fragment_in_vertex_only_pipeline"}));
320