1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/DawnTest.h"
16
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/DawnHelpers.h"
19
20 class ViewportTest : public DawnTest {
21 protected:
CreatePipelineForTest(dawn::CompareFunction depthCompare)22 dawn::RenderPipeline CreatePipelineForTest(dawn::CompareFunction depthCompare) {
23 utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
24
25 // Draw two triangles:
26 // 1. The top-left triangle is red. Its depth values are >= 0.5. After viewport is applied,
27 // the depth might be >= 0.25 if minDepth is 0 and maxDepth is 0.5.
28 // 2. The bottom-right triangle is green. Its depth values are <= 0.5. After viewport is
29 // applied, the depth might be <= 0.25 if minDepth is 0 and maxDepth is 0.5.
30 const char* vs =
31 R"(#version 450
32 layout(location = 0) out vec4 color;
33 const vec3 pos[6] = vec3[6](vec3(-1.0f, -1.0f, 1.0f),
34 vec3(-1.0f, 1.0f, 0.5f),
35 vec3( 1.0f, -1.0f, 0.5f),
36 vec3( 1.0f, -1.0f, 0.5f),
37 vec3(-1.0f, 1.0f, 0.5f),
38 vec3( 1.0f, 1.0f, 0.0f));
39 void main() {
40 gl_Position = vec4(pos[gl_VertexIndex], 1.0);
41 if (gl_VertexIndex < 3) {
42 color = vec4(1.0, 0.0, 0.0, 1.0);
43 } else {
44 color = vec4(0.0, 1.0, 0.0, 1.0);
45 }
46 })";
47 pipelineDescriptor.cVertexStage.module =
48 utils::CreateShaderModule(device, utils::ShaderStage::Vertex, vs);
49
50 const char* fs =
51 R"(#version 450
52 layout(location = 0) in vec4 color;
53 layout(location = 0) out vec4 fragColor;
54 void main() {
55 fragColor = color;
56 })";
57 pipelineDescriptor.cFragmentStage.module =
58 utils::CreateShaderModule(device, utils::ShaderStage::Fragment, fs);
59
60 pipelineDescriptor.cDepthStencilState.depthCompare = depthCompare;
61 pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState;
62
63 return device.CreateRenderPipeline(&pipelineDescriptor);
64 }
65
Create2DTextureForTest(dawn::TextureFormat format)66 dawn::Texture Create2DTextureForTest(dawn::TextureFormat format) {
67 dawn::TextureDescriptor textureDescriptor;
68 textureDescriptor.dimension = dawn::TextureDimension::e2D;
69 textureDescriptor.format = format;
70 textureDescriptor.usage =
71 dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::CopySrc;
72 textureDescriptor.arrayLayerCount = 1;
73 textureDescriptor.mipLevelCount = 1;
74 textureDescriptor.sampleCount = 1;
75 textureDescriptor.size = {kSize, kSize, 1};
76 return device.CreateTexture(&textureDescriptor);
77 }
78
79 enum ColorType {
80 TopLeftTriangleColor,
81 BottomRightTriangleColor,
82 BackgroundColor,
83
84 ColorTypeCount,
85 };
86
87 struct ViewportParams {
88 float x, y, width, height, minDepth, maxDepth;
89 };
90
91 struct TestInfo {
92 ViewportParams viewport;
93 ColorType topLeftPoint;
94 ColorType bottomRightPoint;
95 float clearDepth = 1.0f;
96 bool setViewport = true;
97 };
98
DoTest(const TestInfo & info)99 void DoTest(const TestInfo& info) {
100 dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
101
102 // Create render targets for 2 render passes.
103 dawn::Texture colorTexture1 = Create2DTextureForTest(dawn::TextureFormat::RGBA8Unorm);
104 dawn::Texture depthStencilTexture1 =
105 Create2DTextureForTest(dawn::TextureFormat::Depth24PlusStencil8);
106
107 dawn::Texture colorTexture2 = Create2DTextureForTest(dawn::TextureFormat::RGBA8Unorm);
108 dawn::Texture depthStencilTexture2 =
109 Create2DTextureForTest(dawn::TextureFormat::Depth24PlusStencil8);
110
111 // Create render pass 1
112 // Note that we may explicitly call SetViewport() in this pass
113 {
114 utils::ComboRenderPassDescriptor renderPassDescriptor1(
115 {colorTexture1.CreateDefaultView()}, depthStencilTexture1.CreateDefaultView());
116 renderPassDescriptor1.cColorAttachmentsInfoPtr[0]->clearColor = {0.0, 0.0, 1.0, 1.0};
117 renderPassDescriptor1.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Clear;
118
119 renderPassDescriptor1.cDepthStencilAttachmentInfo.clearDepth = info.clearDepth;
120 renderPassDescriptor1.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Clear;
121
122 dawn::RenderPassEncoder renderPass1 =
123 commandEncoder.BeginRenderPass(&renderPassDescriptor1);
124 renderPass1.SetPipeline(CreatePipelineForTest(dawn::CompareFunction::Less));
125 if (info.setViewport) {
126 ViewportParams viewport = info.viewport;
127 renderPass1.SetViewport(viewport.x, viewport.y, viewport.width, viewport.height,
128 viewport.minDepth, viewport.maxDepth);
129 }
130 renderPass1.Draw(6, 1, 0, 0);
131 renderPass1.EndPass();
132 }
133
134 // Create render pass 2
135 // Note that we never explicitly call SetViewport() in this pass.
136 // Its viewport(x, y, width, height, minDepth, maxDepth) should be
137 // (0, 0, rendertarget's width, rendertarget's height, 0.0, 1.0) by default.
138 {
139 utils::ComboRenderPassDescriptor renderPassDescriptor2(
140 {colorTexture2.CreateDefaultView()}, depthStencilTexture2.CreateDefaultView());
141 renderPassDescriptor2.cColorAttachmentsInfoPtr[0]->clearColor = {0.0, 0.0, 1.0, 1.0};
142 renderPassDescriptor2.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Clear;
143
144 renderPassDescriptor2.cDepthStencilAttachmentInfo.clearDepth = 0.5;
145 renderPassDescriptor2.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Clear;
146
147 dawn::RenderPassEncoder renderPass2 =
148 commandEncoder.BeginRenderPass(&renderPassDescriptor2);
149 renderPass2.SetPipeline(CreatePipelineForTest(dawn::CompareFunction::Greater));
150 renderPass2.Draw(6, 1, 0, 0);
151 renderPass2.EndPass();
152 }
153
154 dawn::CommandBuffer commandBuffer = commandEncoder.Finish();
155 dawn::Queue queue = device.CreateQueue();
156 queue.Submit(1, &commandBuffer);
157
158 constexpr RGBA8 kColor[ColorTypeCount] = {
159 RGBA8(255, 0, 0, 255), // top-left triangle is red
160 RGBA8(0, 255, 0, 255), // bottom-right triangle is green
161 RGBA8(0, 0, 255, 255), // background is blue
162 };
163
164 EXPECT_PIXEL_RGBA8_EQ(kColor[info.topLeftPoint], colorTexture1, 0, 0);
165 EXPECT_PIXEL_RGBA8_EQ(kColor[info.bottomRightPoint], colorTexture1, kSize - 1, kSize - 1);
166
167 // In render pass 2. Point(0, 0) is tend to be covered by the top-left triangle. Point(3, 3)
168 // is tend to be covered by the bottom-right triangle. However, the bottom-right triangle's
169 // depth values are <= 0.5. And the depthCompare is Greater. As a result, point(0, 0) will
170 // be drawn as usual, its color is the top-left triangle's color. But point(3, 3) will not
171 // be drawn by any triangles. Its color is the backgroud color.
172 EXPECT_PIXEL_RGBA8_EQ(kColor[TopLeftTriangleColor], colorTexture2, 0, 0);
173 EXPECT_PIXEL_RGBA8_EQ(kColor[BackgroundColor], colorTexture2, kSize - 1, kSize - 1);
174 }
175
176 static constexpr uint32_t kSize = 4;
177 };
178
179 // The viewport is the same size as the backbuffer if it is not explicitly specified. And minDepth
180 // and maxDepth are 0.0 and 1.0 respectively. The viewport parameters below are not really used.
181 // Point(0, 0) is covered by the top-left triangle. Likewise, point(3, 3) is covered by the
182 // bottom-right triangle.
TEST_P(ViewportTest,Default)183 TEST_P(ViewportTest, Default) {
184 ViewportParams viewport = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
185 TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor, 1.0, false};
186 DoTest(info);
187 }
188
189 // Explicitly specify the viewport as its default value. The result is the same as it is in the test
190 // above.
TEST_P(ViewportTest,Basic)191 TEST_P(ViewportTest, Basic) {
192 ViewportParams viewport = {0.0, 0.0, 4.0, 4.0, 0.0, 1.0};
193 TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor};
194 DoTest(info);
195 }
196
197 // Shift the viewport toward top-left by (2, 2). So the top-left triangle is outside of the back
198 // buffer. We can't see it. And point(0, 0) is covered by the bottom-right triangle now. Point(3, 3)
199 // is not covered by any triangles.
TEST_P(ViewportTest,ShiftToTopLeft)200 TEST_P(ViewportTest, ShiftToTopLeft) {
201 ViewportParams viewport = {-2.0, -2.0, 4.0, 4.0, 0.0, 1.0};
202 TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor};
203 DoTest(info);
204 }
205
206 // Shift the viewport toward bottom-right by (2, 2). So Point(0, 0) is not covered by any triangles.
207 // The top-left triangle is moved to the bottom-right of back buffer. Point(3, 3) is covered by it.
208 // While the bottom-right triangle is moved outside of back buffer now.
TEST_P(ViewportTest,ShiftToBottomRight)209 TEST_P(ViewportTest, ShiftToBottomRight) {
210 ViewportParams viewport = {2.0, 2.0, 4.0, 4.0, 0.0, 1.0};
211 TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor};
212 DoTest(info);
213 }
214
215 // After applying the minDepth/maxDepth value in viewport and projecting to framebuffer coordinate,
216 // depth values of the top-left triangle are >= 0.25. They are greater than the depth values in
217 // depth buffer, so it is not drawn at all. As a result, point(0, 0) is not covered by any
218 // triangles. But the bottom-right triangle is drawn as usual.
TEST_P(ViewportTest,ApplyDepth)219 TEST_P(ViewportTest, ApplyDepth) {
220 ViewportParams viewport = {0.0, 0.0, 4.0, 4.0, 0.0, 0.5};
221 TestInfo info = {viewport, BackgroundColor, BottomRightTriangleColor, 0.25};
222 DoTest(info);
223 }
224
225 // Shift the viewport toward top-left by (2, 2). So the top-left triangle is outside of the back
226 // buffer. We can't see it. And point(0, 0) is covered by the bottom-right triangle now. Its depth
227 // value is < 0.25. So it is drawn as usual. Point(3, 3) is not covered by any triangles.
TEST_P(ViewportTest,ShiftToTopLeftAndApplyDepth)228 TEST_P(ViewportTest, ShiftToTopLeftAndApplyDepth) {
229 // Test failing on Linux Vulkan Intel.
230 // See https://bugs.chromium.org/p/dawn/issues/detail?id=187
231 DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel());
232
233 ViewportParams viewport = {-2.0, -2.0, 4.0, 4.0, 0.0, 0.5};
234 TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor, 0.25};
235 DoTest(info);
236 }
237
238 // Shift the viewport toward bottom-right by (2, 2). So point(0, 0) is not covered by any triangles.
239 // The top-left triangle is moved to the bottom-right of back buffer. However, depth values of the
240 // top-left triangle are >= 0.25. They are greater than the depth values in depth buffer, so it is
241 // not drawn at all. So point(3, 3) is not covered by any triangle, either.
TEST_P(ViewportTest,ShiftToBottomRightAndApplyDepth)242 TEST_P(ViewportTest, ShiftToBottomRightAndApplyDepth) {
243 ViewportParams viewport = {2.0, 2.0, 4.0, 4.0, 0.0, 0.5};
244 TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25};
245 DoTest(info);
246 }
247
248 // Enlarge the viewport by 2 times. So the entire back buffer is covered by the top-left triangle.
TEST_P(ViewportTest,EnlargeViewport)249 TEST_P(ViewportTest, EnlargeViewport) {
250 ViewportParams viewport = {0.0, 0.0, 8.0, 8.0, 0.0, 1.0};
251 TestInfo info = {viewport, TopLeftTriangleColor, TopLeftTriangleColor};
252 DoTest(info);
253 }
254
255 // Enlarge the viewport by 2 times and shift toward top-left by (2, 2). back buffer sits exactly
256 // at the center of the whole viewport. So, point(0, 0) is covered by the top-left triangle, and
257 // point(3, 3) is covered by the bottom-right triangle.
TEST_P(ViewportTest,EnlargeViewportAndShiftToTopLeft)258 TEST_P(ViewportTest, EnlargeViewportAndShiftToTopLeft) {
259 ViewportParams viewport = {-2.0, -2.0, 8.0, 8.0, 0.0, 1.0};
260 TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor};
261 DoTest(info);
262 }
263
264 // Enlarge the viewport by 2 times and shift toward bottom-right by (2, 2). Point(0, 0) is not
265 // covered by any triangle. Point(3, 3) is covered by the top-left triangle.
TEST_P(ViewportTest,EnlargeViewportAndShiftToBottomRight)266 TEST_P(ViewportTest, EnlargeViewportAndShiftToBottomRight) {
267 ViewportParams viewport = {2.0, 2.0, 8.0, 8.0, 0.0, 1.0};
268 TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor};
269 DoTest(info);
270 }
271
272 // Enlarge the viewport by 2 times. So the entire back buffer tend to be covered by the top-left
273 // triangle. However, depth values of the top-left triangle are >= 0.25. They are greater than the
274 // depth values in depth buffer, so the top-left triangle is not drawn at all. As a result, neither
275 // point(0, 0) nor point(3, 3) is covered by any triangles.
TEST_P(ViewportTest,EnlargeViewportAndApplyDepth)276 TEST_P(ViewportTest, EnlargeViewportAndApplyDepth) {
277 ViewportParams viewport = {0.0, 0.0, 8.0, 8.0, 0.0, 0.5};
278 TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25};
279 DoTest(info);
280 }
281
282 // Enlarge the viewport by 2 times and shift toward top-left by (2, 2). The back buffer sits exactly
283 // at the center of the whole viewport. However, depth values of the top-left triangle are >= 0.25.
284 // They are greater than the depth values in depth buffer, so the top-left triangle is not drawn at
285 // all. As a result, point(0, 0) is not covered by it. The bottom-right triangle is drawn because
286 // its depth values are < 0.25. So point(3, 3) is covered by it as usual.
TEST_P(ViewportTest,EnlargeViewportAndShiftToTopLeftAndApplyDepth)287 TEST_P(ViewportTest, EnlargeViewportAndShiftToTopLeftAndApplyDepth) {
288 // Test failing on Linux Vulkan Intel.
289 // See https://bugs.chromium.org/p/dawn/issues/detail?id=187
290 DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel());
291
292 ViewportParams viewport = {-2.0, -2.0, 8.0, 8.0, 0.0, 0.5};
293 TestInfo info = {viewport, BackgroundColor, BottomRightTriangleColor, 0.25};
294 DoTest(info);
295 }
296
297 // Enlarge the viewport by 2 times and shift toward bottom-right by (2, 2). Point(0, 0) is not
298 // covered by any triangle. The point(3, 3) tend to be covered by the top-left triangle. However,
299 // depth values of the top-left triangle are >= 0.25. They are greater than the depth values in
300 // depth buffer, so the top-left triangle is not drawn at all. As a result, point(3, 3) is not
301 // covered by any triangle, either.
TEST_P(ViewportTest,EnlargeViewportAndShiftToBottomRightAndApplyDepth)302 TEST_P(ViewportTest, EnlargeViewportAndShiftToBottomRightAndApplyDepth) {
303 ViewportParams viewport = {2.0, 2.0, 8.0, 8.0, 0.0, 0.5};
304 TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25};
305 DoTest(info);
306 }
307
308 // Shrink the viewport to its half. So point(0, 0) is covered by the top-left triangle, while
309 // point(3, 3) is not covered by any triangles because the drawing area is too small to cover the
310 // entire back buffer.
TEST_P(ViewportTest,ShrinkViewport)311 TEST_P(ViewportTest, ShrinkViewport) {
312 ViewportParams viewport = {0.0, 0.0, 2.0, 2.0, 0.0, 1.0};
313 TestInfo info = {viewport, TopLeftTriangleColor, BackgroundColor};
314 DoTest(info);
315 }
316
317 // Shrink the viewport to its half and move toward top-left by (1, 1), So point(0, 0) is covered by
318 // bottom-right triangle, while point(3, 3) is not covered by any triangles.
TEST_P(ViewportTest,ShrinkViewportAndShiftToTopLeft)319 TEST_P(ViewportTest, ShrinkViewportAndShiftToTopLeft) {
320 ViewportParams viewport = {-1.0, -1.0, 2.0, 2.0, 0.0, 1.0};
321 TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor};
322 DoTest(info);
323 }
324
325 // Shrink the viewport to its half and move toward bottom-right by (3, 3), So point(0, 0) is not
326 // covered by any triangles, and point(3, 3) is covered by the bottom-right triangle.
TEST_P(ViewportTest,ShrinkViewportAndShiftToBottomRight)327 TEST_P(ViewportTest, ShrinkViewportAndShiftToBottomRight) {
328 ViewportParams viewport = {3.0, 3.0, 2.0, 2.0, 0.0, 1.0};
329 TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor};
330 DoTest(info);
331 }
332
333 // Shrink the viewport to its half. So point(0, 0) is tend to be covered by top-left triangle.
334 // However, depth values of the top-left triangle are >= 0.25. They are greater than the depth
335 // values in depth buffer, so the top-left triangle is not drawn at all. As a result, point(0, 0)
336 // is not covered by any triangle. Point(3, 3) is not covered by any triangles, either. Because the
337 // drawing area is too small to cover the entire back buffer.
TEST_P(ViewportTest,ShrinkViewportAndApplyDepth)338 TEST_P(ViewportTest, ShrinkViewportAndApplyDepth) {
339 ViewportParams viewport = {0.0, 0.0, 2.0, 2.0, 0.0, 0.5};
340 TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25};
341 DoTest(info);
342 }
343
344 // Shrink the viewport to its half and move toward top-left by (1, 1), So point(0, 0) is covered by
345 // the bottom-right triangle, while point(3, 3) is not covered by any triangles.
TEST_P(ViewportTest,ShrinkViewportAndShiftToTopLeftAndApplyDepth)346 TEST_P(ViewportTest, ShrinkViewportAndShiftToTopLeftAndApplyDepth) {
347 // Test failing on Linux Vulkan Intel.
348 // See https://bugs.chromium.org/p/dawn/issues/detail?id=187
349 DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel());
350
351 ViewportParams viewport = {-1.0, -1.0, 2.0, 2.0, 0.0, 0.5};
352 TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor, 0.25};
353 DoTest(info);
354 }
355
356 // Shrink the viewport to its half and move toward bottom-right by (3, 3), So point(0, 0) is not
357 // covered by any triangle. Point(3, 3) is tend to be covered by the top-left triangle. However,
358 // depth values of the top-left triangle are >= 0.25. They are greater than the depth values in
359 // depth buffer, so the top-left triangle is not drawn at all. As a result, point(3, 3) is not
360 // covered by any triangle, either.
TEST_P(ViewportTest,ShrinkViewportAndShiftToBottomRightAndApplyDepth)361 TEST_P(ViewportTest, ShrinkViewportAndShiftToBottomRightAndApplyDepth) {
362 ViewportParams viewport = {3.0, 3.0, 2.0, 2.0, 0.0, 0.5};
363 TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25};
364 DoTest(info);
365 }
366
367 DAWN_INSTANTIATE_TEST(ViewportTest, OpenGLBackend, VulkanBackend);
368