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