• 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 "common/Assert.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20 
21 constexpr static unsigned int kRTSize = 1;
22 
23 class DepthClampingTest : public DawnTest {
24   protected:
SetUp()25     void SetUp() override {
26         DawnTest::SetUp();
27         DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({"depth-clamping"}));
28 
29         wgpu::TextureDescriptor renderTargetDescriptor;
30         renderTargetDescriptor.size = {kRTSize, kRTSize};
31         renderTargetDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
32         renderTargetDescriptor.usage =
33             wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
34         renderTarget = device.CreateTexture(&renderTargetDescriptor);
35 
36         renderTargetView = renderTarget.CreateView();
37 
38         wgpu::TextureDescriptor depthDescriptor;
39         depthDescriptor.dimension = wgpu::TextureDimension::e2D;
40         depthDescriptor.size = {kRTSize, kRTSize};
41         depthDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
42         depthDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
43         depthTexture = device.CreateTexture(&depthDescriptor);
44 
45         depthTextureView = depthTexture.CreateView();
46 
47         vsModule = utils::CreateShaderModule(device, R"(
48             [[block]] struct UBO {
49                 color : vec3<f32>;
50                 depth : f32;
51             };
52             [[group(0), binding(0)]] var<uniform> ubo : UBO;
53 
54             [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
55                 return vec4<f32>(0.0, 0.0, ubo.depth, 1.0);
56             })");
57 
58         fsModule = utils::CreateShaderModule(device, R"(
59             [[block]] struct UBO {
60                 color : vec3<f32>;
61                 depth : f32;
62             };
63             [[group(0), binding(0)]] var<uniform> ubo : UBO;
64 
65             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
66                 return vec4<f32>(ubo.color, 1.0);
67             })");
68     }
69 
GetRequiredFeatures()70     std::vector<const char*> GetRequiredFeatures() override {
71         std::vector<const char*> requiredFeatures = {};
72         if (SupportsFeatures({"depth-clamping"})) {
73             requiredFeatures.push_back("depth-clamping");
74         }
75         return requiredFeatures;
76     }
77 
78     struct TestSpec {
79         wgpu::PrimitiveDepthClampingState* depthClampingState;
80         RGBA8 color;
81         float depth;
82         wgpu::CompareFunction depthCompareFunction;
83     };
84 
85     // Each test param represents a pair of triangles with a color, depth, stencil value, and
86     // depthStencil state, one frontfacing, one backfacing Draw the triangles in order and check the
87     // expected colors for the frontfaces and backfaces
DoTest(const std::vector<TestSpec> & testParams,const RGBA8 & expected)88     void DoTest(const std::vector<TestSpec>& testParams, const RGBA8& expected) {
89         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
90 
91         struct TriangleData {
92             float color[3];
93             float depth;
94         };
95 
96         utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView);
97         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
98 
99         for (size_t i = 0; i < testParams.size(); ++i) {
100             const TestSpec& test = testParams[i];
101 
102             TriangleData data = {
103                 {static_cast<float>(test.color.r) / 255.f, static_cast<float>(test.color.g) / 255.f,
104                  static_cast<float>(test.color.b) / 255.f},
105                 test.depth,
106             };
107             // Upload a buffer for each triangle's depth and color data
108             wgpu::Buffer buffer = utils::CreateBufferFromData(device, &data, sizeof(TriangleData),
109                                                               wgpu::BufferUsage::Uniform);
110 
111             // Create a pipeline for the triangles with the test spec's params.
112             utils::ComboRenderPipelineDescriptor descriptor;
113             descriptor.primitive.nextInChain = test.depthClampingState;
114             descriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
115             descriptor.vertex.module = vsModule;
116             descriptor.cFragment.module = fsModule;
117             wgpu::DepthStencilState* depthStencil = descriptor.EnableDepthStencil();
118             depthStencil->depthWriteEnabled = true;
119             depthStencil->depthCompare = test.depthCompareFunction;
120             depthStencil->format = wgpu::TextureFormat::Depth24PlusStencil8;
121 
122             wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
123 
124             // Create a bind group for the data
125             wgpu::BindGroup bindGroup = utils::MakeBindGroup(
126                 device, pipeline.GetBindGroupLayout(0), {{0, buffer}});
127 
128             pass.SetPipeline(pipeline);
129             pass.SetBindGroup(0, bindGroup);
130             pass.Draw(1);
131         }
132         pass.EndPass();
133 
134         wgpu::CommandBuffer commands = encoder.Finish();
135         queue.Submit(1, &commands);
136 
137         EXPECT_PIXEL_RGBA8_EQ(expected, renderTarget,  0, 0) << "Pixel check failed";
138     }
139 
140     wgpu::Texture renderTarget;
141     wgpu::Texture depthTexture;
142     wgpu::TextureView renderTargetView;
143     wgpu::TextureView depthTextureView;
144     wgpu::ShaderModule vsModule;
145     wgpu::ShaderModule fsModule;
146 };
147 
148 // Test that fragments beyond the far plane are clamped to 1.0 if depth clamping is enabled.
TEST_P(DepthClampingTest,ClampOnBeyondFarPlane)149 TEST_P(DepthClampingTest, ClampOnBeyondFarPlane) {
150     wgpu::PrimitiveDepthClampingState clampingState;
151     clampingState.clampDepth = true;
152 
153     DoTest(
154         {
155             // Draw a red triangle at depth 1.
156             {
157                 nullptr, /* depthClampingState */
158                 RGBA8(255, 0, 0, 255), /* color */
159                 1.f, /* depth */
160                 wgpu::CompareFunction::Always,
161             },
162             // Draw a green triangle at depth 2 which should get clamped to 1.
163             {
164                 &clampingState,
165                 RGBA8(0, 255, 0, 255), /* color */
166                 2.f, /* depth */
167                 wgpu::CompareFunction::Equal,
168             },
169         },
170         // Since we draw the green triangle with an "equal" depth compare function, the resulting
171         // fragment should be green.
172         RGBA8(0, 255, 0, 255));
173 }
174 
175 // Test that fragments beyond the near plane are clamped to 0.0 if depth clamping is enabled.
TEST_P(DepthClampingTest,ClampOnBeyondNearPlane)176 TEST_P(DepthClampingTest, ClampOnBeyondNearPlane) {
177     wgpu::PrimitiveDepthClampingState clampingState;
178     clampingState.clampDepth = true;
179 
180     DoTest(
181         {
182             // Draw a red triangle at depth 0.
183             {
184                 nullptr, /* depthClampingState */
185                 RGBA8(255, 0, 0, 255), /* color */
186                 0.f, /* depth */
187                 wgpu::CompareFunction::Always,
188             },
189             // Draw a green triangle at depth -1 which should get clamped to 0.
190             {
191                 &clampingState,
192                 RGBA8(0, 255, 0, 255), /* color */
193                 -1.f, /* depth */
194                 wgpu::CompareFunction::Equal,
195             },
196         },
197         // Since we draw the green triangle with an "equal" depth compare function, the resulting
198         // fragment should be green.
199         RGBA8(0, 255, 0, 255));
200 }
201 
202 // Test that fragments inside the view frustum are unaffected by depth clamping.
TEST_P(DepthClampingTest,ClampOnInsideViewFrustum)203 TEST_P(DepthClampingTest, ClampOnInsideViewFrustum) {
204     wgpu::PrimitiveDepthClampingState clampingState;
205     clampingState.clampDepth = true;
206 
207     DoTest(
208         {
209             {
210                 &clampingState,
211                 RGBA8(0, 255, 0, 255), /* color */
212                 0.5f, /* depth */
213                 wgpu::CompareFunction::Always,
214             },
215         },
216         RGBA8(0, 255, 0, 255));
217 }
218 
219 
220 // Test that fragments outside the view frustum are clipped if depth clamping is disabled.
TEST_P(DepthClampingTest,ClampOffOutsideViewFrustum)221 TEST_P(DepthClampingTest, ClampOffOutsideViewFrustum) {
222     wgpu::PrimitiveDepthClampingState clampingState;
223     clampingState.clampDepth = false;
224 
225     DoTest(
226         {
227             {
228                 &clampingState,
229                 RGBA8(0, 255, 0, 255), /* color */
230                 2.f, /* depth */
231                 wgpu::CompareFunction::Always,
232             },
233             {
234                 &clampingState,
235                 RGBA8(0, 255, 0, 255), /* color */
236                 -1.f, /* depth */
237                 wgpu::CompareFunction::Always,
238             },
239         },
240         RGBA8(0, 0, 0, 0));
241 }
242 
243 // Test that fragments outside the view frustum are clipped if clampDepth is left unspecified.
TEST_P(DepthClampingTest,ClampUnspecifiedOutsideViewFrustum)244 TEST_P(DepthClampingTest, ClampUnspecifiedOutsideViewFrustum) {
245     DoTest(
246         {
247             {
248                 nullptr, /* depthClampingState */
249                 RGBA8(0, 255, 0, 255), /* color */
250                 -1.f, /* depth */
251                 wgpu::CompareFunction::Always,
252             },
253             {
254                 nullptr, /* depthClampingState */
255                 RGBA8(0, 255, 0, 255), /* color */
256                 2.f, /* depth */
257                 wgpu::CompareFunction::Always,
258             },
259         },
260         RGBA8(0, 0, 0, 0));
261 }
262 
263 // Test that fragments are properly clipped or clamped if multiple render pipelines are used
264 // within the same render pass with differing clampDepth values.
TEST_P(DepthClampingTest,MultipleRenderPipelines)265 TEST_P(DepthClampingTest, MultipleRenderPipelines) {
266     wgpu::PrimitiveDepthClampingState clampingState;
267     clampingState.clampDepth = true;
268 
269     wgpu::PrimitiveDepthClampingState clippingState;
270     clippingState.clampDepth = false;
271 
272     DoTest(
273         {
274             // Draw green with clamping
275             {
276                 &clampingState,
277                 RGBA8(0, 255, 0, 255), /* color */
278                 2.f, /* depth */
279                 wgpu::CompareFunction::Always,
280             },
281             // Draw red with clipping
282             {
283                 &clippingState,
284                 RGBA8(255, 0, 0, 255), /* color */
285                 2.f, /* depth */
286                 wgpu::CompareFunction::Always,
287             },
288         },
289         RGBA8(0, 255, 0, 255)); // Result should be green
290 }
291 
292 DAWN_INSTANTIATE_TEST(DepthClampingTest,
293                       D3D12Backend(),
294                       MetalBackend(),
295                       OpenGLBackend(),
296                       OpenGLESBackend(),
297                       VulkanBackend());
298