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