1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Tessellation and geometry shader interaction tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es31fTessellationGeometryInteractionTests.hpp"
25
26 #include "tcuTestLog.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuImageCompare.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuStringTemplate.hpp"
33 #include "gluRenderContext.hpp"
34 #include "gluShaderProgram.hpp"
35 #include "gluStrUtil.hpp"
36 #include "gluContextInfo.hpp"
37 #include "gluObjectWrapper.hpp"
38 #include "gluPixelTransfer.hpp"
39 #include "glwFunctions.hpp"
40 #include "glwEnums.hpp"
41 #include "deStringUtil.hpp"
42 #include "deUniquePtr.hpp"
43
44 #include <sstream>
45 #include <algorithm>
46 #include <iterator>
47
48 namespace deqp
49 {
50 namespace gles31
51 {
52 namespace Functional
53 {
54 namespace
55 {
56
specializeShader(const std::string & shaderSource,const glu::ContextType & contextType)57 static std::string specializeShader(const std::string &shaderSource, const glu::ContextType &contextType)
58 {
59 const bool supportsES32orGL45 = glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
60 glu::contextSupports(contextType, glu::ApiType::core(4, 5));
61
62 const bool supportsGL45 = glu::contextSupports(contextType, glu::ApiType::core(4, 5));
63
64 std::map<std::string, std::string> shaderArgs;
65
66 shaderArgs["VERSION_DECL"] = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(contextType));
67 shaderArgs["EXTENSION_GEOMETRY_SHADER"] =
68 (supportsES32orGL45) ? ("") : ("#extension GL_EXT_geometry_shader : require\n");
69 shaderArgs["EXTENSION_TESSELATION_SHADER"] =
70 (supportsES32orGL45) ? ("") : ("#extension GL_EXT_tessellation_shader : require\n");
71 shaderArgs["EXTENSION_TESSELATION_POINT_SIZE"] =
72 (supportsGL45) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n");
73 shaderArgs["EXTENSION_GEOMETRY_POINT_SIZE"] =
74 (supportsGL45) ? ("") : ("#extension GL_EXT_geometry_point_size : require\n");
75
76 return tcu::StringTemplate(shaderSource).specialize(shaderArgs);
77 }
78
79 static const char *const s_positionVertexShader = "${VERSION_DECL}\n"
80 "in highp vec4 a_position;\n"
81 "void main (void)\n"
82 "{\n"
83 " gl_Position = a_position;\n"
84 "}\n";
85 static const char *const s_whiteOutputFragmentShader = "${VERSION_DECL}\n"
86 "layout(location = 0) out mediump vec4 fragColor;\n"
87 "void main (void)\n"
88 "{\n"
89 " fragColor = vec4(1.0);\n"
90 "}\n";
91
isBlack(const tcu::RGBA & c)92 static bool isBlack(const tcu::RGBA &c)
93 {
94 return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
95 }
96
97 class IdentityShaderCase : public TestCase
98 {
99 public:
100 IdentityShaderCase(Context &context, const char *name, const char *description);
101
102 protected:
103 std::string getVertexSource(void) const;
104 std::string getFragmentSource(void) const;
105 };
106
IdentityShaderCase(Context & context,const char * name,const char * description)107 IdentityShaderCase::IdentityShaderCase(Context &context, const char *name, const char *description)
108 : TestCase(context, name, description)
109 {
110 }
111
getVertexSource(void) const112 std::string IdentityShaderCase::getVertexSource(void) const
113 {
114 std::string source = "${VERSION_DECL}\n"
115 "in highp vec4 a_position;\n"
116 "out highp vec4 v_vertex_color;\n"
117 "void main (void)\n"
118 "{\n"
119 " gl_Position = a_position;\n"
120 " v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
121 "}\n";
122
123 return specializeShader(source, m_context.getRenderContext().getType());
124 }
125
getFragmentSource(void) const126 std::string IdentityShaderCase::getFragmentSource(void) const
127 {
128 std::string source = "${VERSION_DECL}\n"
129 "in mediump vec4 v_fragment_color;\n"
130 "layout(location = 0) out mediump vec4 fragColor;\n"
131 "void main (void)\n"
132 "{\n"
133 " fragColor = v_fragment_color;\n"
134 "}\n";
135
136 return specializeShader(source, m_context.getRenderContext().getType());
137 }
138
139 class IdentityGeometryShaderCase : public IdentityShaderCase
140 {
141 public:
142 enum CaseType
143 {
144 CASE_TRIANGLES = 0,
145 CASE_QUADS,
146 CASE_ISOLINES,
147 };
148
149 IdentityGeometryShaderCase(Context &context, const char *name, const char *description, CaseType caseType);
150 ~IdentityGeometryShaderCase(void);
151
152 private:
153 void init(void);
154 void deinit(void);
155 IterateResult iterate(void);
156
157 std::string getTessellationControlSource(void) const;
158 std::string getTessellationEvaluationSource(bool geometryActive) const;
159 std::string getGeometrySource(void) const;
160
161 enum
162 {
163 RENDER_SIZE = 128,
164 };
165
166 const CaseType m_case;
167 uint32_t m_patchBuffer;
168 };
169
IdentityGeometryShaderCase(Context & context,const char * name,const char * description,CaseType caseType)170 IdentityGeometryShaderCase::IdentityGeometryShaderCase(Context &context, const char *name, const char *description,
171 CaseType caseType)
172 : IdentityShaderCase(context, name, description)
173 , m_case(caseType)
174 , m_patchBuffer(0)
175 {
176 }
177
~IdentityGeometryShaderCase(void)178 IdentityGeometryShaderCase::~IdentityGeometryShaderCase(void)
179 {
180 deinit();
181 }
182
init(void)183 void IdentityGeometryShaderCase::init(void)
184 {
185 // Requirements
186 const bool supportsES32orGL45 =
187 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
188 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
189
190 if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
191 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
192 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
193
194 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
195 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
196 de::toString<int>(RENDER_SIZE) + " or larger render target.");
197
198 // Log
199
200 m_testCtx.getLog()
201 << tcu::TestLog::Message
202 << "Testing tessellating shader program output does not change when a passthrough geometry shader is "
203 "attached.\n"
204 << "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
205 << "Using additive blending to detect overlap.\n"
206 << tcu::TestLog::EndMessage;
207
208 // Resources
209
210 {
211 static const tcu::Vec4 patchBufferData[4] = {
212 tcu::Vec4(-0.9f, -0.9f, 0.0f, 1.0f),
213 tcu::Vec4(-0.9f, 0.9f, 0.0f, 1.0f),
214 tcu::Vec4(0.9f, -0.9f, 0.0f, 1.0f),
215 tcu::Vec4(0.9f, 0.9f, 0.0f, 1.0f),
216 };
217
218 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
219
220 gl.genBuffers(1, &m_patchBuffer);
221 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
222 gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
223 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
224 }
225 }
226
deinit(void)227 void IdentityGeometryShaderCase::deinit(void)
228 {
229 if (m_patchBuffer)
230 {
231 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
232 m_patchBuffer = 0;
233 }
234 }
235
iterate(void)236 IdentityGeometryShaderCase::IterateResult IdentityGeometryShaderCase::iterate(void)
237 {
238 const float innerTessellationLevel = 14.0f;
239 const float outerTessellationLevel = 14.0f;
240 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
241 tcu::Surface resultWithGeometry(RENDER_SIZE, RENDER_SIZE);
242 tcu::Surface resultWithoutGeometry(RENDER_SIZE, RENDER_SIZE);
243
244 const struct
245 {
246 const char *name;
247 const char *description;
248 bool containsGeometryShader;
249 tcu::PixelBufferAccess surfaceAccess;
250 } renderTargets[] = {
251 {"RenderWithGeometryShader", "Render with geometry shader", true, resultWithGeometry.getAccess()},
252 {"RenderWithoutGeometryShader", "Render without geometry shader", false, resultWithoutGeometry.getAccess()},
253 };
254
255 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
256 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
257 GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
258
259 gl.enable(GL_BLEND);
260 gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
261 gl.blendEquation(GL_FUNC_ADD);
262 GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
263
264 m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: inner " << innerTessellationLevel << ", outer "
265 << outerTessellationLevel << tcu::TestLog::EndMessage;
266
267 // render with and without geometry shader
268 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
269 {
270 const tcu::ScopedLogSection section(m_testCtx.getLog(), renderTargets[renderNdx].name,
271 renderTargets[renderNdx].description);
272 glu::ProgramSources sources;
273
274 sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
275 << glu::TessellationControlSource(getTessellationControlSource())
276 << glu::TessellationEvaluationSource(
277 getTessellationEvaluationSource(renderTargets[renderNdx].containsGeometryShader));
278
279 if (renderTargets[renderNdx].containsGeometryShader)
280 sources << glu::GeometrySource(getGeometrySource());
281
282 {
283 const glu::ShaderProgram program(m_context.getRenderContext(), sources);
284 const glu::VertexArray vao(m_context.getRenderContext());
285 const int posLocation = gl.getAttribLocation(program.getProgram(), "a_position");
286 const int innerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_innerTessellationLevel");
287 const int outerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_outerTessellationLevel");
288
289 m_testCtx.getLog() << program;
290
291 if (!program.isOk())
292 throw tcu::TestError("could not build program");
293 if (posLocation == -1)
294 throw tcu::TestError("a_position location was -1");
295 if (outerTessellationLoc == -1)
296 throw tcu::TestError("u_outerTessellationLevel location was -1");
297
298 gl.bindVertexArray(*vao);
299 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
300 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
301 gl.enableVertexAttribArray(posLocation);
302 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
303
304 gl.useProgram(program.getProgram());
305 gl.uniform1f(outerTessellationLoc, outerTessellationLevel);
306
307 if (innerTessellationLoc == -1)
308 gl.uniform1f(innerTessellationLoc, innerTessellationLevel);
309
310 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
311
312 gl.patchParameteri(GL_PATCH_VERTICES, (m_case == CASE_TRIANGLES) ? (3) : (4));
313 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
314
315 gl.clear(GL_COLOR_BUFFER_BIT);
316 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
317
318 gl.drawArrays(GL_PATCHES, 0, 4);
319 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
320
321 glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
322 }
323 }
324
325 if (tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison",
326 resultWithoutGeometry.getAccess(), resultWithGeometry.getAccess(),
327 tcu::UVec4(8, 8, 8, 255), tcu::IVec3(1, 1, 0), true,
328 tcu::COMPARE_LOG_RESULT))
329 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
330 else
331 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
332
333 return STOP;
334 }
335
getTessellationControlSource(void) const336 std::string IdentityGeometryShaderCase::getTessellationControlSource(void) const
337 {
338 std::ostringstream buf;
339
340 buf << "${VERSION_DECL}\n"
341 "${EXTENSION_TESSELATION_SHADER}"
342 "layout(vertices = 4) out;\n"
343 "\n"
344 "uniform highp float u_innerTessellationLevel;\n"
345 "uniform highp float u_outerTessellationLevel;\n"
346 "in highp vec4 v_vertex_color[];\n"
347 "out highp vec4 v_patch_color[];\n"
348 "\n"
349 "void main (void)\n"
350 "{\n"
351 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
352 " v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
353 "\n";
354
355 if (m_case == CASE_TRIANGLES)
356 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
357 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
358 " gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
359 " gl_TessLevelInner[0] = u_innerTessellationLevel;\n";
360 else if (m_case == CASE_QUADS)
361 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
362 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
363 " gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
364 " gl_TessLevelOuter[3] = u_outerTessellationLevel;\n"
365 " gl_TessLevelInner[0] = u_innerTessellationLevel;\n"
366 " gl_TessLevelInner[1] = u_innerTessellationLevel;\n";
367 else if (m_case == CASE_ISOLINES)
368 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
369 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n";
370 else
371 DE_ASSERT(false);
372
373 buf << "}\n";
374
375 return specializeShader(buf.str(), m_context.getRenderContext().getType());
376 }
377
getTessellationEvaluationSource(bool geometryActive) const378 std::string IdentityGeometryShaderCase::getTessellationEvaluationSource(bool geometryActive) const
379 {
380 const char *const colorOutputName = ((geometryActive) ? ("v_evaluated_color") : ("v_fragment_color"));
381 std::ostringstream buf;
382
383 buf << "${VERSION_DECL}\n"
384 "${EXTENSION_TESSELATION_SHADER}"
385 "layout("
386 << ((m_case == CASE_TRIANGLES) ? ("triangles") :
387 (m_case == CASE_QUADS) ? ("quads") :
388 ("isolines"))
389 << ") in;\n"
390 "\n"
391 "in highp vec4 v_patch_color[];\n"
392 "out highp vec4 "
393 << colorOutputName
394 << ";\n"
395 "\n"
396 "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
397 "void main (void)\n"
398 "{\n";
399
400 if (m_case == CASE_TRIANGLES)
401 buf << " vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, "
402 "1.3));\n"
403 " vec3 cweights = gl_TessCoord;\n"
404 " gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + "
405 "weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
406 " "
407 << colorOutputName
408 << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
409 else if (m_case == CASE_QUADS || m_case == CASE_ISOLINES)
410 buf << " vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
411 " vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
412 " vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
413 " vec2 cweights = gl_TessCoord.xy;\n"
414 " gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), "
415 "mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
416 " "
417 << colorOutputName
418 << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], "
419 "cweights.y), cweights.x);\n";
420 else
421 DE_ASSERT(false);
422
423 buf << "}\n";
424
425 return specializeShader(buf.str(), m_context.getRenderContext().getType());
426 }
427
getGeometrySource(void) const428 std::string IdentityGeometryShaderCase::getGeometrySource(void) const
429 {
430 const char *const geometryInputPrimitive = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
431 const char *const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
432 const int numEmitVertices = (m_case == CASE_ISOLINES) ? (2) : (3);
433 std::ostringstream buf;
434
435 buf << "${VERSION_DECL}\n"
436 "${EXTENSION_GEOMETRY_SHADER}"
437 "layout("
438 << geometryInputPrimitive
439 << ") in;\n"
440 "layout("
441 << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
442 << ") out;\n"
443 "\n"
444 "in highp vec4 v_evaluated_color[];\n"
445 "out highp vec4 v_fragment_color;\n"
446 "\n"
447 "void main (void)\n"
448 "{\n"
449 " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
450 " {\n"
451 " gl_Position = gl_in[ndx].gl_Position;\n"
452 " v_fragment_color = v_evaluated_color[ndx];\n"
453 " EmitVertex();\n"
454 " }\n"
455 "}\n";
456
457 return specializeShader(buf.str(), m_context.getRenderContext().getType());
458 }
459
460 class IdentityTessellationShaderCase : public IdentityShaderCase
461 {
462 public:
463 enum CaseType
464 {
465 CASE_TRIANGLES = 0,
466 CASE_ISOLINES,
467 };
468
469 IdentityTessellationShaderCase(Context &context, const char *name, const char *description, CaseType caseType);
470 ~IdentityTessellationShaderCase(void);
471
472 private:
473 void init(void);
474 void deinit(void);
475 IterateResult iterate(void);
476
477 std::string getTessellationControlSource(void) const;
478 std::string getTessellationEvaluationSource(void) const;
479 std::string getGeometrySource(bool tessellationActive) const;
480
481 enum
482 {
483 RENDER_SIZE = 256,
484 };
485
486 const CaseType m_case;
487 uint32_t m_dataBuffer;
488 };
489
IdentityTessellationShaderCase(Context & context,const char * name,const char * description,CaseType caseType)490 IdentityTessellationShaderCase::IdentityTessellationShaderCase(Context &context, const char *name,
491 const char *description, CaseType caseType)
492 : IdentityShaderCase(context, name, description)
493 , m_case(caseType)
494 , m_dataBuffer(0)
495 {
496 }
497
~IdentityTessellationShaderCase(void)498 IdentityTessellationShaderCase::~IdentityTessellationShaderCase(void)
499 {
500 deinit();
501 }
502
init(void)503 void IdentityTessellationShaderCase::init(void)
504 {
505 // Requirements
506 const bool supportsES32orGL45 =
507 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
508 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
509
510 if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
511 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
512 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
513
514 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
515 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
516 de::toString<int>(RENDER_SIZE) + " or larger render target.");
517
518 // Log
519
520 m_testCtx.getLog()
521 << tcu::TestLog::Message
522 << "Testing geometry shading shader program output does not change when a passthrough tessellation shader is "
523 "attached.\n"
524 << "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
525 << "Using additive blending to detect overlap.\n"
526 << tcu::TestLog::EndMessage;
527
528 // Resources
529
530 {
531 static const tcu::Vec4 pointData[] = {
532 tcu::Vec4(-0.4f, 0.4f, 0.0f, 1.0f),
533 tcu::Vec4(0.0f, -0.5f, 0.0f, 1.0f),
534 tcu::Vec4(0.4f, 0.4f, 0.0f, 1.0f),
535 };
536 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
537
538 gl.genBuffers(1, &m_dataBuffer);
539 gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
540 gl.bufferData(GL_ARRAY_BUFFER, sizeof(pointData), pointData, GL_STATIC_DRAW);
541 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
542 }
543 }
544
deinit(void)545 void IdentityTessellationShaderCase::deinit(void)
546 {
547 if (m_dataBuffer)
548 {
549 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_dataBuffer);
550 m_dataBuffer = 0;
551 }
552 }
553
iterate(void)554 IdentityTessellationShaderCase::IterateResult IdentityTessellationShaderCase::iterate(void)
555 {
556 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
557 tcu::Surface resultWithTessellation(RENDER_SIZE, RENDER_SIZE);
558 tcu::Surface resultWithoutTessellation(RENDER_SIZE, RENDER_SIZE);
559 const int numPrimitiveVertices = (m_case == CASE_TRIANGLES) ? (3) : (2);
560
561 const struct
562 {
563 const char *name;
564 const char *description;
565 bool containsTessellationShaders;
566 tcu::PixelBufferAccess surfaceAccess;
567 } renderTargets[] = {
568 {"RenderWithTessellationShader", "Render with tessellation shader", true, resultWithTessellation.getAccess()},
569 {"RenderWithoutTessellationShader", "Render without tessellation shader", false,
570 resultWithoutTessellation.getAccess()},
571 };
572
573 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
574 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
575 GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
576
577 gl.enable(GL_BLEND);
578 gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
579 gl.blendEquation(GL_FUNC_ADD);
580 GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
581
582 // render with and without tessellation shader
583 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
584 {
585 const tcu::ScopedLogSection section(m_testCtx.getLog(), renderTargets[renderNdx].name,
586 renderTargets[renderNdx].description);
587 glu::ProgramSources sources;
588
589 sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
590 << glu::GeometrySource(getGeometrySource(renderTargets[renderNdx].containsTessellationShaders));
591
592 if (renderTargets[renderNdx].containsTessellationShaders)
593 sources << glu::TessellationControlSource(getTessellationControlSource())
594 << glu::TessellationEvaluationSource(getTessellationEvaluationSource());
595
596 {
597 const glu::ShaderProgram program(m_context.getRenderContext(), sources);
598 const glu::VertexArray vao(m_context.getRenderContext());
599 const int posLocation = gl.getAttribLocation(program.getProgram(), "a_position");
600
601 m_testCtx.getLog() << program;
602
603 if (!program.isOk())
604 throw tcu::TestError("could not build program");
605 if (posLocation == -1)
606 throw tcu::TestError("a_position location was -1");
607
608 gl.bindVertexArray(*vao);
609 gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
610 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
611 gl.enableVertexAttribArray(posLocation);
612 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
613
614 gl.useProgram(program.getProgram());
615 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
616
617 gl.clear(GL_COLOR_BUFFER_BIT);
618 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
619
620 if (renderTargets[renderNdx].containsTessellationShaders)
621 {
622 gl.patchParameteri(GL_PATCH_VERTICES, numPrimitiveVertices);
623 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
624
625 gl.drawArrays(GL_PATCHES, 0, numPrimitiveVertices);
626 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
627 }
628 else
629 {
630 gl.drawArrays((m_case == CASE_TRIANGLES) ? (GL_TRIANGLES) : (GL_LINES), 0, numPrimitiveVertices);
631 GLU_EXPECT_NO_ERROR(gl.getError(), "draw primitives");
632 }
633
634 glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
635 }
636 }
637
638 // compare
639 {
640 bool imageOk;
641
642 if (m_context.getRenderTarget().getNumSamples() > 1)
643 imageOk = tcu::fuzzyCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison",
644 resultWithoutTessellation.getAccess(), resultWithTessellation.getAccess(),
645 0.03f, tcu::COMPARE_LOG_RESULT);
646 else
647 imageOk = tcu::intThresholdPositionDeviationCompare(
648 m_testCtx.getLog(), "ImageCompare", "Image comparison", resultWithoutTessellation.getAccess(),
649 resultWithTessellation.getAccess(), tcu::UVec4(8, 8, 8, 255), //!< threshold
650 tcu::IVec3(1, 1, 0), //!< 3x3 search kernel
651 true, //!< fragments may end up over the viewport, just ignore them
652 tcu::COMPARE_LOG_RESULT);
653
654 if (imageOk)
655 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
656 else
657 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
658 }
659
660 return STOP;
661 }
662
getTessellationControlSource(void) const663 std::string IdentityTessellationShaderCase::getTessellationControlSource(void) const
664 {
665 std::ostringstream buf;
666
667 buf << "${VERSION_DECL}\n"
668 "${EXTENSION_TESSELATION_SHADER}"
669 "layout(vertices = "
670 << ((m_case == CASE_TRIANGLES) ? (3) : (2))
671 << ") out;\n"
672 "\n"
673 "in highp vec4 v_vertex_color[];\n"
674 "out highp vec4 v_control_color[];\n"
675 "\n"
676 "void main (void)\n"
677 "{\n"
678 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
679 " v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
680 "\n";
681
682 if (m_case == CASE_TRIANGLES)
683 buf << " gl_TessLevelOuter[0] = 1.0;\n"
684 " gl_TessLevelOuter[1] = 1.0;\n"
685 " gl_TessLevelOuter[2] = 1.0;\n"
686 " gl_TessLevelInner[0] = 1.0;\n";
687 else if (m_case == CASE_ISOLINES)
688 buf << " gl_TessLevelOuter[0] = 1.0;\n"
689 " gl_TessLevelOuter[1] = 1.0;\n";
690 else
691 DE_ASSERT(false);
692
693 buf << "}\n";
694
695 return specializeShader(buf.str(), m_context.getRenderContext().getType());
696 }
697
getTessellationEvaluationSource(void) const698 std::string IdentityTessellationShaderCase::getTessellationEvaluationSource(void) const
699 {
700 std::ostringstream buf;
701
702 buf << "${VERSION_DECL}\n"
703 "${EXTENSION_TESSELATION_SHADER}"
704 "layout("
705 << ((m_case == CASE_TRIANGLES) ? ("triangles") : ("isolines"))
706 << ") in;\n"
707 "\n"
708 "in highp vec4 v_control_color[];\n"
709 "out highp vec4 v_evaluated_color;\n"
710 "\n"
711 "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
712 "void main (void)\n"
713 "{\n";
714
715 if (m_case == CASE_TRIANGLES)
716 buf << " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + "
717 "gl_TessCoord.z * gl_in[2].gl_Position;\n"
718 " v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + "
719 "gl_TessCoord.z * v_control_color[2];\n";
720 else if (m_case == CASE_ISOLINES)
721 buf << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
722 " v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
723 else
724 DE_ASSERT(false);
725
726 buf << "}\n";
727
728 return specializeShader(buf.str(), m_context.getRenderContext().getType());
729 }
730
getGeometrySource(bool tessellationActive) const731 std::string IdentityTessellationShaderCase::getGeometrySource(bool tessellationActive) const
732 {
733 const char *const colorSourceName = (tessellationActive) ? ("v_evaluated_color") : ("v_vertex_color");
734 const char *const geometryInputPrimitive = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
735 const char *const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
736 const int numEmitVertices = (m_case == CASE_ISOLINES) ? (11) : (8);
737 std::ostringstream buf;
738
739 buf << "${VERSION_DECL}\n"
740 "${EXTENSION_GEOMETRY_SHADER}"
741 "layout("
742 << geometryInputPrimitive
743 << ") in;\n"
744 "layout("
745 << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
746 << ") out;\n"
747 "\n"
748 "in highp vec4 "
749 << colorSourceName
750 << "[];\n"
751 "out highp vec4 v_fragment_color;\n"
752 "\n"
753 "void main (void)\n"
754 "{\n";
755
756 if (m_case == CASE_TRIANGLES)
757 {
758 buf << " vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
759 "\n"
760 " for (int ndx = 0; ndx < 4; ++ndx)\n"
761 " {\n"
762 " gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
763 " v_fragment_color = "
764 << colorSourceName
765 << "[ndx % 3];\n"
766 " EmitVertex();\n"
767 "\n"
768 " gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
769 " v_fragment_color = "
770 << colorSourceName
771 << "[ndx % 3];\n"
772 " EmitVertex();\n"
773 " }\n";
774 }
775 else if (m_case == CASE_ISOLINES)
776 {
777 buf << " vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - "
778 "gl_in[0].gl_Position.x, 0.0, 0.0);\n"
779 " for (int i = 0; i <= 10; ++i)\n"
780 " {\n"
781 " float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
782 " float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
783 " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
784 " v_fragment_color = mix("
785 << colorSourceName << "[0], " << colorSourceName
786 << "[1], xweight);\n"
787 " EmitVertex();\n"
788 " }\n";
789 }
790 else
791 DE_ASSERT(false);
792
793 buf << "}\n";
794
795 return specializeShader(buf.str(), m_context.getRenderContext().getType());
796 }
797
798 class FeedbackPrimitiveTypeCase : public TestCase
799 {
800 public:
801 enum TessellationOutputType
802 {
803 TESSELLATION_OUT_TRIANGLES = 0,
804 TESSELLATION_OUT_QUADS,
805 TESSELLATION_OUT_ISOLINES,
806
807 TESSELLATION_OUT_LAST
808 };
809 enum TessellationPointMode
810 {
811 TESSELLATION_POINTMODE_OFF = 0,
812 TESSELLATION_POINTMODE_ON,
813
814 TESSELLATION_POINTMODE_LAST
815 };
816 enum GeometryOutputType
817 {
818 GEOMETRY_OUTPUT_POINTS = 0,
819 GEOMETRY_OUTPUT_LINES,
820 GEOMETRY_OUTPUT_TRIANGLES,
821
822 GEOMETRY_OUTPUT_LAST
823 };
824
825 FeedbackPrimitiveTypeCase(Context &context, const char *name, const char *description,
826 TessellationOutputType tessellationOutput, TessellationPointMode tessellationPointMode,
827 GeometryOutputType geometryOutputType);
828 ~FeedbackPrimitiveTypeCase(void);
829
830 private:
831 void init(void);
832 void deinit(void);
833 IterateResult iterate(void);
834
835 void renderWithFeedback(tcu::Surface &dst);
836 void renderWithoutFeedback(tcu::Surface &dst);
837 void verifyFeedbackResults(const std::vector<tcu::Vec4> &feedbackResult);
838 void verifyRenderedImage(const tcu::Surface &image, const std::vector<tcu::Vec4> &vertices);
839
840 void genTransformFeedback(void);
841 int getNumGeneratedElementsPerPrimitive(void) const;
842 int getNumGeneratedPrimitives(void) const;
843 int getNumTessellatedPrimitives(void) const;
844 int getGeometryAmplification(void) const;
845
846 std::string getVertexSource(void) const;
847 std::string getFragmentSource(void) const;
848 std::string getTessellationControlSource(void) const;
849 std::string getTessellationEvaluationSource(void) const;
850 std::string getGeometrySource(void) const;
851
852 static const char *getTessellationOutputDescription(TessellationOutputType tessellationOutput,
853 TessellationPointMode tessellationPointMode);
854 static const char *getGeometryInputDescription(TessellationOutputType tessellationOutput,
855 TessellationPointMode tessellationPointMode);
856 static const char *getGeometryOutputDescription(GeometryOutputType geometryOutput);
857 glw::GLenum getOutputPrimitiveGLType(void) const;
858
859 enum
860 {
861 RENDER_SIZE = 128,
862 };
863
864 const TessellationOutputType m_tessellationOutput;
865 const TessellationPointMode m_tessellationPointMode;
866 const GeometryOutputType m_geometryOutputType;
867
868 glu::ShaderProgram *m_feedbackProgram;
869 glu::ShaderProgram *m_nonFeedbackProgram;
870 uint32_t m_patchBuffer;
871 uint32_t m_feedbackID;
872 uint32_t m_feedbackBuffer;
873 };
874
FeedbackPrimitiveTypeCase(Context & context,const char * name,const char * description,TessellationOutputType tessellationOutput,TessellationPointMode tessellationPointMode,GeometryOutputType geometryOutputType)875 FeedbackPrimitiveTypeCase::FeedbackPrimitiveTypeCase(Context &context, const char *name, const char *description,
876 TessellationOutputType tessellationOutput,
877 TessellationPointMode tessellationPointMode,
878 GeometryOutputType geometryOutputType)
879 : TestCase(context, name, description)
880 , m_tessellationOutput(tessellationOutput)
881 , m_tessellationPointMode(tessellationPointMode)
882 , m_geometryOutputType(geometryOutputType)
883 , m_feedbackProgram(DE_NULL)
884 , m_nonFeedbackProgram(DE_NULL)
885 , m_patchBuffer(0)
886 , m_feedbackID(0)
887 , m_feedbackBuffer(0)
888 {
889 DE_ASSERT(tessellationOutput < TESSELLATION_OUT_LAST);
890 DE_ASSERT(tessellationPointMode < TESSELLATION_POINTMODE_LAST);
891 DE_ASSERT(geometryOutputType < GEOMETRY_OUTPUT_LAST);
892 }
893
~FeedbackPrimitiveTypeCase(void)894 FeedbackPrimitiveTypeCase::~FeedbackPrimitiveTypeCase(void)
895 {
896 deinit();
897 }
898
init(void)899 void FeedbackPrimitiveTypeCase::init(void)
900 {
901 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
902
903 // Requirements
904 const bool supportsES32orGL45 =
905 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
906 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
907
908 if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
909 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
910 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
911
912 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
913 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
914 de::toString<int>(RENDER_SIZE) + " or larger render target.");
915
916 // Log
917
918 m_testCtx.getLog() << tcu::TestLog::Message << "Testing "
919 << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "->"
920 << getGeometryInputDescription(m_tessellationOutput, m_tessellationPointMode)
921 << " primitive conversion with and without transform feedback.\n"
922 << "Sending a patch of 4 vertices (2x2 uniform grid) to tessellation control shader.\n"
923 << "Control shader emits a patch of 9 vertices (3x3 uniform grid).\n"
924 << "Setting outer tessellation level = 3, inner = 3.\n"
925 << "Primitive generator emits "
926 << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "\n"
927 << "Geometry shader transforms emitted primitives to "
928 << getGeometryOutputDescription(m_geometryOutputType) << "\n"
929 << "Reading back vertex positions of generated primitives using transform feedback.\n"
930 << "Verifying rendered image and feedback vertices are consistent.\n"
931 << "Rendering scene again with identical shader program, but without setting feedback varying. "
932 "Expecting similar output image."
933 << tcu::TestLog::EndMessage;
934
935 // Resources
936
937 {
938 static const tcu::Vec4 patchBufferData[4] = {
939 tcu::Vec4(-0.9f, -0.9f, 0.0f, 1.0f),
940 tcu::Vec4(-0.9f, 0.9f, 0.0f, 1.0f),
941 tcu::Vec4(0.9f, -0.9f, 0.0f, 1.0f),
942 tcu::Vec4(0.9f, 0.9f, 0.0f, 1.0f),
943 };
944
945 gl.genBuffers(1, &m_patchBuffer);
946 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
947 gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
948 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
949 }
950
951 m_feedbackProgram = new glu::ShaderProgram(
952 m_context.getRenderContext(),
953 glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
954 << glu::TessellationControlSource(getTessellationControlSource())
955 << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
956 << glu::GeometrySource(getGeometrySource())
957 << glu::TransformFeedbackVarying("tf_someVertexPosition")
958 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS));
959 m_testCtx.getLog() << *m_feedbackProgram;
960 if (!m_feedbackProgram->isOk())
961 throw tcu::TestError("failed to build program");
962
963 m_nonFeedbackProgram = new glu::ShaderProgram(
964 m_context.getRenderContext(),
965 glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
966 << glu::TessellationControlSource(getTessellationControlSource())
967 << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
968 << glu::GeometrySource(getGeometrySource()));
969 if (!m_nonFeedbackProgram->isOk())
970 {
971 m_testCtx.getLog() << *m_nonFeedbackProgram;
972 throw tcu::TestError("failed to build program");
973 }
974
975 genTransformFeedback();
976 }
977
deinit(void)978 void FeedbackPrimitiveTypeCase::deinit(void)
979 {
980 if (m_patchBuffer)
981 {
982 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
983 m_patchBuffer = 0;
984 }
985
986 if (m_feedbackBuffer)
987 {
988 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_feedbackBuffer);
989 m_feedbackBuffer = 0;
990 }
991
992 if (m_feedbackID)
993 {
994 m_context.getRenderContext().getFunctions().deleteTransformFeedbacks(1, &m_feedbackID);
995 m_feedbackID = 0;
996 }
997
998 if (m_feedbackProgram)
999 {
1000 delete m_feedbackProgram;
1001 m_feedbackProgram = DE_NULL;
1002 }
1003
1004 if (m_nonFeedbackProgram)
1005 {
1006 delete m_nonFeedbackProgram;
1007 m_nonFeedbackProgram = DE_NULL;
1008 }
1009 }
1010
iterate(void)1011 FeedbackPrimitiveTypeCase::IterateResult FeedbackPrimitiveTypeCase::iterate(void)
1012 {
1013 tcu::Surface feedbackResult(RENDER_SIZE, RENDER_SIZE);
1014 tcu::Surface nonFeedbackResult(RENDER_SIZE, RENDER_SIZE);
1015
1016 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1017
1018 // render with and without XFB
1019 renderWithFeedback(feedbackResult);
1020 renderWithoutFeedback(nonFeedbackResult);
1021
1022 // compare
1023 {
1024 bool imageOk;
1025
1026 m_testCtx.getLog() << tcu::TestLog::Message
1027 << "Comparing the image rendered with no transform feedback against the image rendered with "
1028 "enabled transform feedback."
1029 << tcu::TestLog::EndMessage;
1030
1031 if (m_context.getRenderTarget().getNumSamples() > 1)
1032 imageOk =
1033 tcu::fuzzyCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison", feedbackResult.getAccess(),
1034 nonFeedbackResult.getAccess(), 0.03f, tcu::COMPARE_LOG_RESULT);
1035 else
1036 imageOk = tcu::intThresholdPositionDeviationCompare(
1037 m_testCtx.getLog(), "ImageCompare", "Image comparison", feedbackResult.getAccess(),
1038 nonFeedbackResult.getAccess(), tcu::UVec4(8, 8, 8, 255), //!< threshold
1039 tcu::IVec3(1, 1, 0), //!< 3x3 search kernel
1040 true, //!< fragments may end up over the viewport, just ignore them
1041 tcu::COMPARE_LOG_RESULT);
1042
1043 if (!imageOk)
1044 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1045 }
1046
1047 return STOP;
1048 }
1049
renderWithFeedback(tcu::Surface & dst)1050 void FeedbackPrimitiveTypeCase::renderWithFeedback(tcu::Surface &dst)
1051 {
1052 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1053 const glu::VertexArray vao(m_context.getRenderContext());
1054 const glu::Query primitivesGeneratedQuery(m_context.getRenderContext());
1055 const int posLocation = gl.getAttribLocation(m_feedbackProgram->getProgram(), "a_position");
1056 const glw::GLenum feedbackPrimitiveMode = getOutputPrimitiveGLType();
1057
1058 if (posLocation == -1)
1059 throw tcu::TestError("a_position was -1");
1060
1061 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering with transform feedback" << tcu::TestLog::EndMessage;
1062
1063 gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
1064 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1065 gl.clear(GL_COLOR_BUFFER_BIT);
1066 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1067
1068 gl.bindVertexArray(*vao);
1069 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
1070 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
1071 gl.enableVertexAttribArray(posLocation);
1072 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
1073
1074 gl.useProgram(m_feedbackProgram->getProgram());
1075 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1076
1077 gl.patchParameteri(GL_PATCH_VERTICES, 4);
1078 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1079
1080 gl.beginQuery(GL_PRIMITIVES_GENERATED, *primitivesGeneratedQuery);
1081 GLU_EXPECT_NO_ERROR(gl.getError(), "begin GL_PRIMITIVES_GENERATED query");
1082
1083 m_testCtx.getLog() << tcu::TestLog::Message << "Begin transform feedback with mode "
1084 << glu::getPrimitiveTypeStr(feedbackPrimitiveMode) << tcu::TestLog::EndMessage;
1085
1086 gl.beginTransformFeedback(feedbackPrimitiveMode);
1087 GLU_EXPECT_NO_ERROR(gl.getError(), "begin xfb");
1088
1089 m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES"
1090 << tcu::TestLog::EndMessage;
1091
1092 gl.drawArrays(GL_PATCHES, 0, 4);
1093 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1094
1095 gl.endTransformFeedback();
1096 GLU_EXPECT_NO_ERROR(gl.getError(), "end xfb");
1097
1098 gl.endQuery(GL_PRIMITIVES_GENERATED);
1099 GLU_EXPECT_NO_ERROR(gl.getError(), "end GL_PRIMITIVES_GENERATED query");
1100
1101 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1102 GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
1103
1104 // verify GL_PRIMITIVES_GENERATED
1105 {
1106 glw::GLuint primitivesGeneratedResult = 0;
1107 gl.getQueryObjectuiv(*primitivesGeneratedQuery, GL_QUERY_RESULT, &primitivesGeneratedResult);
1108 GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_PRIMITIVES_GENERATED value");
1109
1110 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GL_PRIMITIVES_GENERATED, expecting "
1111 << getNumGeneratedPrimitives() << tcu::TestLog::EndMessage;
1112
1113 if ((int)primitivesGeneratedResult != getNumGeneratedPrimitives())
1114 {
1115 m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_PRIMITIVES_GENERATED was "
1116 << primitivesGeneratedResult << tcu::TestLog::EndMessage;
1117 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected GL_PRIMITIVES_GENERATED");
1118 }
1119 else
1120 m_testCtx.getLog() << tcu::TestLog::Message << "GL_PRIMITIVES_GENERATED valid." << tcu::TestLog::EndMessage;
1121 }
1122
1123 // feedback
1124 {
1125 std::vector<tcu::Vec4> feedbackResults(getNumGeneratedElementsPerPrimitive() * getNumGeneratedPrimitives());
1126 const void *mappedPtr =
1127 gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
1128 (glw::GLsizeiptr)(feedbackResults.size() * sizeof(tcu::Vec4)), GL_MAP_READ_BIT);
1129 glw::GLboolean unmapResult;
1130
1131 GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
1132
1133 m_testCtx.getLog() << tcu::TestLog::Message << "Reading transform feedback buffer." << tcu::TestLog::EndMessage;
1134 if (!mappedPtr)
1135 throw tcu::TestError("mapBufferRange returned null");
1136
1137 deMemcpy(feedbackResults[0].getPtr(), mappedPtr, (int)(feedbackResults.size() * sizeof(tcu::Vec4)));
1138
1139 unmapResult = gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
1140 GLU_EXPECT_NO_ERROR(gl.getError(), "unmapBuffer");
1141
1142 if (unmapResult != GL_TRUE)
1143 throw tcu::TestError("unmapBuffer failed, did not return true");
1144
1145 // verify transform results
1146 verifyFeedbackResults(feedbackResults);
1147
1148 // verify feedback results are consistent with rendered image
1149 verifyRenderedImage(dst, feedbackResults);
1150 }
1151 }
1152
renderWithoutFeedback(tcu::Surface & dst)1153 void FeedbackPrimitiveTypeCase::renderWithoutFeedback(tcu::Surface &dst)
1154 {
1155 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1156 const glu::VertexArray vao(m_context.getRenderContext());
1157 const int posLocation = gl.getAttribLocation(m_nonFeedbackProgram->getProgram(), "a_position");
1158
1159 if (posLocation == -1)
1160 throw tcu::TestError("a_position was -1");
1161
1162 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering without transform feedback" << tcu::TestLog::EndMessage;
1163
1164 gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
1165 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1166 gl.clear(GL_COLOR_BUFFER_BIT);
1167 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1168
1169 gl.bindVertexArray(*vao);
1170 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
1171 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
1172 gl.enableVertexAttribArray(posLocation);
1173 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
1174
1175 gl.useProgram(m_nonFeedbackProgram->getProgram());
1176 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1177
1178 gl.patchParameteri(GL_PATCH_VERTICES, 4);
1179 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1180
1181 m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES"
1182 << tcu::TestLog::EndMessage;
1183
1184 gl.drawArrays(GL_PATCHES, 0, 4);
1185 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1186
1187 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1188 GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
1189 }
1190
verifyFeedbackResults(const std::vector<tcu::Vec4> & feedbackResult)1191 void FeedbackPrimitiveTypeCase::verifyFeedbackResults(const std::vector<tcu::Vec4> &feedbackResult)
1192 {
1193 const int geometryAmplification = getGeometryAmplification();
1194 const int elementsPerPrimitive = getNumGeneratedElementsPerPrimitive();
1195 const int errorFloodThreshold = 8;
1196 int readNdx = 0;
1197 int numErrors = 0;
1198
1199 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying feedback results." << tcu::TestLog::EndMessage;
1200
1201 for (int tessellatedPrimitiveNdx = 0; tessellatedPrimitiveNdx < getNumTessellatedPrimitives();
1202 ++tessellatedPrimitiveNdx)
1203 {
1204 const tcu::Vec4 primitiveVertex = feedbackResult[readNdx];
1205
1206 // check the generated vertices are in the proper range (range: -0.4 <-> 0.4)
1207 {
1208 const float equalThreshold = 1.0e-6f;
1209 const bool centroidOk =
1210 (primitiveVertex.x() >= -0.4f - equalThreshold) && (primitiveVertex.x() <= 0.4f + equalThreshold) &&
1211 (primitiveVertex.y() >= -0.4f - equalThreshold) && (primitiveVertex.y() <= 0.4f + equalThreshold) &&
1212 (de::abs(primitiveVertex.z()) < equalThreshold) &&
1213 (de::abs(primitiveVertex.w() - 1.0f) < equalThreshold);
1214
1215 if (!centroidOk && numErrors++ < errorFloodThreshold)
1216 {
1217 m_testCtx.getLog() << tcu::TestLog::Message << "Element at index " << (readNdx)
1218 << " (tessellation invocation " << tessellatedPrimitiveNdx << ")\n"
1219 << "\texpected vertex in range: ( [-0.4, 0.4], [-0.4, 0.4], 0.0, 1.0 )\n"
1220 << "\tgot: " << primitiveVertex << tcu::TestLog::EndMessage;
1221
1222 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid feedback output");
1223
1224 ++readNdx;
1225 continue;
1226 }
1227 }
1228
1229 // check all other primitives generated from this tessellated primitive have the same feedback value
1230 for (int generatedPrimitiveNdx = 0; generatedPrimitiveNdx < geometryAmplification; ++generatedPrimitiveNdx)
1231 for (int primitiveVertexNdx = 0; primitiveVertexNdx < elementsPerPrimitive; ++primitiveVertexNdx)
1232 {
1233 const tcu::Vec4 generatedElementVertex = feedbackResult[readNdx];
1234 const tcu::Vec4 equalThreshold(1.0e-6f);
1235
1236 if (tcu::boolAny(tcu::greaterThan(tcu::abs(primitiveVertex - generatedElementVertex), equalThreshold)))
1237 {
1238 if (numErrors++ < errorFloodThreshold)
1239 {
1240 m_testCtx.getLog()
1241 << tcu::TestLog::Message << "Element at index " << (readNdx) << " (tessellation invocation "
1242 << tessellatedPrimitiveNdx << ", geometry primitive " << generatedPrimitiveNdx
1243 << ", emitted vertex " << primitiveVertexNdx << "):\n"
1244 << "\tfeedback result was not contant over whole primitive.\n"
1245 << "\tfirst emitted value: " << primitiveVertex << "\n"
1246 << "\tcurrent emitted value:" << generatedElementVertex << "\n"
1247 << tcu::TestLog::EndMessage;
1248 }
1249
1250 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL,
1251 "got multiple different feedback values for a single primitive");
1252 }
1253
1254 readNdx++;
1255 }
1256 }
1257
1258 if (numErrors > errorFloodThreshold)
1259 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (numErrors - errorFloodThreshold) << " error(s)."
1260 << tcu::TestLog::EndMessage;
1261 }
1262
feedbackResultCompare(const tcu::Vec4 & a,const tcu::Vec4 & b)1263 static bool feedbackResultCompare(const tcu::Vec4 &a, const tcu::Vec4 &b)
1264 {
1265 if (a.x() < b.x())
1266 return true;
1267 if (a.x() > b.x())
1268 return false;
1269
1270 return a.y() < b.y();
1271 }
1272
verifyRenderedImage(const tcu::Surface & image,const std::vector<tcu::Vec4> & tfVertices)1273 void FeedbackPrimitiveTypeCase::verifyRenderedImage(const tcu::Surface &image, const std::vector<tcu::Vec4> &tfVertices)
1274 {
1275 std::vector<tcu::Vec4> vertices;
1276
1277 m_testCtx.getLog() << tcu::TestLog::Message << "Comparing result image against feedback results."
1278 << tcu::TestLog::EndMessage;
1279
1280 // Check only unique vertices
1281 std::unique_copy(tfVertices.begin(), tfVertices.end(), std::back_insert_iterator<std::vector<tcu::Vec4>>(vertices));
1282 std::sort(vertices.begin(), vertices.end(), feedbackResultCompare);
1283 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
1284
1285 // Verifying vertices recorded with feedback actually ended up on the result image
1286 for (int ndx = 0; ndx < (int)vertices.size(); ++ndx)
1287 {
1288 // Rasterization (of lines) may deviate by one pixel. In addition to that, allow minimal errors in rasterized position vs. feedback result.
1289 // This minimal error could result in a difference in rounding => allow one additional pixel in deviation
1290
1291 const int rasterDeviation = 2;
1292 const tcu::IVec2 rasterPos((int)deFloatRound((vertices[ndx].x() * 0.5f + 0.5f) * (float)image.getWidth()),
1293 (int)deFloatRound((vertices[ndx].y() * 0.5f + 0.5f) * (float)image.getHeight()));
1294
1295 // Find produced rasterization results
1296 bool found = false;
1297
1298 for (int dy = -rasterDeviation; dy <= rasterDeviation && !found; ++dy)
1299 for (int dx = -rasterDeviation; dx <= rasterDeviation && !found; ++dx)
1300 {
1301 // Raster result could end up outside the viewport
1302 if (rasterPos.x() + dx < 0 || rasterPos.x() + dx >= image.getWidth() || rasterPos.y() + dy < 0 ||
1303 rasterPos.y() + dy >= image.getHeight())
1304 found = true;
1305 else
1306 {
1307 const tcu::RGBA result = image.getPixel(rasterPos.x() + dx, rasterPos.y() + dy);
1308
1309 if (!isBlack(result))
1310 found = true;
1311 }
1312 }
1313
1314 if (!found)
1315 {
1316 m_testCtx.getLog() << tcu::TestLog::Message << "Vertex " << vertices[ndx] << "\n"
1317 << "\tCould not find rasterization output for vertex.\n"
1318 << "\tExpected non-black pixels near " << rasterPos << tcu::TestLog::EndMessage;
1319
1320 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid result image");
1321 }
1322 }
1323 }
1324
genTransformFeedback(void)1325 void FeedbackPrimitiveTypeCase::genTransformFeedback(void)
1326 {
1327 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1328 const int elementsPerPrimitive = getNumGeneratedElementsPerPrimitive();
1329 const int feedbackPrimitives = getNumGeneratedPrimitives();
1330 const int feedbackElements = elementsPerPrimitive * feedbackPrimitives;
1331 const std::vector<tcu::Vec4> initialBuffer(feedbackElements, tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f));
1332
1333 gl.genTransformFeedbacks(1, &m_feedbackID);
1334 gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_feedbackID);
1335 GLU_EXPECT_NO_ERROR(gl.getError(), "gen transform feedback");
1336
1337 gl.genBuffers(1, &m_feedbackBuffer);
1338 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_feedbackBuffer);
1339 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(tcu::Vec4) * initialBuffer.size(), initialBuffer[0].getPtr(),
1340 GL_STATIC_COPY);
1341 GLU_EXPECT_NO_ERROR(gl.getError(), "gen feedback buffer");
1342
1343 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_feedbackBuffer);
1344 GLU_EXPECT_NO_ERROR(gl.getError(), "bind feedback buffer");
1345 }
1346
getTriangleNumOutputPrimitives(int tessellationLevel)1347 static int getTriangleNumOutputPrimitives(int tessellationLevel)
1348 {
1349 if (tessellationLevel == 1)
1350 return 1;
1351 else if (tessellationLevel == 2)
1352 return 6;
1353 else
1354 return 3 * (2 + 2 * (tessellationLevel - 2)) + getTriangleNumOutputPrimitives(tessellationLevel - 2);
1355 }
1356
getTriangleNumOutputPrimitivesPoints(int tessellationLevel)1357 static int getTriangleNumOutputPrimitivesPoints(int tessellationLevel)
1358 {
1359 if (tessellationLevel == 0)
1360 return 1;
1361 else if (tessellationLevel == 1)
1362 return 3;
1363 else
1364 return 3 + 3 * (tessellationLevel - 1) + getTriangleNumOutputPrimitivesPoints(tessellationLevel - 2);
1365 }
1366
getNumGeneratedElementsPerPrimitive(void) const1367 int FeedbackPrimitiveTypeCase::getNumGeneratedElementsPerPrimitive(void) const
1368 {
1369 if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
1370 return 3;
1371 else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
1372 return 2;
1373 else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
1374 return 1;
1375 else
1376 {
1377 DE_ASSERT(false);
1378 return -1;
1379 }
1380 }
1381
getNumGeneratedPrimitives(void) const1382 int FeedbackPrimitiveTypeCase::getNumGeneratedPrimitives(void) const
1383 {
1384 return getNumTessellatedPrimitives() * getGeometryAmplification();
1385 }
1386
getNumTessellatedPrimitives(void) const1387 int FeedbackPrimitiveTypeCase::getNumTessellatedPrimitives(void) const
1388 {
1389 const int tessellationLevel = 3;
1390
1391 if (m_tessellationPointMode == TESSELLATION_POINTMODE_OFF)
1392 {
1393 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1394 return getTriangleNumOutputPrimitives(tessellationLevel);
1395 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1396 return tessellationLevel * tessellationLevel * 2; // tessellated as triangles
1397 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1398 return tessellationLevel * tessellationLevel;
1399 }
1400 else if (m_tessellationPointMode == TESSELLATION_POINTMODE_ON)
1401 {
1402 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1403 return getTriangleNumOutputPrimitivesPoints(tessellationLevel);
1404 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1405 return (tessellationLevel + 1) * (tessellationLevel + 1);
1406 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1407 return tessellationLevel * (tessellationLevel + 1);
1408 }
1409
1410 DE_ASSERT(false);
1411 return -1;
1412 }
1413
getGeometryAmplification(void) const1414 int FeedbackPrimitiveTypeCase::getGeometryAmplification(void) const
1415 {
1416 const int outputAmplification = (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (2) : (1);
1417 const int numInputVertices = (m_tessellationPointMode) ? (1) :
1418 (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) :
1419 (3);
1420
1421 return outputAmplification * numInputVertices;
1422 }
1423
getOutputPrimitiveGLType(void) const1424 glw::GLenum FeedbackPrimitiveTypeCase::getOutputPrimitiveGLType(void) const
1425 {
1426 if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
1427 return GL_TRIANGLES;
1428 else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
1429 return GL_LINES;
1430 else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
1431 return GL_POINTS;
1432 else
1433 {
1434 DE_ASSERT(false);
1435 return -1;
1436 }
1437 }
1438
getVertexSource(void) const1439 std::string FeedbackPrimitiveTypeCase::getVertexSource(void) const
1440 {
1441 return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
1442 }
1443
getFragmentSource(void) const1444 std::string FeedbackPrimitiveTypeCase::getFragmentSource(void) const
1445 {
1446 return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
1447 }
1448
getTessellationControlSource(void) const1449 std::string FeedbackPrimitiveTypeCase::getTessellationControlSource(void) const
1450 {
1451 std::ostringstream buf;
1452
1453 buf << "${VERSION_DECL}\n"
1454 "${EXTENSION_TESSELATION_SHADER}"
1455 "layout(vertices = 9) out;\n"
1456 "\n"
1457 "uniform highp float u_innerTessellationLevel;\n"
1458 "uniform highp float u_outerTessellationLevel;\n"
1459 "\n"
1460 "void main (void)\n"
1461 "{\n"
1462 " if (gl_PatchVerticesIn != 4)\n"
1463 " return;\n"
1464 "\n"
1465 " // Convert input 2x2 grid to 3x3 grid\n"
1466 " float xweight = float(gl_InvocationID % 3) / 2.0f;\n"
1467 " float yweight = float(gl_InvocationID / 3) / 2.0f;\n"
1468 "\n"
1469 " vec4 y0 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, yweight);\n"
1470 " vec4 y1 = mix(gl_in[2].gl_Position, gl_in[3].gl_Position, yweight);\n"
1471 "\n"
1472 " gl_out[gl_InvocationID].gl_Position = mix(y0, y1, xweight);\n"
1473 "\n";
1474
1475 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1476 buf << " gl_TessLevelOuter[0] = 3.0;\n"
1477 " gl_TessLevelOuter[1] = 3.0;\n"
1478 " gl_TessLevelOuter[2] = 3.0;\n"
1479 " gl_TessLevelInner[0] = 3.0;\n";
1480 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1481 buf << " gl_TessLevelOuter[0] = 3.0;\n"
1482 " gl_TessLevelOuter[1] = 3.0;\n"
1483 " gl_TessLevelOuter[2] = 3.0;\n"
1484 " gl_TessLevelOuter[3] = 3.0;\n"
1485 " gl_TessLevelInner[0] = 3.0;\n"
1486 " gl_TessLevelInner[1] = 3.0;\n";
1487 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1488 buf << " gl_TessLevelOuter[0] = 3.0;\n"
1489 " gl_TessLevelOuter[1] = 3.0;\n";
1490 else
1491 DE_ASSERT(false);
1492
1493 buf << "}\n";
1494
1495 return specializeShader(buf.str(), m_context.getRenderContext().getType());
1496 }
1497
getTessellationEvaluationSource(void) const1498 std::string FeedbackPrimitiveTypeCase::getTessellationEvaluationSource(void) const
1499 {
1500 std::ostringstream buf;
1501
1502 buf << "${VERSION_DECL}\n"
1503 "${EXTENSION_TESSELATION_SHADER}"
1504 "layout("
1505 << ((m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) ? ("triangles") :
1506 (m_tessellationOutput == TESSELLATION_OUT_QUADS) ? ("quads") :
1507 ("isolines"))
1508 << ((m_tessellationPointMode) ? (", point_mode") : (""))
1509 << ") in;\n"
1510 "\n"
1511 "out highp vec4 v_tessellationCoords;\n"
1512 "\n"
1513 "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
1514 "void main (void)\n"
1515 "{\n"
1516 " if (gl_PatchVerticesIn != 9)\n"
1517 " return;\n"
1518 "\n"
1519 " vec4 patchCentroid = vec4(0.0);\n"
1520 " for (int ndx = 0; ndx < gl_PatchVerticesIn; ++ndx)\n"
1521 " patchCentroid += gl_in[ndx].gl_Position;\n"
1522 " patchCentroid /= patchCentroid.w;\n"
1523 "\n";
1524
1525 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1526 buf << " // map barycentric coords to 2d coords\n"
1527 " const vec3 tessDirX = vec3( 0.4, 0.4, 0.0);\n"
1528 " const vec3 tessDirY = vec3( 0.0, -0.4, 0.0);\n"
1529 " const vec3 tessDirZ = vec3(-0.4, 0.4, 0.0);\n"
1530 " gl_Position = patchCentroid + vec4(gl_TessCoord.x * tessDirX + gl_TessCoord.y * tessDirY + "
1531 "gl_TessCoord.z * tessDirZ, 0.0);\n";
1532 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS || m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1533 buf << " gl_Position = patchCentroid + vec4(gl_TessCoord.x * 0.8 - 0.4, gl_TessCoord.y * 0.8 - 0.4, 0.0, "
1534 "0.0);\n";
1535 else
1536 DE_ASSERT(false);
1537
1538 buf << " v_tessellationCoords = vec4(gl_TessCoord, 0.0);\n"
1539 "}\n";
1540
1541 return specializeShader(buf.str(), m_context.getRenderContext().getType());
1542 }
1543
getGeometrySource(void) const1544 std::string FeedbackPrimitiveTypeCase::getGeometrySource(void) const
1545 {
1546 const char *const geometryInputPrimitive = (m_tessellationPointMode) ? ("points") :
1547 (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? ("lines") :
1548 ("triangles");
1549 const char *const geometryOutputPrimitive = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? ("points") :
1550 (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? ("line_strip") :
1551 ("triangle_strip");
1552 const int numInputVertices = (m_tessellationPointMode) ? (1) :
1553 (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) :
1554 (3);
1555 const int numSingleVertexOutputVertices = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? (1) :
1556 (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (4) :
1557 (3);
1558 const int numEmitVertices = numInputVertices * numSingleVertexOutputVertices;
1559 std::ostringstream buf;
1560
1561 buf << "${VERSION_DECL}\n"
1562 "${EXTENSION_GEOMETRY_SHADER}"
1563 "layout("
1564 << geometryInputPrimitive
1565 << ") in;\n"
1566 "layout("
1567 << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
1568 << ") out;\n"
1569 "\n"
1570 "in highp vec4 v_tessellationCoords[];\n"
1571 "out highp vec4 tf_someVertexPosition;\n"
1572 "\n"
1573 "void main (void)\n"
1574 "{\n"
1575 " // Emit primitive\n"
1576 " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
1577 " {\n";
1578
1579 switch (m_geometryOutputType)
1580 {
1581 case GEOMETRY_OUTPUT_POINTS:
1582 buf << " // Draw point on vertex\n"
1583 " gl_Position = gl_in[ndx].gl_Position;\n"
1584 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1585 " EmitVertex();\n";
1586 break;
1587
1588 case GEOMETRY_OUTPUT_LINES:
1589 buf << " // Draw cross on vertex\n"
1590 " gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, -0.02, 0.0, 0.0);\n"
1591 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1592 " EmitVertex();\n"
1593 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, 0.02, 0.0, 0.0);\n"
1594 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1595 " EmitVertex();\n"
1596 " EndPrimitive();\n"
1597 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, -0.02, 0.0, 0.0);\n"
1598 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1599 " EmitVertex();\n"
1600 " gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, 0.02, 0.0, 0.0);\n"
1601 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1602 " EmitVertex();\n"
1603 " EndPrimitive();\n";
1604 break;
1605
1606 case GEOMETRY_OUTPUT_TRIANGLES:
1607 buf << " // Draw triangle on vertex\n"
1608 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.00, -0.02, 0.0, 0.0);\n"
1609 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1610 " EmitVertex();\n"
1611 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, 0.00, 0.0, 0.0);\n"
1612 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1613 " EmitVertex();\n"
1614 " gl_Position = gl_in[ndx].gl_Position + vec4( -0.02, 0.00, 0.0, 0.0);\n"
1615 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1616 " EmitVertex();\n"
1617 " EndPrimitive();\n";
1618 break;
1619
1620 default:
1621 DE_ASSERT(false);
1622 return "";
1623 }
1624
1625 buf << " }\n"
1626 "}\n";
1627
1628 return specializeShader(buf.str(), m_context.getRenderContext().getType());
1629 }
1630
getTessellationOutputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1631 const char *FeedbackPrimitiveTypeCase::getTessellationOutputDescription(TessellationOutputType tessellationOutput,
1632 TessellationPointMode pointMode)
1633 {
1634 switch (tessellationOutput)
1635 {
1636 case TESSELLATION_OUT_TRIANGLES:
1637 return (pointMode) ? ("points (triangles in point mode)") : ("triangles");
1638 case TESSELLATION_OUT_QUADS:
1639 return (pointMode) ? ("points (quads in point mode)") : ("quads");
1640 case TESSELLATION_OUT_ISOLINES:
1641 return (pointMode) ? ("points (isolines in point mode)") : ("isolines");
1642 default:
1643 DE_ASSERT(false);
1644 return DE_NULL;
1645 }
1646 }
1647
getGeometryInputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1648 const char *FeedbackPrimitiveTypeCase::getGeometryInputDescription(TessellationOutputType tessellationOutput,
1649 TessellationPointMode pointMode)
1650 {
1651 switch (tessellationOutput)
1652 {
1653 case TESSELLATION_OUT_TRIANGLES:
1654 return (pointMode) ? ("points") : ("triangles");
1655 case TESSELLATION_OUT_QUADS:
1656 return (pointMode) ? ("points") : ("triangles");
1657 case TESSELLATION_OUT_ISOLINES:
1658 return (pointMode) ? ("points") : ("lines");
1659 default:
1660 DE_ASSERT(false);
1661 return DE_NULL;
1662 }
1663 }
1664
getGeometryOutputDescription(GeometryOutputType geometryOutput)1665 const char *FeedbackPrimitiveTypeCase::getGeometryOutputDescription(GeometryOutputType geometryOutput)
1666 {
1667 switch (geometryOutput)
1668 {
1669 case GEOMETRY_OUTPUT_POINTS:
1670 return "points";
1671 case GEOMETRY_OUTPUT_LINES:
1672 return "lines";
1673 case GEOMETRY_OUTPUT_TRIANGLES:
1674 return "triangles";
1675 default:
1676 DE_ASSERT(false);
1677 return DE_NULL;
1678 }
1679 }
1680
1681 class PointSizeCase : public TestCase
1682 {
1683 public:
1684 enum Flags
1685 {
1686 FLAG_VERTEX_SET = 0x01, // !< set gl_PointSize in vertex shader
1687 FLAG_TESSELLATION_CONTROL_SET = 0x02, // !< set gl_PointSize in tessellation evaluation shader
1688 FLAG_TESSELLATION_EVALUATION_SET = 0x04, // !< set gl_PointSize in tessellation control shader
1689 FLAG_TESSELLATION_ADD = 0x08, // !< read and add to gl_PointSize in tessellation shader pair
1690 FLAG_TESSELLATION_DONT_SET = 0x10, // !< don't set gl_PointSize in tessellation shader
1691 FLAG_GEOMETRY_SET = 0x20, // !< set gl_PointSize in geometry shader
1692 FLAG_GEOMETRY_ADD = 0x40, // !< read and add to gl_PointSize in geometry shader
1693 FLAG_GEOMETRY_DONT_SET = 0x80, // !< don't set gl_PointSize in geometry shader
1694 };
1695
1696 PointSizeCase(Context &context, const char *name, const char *description, int flags);
1697 ~PointSizeCase(void);
1698
1699 static std::string genTestCaseName(int flags);
1700 static std::string genTestCaseDescription(int flags);
1701
1702 private:
1703 void init(void);
1704 void deinit(void);
1705 IterateResult iterate(void);
1706
1707 void checkExtensions(void) const;
1708 void checkPointSizeRequirements(void) const;
1709
1710 void renderTo(tcu::Surface &dst);
1711 bool verifyImage(const tcu::Surface &src);
1712 int getExpectedPointSize(void) const;
1713
1714 std::string genVertexSource(void) const;
1715 std::string genFragmentSource(void) const;
1716 std::string genTessellationControlSource(void) const;
1717 std::string genTessellationEvaluationSource(void) const;
1718 std::string genGeometrySource(void) const;
1719
1720 enum
1721 {
1722 RENDER_SIZE = 32,
1723 };
1724
1725 const int m_flags;
1726 glu::ShaderProgram *m_program;
1727 };
1728
PointSizeCase(Context & context,const char * name,const char * description,int flags)1729 PointSizeCase::PointSizeCase(Context &context, const char *name, const char *description, int flags)
1730 : TestCase(context, name, description)
1731 , m_flags(flags)
1732 , m_program(DE_NULL)
1733 {
1734 }
1735
~PointSizeCase(void)1736 PointSizeCase::~PointSizeCase(void)
1737 {
1738 deinit();
1739 }
1740
genTestCaseName(int flags)1741 std::string PointSizeCase::genTestCaseName(int flags)
1742 {
1743 std::ostringstream buf;
1744
1745 // join per-bit descriptions into a single string with '_' separator
1746 if (flags & FLAG_VERTEX_SET)
1747 buf << "vertex_set";
1748 if (flags & FLAG_TESSELLATION_CONTROL_SET)
1749 buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET - 1)) ? ("_") : ("")) << "control_set";
1750 if (flags & FLAG_TESSELLATION_EVALUATION_SET)
1751 buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET - 1)) ? ("_") : ("")) << "evaluation_set";
1752 if (flags & FLAG_TESSELLATION_ADD)
1753 buf << ((flags & (FLAG_TESSELLATION_ADD - 1)) ? ("_") : ("")) << "control_pass_eval_add";
1754 if (flags & FLAG_TESSELLATION_DONT_SET)
1755 buf << ((flags & (FLAG_TESSELLATION_DONT_SET - 1)) ? ("_") : ("")) << "eval_default";
1756 if (flags & FLAG_GEOMETRY_SET)
1757 buf << ((flags & (FLAG_GEOMETRY_SET - 1)) ? ("_") : ("")) << "geometry_set";
1758 if (flags & FLAG_GEOMETRY_ADD)
1759 buf << ((flags & (FLAG_GEOMETRY_ADD - 1)) ? ("_") : ("")) << "geometry_add";
1760 if (flags & FLAG_GEOMETRY_DONT_SET)
1761 buf << ((flags & (FLAG_GEOMETRY_DONT_SET - 1)) ? ("_") : ("")) << "geometry_default";
1762
1763 return buf.str();
1764 }
1765
genTestCaseDescription(int flags)1766 std::string PointSizeCase::genTestCaseDescription(int flags)
1767 {
1768 std::ostringstream buf;
1769
1770 // join per-bit descriptions into a single string with ", " separator
1771 if (flags & FLAG_VERTEX_SET)
1772 buf << "set point size in vertex shader";
1773 if (flags & FLAG_TESSELLATION_CONTROL_SET)
1774 buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET - 1)) ? (", ") : (""))
1775 << "set point size in tessellation control shader";
1776 if (flags & FLAG_TESSELLATION_EVALUATION_SET)
1777 buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET - 1)) ? (", ") : (""))
1778 << "set point size in tessellation evaluation shader";
1779 if (flags & FLAG_TESSELLATION_ADD)
1780 buf << ((flags & (FLAG_TESSELLATION_ADD - 1)) ? (", ") : ("")) << "add to point size in tessellation shader";
1781 if (flags & FLAG_TESSELLATION_DONT_SET)
1782 buf << ((flags & (FLAG_TESSELLATION_DONT_SET - 1)) ? (", ") : (""))
1783 << "don't set point size in tessellation evaluation shader";
1784 if (flags & FLAG_GEOMETRY_SET)
1785 buf << ((flags & (FLAG_GEOMETRY_SET - 1)) ? (", ") : ("")) << "set point size in geometry shader";
1786 if (flags & FLAG_GEOMETRY_ADD)
1787 buf << ((flags & (FLAG_GEOMETRY_ADD - 1)) ? (", ") : ("")) << "add to point size in geometry shader";
1788 if (flags & FLAG_GEOMETRY_DONT_SET)
1789 buf << ((flags & (FLAG_GEOMETRY_DONT_SET - 1)) ? (", ") : ("")) << "don't set point size in geometry shader";
1790
1791 return buf.str();
1792 }
1793
init(void)1794 void PointSizeCase::init(void)
1795 {
1796 checkExtensions();
1797 checkPointSizeRequirements();
1798
1799 if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
1800 {
1801 m_context.getRenderContext().getFunctions().enable(GL_PROGRAM_POINT_SIZE);
1802 }
1803
1804 // log
1805
1806 if (m_flags & FLAG_VERTEX_SET)
1807 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0."
1808 << tcu::TestLog::EndMessage;
1809 if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
1810 m_testCtx.getLog()
1811 << tcu::TestLog::Message
1812 << "Setting point size in tessellation control shader to 4.0. (And ignoring it in evaluation)."
1813 << tcu::TestLog::EndMessage;
1814 if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
1815 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0."
1816 << tcu::TestLog::EndMessage;
1817 if (m_flags & FLAG_TESSELLATION_ADD)
1818 m_testCtx.getLog() << tcu::TestLog::Message
1819 << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation."
1820 << tcu::TestLog::EndMessage;
1821 if (m_flags & FLAG_TESSELLATION_DONT_SET)
1822 m_testCtx.getLog()
1823 << tcu::TestLog::Message
1824 << "Not setting point size in tessellation evaluation shader (resulting in the default point size)."
1825 << tcu::TestLog::EndMessage;
1826 if (m_flags & FLAG_GEOMETRY_SET)
1827 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0."
1828 << tcu::TestLog::EndMessage;
1829 if (m_flags & FLAG_GEOMETRY_ADD)
1830 m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0."
1831 << tcu::TestLog::EndMessage;
1832 if (m_flags & FLAG_GEOMETRY_DONT_SET)
1833 m_testCtx.getLog() << tcu::TestLog::Message
1834 << "Not setting point size in geometry shader (resulting in the default point size)."
1835 << tcu::TestLog::EndMessage;
1836
1837 // program
1838
1839 {
1840 glu::ProgramSources sources;
1841 sources << glu::VertexSource(genVertexSource()) << glu::FragmentSource(genFragmentSource());
1842
1843 if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD |
1844 FLAG_TESSELLATION_DONT_SET))
1845 sources << glu::TessellationControlSource(genTessellationControlSource())
1846 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
1847
1848 if (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))
1849 sources << glu::GeometrySource(genGeometrySource());
1850
1851 m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
1852
1853 m_testCtx.getLog() << *m_program;
1854 if (!m_program->isOk())
1855 throw tcu::TestError("failed to build program");
1856 }
1857 }
1858
deinit(void)1859 void PointSizeCase::deinit(void)
1860 {
1861 if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
1862 {
1863 m_context.getRenderContext().getFunctions().disable(GL_PROGRAM_POINT_SIZE);
1864 }
1865
1866 delete m_program;
1867 m_program = DE_NULL;
1868 }
1869
iterate(void)1870 PointSizeCase::IterateResult PointSizeCase::iterate(void)
1871 {
1872 tcu::Surface resultImage(RENDER_SIZE, RENDER_SIZE);
1873
1874 renderTo(resultImage);
1875
1876 if (verifyImage(resultImage))
1877 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1878 else
1879 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1880
1881 return STOP;
1882 }
1883
checkExtensions(void) const1884 void PointSizeCase::checkExtensions(void) const
1885 {
1886 glu::ContextType contextType = m_context.getRenderContext().getType();
1887 if (glu::contextSupports(contextType, glu::ApiType::core(4, 5)))
1888 return;
1889
1890 std::vector<std::string> requiredExtensions;
1891 const bool supportsES32 = glu::contextSupports(contextType, glu::ApiType::es(3, 2));
1892 bool allOk = true;
1893
1894 if ((m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD |
1895 FLAG_TESSELLATION_DONT_SET)) &&
1896 !supportsES32)
1897 requiredExtensions.push_back("GL_EXT_tessellation_shader");
1898
1899 if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD))
1900 requiredExtensions.push_back("GL_EXT_tessellation_point_size");
1901
1902 if ((m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))) && !supportsES32)
1903 requiredExtensions.push_back("GL_EXT_geometry_shader");
1904
1905 if (m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)))
1906 requiredExtensions.push_back("GL_EXT_geometry_point_size");
1907
1908 for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
1909 if (!m_context.getContextInfo().isExtensionSupported(requiredExtensions[ndx].c_str()))
1910 allOk = false;
1911
1912 if (!allOk)
1913 {
1914 std::ostringstream extensionList;
1915
1916 for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
1917 {
1918 if (ndx != 0)
1919 extensionList << ", ";
1920 extensionList << requiredExtensions[ndx];
1921 }
1922
1923 throw tcu::NotSupportedError("Test requires {" + extensionList.str() + "} extension(s)");
1924 }
1925 }
1926
checkPointSizeRequirements(void) const1927 void PointSizeCase::checkPointSizeRequirements(void) const
1928 {
1929 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1930 float aliasedSizeRange[2] = {0.0f, 0.0f};
1931 const int requiredSize = getExpectedPointSize();
1932
1933 gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedSizeRange);
1934
1935 if (float(requiredSize) > aliasedSizeRange[1])
1936 throw tcu::NotSupportedError("Test requires point size " + de::toString(requiredSize));
1937 }
1938
renderTo(tcu::Surface & dst)1939 void PointSizeCase::renderTo(tcu::Surface &dst)
1940 {
1941 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1942 const bool tessellationActive = (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET |
1943 FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) != 0;
1944 const int positionLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
1945 const glu::VertexArray vao(m_context.getRenderContext());
1946
1947 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point." << tcu::TestLog::EndMessage;
1948
1949 if (positionLocation == -1)
1950 throw tcu::TestError("Attribute a_position location was -1");
1951
1952 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
1953 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1954 gl.clear(GL_COLOR_BUFFER_BIT);
1955 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1956
1957 gl.bindVertexArray(*vao);
1958 GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
1959
1960 gl.useProgram(m_program->getProgram());
1961 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1962
1963 gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
1964
1965 if (tessellationActive)
1966 {
1967 gl.patchParameteri(GL_PATCH_VERTICES, 1);
1968 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1969
1970 gl.drawArrays(GL_PATCHES, 0, 1);
1971 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1972 }
1973 else
1974 {
1975 gl.drawArrays(GL_POINTS, 0, 1);
1976 GLU_EXPECT_NO_ERROR(gl.getError(), "draw points");
1977 }
1978
1979 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1980 }
1981
verifyImage(const tcu::Surface & src)1982 bool PointSizeCase::verifyImage(const tcu::Surface &src)
1983 {
1984 const bool MSAATarget = (m_context.getRenderTarget().getNumSamples() > 1);
1985 const int expectedSize = getExpectedPointSize();
1986
1987 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize
1988 << " pixels." << tcu::TestLog::EndMessage;
1989 m_testCtx.getLog() << tcu::TestLog::Image("RenderImage", "Rendered image", src.getAccess());
1990
1991 {
1992 bool resultAreaFound = false;
1993 tcu::IVec4 resultArea;
1994
1995 // Find rasterization output area
1996
1997 for (int y = 0; y < src.getHeight(); ++y)
1998 for (int x = 0; x < src.getWidth(); ++x)
1999 {
2000 if (!isBlack(src.getPixel(x, y)))
2001 {
2002 if (!resultAreaFound)
2003 {
2004 // first fragment
2005 resultArea = tcu::IVec4(x, y, x + 1, y + 1);
2006 resultAreaFound = true;
2007 }
2008 else
2009 {
2010 // union area
2011 resultArea.x() = de::min(resultArea.x(), x);
2012 resultArea.y() = de::min(resultArea.y(), y);
2013 resultArea.z() = de::max(resultArea.z(), x + 1);
2014 resultArea.w() = de::max(resultArea.w(), y + 1);
2015 }
2016 }
2017 }
2018
2019 if (!resultAreaFound)
2020 {
2021 m_testCtx.getLog() << tcu::TestLog::Message << "Verification failed, could not find any point fragments."
2022 << tcu::TestLog::EndMessage;
2023 return false;
2024 }
2025
2026 // verify area size
2027 if (MSAATarget)
2028 {
2029 const tcu::IVec2 pointSize = resultArea.swizzle(2, 3) - resultArea.swizzle(0, 1);
2030
2031 // MSAA: edges may be a little fuzzy
2032 if (de::abs(pointSize.x() - pointSize.y()) > 1)
2033 {
2034 m_testCtx.getLog() << tcu::TestLog::Message
2035 << "ERROR! Rasterized point is not a square. Detected point size was " << pointSize
2036 << tcu::TestLog::EndMessage;
2037 return false;
2038 }
2039
2040 // MSAA may produce larger areas, allow one pixel larger
2041 if (expectedSize != de::max(pointSize.x(), pointSize.y()) &&
2042 (expectedSize + 1) != de::max(pointSize.x(), pointSize.y()))
2043 {
2044 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize
2045 << ", got " << de::max(pointSize.x(), pointSize.y()) << tcu::TestLog::EndMessage;
2046 return false;
2047 }
2048 }
2049 else
2050 {
2051 const tcu::IVec2 pointSize = resultArea.swizzle(2, 3) - resultArea.swizzle(0, 1);
2052
2053 if (pointSize.x() != pointSize.y())
2054 {
2055 m_testCtx.getLog() << tcu::TestLog::Message
2056 << "ERROR! Rasterized point is not a square. Point size was " << pointSize
2057 << tcu::TestLog::EndMessage;
2058 return false;
2059 }
2060
2061 if (pointSize.x() != expectedSize)
2062 {
2063 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize
2064 << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
2065 return false;
2066 }
2067 }
2068 }
2069
2070 return true;
2071 }
2072
getExpectedPointSize(void) const2073 int PointSizeCase::getExpectedPointSize(void) const
2074 {
2075 int addition = 0;
2076
2077 // geometry
2078 if (m_flags & FLAG_GEOMETRY_DONT_SET)
2079 return 1;
2080 else if (m_flags & FLAG_GEOMETRY_SET)
2081 return 6;
2082 else if (m_flags & FLAG_GEOMETRY_ADD)
2083 addition += 2;
2084
2085 // tessellation
2086 if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
2087 return 4 + addition;
2088 else if (m_flags & FLAG_TESSELLATION_ADD)
2089 addition += 2;
2090 else if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_DONT_SET))
2091 {
2092 DE_ASSERT((m_flags & FLAG_GEOMETRY_ADD) == 0); // reading pointSize undefined
2093 return 1;
2094 }
2095
2096 // vertex
2097 if (m_flags & FLAG_VERTEX_SET)
2098 return 2 + addition;
2099
2100 // undefined
2101 DE_ASSERT(false);
2102 return -1;
2103 }
2104
genVertexSource(void) const2105 std::string PointSizeCase::genVertexSource(void) const
2106 {
2107 std::ostringstream buf;
2108
2109 buf << "${VERSION_DECL}\n"
2110 << "in highp vec4 a_position;\n"
2111 << "void main ()\n"
2112 << "{\n"
2113 << " gl_Position = a_position;\n";
2114
2115 if (m_flags & FLAG_VERTEX_SET)
2116 buf << " gl_PointSize = 2.0;\n";
2117
2118 buf << "}\n";
2119
2120 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2121 }
2122
genFragmentSource(void) const2123 std::string PointSizeCase::genFragmentSource(void) const
2124 {
2125 return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
2126 }
2127
genTessellationControlSource(void) const2128 std::string PointSizeCase::genTessellationControlSource(void) const
2129 {
2130 std::ostringstream buf;
2131
2132 buf << "${VERSION_DECL}\n"
2133 << "${EXTENSION_TESSELATION_SHADER}"
2134 << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("${EXTENSION_TESSELATION_POINT_SIZE}"))
2135 << "layout(vertices = 1) out;\n"
2136 << "void main ()\n"
2137 << "{\n"
2138 << " gl_TessLevelOuter[0] = 3.0;\n"
2139 << " gl_TessLevelOuter[1] = 3.0;\n"
2140 << " gl_TessLevelOuter[2] = 3.0;\n"
2141 << " gl_TessLevelInner[0] = 3.0;\n"
2142 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
2143
2144 if (m_flags & FLAG_TESSELLATION_ADD)
2145 buf << " // pass as is to eval\n"
2146 << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
2147 else if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
2148 buf << " // thrown away\n"
2149 << " gl_out[gl_InvocationID].gl_PointSize = 4.0;\n";
2150
2151 buf << "}\n";
2152
2153 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2154 }
2155
genTessellationEvaluationSource(void) const2156 std::string PointSizeCase::genTessellationEvaluationSource(void) const
2157 {
2158 std::ostringstream buf;
2159
2160 buf << "${VERSION_DECL}\n"
2161 << "${EXTENSION_TESSELATION_SHADER}"
2162 << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("${EXTENSION_TESSELATION_POINT_SIZE}"))
2163 << "layout(triangles, point_mode) in;\n"
2164 << "void main ()\n"
2165 << "{\n"
2166 << " // hide all but one vertex\n"
2167 << " if (gl_TessCoord.x < 0.99)\n"
2168 << " gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
2169 << " else\n"
2170 << " gl_Position = gl_in[0].gl_Position;\n";
2171
2172 if (m_flags & FLAG_TESSELLATION_ADD)
2173 buf << "\n"
2174 << " // add to point size\n"
2175 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
2176 else if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
2177 buf << "\n"
2178 << " // set point size\n"
2179 << " gl_PointSize = 4.0;\n";
2180
2181 buf << "}\n";
2182
2183 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2184 }
2185
genGeometrySource(void) const2186 std::string PointSizeCase::genGeometrySource(void) const
2187 {
2188 std::ostringstream buf;
2189
2190 buf << "${VERSION_DECL}\n"
2191 << "${EXTENSION_GEOMETRY_SHADER}"
2192 << ((m_flags & FLAG_GEOMETRY_DONT_SET) ? ("") : ("${EXTENSION_GEOMETRY_POINT_SIZE}")) << "layout (points) in;\n"
2193 << "layout (points, max_vertices=1) out;\n"
2194 << "\n"
2195 << "void main ()\n"
2196 << "{\n";
2197
2198 if (m_flags & FLAG_GEOMETRY_SET)
2199 buf << " gl_Position = gl_in[0].gl_Position;\n"
2200 << " gl_PointSize = 6.0;\n";
2201 else if (m_flags & FLAG_GEOMETRY_ADD)
2202 buf << " gl_Position = gl_in[0].gl_Position;\n"
2203 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
2204 else if (m_flags & FLAG_GEOMETRY_DONT_SET)
2205 buf << " gl_Position = gl_in[0].gl_Position;\n";
2206
2207 buf << " EmitVertex();\n"
2208 << "}\n";
2209
2210 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2211 }
2212
2213 class AllowedRenderFailureException : public std::runtime_error
2214 {
2215 public:
AllowedRenderFailureException(const char * message)2216 AllowedRenderFailureException(const char *message) : std::runtime_error(message)
2217 {
2218 }
2219 };
2220
2221 class GridRenderCase : public TestCase
2222 {
2223 public:
2224 enum Flags
2225 {
2226 FLAG_TESSELLATION_MAX_SPEC = 0x0001,
2227 FLAG_TESSELLATION_MAX_IMPLEMENTATION = 0x0002,
2228 FLAG_GEOMETRY_MAX_SPEC = 0x0004,
2229 FLAG_GEOMETRY_MAX_IMPLEMENTATION = 0x0008,
2230 FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC = 0x0010,
2231 FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION = 0x0020,
2232
2233 FLAG_GEOMETRY_SCATTER_INSTANCES = 0x0040,
2234 FLAG_GEOMETRY_SCATTER_PRIMITIVES = 0x0080,
2235 FLAG_GEOMETRY_SEPARATE_PRIMITIVES =
2236 0x0100, //!< if set, geometry shader outputs separate grid cells and not continuous slices
2237 FLAG_GEOMETRY_SCATTER_LAYERS = 0x0200,
2238
2239 FLAG_ALLOW_OUT_OF_MEMORY = 0x0400, //!< allow draw command to set GL_OUT_OF_MEMORY
2240 };
2241
2242 GridRenderCase(Context &context, const char *name, const char *description, int flags);
2243 ~GridRenderCase(void);
2244
2245 private:
2246 void init(void);
2247 void deinit(void);
2248 IterateResult iterate(void);
2249
2250 void renderTo(std::vector<tcu::Surface> &dst);
2251 bool verifyResultLayer(int layerNdx, const tcu::Surface &dst);
2252
2253 std::string getVertexSource(void);
2254 std::string getFragmentSource(void);
2255 std::string getTessellationControlSource(int tessLevel);
2256 std::string getTessellationEvaluationSource(int tessLevel);
2257 std::string getGeometryShaderSource(int numPrimitives, int numInstances, int tessLevel);
2258
2259 enum
2260 {
2261 RENDER_SIZE = 256
2262 };
2263
2264 std::string m_description;
2265
2266 const int m_flags;
2267
2268 glu::ShaderProgram *m_program;
2269 uint32_t m_texture;
2270 int m_numLayers;
2271 };
2272
GridRenderCase(Context & context,const char * name,const char * description,int flags)2273 GridRenderCase::GridRenderCase(Context &context, const char *name, const char *description, int flags)
2274 : TestCase(context, name, description)
2275 , m_description(description)
2276 , m_flags(flags)
2277 , m_program(DE_NULL)
2278 , m_texture(0)
2279 , m_numLayers(1)
2280 {
2281 DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0) || ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0));
2282 DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0) || ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0));
2283 DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0) ||
2284 ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0));
2285 DE_ASSERT(((m_flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) ==
2286 ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
2287 }
2288
~GridRenderCase(void)2289 GridRenderCase::~GridRenderCase(void)
2290 {
2291 deinit();
2292 }
2293
init(void)2294 void GridRenderCase::init(void)
2295 {
2296 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2297 glu::ContextType contextType = m_context.getRenderContext().getType();
2298 const bool supportsES32orGL45 = glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
2299 glu::contextSupports(contextType, glu::ApiType::core(4, 5));
2300
2301 // Requirements
2302
2303 if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
2304 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
2305 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
2306
2307 if ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) == 0)
2308 {
2309 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
2310 m_context.getRenderTarget().getHeight() < RENDER_SIZE)
2311 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
2312 de::toString<int>(RENDER_SIZE) + " or larger render target.");
2313 }
2314
2315 // Log
2316
2317 m_testCtx.getLog() << tcu::TestLog::Message
2318 << "Testing tessellation and geometry shaders that output a large number of primitives.\n"
2319 << m_description << tcu::TestLog::EndMessage;
2320
2321 // Render target
2322 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2323 {
2324 // set limits
2325 m_numLayers = 8;
2326
2327 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers
2328 << tcu::TestLog::EndMessage;
2329
2330 gl.genTextures(1, &m_texture);
2331 gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_texture);
2332 gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, RENDER_SIZE, RENDER_SIZE, m_numLayers);
2333
2334 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2335 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2336 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2337 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2338
2339 GLU_EXPECT_NO_ERROR(gl.getError(), "gen texture");
2340 }
2341
2342 // Gen program
2343 {
2344 glu::ProgramSources sources;
2345 int tessGenLevel = -1;
2346
2347 sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource());
2348
2349 // Tessellation limits
2350 {
2351 if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION)
2352 {
2353 gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel);
2354 GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits");
2355 }
2356 else if (m_flags & FLAG_TESSELLATION_MAX_SPEC)
2357 {
2358 tessGenLevel = 64;
2359 }
2360 else
2361 {
2362 tessGenLevel = 5;
2363 }
2364
2365 m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: " << tessGenLevel << ", mode = quad.\n"
2366 << "\tEach input patch produces " << (tessGenLevel * tessGenLevel) << " ("
2367 << (tessGenLevel * tessGenLevel * 2) << " triangles)\n"
2368 << tcu::TestLog::EndMessage;
2369
2370 sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel))
2371 << glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel));
2372 }
2373
2374 // Geometry limits
2375 {
2376 int geometryOutputComponents = -1;
2377 int geometryOutputVertices = -1;
2378 int geometryTotalOutputComponents = -1;
2379 int geometryShaderInvocations = -1;
2380 bool logGeometryLimits = false;
2381 bool logInvocationLimits = false;
2382
2383 if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION)
2384 {
2385 m_testCtx.getLog() << tcu::TestLog::Message
2386 << "Using implementation maximum geometry shader output limits."
2387 << tcu::TestLog::EndMessage;
2388
2389 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents);
2390 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices);
2391 gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents);
2392 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits");
2393
2394 logGeometryLimits = true;
2395 }
2396 else if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
2397 {
2398 m_testCtx.getLog() << tcu::TestLog::Message
2399 << "Using geometry shader extension minimum maximum output limits."
2400 << tcu::TestLog::EndMessage;
2401
2402 geometryOutputComponents = 128;
2403 geometryOutputVertices = 256;
2404 geometryTotalOutputComponents = 1024;
2405 logGeometryLimits = true;
2406 }
2407 else
2408 {
2409 geometryOutputComponents = 128;
2410 geometryOutputVertices = 16;
2411 geometryTotalOutputComponents = 1024;
2412 }
2413
2414 if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION)
2415 {
2416 gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations);
2417 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits");
2418
2419 logInvocationLimits = true;
2420 }
2421 else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
2422 {
2423 geometryShaderInvocations = 32;
2424 logInvocationLimits = true;
2425 }
2426 else
2427 {
2428 geometryShaderInvocations = 4;
2429 }
2430
2431 if (logGeometryLimits || logInvocationLimits)
2432 {
2433 tcu::MessageBuilder msg(&m_testCtx.getLog());
2434
2435 msg << "Geometry shader, targeting following limits:\n";
2436
2437 if (logGeometryLimits)
2438 msg << "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n"
2439 << "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n"
2440 << "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n";
2441
2442 if (logInvocationLimits)
2443 msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations;
2444
2445 msg << tcu::TestLog::EndMessage;
2446 }
2447
2448 {
2449 const bool separatePrimitives = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
2450 const int numComponentsPerVertex = 8; // vec4 pos, vec4 color
2451 int numVerticesPerInvocation;
2452 int numPrimitivesPerInvocation;
2453 int geometryVerticesPerPrimitive;
2454 int geometryPrimitivesOutPerPrimitive;
2455
2456 if (separatePrimitives)
2457 {
2458 const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex);
2459 const int numOutputLimit = geometryOutputVertices / 4;
2460
2461 numPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit);
2462 numVerticesPerInvocation = numPrimitivesPerInvocation * 4;
2463 }
2464 else
2465 {
2466 // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
2467 // Each slice is a triangle strip and is generated by a single shader invocation.
2468 // One slice with 4 segment ends (nodes) and 3 segments:
2469 // .__.__.__.
2470 // |\ |\ |\ |
2471 // |_\|_\|_\|
2472
2473 const int numSliceNodesComponentLimit =
2474 geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices
2475 const int numSliceNodesOutputLimit = geometryOutputVertices / 2; // each node 2 vertices
2476 const int numSliceNodes = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
2477
2478 numVerticesPerInvocation = numSliceNodes * 2;
2479 numPrimitivesPerInvocation = (numSliceNodes - 1) * 2;
2480 }
2481
2482 geometryVerticesPerPrimitive = numVerticesPerInvocation * geometryShaderInvocations;
2483 geometryPrimitivesOutPerPrimitive = numPrimitivesPerInvocation * geometryShaderInvocations;
2484
2485 m_testCtx.getLog() << tcu::TestLog::Message << "Geometry shader:\n"
2486 << "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation)
2487 << "\n"
2488 << "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation)
2489 << "\n"
2490 << "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n"
2491 << "\tTotal output vertex count per input primitive: "
2492 << (geometryVerticesPerPrimitive) << "\n"
2493 << "\tTotal output primitive count per input primitive: "
2494 << (geometryPrimitivesOutPerPrimitive) << "\n"
2495 << tcu::TestLog::EndMessage;
2496
2497 sources << glu::GeometrySource(
2498 getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations, tessGenLevel));
2499
2500 m_testCtx.getLog() << tcu::TestLog::Message << "Program:\n"
2501 << "\tTotal program output vertices count per input patch: "
2502 << (tessGenLevel * tessGenLevel * 2 * geometryVerticesPerPrimitive) << "\n"
2503 << "\tTotal program output primitive count per input patch: "
2504 << (tessGenLevel * tessGenLevel * 2 * geometryPrimitivesOutPerPrimitive) << "\n"
2505 << tcu::TestLog::EndMessage;
2506 }
2507 }
2508
2509 m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
2510 m_testCtx.getLog() << *m_program;
2511 if (!m_program->isOk())
2512 throw tcu::TestError("failed to build program");
2513 }
2514 }
2515
deinit(void)2516 void GridRenderCase::deinit(void)
2517 {
2518 delete m_program;
2519 m_program = DE_NULL;
2520
2521 if (m_texture)
2522 {
2523 m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture);
2524 m_texture = 0;
2525 }
2526 }
2527
iterate(void)2528 GridRenderCase::IterateResult GridRenderCase::iterate(void)
2529 {
2530 std::vector<tcu::Surface> renderedLayers(m_numLayers);
2531 bool allLayersOk = true;
2532
2533 for (int ndx = 0; ndx < m_numLayers; ++ndx)
2534 renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE);
2535
2536 m_testCtx.getLog() << tcu::TestLog::Message
2537 << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. "
2538 "(High-frequency grid may appear unicolored)."
2539 << tcu::TestLog::EndMessage;
2540
2541 try
2542 {
2543 renderTo(renderedLayers);
2544 }
2545 catch (const AllowedRenderFailureException &ex)
2546 {
2547 // Got accepted failure
2548 m_testCtx.getLog() << tcu::TestLog::Message << "Could not render, reason: " << ex.what() << "\n"
2549 << "Failure is allowed." << tcu::TestLog::EndMessage;
2550
2551 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2552 return STOP;
2553 }
2554
2555 for (int ndx = 0; ndx < m_numLayers; ++ndx)
2556 allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]);
2557
2558 if (allLayersOk)
2559 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2560 else
2561 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2562 return STOP;
2563 }
2564
renderTo(std::vector<tcu::Surface> & dst)2565 void GridRenderCase::renderTo(std::vector<tcu::Surface> &dst)
2566 {
2567 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2568 const int positionLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
2569 const glu::VertexArray vao(m_context.getRenderContext());
2570 de::MovePtr<glu::Framebuffer> fbo;
2571
2572 if (positionLocation == -1)
2573 throw tcu::TestError("Attribute a_position location was -1");
2574
2575 gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight());
2576 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2577 GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
2578
2579 gl.bindVertexArray(*vao);
2580 GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
2581
2582 gl.useProgram(m_program->getProgram());
2583 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
2584
2585 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2586 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
2587
2588 gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
2589
2590 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2591 {
2592 // clear texture contents
2593 {
2594 glu::Framebuffer clearFbo(m_context.getRenderContext());
2595 gl.bindFramebuffer(GL_FRAMEBUFFER, *clearFbo);
2596
2597 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2598 {
2599 gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2600 gl.clear(GL_COLOR_BUFFER_BIT);
2601 }
2602
2603 GLU_EXPECT_NO_ERROR(gl.getError(), "clear tex contents");
2604 }
2605
2606 // create and bind layered fbo
2607
2608 fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
2609
2610 gl.bindFramebuffer(GL_FRAMEBUFFER, **fbo);
2611 gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
2612 GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo");
2613 }
2614 else
2615 {
2616 // clear viewport
2617 gl.clear(GL_COLOR_BUFFER_BIT);
2618 }
2619
2620 // draw
2621 {
2622 glw::GLenum glerror;
2623
2624 gl.drawArrays(GL_PATCHES, 0, 1);
2625
2626 glerror = gl.getError();
2627 if (glerror == GL_OUT_OF_MEMORY && (m_flags & FLAG_ALLOW_OUT_OF_MEMORY))
2628 throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing");
2629
2630 GLU_EXPECT_NO_ERROR(glerror, "draw patches");
2631 }
2632
2633 // Read layers
2634
2635 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2636 {
2637 glu::Framebuffer readFbo(m_context.getRenderContext());
2638 gl.bindFramebuffer(GL_FRAMEBUFFER, *readFbo);
2639
2640 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2641 {
2642 gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2643 glu::readPixels(m_context.getRenderContext(), 0, 0, dst[layerNdx].getAccess());
2644 GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2645 }
2646 }
2647 else
2648 {
2649 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess());
2650 GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2651 }
2652 }
2653
verifyResultLayer(int layerNdx,const tcu::Surface & image)2654 bool GridRenderCase::verifyResultLayer(int layerNdx, const tcu::Surface &image)
2655 {
2656 tcu::Surface errorMask(image.getWidth(), image.getHeight());
2657 bool foundError = false;
2658
2659 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
2660
2661 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage;
2662
2663 for (int y = 0; y < image.getHeight(); ++y)
2664 for (int x = 0; x < image.getWidth(); ++x)
2665 {
2666 const int threshold = 8;
2667 const tcu::RGBA color = image.getPixel(x, y);
2668
2669 // Color must be a linear combination of green and yellow
2670 if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
2671 {
2672 errorMask.setPixel(x, y, tcu::RGBA::red());
2673 foundError = true;
2674 }
2675 }
2676
2677 if (!foundError)
2678 {
2679 m_testCtx.getLog() << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
2680 << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2681 << tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2682 << tcu::TestLog::EndImageSet;
2683 return true;
2684 }
2685 else
2686 {
2687 m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed, found invalid pixels."
2688 << tcu::TestLog::EndMessage
2689 << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2690 << tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2691 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
2692 << tcu::TestLog::EndImageSet;
2693 return false;
2694 }
2695 }
2696
getVertexSource(void)2697 std::string GridRenderCase::getVertexSource(void)
2698 {
2699 return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
2700 }
2701
getFragmentSource(void)2702 std::string GridRenderCase::getFragmentSource(void)
2703 {
2704 const char *source = "${VERSION_DECL}\n"
2705 "flat in mediump vec4 v_color;\n"
2706 "layout(location = 0) out mediump vec4 fragColor;\n"
2707 "void main (void)\n"
2708 "{\n"
2709 " fragColor = v_color;\n"
2710 "}\n";
2711
2712 return specializeShader(source, m_context.getRenderContext().getType());
2713 }
2714
getTessellationControlSource(int tessLevel)2715 std::string GridRenderCase::getTessellationControlSource(int tessLevel)
2716 {
2717 std::ostringstream buf;
2718
2719 buf << "${VERSION_DECL}\n"
2720 "${EXTENSION_TESSELATION_SHADER}"
2721 "layout(vertices=1) out;\n"
2722 "\n"
2723 "void main()\n"
2724 "{\n"
2725 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
2726 " gl_TessLevelOuter[0] = "
2727 << tessLevel
2728 << ".0;\n"
2729 " gl_TessLevelOuter[1] = "
2730 << tessLevel
2731 << ".0;\n"
2732 " gl_TessLevelOuter[2] = "
2733 << tessLevel
2734 << ".0;\n"
2735 " gl_TessLevelOuter[3] = "
2736 << tessLevel
2737 << ".0;\n"
2738 " gl_TessLevelInner[0] = "
2739 << tessLevel
2740 << ".0;\n"
2741 " gl_TessLevelInner[1] = "
2742 << tessLevel
2743 << ".0;\n"
2744 "}\n";
2745
2746 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2747 }
2748
getTessellationEvaluationSource(int tessLevel)2749 std::string GridRenderCase::getTessellationEvaluationSource(int tessLevel)
2750 {
2751 std::ostringstream buf;
2752
2753 buf << "${VERSION_DECL}\n"
2754 "${EXTENSION_TESSELATION_SHADER}"
2755 "layout(quads) in;\n"
2756 "\n"
2757 "out mediump ivec2 v_tessellationGridPosition;\n"
2758 "\n"
2759 "// note: No need to use precise gl_Position since position does not depend on order\n"
2760 "void main (void)\n"
2761 "{\n";
2762
2763 if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
2764 buf << " // Cover only a small area in a corner. The area will be expanded in geometry shader to cover "
2765 "whole viewport\n"
2766 " gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
2767 else
2768 buf << " // Fill the whole viewport\n"
2769 " gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
2770
2771 buf << " // Calculate position in tessellation grid\n"
2772 " v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float("
2773 << tessLevel
2774 << ")));\n"
2775 "}\n";
2776
2777 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2778 }
2779
getGeometryShaderSource(int numPrimitives,int numInstances,int tessLevel)2780 std::string GridRenderCase::getGeometryShaderSource(int numPrimitives, int numInstances, int tessLevel)
2781 {
2782 std::ostringstream buf;
2783
2784 buf << "${VERSION_DECL}\n"
2785 "${EXTENSION_GEOMETRY_SHADER}"
2786 "layout(triangles, invocations="
2787 << numInstances
2788 << ") in;\n"
2789 "layout(triangle_strip, max_vertices="
2790 << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2))
2791 << ") out;\n"
2792 "\n"
2793 "in mediump ivec2 v_tessellationGridPosition[];\n"
2794 "flat out highp vec4 v_color;\n"
2795 "\n"
2796 "void main ()\n"
2797 "{\n"
2798 " const float equalThreshold = 0.001;\n"
2799 " const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. "
2800 "Fill potential gaps by enlarging the output slice a little.\n"
2801 "\n"
2802 " // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
2803 " // Original rectangle can be found by finding the bounding AABB of the triangle\n"
2804 " vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2805 " min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
2806 " max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2807 " max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
2808 "\n"
2809 " // Location in tessellation grid\n"
2810 " ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], "
2811 "v_tessellationGridPosition[2])));\n"
2812 "\n"
2813 " // Which triangle of the two that split the grid cell\n"
2814 " int numVerticesOnBottomEdge = 0;\n"
2815 " for (int ndx = 0; ndx < 3; ++ndx)\n"
2816 " if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
2817 " ++numVerticesOnBottomEdge;\n"
2818 " bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
2819 "\n";
2820
2821 if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
2822 {
2823 // scatter primitives
2824 buf << " // Draw grid cells\n"
2825 " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2826 " for (int ndx = 0; ndx < "
2827 << numPrimitives
2828 << "; ++ndx)\n"
2829 " {\n"
2830 " ivec2 dstGridSize = ivec2("
2831 << tessLevel << " * " << numPrimitives << ", 2 * " << tessLevel << " * " << numInstances
2832 << ");\n"
2833 " ivec2 dstGridNdx = ivec2("
2834 << tessLevel << " * ndx + gridPosition.x, " << tessLevel
2835 << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
2836 " vec4 dstArea;\n"
2837 " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2838 " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2839 " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2840 " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2841 "\n"
2842 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2843 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2844 " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2845 "\n"
2846 " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2847 " v_color = outputColor;\n"
2848 " EmitVertex();\n"
2849 "\n"
2850 " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2851 " v_color = outputColor;\n"
2852 " EmitVertex();\n"
2853 "\n"
2854 " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2855 " v_color = outputColor;\n"
2856 " EmitVertex();\n"
2857 "\n"
2858 " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2859 " v_color = outputColor;\n"
2860 " EmitVertex();\n"
2861 " EndPrimitive();\n"
2862 " }\n";
2863 }
2864 else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2865 {
2866 // Number of subrectangle instances = num layers
2867 DE_ASSERT(m_numLayers == numInstances * 2);
2868
2869 buf << " // Draw grid cells, send each primitive to a separate layer\n"
2870 " int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2871 " for (int ndx = 0; ndx < "
2872 << numPrimitives
2873 << "; ++ndx)\n"
2874 " {\n"
2875 " ivec2 dstGridSize = ivec2("
2876 << tessLevel << " * " << numPrimitives << ", " << tessLevel
2877 << ");\n"
2878 " ivec2 dstGridNdx = ivec2((gridPosition.x * "
2879 << numPrimitives
2880 << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
2881 " vec4 dstArea;\n"
2882 " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2883 " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2884 " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2885 " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2886 "\n"
2887 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2888 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2889 " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2890 "\n"
2891 " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2892 " v_color = outputColor;\n"
2893 " gl_Layer = ((baseLayer + ndx) * 11) % "
2894 << m_numLayers
2895 << ";\n"
2896 " EmitVertex();\n"
2897 "\n"
2898 " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2899 " v_color = outputColor;\n"
2900 " gl_Layer = ((baseLayer + ndx) * 11) % "
2901 << m_numLayers
2902 << ";\n"
2903 " EmitVertex();\n"
2904 "\n"
2905 " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2906 " v_color = outputColor;\n"
2907 " gl_Layer = ((baseLayer + ndx) * 11) % "
2908 << m_numLayers
2909 << ";\n"
2910 " EmitVertex();\n"
2911 "\n"
2912 " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2913 " v_color = outputColor;\n"
2914 " gl_Layer = ((baseLayer + ndx) * 11) % "
2915 << m_numLayers
2916 << ";\n"
2917 " EmitVertex();\n"
2918 " EndPrimitive();\n"
2919 " }\n";
2920 }
2921 else
2922 {
2923 if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
2924 {
2925 buf << " // Scatter slices\n"
2926 " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2927 " ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * "
2928 << (numInstances * 2)
2929 << " + inputTriangleNdx);\n"
2930 " ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2("
2931 << tessLevel << ", " << tessLevel << " * " << (numInstances * 2)
2932 << ");\n"
2933 "\n"
2934 " // Draw slice to the dstSlice slot\n"
2935 " vec4 outputSliceArea;\n"
2936 " outputSliceArea.x = float(dstSliceNdx.x) / float("
2937 << tessLevel
2938 << ") * 2.0 - 1.0 - gapOffset;\n"
2939 " outputSliceArea.y = float(dstSliceNdx.y) / float("
2940 << (tessLevel * numInstances * 2)
2941 << ") * 2.0 - 1.0 - gapOffset;\n"
2942 " outputSliceArea.z = float(dstSliceNdx.x+1) / float("
2943 << tessLevel
2944 << ") * 2.0 - 1.0 + gapOffset;\n"
2945 " outputSliceArea.w = float(dstSliceNdx.y+1) / float("
2946 << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
2947 }
2948 else
2949 {
2950 buf << " // Fill the input area with slices\n"
2951 " // Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
2952 " float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
2953 " // Each slice is a invocation\n"
2954 " float sliceHeight = (aabb.w - aabb.y) / float(2 * "
2955 << numInstances
2956 << ");\n"
2957 " float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
2958 "\n"
2959 " vec4 outputSliceArea;\n"
2960 " outputSliceArea.x = aabb.x - gapOffset;\n"
2961 " outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
2962 " outputSliceArea.z = aabb.z + gapOffset;\n"
2963 " outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
2964 }
2965
2966 buf << "\n"
2967 " // Draw slice\n"
2968 " for (int ndx = 0; ndx < "
2969 << ((numPrimitives + 2) / 2)
2970 << "; ++ndx)\n"
2971 " {\n"
2972 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2973 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2974 " vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
2975 " float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float("
2976 << (numPrimitives / 2)
2977 << "));\n"
2978 "\n"
2979 " gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
2980 " v_color = outputColor;\n"
2981 " EmitVertex();\n"
2982 "\n"
2983 " gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
2984 " v_color = outputColor;\n"
2985 " EmitVertex();\n"
2986 " }\n";
2987 }
2988
2989 buf << "}\n";
2990
2991 return specializeShader(buf.str(), m_context.getRenderContext().getType());
2992 }
2993
2994 class FeedbackRecordVariableSelectionCase : public TestCase
2995 {
2996 public:
2997 FeedbackRecordVariableSelectionCase(Context &context, const char *name, const char *description);
2998 ~FeedbackRecordVariableSelectionCase(void);
2999
3000 private:
3001 void init(void);
3002 void deinit(void);
3003 IterateResult iterate(void);
3004
3005 std::string getVertexSource(void);
3006 std::string getFragmentSource(void);
3007 std::string getTessellationControlSource(void);
3008 std::string getTessellationEvaluationSource(void);
3009 std::string getGeometrySource(void);
3010
3011 glu::ShaderProgram *m_program;
3012 uint32_t m_xfbBuf;
3013 };
3014
FeedbackRecordVariableSelectionCase(Context & context,const char * name,const char * description)3015 FeedbackRecordVariableSelectionCase::FeedbackRecordVariableSelectionCase(Context &context, const char *name,
3016 const char *description)
3017 : TestCase(context, name, description)
3018 , m_program(DE_NULL)
3019 , m_xfbBuf(0)
3020 {
3021 }
3022
~FeedbackRecordVariableSelectionCase(void)3023 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase(void)
3024 {
3025 deinit();
3026 }
3027
init(void)3028 void FeedbackRecordVariableSelectionCase::init(void)
3029 {
3030 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
3031 const bool supportsCore40 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0));
3032
3033 if ((!supportsES32 && !supportsCore40) &&
3034 (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
3035 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
3036 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
3037
3038 m_testCtx.getLog() << tcu::TestLog::Message
3039 << "Declaring multiple output variables with the same name in multiple shader stages. Capturing "
3040 "the value of the varying using transform feedback."
3041 << tcu::TestLog::EndMessage;
3042
3043 // gen feedback buffer fit for 1 triangle (4 components)
3044 {
3045 static const tcu::Vec4 initialData[3] = {
3046 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3047 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3048 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3049 };
3050
3051 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3052
3053 m_testCtx.getLog()
3054 << tcu::TestLog::Message
3055 << "Creating buffer for transform feedback. Allocating storage for one triangle. Filling with -1.0"
3056 << tcu::TestLog::EndMessage;
3057
3058 gl.genBuffers(1, &m_xfbBuf);
3059 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfbBuf);
3060 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (int)(sizeof(tcu::Vec4[3])), initialData, GL_DYNAMIC_READ);
3061 GLU_EXPECT_NO_ERROR(gl.getError(), "gen xfb buf");
3062 }
3063
3064 // gen shader
3065 m_program = new glu::ShaderProgram(
3066 m_context.getRenderContext(),
3067 glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
3068 << glu::TessellationControlSource(getTessellationControlSource())
3069 << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
3070 << glu::GeometrySource(getGeometrySource())
3071 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
3072 << glu::TransformFeedbackVarying("tf_feedback"));
3073 m_testCtx.getLog() << *m_program;
3074
3075 if (!m_program->isOk())
3076 throw tcu::TestError("could not build program");
3077 }
3078
deinit(void)3079 void FeedbackRecordVariableSelectionCase::deinit(void)
3080 {
3081 delete m_program;
3082 m_program = DE_NULL;
3083
3084 if (m_xfbBuf)
3085 {
3086 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_xfbBuf);
3087 m_xfbBuf = 0;
3088 }
3089 }
3090
iterate(void)3091 FeedbackRecordVariableSelectionCase::IterateResult FeedbackRecordVariableSelectionCase::iterate(void)
3092 {
3093 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3094 const int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position");
3095 const glu::VertexArray vao(m_context.getRenderContext());
3096
3097 if (posLoc == -1)
3098 throw tcu::TestError("a_position attribute location was -1");
3099
3100 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3101
3102 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering a patch of size 3." << tcu::TestLog::EndMessage;
3103
3104 // Render and feed back
3105
3106 gl.viewport(0, 0, 1, 1);
3107 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3108 gl.clear(GL_COLOR_BUFFER_BIT);
3109 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
3110
3111 gl.bindVertexArray(*vao);
3112 GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray");
3113
3114 gl.vertexAttrib4f(posLoc, 0.0f, 0.0f, 0.0f, 1.0f);
3115 GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib4f");
3116
3117 gl.useProgram(m_program->getProgram());
3118 GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
3119
3120 gl.patchParameteri(GL_PATCH_VERTICES, 3);
3121 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
3122
3123 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfbBuf);
3124 GLU_EXPECT_NO_ERROR(gl.getError(), "bind xfb buf");
3125
3126 gl.beginTransformFeedback(GL_TRIANGLES);
3127 GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
3128
3129 gl.drawArrays(GL_PATCHES, 0, 3);
3130 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
3131
3132 gl.endTransformFeedback();
3133 GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
3134
3135 m_testCtx.getLog() << tcu::TestLog::Message
3136 << "Verifying the value of tf_feedback using transform feedback, expecting (3.0, 3.0, 3.0, 3.0)."
3137 << tcu::TestLog::EndMessage;
3138
3139 // Read back result (one triangle)
3140 {
3141 tcu::Vec4 feedbackValues[3];
3142 const void *mapPtr =
3143 gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (int)sizeof(feedbackValues), GL_MAP_READ_BIT);
3144 GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
3145
3146 if (mapPtr == DE_NULL)
3147 throw tcu::TestError("mapBufferRange returned null");
3148
3149 deMemcpy(feedbackValues, mapPtr, sizeof(feedbackValues));
3150
3151 if (gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER) != GL_TRUE)
3152 throw tcu::TestError("unmapBuffer did not return TRUE");
3153
3154 for (int ndx = 0; ndx < 3; ++ndx)
3155 {
3156 if (!tcu::boolAll(tcu::lessThan(tcu::abs(feedbackValues[ndx] - tcu::Vec4(3.0f)), tcu::Vec4(0.001f))))
3157 {
3158 m_testCtx.getLog() << tcu::TestLog::Message << "Feedback vertex " << ndx
3159 << ": expected (3.0, 3.0, 3.0, 3.0), got " << feedbackValues[ndx]
3160 << tcu::TestLog::EndMessage;
3161 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected feedback results");
3162 }
3163 }
3164 }
3165
3166 return STOP;
3167 }
3168
getVertexSource(void)3169 std::string FeedbackRecordVariableSelectionCase::getVertexSource(void)
3170 {
3171 std::string source = "${VERSION_DECL}\n"
3172 "in highp vec4 a_position;\n"
3173 "out highp vec4 tf_feedback;\n"
3174 "void main()\n"
3175 "{\n"
3176 " gl_Position = a_position;\n"
3177 " tf_feedback = vec4(1.0, 1.0, 1.0, 1.0);\n"
3178 "}\n";
3179
3180 return specializeShader(source, m_context.getRenderContext().getType());
3181 }
3182
getFragmentSource(void)3183 std::string FeedbackRecordVariableSelectionCase::getFragmentSource(void)
3184 {
3185 return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
3186 }
3187
getTessellationControlSource(void)3188 std::string FeedbackRecordVariableSelectionCase::getTessellationControlSource(void)
3189 {
3190 std::string source = "${VERSION_DECL}\n"
3191 "${EXTENSION_TESSELATION_SHADER}"
3192 "layout(vertices=3) out;\n"
3193 "void main()\n"
3194 "{\n"
3195 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3196 " gl_TessLevelOuter[0] = 1.0;\n"
3197 " gl_TessLevelOuter[1] = 1.0;\n"
3198 " gl_TessLevelOuter[2] = 1.0;\n"
3199 " gl_TessLevelInner[0] = 1.0;\n"
3200 "}\n";
3201
3202 return specializeShader(source, m_context.getRenderContext().getType());
3203 }
3204
getTessellationEvaluationSource(void)3205 std::string FeedbackRecordVariableSelectionCase::getTessellationEvaluationSource(void)
3206 {
3207 std::string source = "${VERSION_DECL}\n"
3208 "${EXTENSION_TESSELATION_SHADER}"
3209 "layout(triangles) in;\n"
3210 "out highp vec4 tf_feedback;\n"
3211 "void main()\n"
3212 "{\n"
3213 " gl_Position = gl_in[0].gl_Position * gl_TessCoord.x + gl_in[1].gl_Position * "
3214 "gl_TessCoord.y + gl_in[2].gl_Position * gl_TessCoord.z;\n"
3215 " tf_feedback = vec4(2.0, 2.0, 2.0, 2.0);\n"
3216 "}\n";
3217
3218 return specializeShader(source, m_context.getRenderContext().getType());
3219 }
3220
getGeometrySource(void)3221 std::string FeedbackRecordVariableSelectionCase::getGeometrySource(void)
3222 {
3223 std::string source =
3224 "${VERSION_DECL}\n"
3225 "${EXTENSION_GEOMETRY_SHADER}"
3226 "layout (triangles) in;\n"
3227 "layout (triangle_strip, max_vertices=3) out;\n"
3228 "out highp vec4 tf_feedback;\n"
3229 "void main()\n"
3230 "{\n"
3231 " for (int ndx = 0; ndx < 3; ++ndx)\n"
3232 " {\n"
3233 " gl_Position = gl_in[ndx].gl_Position + vec4(float(ndx), float(ndx)*float(ndx), 0.0, 0.0);\n"
3234 " tf_feedback = vec4(3.0, 3.0, 3.0, 3.0);\n"
3235 " EmitVertex();\n"
3236 " }\n"
3237 " EndPrimitive();\n"
3238 "}\n";
3239
3240 return specializeShader(source, m_context.getRenderContext().getType());
3241 }
3242
3243 } // namespace
3244
TessellationGeometryInteractionTests(Context & context,bool isGL45)3245 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests(Context &context, bool isGL45)
3246 : TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests")
3247 , m_isGL45(isGL45)
3248 {
3249 }
3250
~TessellationGeometryInteractionTests(void)3251 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests(void)
3252 {
3253 }
3254
init(void)3255 void TessellationGeometryInteractionTests::init(void)
3256 {
3257 tcu::TestCaseGroup *const renderGroup = new tcu::TestCaseGroup(m_testCtx, "render", "Various render tests");
3258 tcu::TestCaseGroup *const feedbackGroup = new tcu::TestCaseGroup(m_testCtx, "feedback", "Test transform feedback");
3259 tcu::TestCaseGroup *const pointSizeGroup = new tcu::TestCaseGroup(m_testCtx, "point_size", "Test point size");
3260
3261 addChild(renderGroup);
3262 addChild(feedbackGroup);
3263 addChild(pointSizeGroup);
3264
3265 // .render
3266 {
3267 tcu::TestCaseGroup *const passthroughGroup = new tcu::TestCaseGroup(
3268 m_testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader");
3269 tcu::TestCaseGroup *const limitGroup =
3270 new tcu::TestCaseGroup(m_testCtx, "limits", "Render with properties near their limits");
3271 tcu::TestCaseGroup *const scatterGroup =
3272 new tcu::TestCaseGroup(m_testCtx, "scatter", "Scatter output primitives");
3273
3274 renderGroup->addChild(passthroughGroup);
3275 renderGroup->addChild(limitGroup);
3276 renderGroup->addChild(scatterGroup);
3277
3278 // .passthrough
3279 {
3280 // tessellate_tris_passthrough_geometry_no_change
3281 // tessellate_quads_passthrough_geometry_no_change
3282 // tessellate_isolines_passthrough_geometry_no_change
3283 passthroughGroup->addChild(new IdentityGeometryShaderCase(
3284 m_context, "tessellate_tris_passthrough_geometry_no_change",
3285 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_TRIANGLES));
3286 passthroughGroup->addChild(new IdentityGeometryShaderCase(
3287 m_context, "tessellate_quads_passthrough_geometry_no_change",
3288 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_QUADS));
3289 passthroughGroup->addChild(new IdentityGeometryShaderCase(
3290 m_context, "tessellate_isolines_passthrough_geometry_no_change",
3291 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_ISOLINES));
3292
3293 // passthrough_tessellation_geometry_shade_triangles_no_change
3294 // passthrough_tessellation_geometry_shade_lines_no_change
3295 passthroughGroup->addChild(new IdentityTessellationShaderCase(
3296 m_context, "passthrough_tessellation_geometry_shade_triangles_no_change",
3297 "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_TRIANGLES));
3298 passthroughGroup->addChild(new IdentityTessellationShaderCase(
3299 m_context, "passthrough_tessellation_geometry_shade_lines_no_change",
3300 "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_ISOLINES));
3301 }
3302
3303 // .limits
3304 {
3305 static const struct LimitCaseDef
3306 {
3307 const char *name;
3308 const char *desc;
3309 int flags;
3310 } cases[] = {
3311 // Test single limit
3312 {"output_required_max_tessellation", "Minimum maximum tessellation level",
3313 GridRenderCase::FLAG_TESSELLATION_MAX_SPEC},
3314 {"output_implementation_max_tessellation", "Maximum tessellation level supported by the implementation",
3315 GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION},
3316 {"output_required_max_geometry", "Output minimum maximum number of vertices the geometry shader",
3317 GridRenderCase::FLAG_GEOMETRY_MAX_SPEC},
3318 {"output_implementation_max_geometry",
3319 "Output maximum number of vertices in the geometry shader supported by the implementation",
3320 GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION},
3321 {"output_required_max_invocations", "Minimum maximum number of geometry shader invocations",
3322 GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC},
3323 {"output_implementation_max_invocations",
3324 "Maximum number of geometry shader invocations supported by the implementation",
3325 GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION},
3326 };
3327
3328 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
3329 limitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
3330 }
3331
3332 // .scatter
3333 {
3334 scatterGroup->addChild(new GridRenderCase(
3335 m_context, "geometry_scatter_instances",
3336 "Each geometry shader instance outputs its primitives far from other instances of the same execution",
3337 GridRenderCase::FLAG_GEOMETRY_SCATTER_INSTANCES));
3338 scatterGroup->addChild(new GridRenderCase(
3339 m_context, "geometry_scatter_primitives",
3340 "Each geometry shader instance outputs its primitives far from other primitives of the same instance",
3341 GridRenderCase::FLAG_GEOMETRY_SCATTER_PRIMITIVES | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3342 scatterGroup->addChild(new GridRenderCase(
3343 m_context, "geometry_scatter_layers",
3344 "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives "
3345 "of the same instance",
3346 GridRenderCase::FLAG_GEOMETRY_SCATTER_LAYERS | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3347 }
3348 }
3349
3350 // .feedback
3351 {
3352 static const struct PrimitiveCaseConfig
3353 {
3354 const char *name;
3355 const char *description;
3356 FeedbackPrimitiveTypeCase::TessellationOutputType tessellationOutput;
3357 FeedbackPrimitiveTypeCase::TessellationPointMode tessellationPointMode;
3358 FeedbackPrimitiveTypeCase::GeometryOutputType geometryOutputType;
3359 } caseConfigs[] = {
3360 // tess output triangles -> geo input triangles, output points
3361 {"tessellation_output_triangles_geometry_output_points",
3362 "Tessellation outputs triangles, geometry outputs points",
3363 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3364 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3365
3366 // tess output quads <-> geo input triangles, output points
3367 {"tessellation_output_quads_geometry_output_points", "Tessellation outputs quads, geometry outputs points",
3368 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
3369 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3370
3371 // tess output isolines <-> geo input lines, output points
3372 {"tessellation_output_isolines_geometry_output_points",
3373 "Tessellation outputs isolines, geometry outputs points",
3374 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
3375 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3376
3377 // tess output triangles, point_mode <-> geo input points, output lines
3378 {"tessellation_output_triangles_point_mode_geometry_output_lines",
3379 "Tessellation outputs triangles in point mode, geometry outputs lines",
3380 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3381 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES},
3382
3383 // tess output quads, point_mode <-> geo input points, output lines
3384 {"tessellation_output_quads_point_mode_geometry_output_lines",
3385 "Tessellation outputs quads in point mode, geometry outputs lines",
3386 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3387 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES},
3388
3389 // tess output isolines, point_mode <-> geo input points, output triangles
3390 {"tessellation_output_isolines_point_mode_geometry_output_triangles",
3391 "Tessellation outputs isolines in point mode, geometry outputs triangles",
3392 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3393 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_TRIANGLES},
3394 };
3395
3396 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3397 {
3398 feedbackGroup->addChild(new FeedbackPrimitiveTypeCase(
3399 m_context, caseConfigs[ndx].name, caseConfigs[ndx].description, caseConfigs[ndx].tessellationOutput,
3400 caseConfigs[ndx].tessellationPointMode, caseConfigs[ndx].geometryOutputType));
3401 }
3402
3403 feedbackGroup->addChild(new FeedbackRecordVariableSelectionCase(
3404 m_context, "record_variable_selection",
3405 "Record a variable that has been declared as an output variable in multiple shader stages"));
3406 }
3407
3408 // .point_size
3409 {
3410 static const struct PointSizeCaseConfig
3411 {
3412 const int caseMask;
3413 const bool isSupportedInGL; // is this case supported in OpenGL
3414 } caseConfigs[] = {
3415 {PointSizeCase::FLAG_VERTEX_SET, true},
3416 {PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, true},
3417 {PointSizeCase::FLAG_GEOMETRY_SET, true},
3418 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_CONTROL_SET, false},
3419 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, true},
3420 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_DONT_SET, false},
3421 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_GEOMETRY_SET, true},
3422 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET |
3423 PointSizeCase::FLAG_GEOMETRY_SET,
3424 true},
3425 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_ADD | PointSizeCase::FLAG_GEOMETRY_ADD,
3426 true},
3427 {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET |
3428 PointSizeCase::FLAG_GEOMETRY_DONT_SET,
3429 false},
3430 };
3431
3432 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3433 {
3434 if (m_isGL45 && !caseConfigs[ndx].isSupportedInGL)
3435 continue;
3436
3437 const std::string name = PointSizeCase::genTestCaseName(caseConfigs[ndx].caseMask);
3438 const std::string desc = PointSizeCase::genTestCaseDescription(caseConfigs[ndx].caseMask);
3439
3440 pointSizeGroup->addChild(
3441 new PointSizeCase(m_context, name.c_str(), desc.c_str(), caseConfigs[ndx].caseMask));
3442 }
3443 }
3444 }
3445
3446 } // namespace Functional
3447 } // namespace gles31
3448 } // namespace deqp
3449