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