1 // Copyright 2020 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 "common/Assert.h"
16 #include "tests/DawnTest.h"
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/WGPUHelpers.h"
19
20 namespace {
21
22 constexpr wgpu::TextureFormat kDepthFormats[] = {
23 wgpu::TextureFormat::Depth32Float,
24 wgpu::TextureFormat::Depth24Plus,
25 wgpu::TextureFormat::Depth24PlusStencil8,
26 wgpu::TextureFormat::Depth16Unorm,
27 };
28
29 constexpr wgpu::TextureFormat kStencilFormats[] = {
30 wgpu::TextureFormat::Depth24PlusStencil8,
31 };
32
33 constexpr wgpu::CompareFunction kCompareFunctions[] = {
34 wgpu::CompareFunction::Never, wgpu::CompareFunction::Less,
35 wgpu::CompareFunction::LessEqual, wgpu::CompareFunction::Greater,
36 wgpu::CompareFunction::GreaterEqual, wgpu::CompareFunction::Equal,
37 wgpu::CompareFunction::NotEqual, wgpu::CompareFunction::Always,
38 };
39
40 // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
41 constexpr float kCompareRefs[] = {-0.1, 0.4, 1.2};
42
43 // Test 0, below the ref, equal to, above the ref, and 1.
44 const std::vector<float> kNormalizedTextureValues = {0.0, 0.3, 0.4, 0.5, 1.0};
45
46 // Test the limits, and some values in between.
47 const std::vector<uint32_t> kStencilValues = {0, 1, 38, 255};
48
49 } // anonymous namespace
50
51 class DepthStencilSamplingTest : public DawnTest {
52 protected:
53 enum class TestAspect {
54 Depth,
55 Stencil,
56 };
57
SetUp()58 void SetUp() override {
59 DawnTest::SetUp();
60
61 wgpu::BufferDescriptor uniformBufferDesc;
62 uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
63 uniformBufferDesc.size = sizeof(float);
64 mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
65 }
66
GenerateSamplingShader(const std::vector<TestAspect> & aspects,const std::vector<uint32_t> components,std::ostringstream & shaderSource,std::ostringstream & shaderBody)67 void GenerateSamplingShader(const std::vector<TestAspect>& aspects,
68 const std::vector<uint32_t> components,
69 std::ostringstream& shaderSource,
70 std::ostringstream& shaderBody) {
71 shaderSource << "type StencilValues = array<u32, " << components.size() << ">;\n";
72 shaderSource << R"(
73 [[block]] struct DepthResult {
74 value : f32;
75 };
76 [[block]] struct StencilResult {
77 values : StencilValues;
78 };)";
79 shaderSource << "\n";
80
81 uint32_t index = 0;
82 for (TestAspect aspect : aspects) {
83 switch (aspect) {
84 case TestAspect::Depth:
85 shaderSource << "[[group(0), binding(" << 2 * index << ")]] var tex" << index
86 << " : texture_depth_2d;\n";
87
88 shaderSource << "[[group(0), binding(" << 2 * index + 1
89 << ")]] var<storage, read_write> result" << index
90 << " : DepthResult;\n";
91
92 ASSERT(components.size() == 1 && components[0] == 0);
93 shaderBody << "\nresult" << index << ".value = textureLoad(tex" << index
94 << ", vec2<i32>(0, 0), 0);";
95 break;
96 case TestAspect::Stencil:
97 shaderSource << "[[group(0), binding(" << 2 * index << ")]] var tex" << index
98 << " : texture_2d<u32>;\n";
99
100 shaderSource << "[[group(0), binding(" << 2 * index + 1
101 << ")]] var<storage, read_write> result" << index
102 << " : StencilResult;\n";
103
104 shaderBody << "var texel = textureLoad(tex" << index
105 << ", vec2<i32>(0, 0), 0);";
106
107 for (uint32_t i = 0; i < components.size(); ++i) {
108 shaderBody << "\nresult" << index << ".values[" << i << "] = texel["
109 << components[i] << "];";
110 }
111 break;
112 }
113
114 index++;
115 }
116 }
117
CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,std::vector<uint32_t> components)118 wgpu::RenderPipeline CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,
119 std::vector<uint32_t> components) {
120 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
121 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
122 return vec4<f32>(0.0, 0.0, 0.0, 1.0);
123 })");
124
125 utils::ComboRenderPipelineDescriptor pipelineDescriptor;
126
127 std::ostringstream shaderSource;
128 std::ostringstream shaderOutputStruct;
129 std::ostringstream shaderBody;
130
131 GenerateSamplingShader(aspects, components, shaderSource, shaderBody);
132
133 shaderSource << "[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {\n";
134 shaderSource << shaderBody.str() << "return vec4<f32>();\n }";
135
136 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
137 pipelineDescriptor.vertex.module = vsModule;
138 pipelineDescriptor.cFragment.module = fsModule;
139 pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
140 pipelineDescriptor.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
141
142 return device.CreateRenderPipeline(&pipelineDescriptor);
143 }
144
CreateSamplingComputePipeline(std::vector<TestAspect> aspects,std::vector<uint32_t> components)145 wgpu::ComputePipeline CreateSamplingComputePipeline(std::vector<TestAspect> aspects,
146 std::vector<uint32_t> components) {
147 std::ostringstream shaderSource;
148 std::ostringstream shaderBody;
149 GenerateSamplingShader(aspects, components, shaderSource, shaderBody);
150
151 shaderSource << "[[stage(compute), workgroup_size(1)]] fn main() { " << shaderBody.str()
152 << "\n}";
153
154 wgpu::ShaderModule csModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
155
156 wgpu::ComputePipelineDescriptor pipelineDescriptor;
157 pipelineDescriptor.compute.module = csModule;
158 pipelineDescriptor.compute.entryPoint = "main";
159
160 return device.CreateComputePipeline(&pipelineDescriptor);
161 }
162
CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,uint32_t componentIndex)163 wgpu::RenderPipeline CreateSamplingRenderPipeline(std::vector<TestAspect> aspects,
164 uint32_t componentIndex) {
165 return CreateSamplingRenderPipeline(std::move(aspects),
166 std::vector<uint32_t>{componentIndex});
167 }
168
CreateSamplingComputePipeline(std::vector<TestAspect> aspects,uint32_t componentIndex)169 wgpu::ComputePipeline CreateSamplingComputePipeline(std::vector<TestAspect> aspects,
170 uint32_t componentIndex) {
171 return CreateSamplingComputePipeline(std::move(aspects),
172 std::vector<uint32_t>{componentIndex});
173 }
174
CreateComparisonRenderPipeline()175 wgpu::RenderPipeline CreateComparisonRenderPipeline() {
176 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
177 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
178 return vec4<f32>(0.0, 0.0, 0.0, 1.0);
179 })");
180
181 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
182 [[group(0), binding(0)]] var samp : sampler_comparison;
183 [[group(0), binding(1)]] var tex : texture_depth_2d;
184 [[block]] struct Uniforms {
185 compareRef : f32;
186 };
187 [[group(0), binding(2)]] var<uniform> uniforms : Uniforms;
188
189 [[stage(fragment)]] fn main() -> [[location(0)]] f32 {
190 return textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
191 })");
192
193 utils::ComboRenderPipelineDescriptor pipelineDescriptor;
194 pipelineDescriptor.vertex.module = vsModule;
195 pipelineDescriptor.cFragment.module = fsModule;
196 pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
197 pipelineDescriptor.cTargets[0].format = wgpu::TextureFormat::R32Float;
198
199 return device.CreateRenderPipeline(&pipelineDescriptor);
200 }
201
CreateComparisonComputePipeline()202 wgpu::ComputePipeline CreateComparisonComputePipeline() {
203 wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
204 [[group(0), binding(0)]] var samp : sampler_comparison;
205 [[group(0), binding(1)]] var tex : texture_depth_2d;
206 [[block]] struct Uniforms {
207 compareRef : f32;
208 };
209 [[group(0), binding(2)]] var<uniform> uniforms : Uniforms;
210
211 [[block]] struct SamplerResult {
212 value : f32;
213 };
214 [[group(0), binding(3)]] var<storage, read_write> samplerResult : SamplerResult;
215
216 [[stage(compute), workgroup_size(1)]] fn main() {
217 samplerResult.value = textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
218 })");
219
220 wgpu::ComputePipelineDescriptor pipelineDescriptor;
221 pipelineDescriptor.compute.module = csModule;
222 pipelineDescriptor.compute.entryPoint = "main";
223
224 return device.CreateComputePipeline(&pipelineDescriptor);
225 }
226
CreateInputTexture(wgpu::TextureFormat format)227 wgpu::Texture CreateInputTexture(wgpu::TextureFormat format) {
228 wgpu::TextureDescriptor inputTextureDesc;
229 inputTextureDesc.usage =
230 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
231 inputTextureDesc.size = {1, 1, 1};
232 inputTextureDesc.format = format;
233 return device.CreateTexture(&inputTextureDesc);
234 }
235
CreateOutputTexture(wgpu::TextureFormat format)236 wgpu::Texture CreateOutputTexture(wgpu::TextureFormat format) {
237 wgpu::TextureDescriptor outputTextureDesc;
238 outputTextureDesc.usage =
239 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
240 outputTextureDesc.size = {1, 1, 1};
241 outputTextureDesc.format = format;
242 return device.CreateTexture(&outputTextureDesc);
243 }
244
CreateOutputBuffer(uint32_t componentCount=1)245 wgpu::Buffer CreateOutputBuffer(uint32_t componentCount = 1) {
246 wgpu::BufferDescriptor outputBufferDesc;
247 outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
248 outputBufferDesc.size = sizeof(uint32_t) * componentCount;
249 return device.CreateBuffer(&outputBufferDesc);
250 }
251
UpdateInputDepth(wgpu::CommandEncoder commandEncoder,wgpu::Texture texture,float depthValue)252 void UpdateInputDepth(wgpu::CommandEncoder commandEncoder,
253 wgpu::Texture texture,
254 float depthValue) {
255 utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
256 passDescriptor.cDepthStencilAttachmentInfo.clearDepth = depthValue;
257
258 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
259 pass.EndPass();
260 }
261
UpdateInputStencil(wgpu::CommandEncoder commandEncoder,wgpu::Texture texture,uint8_t stencilValue)262 void UpdateInputStencil(wgpu::CommandEncoder commandEncoder,
263 wgpu::Texture texture,
264 uint8_t stencilValue) {
265 utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
266 passDescriptor.cDepthStencilAttachmentInfo.clearStencil = stencilValue;
267
268 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
269 pass.EndPass();
270 }
271
272 template <typename T, typename CheckBufferFn>
DoSamplingTestImpl(TestAspect aspect,wgpu::RenderPipeline pipeline,wgpu::TextureFormat format,std::vector<T> textureValues,uint32_t componentCount,CheckBufferFn CheckBuffer)273 void DoSamplingTestImpl(TestAspect aspect,
274 wgpu::RenderPipeline pipeline,
275 wgpu::TextureFormat format,
276 std::vector<T> textureValues,
277 uint32_t componentCount,
278 CheckBufferFn CheckBuffer) {
279 wgpu::Texture inputTexture = CreateInputTexture(format);
280 wgpu::TextureViewDescriptor inputViewDesc = {};
281 switch (aspect) {
282 case TestAspect::Depth:
283 inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
284 break;
285 case TestAspect::Stencil:
286 inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
287 break;
288 }
289
290 wgpu::Buffer outputBuffer = CreateOutputBuffer(componentCount);
291
292 wgpu::BindGroup bindGroup =
293 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
294 {{0, inputTexture.CreateView(&inputViewDesc)}, {1, outputBuffer}});
295
296 for (size_t i = 0; i < textureValues.size(); ++i) {
297 // Set the input depth texture to the provided texture value
298 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
299 switch (aspect) {
300 case TestAspect::Depth:
301 UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
302 break;
303 case TestAspect::Stencil:
304 UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
305 break;
306 }
307
308 // Render into the output texture
309 {
310 utils::BasicRenderPass renderPass =
311 utils::CreateBasicRenderPass(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm);
312 wgpu::RenderPassEncoder pass =
313 commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
314 pass.SetPipeline(pipeline);
315 pass.SetBindGroup(0, bindGroup);
316 pass.Draw(1);
317 pass.EndPass();
318 }
319
320 wgpu::CommandBuffer commands = commandEncoder.Finish();
321 queue.Submit(1, &commands);
322
323 CheckBuffer(textureValues[i], outputBuffer);
324 }
325 }
326
327 template <typename T, typename CheckBufferFn>
DoSamplingTestImpl(TestAspect aspect,wgpu::ComputePipeline pipeline,wgpu::TextureFormat format,std::vector<T> textureValues,uint32_t componentCount,CheckBufferFn CheckBuffer)328 void DoSamplingTestImpl(TestAspect aspect,
329 wgpu::ComputePipeline pipeline,
330 wgpu::TextureFormat format,
331 std::vector<T> textureValues,
332 uint32_t componentCount,
333 CheckBufferFn CheckBuffer) {
334 wgpu::Texture inputTexture = CreateInputTexture(format);
335 wgpu::TextureViewDescriptor inputViewDesc = {};
336 switch (aspect) {
337 case TestAspect::Depth:
338 inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
339 break;
340 case TestAspect::Stencil:
341 inputViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
342 break;
343 }
344
345 wgpu::Buffer outputBuffer = CreateOutputBuffer(componentCount);
346
347 wgpu::BindGroup bindGroup =
348 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
349 {{0, inputTexture.CreateView(&inputViewDesc)}, {1, outputBuffer}});
350
351 for (size_t i = 0; i < textureValues.size(); ++i) {
352 // Set the input depth texture to the provided texture value
353 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
354 switch (aspect) {
355 case TestAspect::Depth:
356 UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
357 break;
358 case TestAspect::Stencil:
359 UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
360 break;
361 }
362
363 // Sample into the output buffer
364 {
365 wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
366 pass.SetPipeline(pipeline);
367 pass.SetBindGroup(0, bindGroup);
368 pass.Dispatch(1);
369 pass.EndPass();
370 }
371
372 wgpu::CommandBuffer commands = commandEncoder.Finish();
373 queue.Submit(1, &commands);
374
375 CheckBuffer(textureValues[i], outputBuffer);
376 }
377 }
378
379 template <typename T>
DoSamplingTest(TestAspect aspect,wgpu::RenderPipeline pipeline,wgpu::TextureFormat format,std::vector<T> textureValues,T tolerance={})380 void DoSamplingTest(TestAspect aspect,
381 wgpu::RenderPipeline pipeline,
382 wgpu::TextureFormat format,
383 std::vector<T> textureValues,
384 T tolerance = {}) {
385 DoSamplingTestImpl(aspect, pipeline, format, textureValues, 1,
__anonf2bca5000202(T expected, wgpu::Buffer buffer) 386 [this, tolerance](T expected, wgpu::Buffer buffer) {
387 EXPECT_BUFFER(buffer, 0, sizeof(T),
388 new ::detail::ExpectEq<T>(expected, tolerance));
389 });
390 }
391
392 template <typename T>
DoSamplingTest(TestAspect aspect,wgpu::ComputePipeline pipeline,wgpu::TextureFormat format,std::vector<T> textureValues,T tolerance={})393 void DoSamplingTest(TestAspect aspect,
394 wgpu::ComputePipeline pipeline,
395 wgpu::TextureFormat format,
396 std::vector<T> textureValues,
397 T tolerance = {}) {
398 DoSamplingTestImpl(aspect, pipeline, format, textureValues, 1,
__anonf2bca5000302(T expected, wgpu::Buffer buffer) 399 [this, tolerance](T expected, wgpu::Buffer buffer) {
400 EXPECT_BUFFER(buffer, 0, sizeof(T),
401 new ::detail::ExpectEq<T>(expected, tolerance));
402 });
403 }
404
405 class ExtraStencilComponentsExpectation : public detail::Expectation {
406 using StencilData = std::array<uint32_t, 4>;
407
408 public:
ExtraStencilComponentsExpectation(uint32_t expected)409 ExtraStencilComponentsExpectation(uint32_t expected) : mExpected(expected) {
410 }
411
412 ~ExtraStencilComponentsExpectation() override = default;
413
Check(const void * rawData,size_t size)414 testing::AssertionResult Check(const void* rawData, size_t size) override {
415 ASSERT(size == sizeof(StencilData));
416 const uint32_t* data = static_cast<const uint32_t*>(rawData);
417
418 StencilData ssss = {mExpected, mExpected, mExpected, mExpected};
419 StencilData s001 = {mExpected, 0, 0, 1};
420
421 if (memcmp(data, ssss.data(), size) == 0 || memcmp(data, s001.data(), size) == 0) {
422 return testing::AssertionSuccess();
423 }
424
425 return testing::AssertionFailure() << "Expected stencil data to be "
426 << "(" << ssss[0] << ", " << ssss[1] << ", "
427 << ssss[2] << ", " << ssss[3] << ") or "
428 << "(" << s001[0] << ", " << s001[1] << ", "
429 << s001[2] << ", " << s001[3] << "). Got "
430 << "(" << data[0] << ", " << data[1] << ", "
431 << data[2] << ", " << data[3] << ").";
432 }
433
434 private:
435 uint32_t mExpected;
436 };
437
DoSamplingExtraStencilComponentsRenderTest(TestAspect aspect,wgpu::TextureFormat format,std::vector<uint8_t> textureValues)438 void DoSamplingExtraStencilComponentsRenderTest(TestAspect aspect,
439 wgpu::TextureFormat format,
440 std::vector<uint8_t> textureValues) {
441 DoSamplingTestImpl(aspect,
442 CreateSamplingRenderPipeline({TestAspect::Stencil}, {0, 1, 2, 3}),
443 format, textureValues, 4, [&](uint32_t expected, wgpu::Buffer buffer) {
444 EXPECT_BUFFER(buffer, 0, 4 * sizeof(uint32_t),
445 new ExtraStencilComponentsExpectation(expected));
446 });
447 }
448
DoSamplingExtraStencilComponentsComputeTest(TestAspect aspect,wgpu::TextureFormat format,std::vector<uint8_t> textureValues)449 void DoSamplingExtraStencilComponentsComputeTest(TestAspect aspect,
450 wgpu::TextureFormat format,
451 std::vector<uint8_t> textureValues) {
452 DoSamplingTestImpl(aspect,
453 CreateSamplingComputePipeline({TestAspect::Stencil}, {0, 1, 2, 3}),
454 format, textureValues, 4, [&](uint32_t expected, wgpu::Buffer buffer) {
455 EXPECT_BUFFER(buffer, 0, 4 * sizeof(uint32_t),
456 new ExtraStencilComponentsExpectation(expected));
457 });
458 }
459
CompareFunctionPasses(float compareRef,wgpu::CompareFunction compare,float textureValue)460 static bool CompareFunctionPasses(float compareRef,
461 wgpu::CompareFunction compare,
462 float textureValue) {
463 switch (compare) {
464 case wgpu::CompareFunction::Never:
465 return false;
466 case wgpu::CompareFunction::Less:
467 return compareRef < textureValue;
468 case wgpu::CompareFunction::LessEqual:
469 return compareRef <= textureValue;
470 case wgpu::CompareFunction::Greater:
471 return compareRef > textureValue;
472 case wgpu::CompareFunction::GreaterEqual:
473 return compareRef >= textureValue;
474 case wgpu::CompareFunction::Equal:
475 return compareRef == textureValue;
476 case wgpu::CompareFunction::NotEqual:
477 return compareRef != textureValue;
478 case wgpu::CompareFunction::Always:
479 return true;
480 default:
481 return false;
482 }
483 }
484
DoDepthCompareRefTest(wgpu::RenderPipeline pipeline,wgpu::TextureFormat format,float compareRef,wgpu::CompareFunction compare,std::vector<float> textureValues)485 void DoDepthCompareRefTest(wgpu::RenderPipeline pipeline,
486 wgpu::TextureFormat format,
487 float compareRef,
488 wgpu::CompareFunction compare,
489 std::vector<float> textureValues) {
490 queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
491
492 wgpu::SamplerDescriptor samplerDesc;
493 samplerDesc.compare = compare;
494 wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
495
496 wgpu::Texture inputTexture = CreateInputTexture(format);
497 wgpu::TextureViewDescriptor inputViewDesc = {};
498 inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
499
500 wgpu::BindGroup bindGroup =
501 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
502 {
503 {0, sampler},
504 {1, inputTexture.CreateView(&inputViewDesc)},
505 {2, mUniformBuffer},
506 });
507
508 wgpu::Texture outputTexture = CreateOutputTexture(wgpu::TextureFormat::R32Float);
509 for (float textureValue : textureValues) {
510 // Set the input depth texture to the provided texture value
511 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
512 UpdateInputDepth(commandEncoder, inputTexture, textureValue);
513
514 // Render into the output texture
515 {
516 utils::ComboRenderPassDescriptor passDescriptor({outputTexture.CreateView()});
517 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
518 pass.SetPipeline(pipeline);
519 pass.SetBindGroup(0, bindGroup);
520 pass.Draw(1);
521 pass.EndPass();
522 }
523
524 wgpu::CommandBuffer commands = commandEncoder.Finish();
525 queue.Submit(1, &commands);
526
527 EXPECT_TEXTURE_EQ(CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f,
528 outputTexture, {0, 0});
529 }
530 }
531
DoDepthCompareRefTest(wgpu::ComputePipeline pipeline,wgpu::TextureFormat format,float compareRef,wgpu::CompareFunction compare,std::vector<float> textureValues)532 void DoDepthCompareRefTest(wgpu::ComputePipeline pipeline,
533 wgpu::TextureFormat format,
534 float compareRef,
535 wgpu::CompareFunction compare,
536 std::vector<float> textureValues) {
537 queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
538
539 wgpu::SamplerDescriptor samplerDesc;
540 samplerDesc.compare = compare;
541 wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
542
543 wgpu::Texture inputTexture = CreateInputTexture(format);
544 wgpu::TextureViewDescriptor inputViewDesc = {};
545 inputViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
546
547 wgpu::Buffer outputBuffer = CreateOutputBuffer();
548
549 wgpu::BindGroup bindGroup =
550 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
551 {{0, sampler},
552 {1, inputTexture.CreateView(&inputViewDesc)},
553 {2, mUniformBuffer},
554 {3, outputBuffer}});
555
556 for (float textureValue : textureValues) {
557 // Set the input depth texture to the provided texture value
558 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
559 UpdateInputDepth(commandEncoder, inputTexture, textureValue);
560
561 // Sample into the output buffer
562 {
563 wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
564 pass.SetPipeline(pipeline);
565 pass.SetBindGroup(0, bindGroup);
566 pass.Dispatch(1);
567 pass.EndPass();
568 }
569
570 wgpu::CommandBuffer commands = commandEncoder.Finish();
571 queue.Submit(1, &commands);
572
573 float float0 = 0.f;
574 float float1 = 1.f;
575 float* expected =
576 CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0;
577
578 EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), outputBuffer, 0);
579 }
580 }
581
582 private:
583 wgpu::Buffer mUniformBuffer;
584 };
585
586 // Test that sampling a depth texture with a render/compute pipeline works
TEST_P(DepthStencilSamplingTest,SampleDepth)587 TEST_P(DepthStencilSamplingTest, SampleDepth) {
588 for (wgpu::TextureFormat format : kDepthFormats) {
589 float tolerance = 0.0f;
590 if (format == wgpu::TextureFormat::Depth16Unorm) {
591 tolerance = 0.001f;
592 }
593 // Test 0, between [0, 1], and 1.
594 DoSamplingTest(TestAspect::Depth, CreateSamplingRenderPipeline({TestAspect::Depth}, 0),
595 format, kNormalizedTextureValues, tolerance);
596
597 DoSamplingTest(TestAspect::Depth, CreateSamplingComputePipeline({TestAspect::Depth}, 0),
598 format, kNormalizedTextureValues, tolerance);
599 }
600 }
601
602 // Test that sampling a stencil texture with a render/compute pipeline works
TEST_P(DepthStencilSamplingTest,SampleStencil)603 TEST_P(DepthStencilSamplingTest, SampleStencil) {
604 // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
605 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
606 for (wgpu::TextureFormat format : kStencilFormats) {
607 DoSamplingTest(TestAspect::Stencil, CreateSamplingRenderPipeline({TestAspect::Stencil}, 0),
608 format, kStencilValues);
609
610 DoSamplingTest(TestAspect::Stencil, CreateSamplingComputePipeline({TestAspect::Stencil}, 0),
611 format, kStencilValues);
612 }
613 }
614
615 // Test that sampling a depth/stencil texture at components 1, 2, and 3 yield 0, 0, and 1
616 // respectively
TEST_P(DepthStencilSamplingTest,SampleExtraComponents)617 TEST_P(DepthStencilSamplingTest, SampleExtraComponents) {
618 // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
619 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
620
621 DoSamplingExtraStencilComponentsRenderTest(
622 TestAspect::Stencil, wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(42), uint8_t(37)});
623
624 DoSamplingExtraStencilComponentsComputeTest(
625 TestAspect::Stencil, wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(42), uint8_t(37)});
626 }
627
628 // Test sampling both depth and stencil with a render/compute pipeline works.
TEST_P(DepthStencilSamplingTest,SampleDepthAndStencilRender)629 TEST_P(DepthStencilSamplingTest, SampleDepthAndStencilRender) {
630 // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
631 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
632 wgpu::SamplerDescriptor samplerDesc;
633 wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
634
635 wgpu::Texture inputTexture = CreateInputTexture(wgpu::TextureFormat::Depth24PlusStencil8);
636
637 wgpu::TextureViewDescriptor depthViewDesc = {};
638 depthViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
639
640 wgpu::TextureViewDescriptor stencilViewDesc = {};
641 stencilViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
642
643 // With render pipeline
644 {
645 wgpu::RenderPipeline pipeline =
646 CreateSamplingRenderPipeline({TestAspect::Depth, TestAspect::Stencil}, 0);
647
648 wgpu::Buffer depthOutput = CreateOutputBuffer();
649 wgpu::Buffer stencilOutput = CreateOutputBuffer();
650
651 wgpu::BindGroup bindGroup =
652 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
653 {
654 {0, inputTexture.CreateView(&depthViewDesc)},
655 {1, depthOutput},
656 {2, inputTexture.CreateView(&stencilViewDesc)},
657 {3, stencilOutput},
658 });
659
660 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
661
662 // Initialize both depth and stencil aspects.
663 utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
664 passDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.43f;
665 passDescriptor.cDepthStencilAttachmentInfo.clearStencil = 31;
666
667 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
668 pass.EndPass();
669
670 // Render into the output textures
671 {
672 utils::BasicRenderPass renderPass =
673 utils::CreateBasicRenderPass(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm);
674 wgpu::RenderPassEncoder pass =
675 commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
676 pass.SetPipeline(pipeline);
677 pass.SetBindGroup(0, bindGroup);
678 pass.Draw(1);
679 pass.EndPass();
680 }
681
682 wgpu::CommandBuffer commands = commandEncoder.Finish();
683 queue.Submit(1, &commands);
684
685 uint32_t expectedValueU32 = 0;
686 memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
687 sizeof(float));
688 EXPECT_BUFFER_U32_EQ(expectedValueU32, depthOutput, 0);
689
690 expectedValueU32 = 0;
691 memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
692 sizeof(uint8_t));
693 EXPECT_BUFFER_U32_EQ(expectedValueU32, stencilOutput, 0);
694 }
695
696 // With compute pipeline
697 {
698 wgpu::ComputePipeline pipeline =
699 CreateSamplingComputePipeline({TestAspect::Depth, TestAspect::Stencil}, 0);
700
701 wgpu::Buffer depthOutput = CreateOutputBuffer();
702 wgpu::Buffer stencilOutput = CreateOutputBuffer();
703
704 wgpu::BindGroup bindGroup =
705 utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
706 {{0, inputTexture.CreateView(&depthViewDesc)},
707 {1, depthOutput},
708 {2, inputTexture.CreateView(&stencilViewDesc)},
709 {3, stencilOutput}});
710
711 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
712 // Initialize both depth and stencil aspects.
713 utils::ComboRenderPassDescriptor passDescriptor({}, inputTexture.CreateView());
714 passDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.43f;
715 passDescriptor.cDepthStencilAttachmentInfo.clearStencil = 31;
716
717 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
718 pass.EndPass();
719
720 // Sample into the output buffers
721 {
722 wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
723 pass.SetPipeline(pipeline);
724 pass.SetBindGroup(0, bindGroup);
725 pass.Dispatch(1);
726 pass.EndPass();
727 }
728
729 wgpu::CommandBuffer commands = commandEncoder.Finish();
730 queue.Submit(1, &commands);
731
732 uint32_t expectedValueU32 = 0;
733 memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
734 sizeof(float));
735 EXPECT_BUFFER_U32_EQ(expectedValueU32, depthOutput, 0);
736
737 expectedValueU32 = 0;
738 memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
739 sizeof(uint8_t));
740 EXPECT_BUFFER_U32_EQ(expectedValueU32, stencilOutput, 0);
741 }
742 }
743
744 // Test that sampling in a render pipeline with all of the compare functions works.
TEST_P(DepthStencilSamplingTest,CompareFunctionsRender)745 TEST_P(DepthStencilSamplingTest, CompareFunctionsRender) {
746 // Initialization via renderPass loadOp doesn't work on Mac Intel.
747 DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
748
749 wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
750
751 for (wgpu::TextureFormat format : kDepthFormats) {
752 // Test does not account for precision issues when comparison testing Depth16Unorm.
753 if (format == wgpu::TextureFormat::Depth16Unorm) {
754 continue;
755 }
756
757 // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
758 for (float compareRef : kCompareRefs) {
759 // Test 0, below the ref, equal to, above the ref, and 1.
760 for (wgpu::CompareFunction f : kCompareFunctions) {
761 DoDepthCompareRefTest(pipeline, format, compareRef, f, kNormalizedTextureValues);
762 }
763 }
764 }
765 }
766
767 DAWN_INSTANTIATE_TEST(DepthStencilSamplingTest,
768 D3D12Backend(),
769 MetalBackend(),
770 OpenGLBackend(),
771 OpenGLESBackend(),
772 VulkanBackend());
773