1 //
2 // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // PolygonOffsetClampTest.cpp: Test cases for GL_EXT_polygon_offset_clamp
7 //
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11 #include "util/EGLWindow.h"
12 #include "util/test_utils.h"
13
14 using namespace angle;
15
16 class PolygonOffsetClampTest : public ANGLETest<>
17 {
18 protected:
PolygonOffsetClampTest()19 PolygonOffsetClampTest()
20 {
21 setConfigRedBits(8);
22 setConfigGreenBits(8);
23 setConfigBlueBits(8);
24 setConfigAlphaBits(8);
25 setConfigDepthBits(24);
26 setExtensionsEnabled(false);
27 }
28 };
29
30 // Test state queries and updates
TEST_P(PolygonOffsetClampTest,State)31 TEST_P(PolygonOffsetClampTest, State)
32 {
33 // New state query fails without the extension
34 {
35 GLfloat clamp = -1.0f;
36 glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
37 EXPECT_GL_ERROR(GL_INVALID_ENUM);
38 EXPECT_EQ(clamp, -1.0f);
39
40 ASSERT_GL_NO_ERROR();
41 }
42
43 // New function does nothing without enabling the extension
44 {
45 glPolygonOffsetClampEXT(1.0f, 2.0f, 3.0f);
46 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
47
48 GLfloat factor = -1.0f;
49 glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
50 EXPECT_EQ(factor, 0.0f);
51
52 GLfloat units = -1.0f;
53 glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
54 EXPECT_EQ(units, 0.0f);
55
56 ASSERT_GL_NO_ERROR();
57 }
58
59 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_polygon_offset_clamp"));
60
61 // Default state
62 {
63 GLfloat factor = -1.0f;
64 glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
65 EXPECT_EQ(factor, 0.0f);
66
67 GLfloat units = -1.0f;
68 glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
69 EXPECT_EQ(units, 0.0f);
70
71 GLfloat clamp = -1.0f;
72 glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
73 EXPECT_EQ(clamp, 0.0f);
74
75 ASSERT_GL_NO_ERROR();
76 }
77
78 // Full state update using the new function
79 {
80 glPolygonOffsetClampEXT(1.0f, 2.0f, 3.0f);
81
82 GLfloat factor = -1.0f;
83 glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
84 EXPECT_EQ(factor, 1.0f);
85
86 GLfloat units = -1.0f;
87 glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
88 EXPECT_EQ(units, 2.0f);
89
90 GLfloat clamp = -1.0f;
91 glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
92 EXPECT_EQ(clamp, 3.0f);
93
94 ASSERT_GL_NO_ERROR();
95 }
96
97 // Core function resets the clamp value to zero
98 {
99 glPolygonOffset(4.0f, 5.0f);
100
101 GLfloat factor = -1.0f;
102 glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
103 EXPECT_EQ(factor, 4.0f);
104
105 GLfloat units = -1.0f;
106 glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
107 EXPECT_EQ(units, 5.0f);
108
109 GLfloat clamp = -1.0f;
110 glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
111 EXPECT_EQ(clamp, 0.0f);
112
113 ASSERT_GL_NO_ERROR();
114 }
115
116 // NaNs must be accepted and replaced with zeros
117 {
118 glPolygonOffsetClampEXT(6.0f, 7.0f, 8.0f);
119
120 float nan = std::numeric_limits<float>::quiet_NaN();
121 glPolygonOffsetClampEXT(nan, nan, nan);
122
123 GLfloat factor = -1.0f;
124 glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
125 EXPECT_EQ(factor, 0.0f);
126
127 GLfloat units = -1.0f;
128 glGetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
129 EXPECT_EQ(units, 0.0f);
130
131 GLfloat clamp = -1.0f;
132 glGetFloatv(GL_POLYGON_OFFSET_CLAMP_EXT, &clamp);
133 EXPECT_EQ(clamp, 0.0f);
134
135 ASSERT_GL_NO_ERROR();
136 }
137 }
138
139 // Test polygon offset clamping behavior. Ported from dEQP.
TEST_P(PolygonOffsetClampTest,Operation)140 TEST_P(PolygonOffsetClampTest, Operation)
141 {
142 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_polygon_offset_clamp"));
143 ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_depth_texture"));
144
145 GLTexture depthTexture;
146 glBindTexture(GL_TEXTURE_2D, depthTexture);
147 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 64, 64, 0, GL_DEPTH_COMPONENT,
148 GL_UNSIGNED_SHORT, nullptr);
149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
151
152 GLTexture colorTexture;
153 glBindTexture(GL_TEXTURE_2D, colorTexture);
154 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
157
158 GLFramebuffer fbo;
159 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
160 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
161 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
162
163 GLTexture colorReadbackTexture;
164 glBindTexture(GL_TEXTURE_2D, colorReadbackTexture);
165 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
166 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
168
169 GLFramebuffer fboReadback;
170 glBindFramebuffer(GL_FRAMEBUFFER, fboReadback);
171 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
172 colorReadbackTexture, 0);
173
174 ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
175
176 constexpr char kFS[] = R"(
177 varying highp vec2 v_texCoord;
178 uniform highp sampler2D tex;
179 void main()
180 {
181 // Store as unorm24
182 highp float d = floor(texture2D(tex, v_texCoord).r * 16777215.0);
183 highp float r = floor(d / 65536.0);
184 highp float g = floor(mod(d, 65536.0) / 256.0);
185 highp float b = mod(d, 256.0);
186 gl_FragColor = vec4(r, g, b, 1.0) / 255.0;
187 })";
188
189 ANGLE_GL_PROGRAM(readDepthProgram, essl1_shaders::vs::Texture2D(), kFS);
190
191 // Setup depth testing
192 glEnable(GL_DEPTH_TEST);
193 glDepthFunc(GL_ALWAYS);
194
195 // Bind framebuffer for drawing
196 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
197
198 // Offset units and clamp values. Should work with all depth buffer formats.
199 const std::vector<std::array<GLfloat, 2>> testValues = {
200 {-5000.0f, -0.0001f},
201 {+5000.0f, +0.0001f},
202 {-5000.0f, 0.0f},
203 {+5000.0f, 0.0f},
204 {-5000.0f, -std::numeric_limits<float>::infinity()},
205 {+5000.0f, +std::numeric_limits<float>::infinity()}};
206
207 auto readDepthValue = [&]() {
208 glBindFramebuffer(GL_FRAMEBUFFER, fboReadback);
209 glDisable(GL_DEPTH_TEST);
210 glUseProgram(readDepthProgram);
211 glUniform1i(glGetUniformLocation(readDepthProgram, "tex"), 0);
212
213 glBindTexture(GL_TEXTURE_2D, depthTexture);
214 drawQuad(readDepthProgram, "a_position", 0.5);
215
216 GLubyte pixels[4];
217 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
218
219 // Convert read depth value to GLfloat normalized
220 GLfloat depthValue = (GLfloat)(pixels[0] * 65536 + pixels[1] * 256 + pixels[2]) / 0xFFFFFF;
221
222 // Restore state
223 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
224 glEnable(GL_DEPTH_TEST);
225 glUseProgram(testProgram);
226
227 return depthValue;
228 };
229
230 for (auto testValue : testValues)
231 {
232 const GLfloat units = testValue[0];
233 const GLfloat clamp = testValue[1];
234
235 // Draw reference polygon
236 glDisable(GL_POLYGON_OFFSET_FILL);
237 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
238 drawQuad(testProgram, "a_position", 0.0);
239
240 // Get reference depth value
241 const GLfloat depthValue = readDepthValue();
242
243 // Draw polygon with depth offset
244 glEnable(GL_POLYGON_OFFSET_FILL);
245 glPolygonOffset(0.0f, units);
246 drawQuad(testProgram, "a_position", 0.0);
247
248 // Get depth value with offset
249 const GLfloat depthValueOffset = readDepthValue();
250
251 // Draw reference polygon
252 glDisable(GL_POLYGON_OFFSET_FILL);
253 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
254 drawQuad(testProgram, "a_position", 0.0);
255
256 // Draw polygon with depth offset
257 glEnable(GL_POLYGON_OFFSET_FILL);
258 glPolygonOffsetClampEXT(0.0f, units, clamp);
259 drawQuad(testProgram, "a_position", 0.0);
260
261 // Get depth value with clamped offset
262 const GLfloat depthValueOffsetClamp = readDepthValue();
263
264 // Check depth values
265 {
266 if (clamp == 0.0f || isinf(clamp))
267 {
268 ASSERT_NE(units, 0.0f);
269
270 // Ensure that offset works
271 if (units > 0.0f)
272 {
273 EXPECT_LT(depthValue, depthValueOffset);
274 EXPECT_LT(depthValue, depthValueOffsetClamp);
275 }
276 else if (units < 0.0f)
277 {
278 EXPECT_GT(depthValue, depthValueOffset);
279 EXPECT_GT(depthValue, depthValueOffsetClamp);
280 }
281
282 // Clamping must have no effect
283 EXPECT_EQ(depthValueOffset, depthValueOffsetClamp);
284 }
285 else if (clamp < 0.0f)
286 {
287 ASSERT_LT(units, 0);
288
289 // Negative clamp value sets effective offset to max(offset, clamp)
290 EXPECT_GT(depthValue, depthValueOffset);
291 EXPECT_GT(depthValue, depthValueOffsetClamp);
292 EXPECT_LT(depthValueOffset, depthValueOffsetClamp);
293 }
294 else if (clamp > 0.0f)
295 {
296 ASSERT_GT(units, 0);
297
298 // Positive clamp value sets effective offset to min(offset, clamp)
299 EXPECT_LT(depthValue, depthValueOffset);
300 EXPECT_LT(depthValue, depthValueOffsetClamp);
301 EXPECT_GT(depthValueOffset, depthValueOffsetClamp);
302 }
303 }
304 }
305 }
306
307 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PolygonOffsetClampTest);
308