• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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