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/Constants.h"
16 #include "common/Math.h"
17 #include "tests/DawnTest.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/TextureUtils.h"
20 #include "utils/WGPUHelpers.h"
21
22 constexpr static unsigned int kRTSize = 2;
23
24 enum class QuadAngle { Flat, TiltedX };
25
26 class DepthBiasTests : public DawnTest {
27 protected:
RunDepthBiasTest(wgpu::TextureFormat depthFormat,float depthClear,QuadAngle quadAngle,int32_t bias,float biasSlopeScale,float biasClamp)28 void RunDepthBiasTest(wgpu::TextureFormat depthFormat,
29 float depthClear,
30 QuadAngle quadAngle,
31 int32_t bias,
32 float biasSlopeScale,
33 float biasClamp) {
34 const char* vertexSource = nullptr;
35 switch (quadAngle) {
36 case QuadAngle::Flat:
37 // Draw a square at z = 0.25
38 vertexSource = R"(
39 [[stage(vertex)]]
40 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
41 var pos = array<vec2<f32>, 6>(
42 vec2<f32>(-1.0, -1.0),
43 vec2<f32>( 1.0, -1.0),
44 vec2<f32>(-1.0, 1.0),
45 vec2<f32>(-1.0, 1.0),
46 vec2<f32>( 1.0, -1.0),
47 vec2<f32>( 1.0, 1.0));
48 return vec4<f32>(pos[VertexIndex], 0.25, 1.0);
49 })";
50 break;
51
52 case QuadAngle::TiltedX:
53 // Draw a square ranging from 0 to 0.5, bottom to top
54 vertexSource = R"(
55 [[stage(vertex)]]
56 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
57 var pos = array<vec3<f32>, 6>(
58 vec3<f32>(-1.0, -1.0, 0.0),
59 vec3<f32>( 1.0, -1.0, 0.0),
60 vec3<f32>(-1.0, 1.0, 0.5),
61 vec3<f32>(-1.0, 1.0, 0.5),
62 vec3<f32>( 1.0, -1.0, 0.0),
63 vec3<f32>( 1.0, 1.0, 0.5));
64 return vec4<f32>(pos[VertexIndex], 1.0);
65 })";
66 break;
67 }
68
69 wgpu::ShaderModule vertexModule = utils::CreateShaderModule(device, vertexSource);
70
71 wgpu::ShaderModule fragmentModule = utils::CreateShaderModule(device, R"(
72 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
73 return vec4<f32>(1.0, 0.0, 0.0, 1.0);
74 })");
75
76 {
77 wgpu::TextureDescriptor descriptor;
78 descriptor.size = {kRTSize, kRTSize, 1};
79 descriptor.format = depthFormat;
80 descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
81 mDepthTexture = device.CreateTexture(&descriptor);
82 }
83
84 {
85 wgpu::TextureDescriptor descriptor;
86 descriptor.size = {kRTSize, kRTSize, 1};
87 descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
88 descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
89 mRenderTarget = device.CreateTexture(&descriptor);
90 }
91
92 // Create a render pass which clears depth to depthClear
93 utils::ComboRenderPassDescriptor renderPassDesc({mRenderTarget.CreateView()},
94 mDepthTexture.CreateView());
95 renderPassDesc.cDepthStencilAttachmentInfo.clearDepth = depthClear;
96
97 // Create a render pipeline to render the quad
98 utils::ComboRenderPipelineDescriptor renderPipelineDesc;
99
100 renderPipelineDesc.vertex.module = vertexModule;
101 renderPipelineDesc.cFragment.module = fragmentModule;
102 wgpu::DepthStencilState* depthStencil = renderPipelineDesc.EnableDepthStencil(depthFormat);
103 depthStencil->depthWriteEnabled = true;
104 depthStencil->depthBias = bias;
105 depthStencil->depthBiasSlopeScale = biasSlopeScale;
106 depthStencil->depthBiasClamp = biasClamp;
107
108 if (depthFormat != wgpu::TextureFormat::Depth32Float) {
109 depthStencil->depthCompare = wgpu::CompareFunction::Greater;
110 }
111
112 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
113
114 // Draw the quad (two triangles)
115 wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
116 wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPassDesc);
117 pass.SetPipeline(pipeline);
118 pass.Draw(6);
119 pass.EndPass();
120
121 wgpu::CommandBuffer commands = commandEncoder.Finish();
122 queue.Submit(1, &commands);
123 }
124
125 // Floating point depth buffers use the following formula to calculate bias
126 // bias = depthBias * 2 ** (exponent(max z of primitive) - number of bits in mantissa) +
127 // slopeScale * maxSlope
128 // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
129 // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdSetDepthBias.html
130 // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516269-setdepthbias
131 //
132 // To get a final bias of 0.25 for primitives with z = 0.25, we can use
133 // depthBias = 0.25 / (2 ** (-2 - 23)) = 8388608
134 static constexpr int32_t kPointTwoFiveBiasForPointTwoFiveZOnFloat = 8388608;
135
136 wgpu::Texture mDepthTexture;
137 wgpu::Texture mRenderTarget;
138 };
139
140 // Test adding positive bias to output
TEST_P(DepthBiasTests,PositiveBiasOnFloat)141 TEST_P(DepthBiasTests, PositiveBiasOnFloat) {
142 // NVIDIA GPUs under Vulkan seem to be using a different scale than everyone else.
143 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsNvidia());
144
145 // OpenGL uses a different scale than the other APIs
146 DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
147 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
148
149 // Draw quad flat on z = 0.25 with 0.25 bias
150 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::Flat,
151 kPointTwoFiveBiasForPointTwoFiveZOnFloat, 0, 0);
152
153 // Quad at z = 0.25 + 0.25 bias = 0.5
154 std::vector<float> expected = {
155 0.5, 0.5, //
156 0.5, 0.5, //
157 };
158
159 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
160 wgpu::TextureAspect::DepthOnly);
161 }
162
163 // Test adding positive bias to output with a clamp
TEST_P(DepthBiasTests,PositiveBiasOnFloatWithClamp)164 TEST_P(DepthBiasTests, PositiveBiasOnFloatWithClamp) {
165 // Clamping support in OpenGL is spotty
166 DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
167 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
168
169 // Draw quad flat on z = 0.25 with 0.25 bias clamped at 0.125.
170 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::Flat,
171 kPointTwoFiveBiasForPointTwoFiveZOnFloat, 0, 0.125);
172
173 // Quad at z = 0.25 + min(0.25 bias, 0.125 clamp) = 0.375
174 std::vector<float> expected = {
175 0.375, 0.375, //
176 0.375, 0.375, //
177 };
178
179 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
180 wgpu::TextureAspect::DepthOnly);
181 }
182
183 // Test adding negative bias to output
TEST_P(DepthBiasTests,NegativeBiasOnFloat)184 TEST_P(DepthBiasTests, NegativeBiasOnFloat) {
185 // NVIDIA GPUs seems to be using a different scale than everyone else
186 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsNvidia());
187
188 // OpenGL uses a different scale than the other APIs
189 DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
190
191 // Draw quad flat on z = 0.25 with -0.25 bias, depth clear of 0.125
192 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0.125, QuadAngle::Flat,
193 -kPointTwoFiveBiasForPointTwoFiveZOnFloat, 0, 0);
194
195 // Quad at z = 0.25 - 0.25 bias = 0
196 std::vector<float> expected = {
197 0.0, 0.0, //
198 0.0, 0.0, //
199 };
200
201 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
202 wgpu::TextureAspect::DepthOnly);
203 }
204
205 // Test adding negative bias to output with a clamp
TEST_P(DepthBiasTests,NegativeBiasOnFloatWithClamp)206 TEST_P(DepthBiasTests, NegativeBiasOnFloatWithClamp) {
207 // Clamping support in OpenGL is spotty
208 DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
209 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
210
211 // Draw quad flat on z = 0.25 with -0.25 bias clamped at -0.125.
212 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::Flat,
213 -kPointTwoFiveBiasForPointTwoFiveZOnFloat, 0, -0.125);
214
215 // Quad at z = 0.25 + max(-0.25 bias, -0.125 clamp) = 0.125
216 std::vector<float> expected = {
217 0.125, 0.125, //
218 0.125, 0.125, //
219 };
220
221 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
222 wgpu::TextureAspect::DepthOnly);
223 }
224
225 // Test adding positive infinite slope bias to output
TEST_P(DepthBiasTests,PositiveInfinitySlopeBiasOnFloat)226 TEST_P(DepthBiasTests, PositiveInfinitySlopeBiasOnFloat) {
227 // NVIDIA GPUs do not clamp values to 1 when using Inf slope bias.
228 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsNvidia());
229
230 // Draw quad with z from 0 to 0.5 with inf slope bias
231 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0.125, QuadAngle::TiltedX, 0,
232 std::numeric_limits<float>::infinity(), 0);
233
234 // Value at the center of the pixel + (0.25 slope * Inf slope bias) = 1 (clamped)
235 std::vector<float> expected = {
236 1.0, 1.0, //
237 1.0, 1.0, //
238 };
239
240 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
241 wgpu::TextureAspect::DepthOnly);
242 }
243
244 // Test adding positive infinite slope bias to output
TEST_P(DepthBiasTests,NegativeInfinityBiasOnFloat)245 TEST_P(DepthBiasTests, NegativeInfinityBiasOnFloat) {
246 // NVIDIA GPUs do not clamp values to 0 when using -Inf slope bias.
247 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsNvidia());
248
249 // Draw quad with z from 0 to 0.5 with -inf slope bias
250 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0.125, QuadAngle::TiltedX, 0,
251 -std::numeric_limits<float>::infinity(), 0);
252
253 // Value at the center of the pixel + (0.25 slope * -Inf slope bias) = 0 (clamped)
254 std::vector<float> expected = {
255 0.0, 0.0, //
256 0.0, 0.0, //
257 };
258
259 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
260 wgpu::TextureAspect::DepthOnly);
261 }
262
263 // Test tiledX quad with no bias
TEST_P(DepthBiasTests,NoBiasTiltedXOnFloat)264 TEST_P(DepthBiasTests, NoBiasTiltedXOnFloat) {
265 // Draw quad with z from 0 to 0.5 with no bias
266 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::TiltedX, 0, 0, 0);
267
268 // Depth values of TiltedX quad. Values at the center of the pixels.
269 std::vector<float> expected = {
270 0.375, 0.375, //
271 0.125, 0.125, //
272 };
273
274 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
275 wgpu::TextureAspect::DepthOnly);
276 }
277
278 // Test adding positive slope bias to output
TEST_P(DepthBiasTests,PositiveSlopeBiasOnFloat)279 TEST_P(DepthBiasTests, PositiveSlopeBiasOnFloat) {
280 // Draw quad with z from 0 to 0.5 with a slope bias of 1
281 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::TiltedX, 0, 1, 0);
282
283 // Value at the center of the pixel + (0.25 slope * 1.0 slope bias)
284 std::vector<float> expected = {
285 0.625, 0.625, //
286 0.375, 0.375, //
287 };
288
289 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
290 wgpu::TextureAspect::DepthOnly);
291 }
292
293 // Test adding negative half slope bias to output
TEST_P(DepthBiasTests,NegativeHalfSlopeBiasOnFloat)294 TEST_P(DepthBiasTests, NegativeHalfSlopeBiasOnFloat) {
295 // Draw quad with z from 0 to 0.5 with a slope bias of -0.5
296 RunDepthBiasTest(wgpu::TextureFormat::Depth32Float, 0, QuadAngle::TiltedX, 0, -0.5, 0);
297
298 // Value at the center of the pixel + (0.25 slope * -0.5 slope bias)
299 std::vector<float> expected = {
300 0.25, 0.25, //
301 0.0, 0.0, //
302 };
303
304 EXPECT_TEXTURE_EQ(expected.data(), mDepthTexture, {0, 0}, {kRTSize, kRTSize}, 0,
305 wgpu::TextureAspect::DepthOnly);
306 }
307
308 // Test adding positive bias to output
TEST_P(DepthBiasTests,PositiveBiasOn24bit)309 TEST_P(DepthBiasTests, PositiveBiasOn24bit) {
310 // Draw quad flat on z = 0.25 with 0.25 bias
311 RunDepthBiasTest(wgpu::TextureFormat::Depth24PlusStencil8, 0.4f, QuadAngle::Flat,
312 0.25f * (1 << 25), 0, 0);
313
314 // Only the bottom left quad has colors. 0.5 quad > 0.4 clear.
315 // TODO(crbug.com/dawn/820): Switch to depth sampling once feature has been enabled.
316 std::vector<RGBA8> expected = {
317 RGBA8::kRed, RGBA8::kRed, //
318 RGBA8::kRed, RGBA8::kRed, //
319 };
320
321 EXPECT_TEXTURE_EQ(expected.data(), mRenderTarget, {0, 0}, {kRTSize, kRTSize});
322 }
323
324 // Test adding positive bias to output with a clamp
TEST_P(DepthBiasTests,PositiveBiasOn24bitWithClamp)325 TEST_P(DepthBiasTests, PositiveBiasOn24bitWithClamp) {
326 // Clamping support in OpenGL is spotty
327 DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
328 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
329
330 // Draw quad flat on z = 0.25 with 0.25 bias clamped at 0.125.
331 RunDepthBiasTest(wgpu::TextureFormat::Depth24PlusStencil8, 0.4f, QuadAngle::Flat,
332 0.25f * (1 << 25), 0, 0.1f);
333
334 // Since we cleared with a depth of 0.4 and clamped bias at 0.4, the depth test will fail. 0.25
335 // + 0.125 < 0.4 clear.
336 // TODO(crbug.com/dawn/820): Switch to depth sampling once feature has been enabled.
337 std::vector<RGBA8> zero = {
338 RGBA8::kZero, RGBA8::kZero, //
339 RGBA8::kZero, RGBA8::kZero, //
340 };
341
342 EXPECT_TEXTURE_EQ(zero.data(), mRenderTarget, {0, 0}, {kRTSize, kRTSize});
343 }
344
345 // Test adding positive bias to output
TEST_P(DepthBiasTests,PositiveSlopeBiasOn24bit)346 TEST_P(DepthBiasTests, PositiveSlopeBiasOn24bit) {
347 // Draw quad with z from 0 to 0.5 with a slope bias of 1
348 RunDepthBiasTest(wgpu::TextureFormat::Depth24PlusStencil8, 0.4f, QuadAngle::TiltedX, 0, 1, 0);
349
350 // Only the top half of the quad has a depth > 0.4 clear
351 // TODO(crbug.com/dawn/820): Switch to depth sampling once feature has been enabled.
352 std::vector<RGBA8> expected = {
353 RGBA8::kRed, RGBA8::kRed, //
354 RGBA8::kZero, RGBA8::kZero, //
355 };
356
357 EXPECT_TEXTURE_EQ(expected.data(), mRenderTarget, {0, 0}, {kRTSize, kRTSize});
358 }
359
360 DAWN_INSTANTIATE_TEST(DepthBiasTests,
361 D3D12Backend(),
362 MetalBackend(),
363 OpenGLBackend(),
364 OpenGLESBackend(),
365 VulkanBackend());
366