1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2015 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 Primitive bounding box tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es31fPrimitiveBoundingBoxTests.hpp"
25
26 #include "tcuTestLog.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "gluCallLogWrapper.hpp"
32 #include "gluContextInfo.hpp"
33 #include "gluRenderContext.hpp"
34 #include "gluStrUtil.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "gluObjectWrapper.hpp"
37 #include "gluPixelTransfer.hpp"
38 #include "glsStateQueryUtil.hpp"
39 #include "glwFunctions.hpp"
40 #include "glwEnums.hpp"
41 #include "deRandom.hpp"
42 #include "deUniquePtr.hpp"
43 #include "deStringUtil.hpp"
44
45 #include <vector>
46 #include <sstream>
47 #include <algorithm>
48
49 namespace deqp
50 {
51 namespace gles31
52 {
53 namespace Functional
54 {
55 namespace
56 {
57
58 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
59
60 struct BoundingBox
61 {
62 tcu::Vec4 min;
63 tcu::Vec4 max;
64
65 /*--------------------------------------------------------------------*//*!
66 * Get component by index of a 8-component vector constructed by
67 * concatenating 4-component min and max vectors.
68 *//*--------------------------------------------------------------------*/
69 float& getComponentAccess (int ndx);
70 const float& getComponentAccess (int ndx) const;
71 };
72
getComponentAccess(int ndx)73 float& BoundingBox::getComponentAccess (int ndx)
74 {
75 DE_ASSERT(ndx >= 0 && ndx < 8);
76 if (ndx < 4)
77 return min[ndx];
78 else
79 return max[ndx-4];
80 }
81
getComponentAccess(int ndx) const82 const float& BoundingBox::getComponentAccess (int ndx) const
83 {
84 return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
85 }
86
87 struct ProjectedBBox
88 {
89 tcu::Vec3 min;
90 tcu::Vec3 max;
91 };
92
projectBoundingBox(const BoundingBox & bbox)93 static ProjectedBBox projectBoundingBox (const BoundingBox& bbox)
94 {
95 const float wMin = de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires
96 const float wMax = de::max(0.0f, bbox.max.w());
97 ProjectedBBox retVal;
98
99 retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin,
100 bbox.min.swizzle(0, 1, 2) / wMax);
101 retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin,
102 bbox.max.swizzle(0, 1, 2) / wMax);
103 return retVal;
104 }
105
getViewportBoundingBoxArea(const ProjectedBBox & bbox,const tcu::IVec2 & viewportSize,float size=0.0f)106 static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f)
107 {
108 tcu::Vec4 vertexBox;
109 tcu::IVec4 pixelBox;
110
111 vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x();
112 vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y();
113 vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x();
114 vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y();
115
116 pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size/2.0f);
117 pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size/2.0f);
118 pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size/2.0f);
119 pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size/2.0f);
120 return pixelBox;
121 }
122
123
124 class InitialValueCase : public TestCase
125 {
126 public:
127 InitialValueCase (Context& context, const char* name, const char* desc);
128
129 void init (void);
130 IterateResult iterate (void);
131 };
132
InitialValueCase(Context & context,const char * name,const char * desc)133 InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
134 : TestCase(context, name, desc)
135 {
136 }
137
init(void)138 void InitialValueCase::init (void)
139 {
140 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
141 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
142 }
143
iterate(void)144 InitialValueCase::IterateResult InitialValueCase::iterate (void)
145 {
146 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
147 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
148
149 gl.enableLogging(true);
150
151 m_testCtx.getLog()
152 << tcu::TestLog::Message
153 << "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)"
154 << tcu::TestLog::EndMessage;
155
156 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
157 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
158
159 if (!state.verifyValidity(m_testCtx))
160 return STOP;
161
162 m_testCtx.getLog()
163 << tcu::TestLog::Message
164 << "Got " << tcu::formatArray(&state[0], &state[8])
165 << tcu::TestLog::EndMessage;
166
167 if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) ||
168 (state[4] != 1.0f) || (state[5] != 1.0f) || (state[6] != 1.0f) || (state[7] != 1.0f))
169 {
170 m_testCtx.getLog()
171 << tcu::TestLog::Message
172 << "Error, unexpected value"
173 << tcu::TestLog::EndMessage;
174
175 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value");
176 }
177 else
178 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
179
180 return STOP;
181 }
182
183 class QueryCase : public TestCase
184 {
185 public:
186 enum QueryMethod
187 {
188 QUERY_FLOAT = 0,
189 QUERY_BOOLEAN,
190 QUERY_INT,
191 QUERY_INT64,
192
193 QUERY_LAST
194 };
195
196 QueryCase (Context& context, const char* name, const char* desc, QueryMethod method);
197
198 private:
199 void init (void);
200 IterateResult iterate (void);
201
202 bool verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const;
203
204 const QueryMethod m_method;
205 };
206
QueryCase(Context & context,const char * name,const char * desc,QueryMethod method)207 QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method)
208 : TestCase (context, name, desc)
209 , m_method (method)
210 {
211 DE_ASSERT(method < QUERY_LAST);
212 }
213
init(void)214 void QueryCase::init (void)
215 {
216 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
217 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
218 }
219
iterate(void)220 QueryCase::IterateResult QueryCase::iterate (void)
221 {
222 static const BoundingBox fixedCases[] =
223 {
224 { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f) },
225 { tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, -0.0f) },
226 { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 1.0f, 1.0f, 1.0f, -1.0f) },
227 { tcu::Vec4( 2.0f, 2.0f, 2.0f, 2.0f), tcu::Vec4( 1.5f, 1.5f, 1.5f, 1.0f) },
228 { tcu::Vec4( 1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f) },
229 { tcu::Vec4( 1.0f, 1.0f, 1.0f, 0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f) },
230 };
231
232 const int numRandomCases = 9;
233 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
234 de::Random rnd (0xDE3210);
235 std::vector<BoundingBox> cases;
236
237 cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases));
238 for (int ndx = 0; ndx < numRandomCases; ++ndx)
239 {
240 BoundingBox boundingBox;
241
242 // parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...)
243 for (int coordNdx = 0; coordNdx < 8; ++coordNdx)
244 boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f);
245
246 cases.push_back(boundingBox);
247 }
248
249 gl.enableLogging(true);
250 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
251
252 for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx)
253 {
254 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1));
255 const BoundingBox& boundingBox = cases[caseNdx];
256
257 gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(),
258 boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w());
259
260 if (!verifyState(gl, boundingBox))
261 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
262 }
263
264 return STOP;
265 }
266
verifyState(glu::CallLogWrapper & gl,const BoundingBox & bbox) const267 bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const
268 {
269 switch (m_method)
270 {
271 case QUERY_FLOAT:
272 {
273 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
274 bool error = false;
275
276 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
277 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
278
279 if (!state.verifyValidity(m_testCtx))
280 return false;
281
282 m_testCtx.getLog()
283 << tcu::TestLog::Message
284 << "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8])
285 << tcu::TestLog::EndMessage;
286
287 for (int ndx = 0; ndx < 8; ++ndx)
288 if (state[ndx] != bbox.getComponentAccess(ndx))
289 error = true;
290
291 if (error)
292 {
293 m_testCtx.getLog()
294 << tcu::TestLog::Message
295 << "Error, unexpected value\n"
296 << "Expected ["
297 << bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", " << bbox.min.w() << ", "
298 << bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z() << ", " << bbox.max.w() << "]"
299 << tcu::TestLog::EndMessage;
300 return false;
301 }
302 return true;
303 }
304
305 case QUERY_INT:
306 {
307 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]> state;
308 bool error = false;
309
310 gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
311 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
312
313 if (!state.verifyValidity(m_testCtx))
314 return false;
315
316 m_testCtx.getLog()
317 << tcu::TestLog::Message
318 << "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8])
319 << tcu::TestLog::EndMessage;
320
321 for (int ndx = 0; ndx < 8; ++ndx)
322 if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) &&
323 state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)))
324 error = true;
325
326 if (error)
327 {
328 tcu::MessageBuilder builder(&m_testCtx.getLog());
329
330 builder << "Error, unexpected value\n"
331 << "Expected [";
332
333 for (int ndx = 0; ndx < 8; ++ndx)
334 {
335 const glw::GLint roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx));
336 const glw::GLint roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx));
337
338 if (ndx != 0)
339 builder << ", ";
340
341 if (roundDown == roundUp)
342 builder << roundDown;
343 else
344 builder << "{" << roundDown << ", " << roundUp << "}";
345 }
346
347 builder << "]"
348 << tcu::TestLog::EndMessage;
349 return false;
350 }
351 return true;
352 }
353
354 case QUERY_INT64:
355 {
356 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]> state;
357 bool error = false;
358
359 gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
360 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
361
362 if (!state.verifyValidity(m_testCtx))
363 return false;
364
365 m_testCtx.getLog()
366 << tcu::TestLog::Message
367 << "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8])
368 << tcu::TestLog::EndMessage;
369
370 for (int ndx = 0; ndx < 8; ++ndx)
371 if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) &&
372 state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)))
373 error = true;
374
375 if (error)
376 {
377 tcu::MessageBuilder builder(&m_testCtx.getLog());
378
379 builder << "Error, unexpected value\n"
380 << "Expected [";
381
382 for (int ndx = 0; ndx < 8; ++ndx)
383 {
384 const glw::GLint64 roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx));
385 const glw::GLint64 roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx));
386
387 if (ndx != 0)
388 builder << ", ";
389
390 if (roundDown == roundUp)
391 builder << roundDown;
392 else
393 builder << "{" << roundDown << ", " << roundUp << "}";
394 }
395
396 builder << "]"
397 << tcu::TestLog::EndMessage;
398 return false;
399 }
400 return true;
401 }
402
403 case QUERY_BOOLEAN:
404 {
405 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]> state;
406 bool error = false;
407
408 gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
409 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
410
411 if (!state.verifyValidity(m_testCtx))
412 return false;
413
414 m_testCtx.getLog()
415 << tcu::TestLog::Message
416 << "glGetBooleanv returned ["
417 << glu::getBooleanStr(state[0]) << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", " << glu::getBooleanStr(state[3]) << ", "
418 << glu::getBooleanStr(state[4]) << ", " << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", " << glu::getBooleanStr(state[7]) << "]\n"
419 << tcu::TestLog::EndMessage;
420
421 for (int ndx = 0; ndx < 8; ++ndx)
422 if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE)))
423 error = true;
424
425 if (error)
426 {
427 tcu::MessageBuilder builder(&m_testCtx.getLog());
428
429 builder << "Error, unexpected value\n"
430 << "Expected [";
431
432 for (int ndx = 0; ndx < 8; ++ndx)
433 {
434 if (ndx != 0)
435 builder << ", ";
436
437 builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE"));
438 }
439
440 builder << "]"
441 << tcu::TestLog::EndMessage;
442 return false;
443 }
444 return true;
445 }
446
447 default:
448 DE_ASSERT(false);
449 return true;
450 }
451 }
452
453 class BBoxRenderCase : public TestCase
454 {
455 public:
456 enum
457 {
458 FLAG_RENDERTARGET_DEFAULT = 1u << 0, //!< render to default renderbuffer
459 FLAG_RENDERTARGET_FBO = 1u << 1, //!< render to framebuffer object
460
461 FLAG_BBOXSIZE_EQUAL = 1u << 2, //!< set tight primitive bounding box
462 FLAG_BBOXSIZE_LARGER = 1u << 3, //!< set padded primitive bounding box
463 FLAG_BBOXSIZE_SMALLER = 1u << 4, //!< set too small primitive bounding box
464
465 FLAG_TESSELLATION = 1u << 5, //!< use tessellation shader
466 FLAG_GEOMETRY = 1u << 6, //!< use geometry shader
467
468 FLAG_SET_BBOX_STATE = 1u << 7, //!< set primitive bounding box using global state
469 FLAG_SET_BBOX_OUTPUT = 1u << 8, //!< set primitive bounding box using tessellation output
470 FLAG_PER_PRIMITIVE_BBOX = 1u << 9, //!< set primitive bounding per primitive
471
472 FLAGBIT_USER_BIT = 10u //!< bits N and and up are reserved for subclasses
473 };
474
475 BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags);
476 ~BBoxRenderCase (void);
477
478 protected:
479 enum RenderTarget
480 {
481 RENDERTARGET_DEFAULT,
482 RENDERTARGET_FBO,
483 };
484 enum BBoxSize
485 {
486 BBOXSIZE_EQUAL,
487 BBOXSIZE_LARGER,
488 BBOXSIZE_SMALLER,
489 };
490
491 enum
492 {
493 RENDER_TARGET_MIN_SIZE = 256,
494 FBO_SIZE = 512,
495 MIN_VIEWPORT_SIZE = 256,
496 MAX_VIEWPORT_SIZE = 512,
497 };
498 DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE);
499
500 enum
501 {
502 VA_POS_VEC_NDX = 0,
503 VA_COL_VEC_NDX = 1,
504 VA_NUM_ATTRIB_VECS = 2,
505 };
506
507 enum AABBRoundDirection
508 {
509 ROUND_INWARDS = 0,
510 ROUND_OUTWARDS
511 };
512
513 struct IterationConfig
514 {
515 tcu::IVec2 viewportPos;
516 tcu::IVec2 viewportSize;
517 tcu::Vec2 patternPos; //!< in NDC
518 tcu::Vec2 patternSize; //!< in NDC
519 BoundingBox bbox;
520 };
521
522 virtual void init (void);
523 virtual void deinit (void);
524 IterateResult iterate (void);
525
526 virtual std::string genVertexSource (void) const = 0;
527 virtual std::string genFragmentSource (void) const = 0;
528 virtual std::string genTessellationControlSource (void) const = 0;
529 virtual std::string genTessellationEvaluationSource (void) const = 0;
530 virtual std::string genGeometrySource (void) const = 0;
531
532 virtual IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const = 0;
533 virtual void getAttributeData (std::vector<tcu::Vec4>& data) const = 0;
534 virtual void renderTestPattern (const IterationConfig& config) = 0;
535 virtual void verifyRenderResult (const IterationConfig& config) = 0;
536
537 IterationConfig generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const;
538 tcu::IVec4 getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const;
539
540 void setupRender (const IterationConfig& config);
541
542 enum ShaderFunction
543 {
544 SHADER_FUNC_MIRROR_X,
545 SHADER_FUNC_MIRROR_Y,
546 SHADER_FUNC_INSIDE_BBOX,
547 };
548
549 const char* genShaderFunction (ShaderFunction func) const;
550
551 const RenderTarget m_renderTarget;
552 const BBoxSize m_bboxSize;
553 const bool m_hasTessellationStage;
554 const bool m_hasGeometryStage;
555 const bool m_useGlobalState;
556 const bool m_calcPerPrimitiveBBox;
557 const int m_numIterations;
558
559 de::MovePtr<glu::ShaderProgram> m_program;
560 de::MovePtr<glu::Buffer> m_vbo;
561 de::MovePtr<glu::Framebuffer> m_fbo;
562
563 private:
564 std::vector<IterationConfig> m_iterationConfigs;
565 int m_iteration;
566 };
567
BBoxRenderCase(Context & context,const char * name,const char * description,int numIterations,deUint32 flags)568 BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags)
569 : TestCase (context, name, description)
570 , m_renderTarget ((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO))
571 , m_bboxSize ((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER))
572 , m_hasTessellationStage ((flags & FLAG_TESSELLATION) != 0)
573 , m_hasGeometryStage ((flags & FLAG_GEOMETRY) != 0)
574 , m_useGlobalState ((flags & FLAG_SET_BBOX_STATE) != 0)
575 , m_calcPerPrimitiveBBox ((flags & FLAG_PER_PRIMITIVE_BBOX) != 0)
576 , m_numIterations (numIterations)
577 , m_iteration (0)
578 {
579 // validate flags
580 DE_ASSERT((((m_renderTarget == RENDERTARGET_DEFAULT) ? (FLAG_RENDERTARGET_DEFAULT) : (0)) |
581 ((m_renderTarget == RENDERTARGET_FBO) ? (FLAG_RENDERTARGET_FBO) : (0)) |
582 ((m_bboxSize == BBOXSIZE_EQUAL) ? (FLAG_BBOXSIZE_EQUAL) : (0)) |
583 ((m_bboxSize == BBOXSIZE_LARGER) ? (FLAG_BBOXSIZE_LARGER) : (0)) |
584 ((m_bboxSize == BBOXSIZE_SMALLER) ? (FLAG_BBOXSIZE_SMALLER) : (0)) |
585 ((m_hasTessellationStage) ? (FLAG_TESSELLATION) : (0)) |
586 ((m_hasGeometryStage) ? (FLAG_GEOMETRY) : (0)) |
587 ((m_useGlobalState) ? (FLAG_SET_BBOX_STATE) : (0)) |
588 ((!m_useGlobalState) ? (FLAG_SET_BBOX_OUTPUT) : (0)) |
589 ((m_calcPerPrimitiveBBox) ? (FLAG_PER_PRIMITIVE_BBOX) : (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1)));
590
591 DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation
592
593 if (m_calcPerPrimitiveBBox)
594 {
595 DE_ASSERT(!m_useGlobalState); // per-primitive test requires per-primitive (non-global) state
596 DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting
597 }
598 }
599
~BBoxRenderCase(void)600 BBoxRenderCase::~BBoxRenderCase (void)
601 {
602 deinit();
603 }
604
init(void)605 void BBoxRenderCase::init (void)
606 {
607 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
608 const tcu::IVec2 renderTargetSize = (m_renderTarget == RENDERTARGET_DEFAULT) ?
609 (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
610 (tcu::IVec2(FBO_SIZE, FBO_SIZE));
611
612 // requirements
613 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
614 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
615 if (m_hasTessellationStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
616 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
617 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
618 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
619 if (m_renderTarget == RENDERTARGET_DEFAULT && (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE))
620 throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) + "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer");
621
622 // log case specifics
623 m_testCtx.getLog()
624 << tcu::TestLog::Message
625 << "Setting primitive bounding box "
626 << ((m_calcPerPrimitiveBBox) ? ("to exactly cover each generated primitive")
627 : (m_bboxSize == BBOXSIZE_EQUAL) ? ("to exactly cover rendered grid")
628 : (m_bboxSize == BBOXSIZE_LARGER) ? ("to cover the grid and include some padding")
629 : (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid")
630 : (DE_NULL))
631 << ".\n"
632 << "Rendering with vertex"
633 << ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : (""))
634 << ((m_hasGeometryStage) ? ("-geometry") : (""))
635 << "-fragment program.\n"
636 << "Set bounding box using "
637 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
638 << "\n"
639 << "Verifying rendering results are valid within the bounding box."
640 << tcu::TestLog::EndMessage;
641
642 // resources
643
644 {
645 glu::ProgramSources sources;
646 sources << glu::VertexSource(genVertexSource());
647 sources << glu::FragmentSource(genFragmentSource());
648
649 if (m_hasTessellationStage)
650 sources << glu::TessellationControlSource(genTessellationControlSource())
651 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
652 if (m_hasGeometryStage)
653 sources << glu::GeometrySource(genGeometrySource());
654
655 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
656 GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
657
658 {
659 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
660 m_testCtx.getLog() << *m_program;
661 }
662
663 if (!m_program->isOk())
664 throw tcu::TestError("failed to build program");
665 }
666
667 if (m_renderTarget == RENDERTARGET_FBO)
668 {
669 glu::Texture colorAttachment(m_context.getRenderContext());
670
671 gl.bindTexture(GL_TEXTURE_2D, *colorAttachment);
672 gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE);
673 GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex");
674
675 m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
676 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
677 gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0);
678 GLU_EXPECT_NO_ERROR(gl.getError(), "attach");
679
680 // unbind to prevent texture name deletion from removing it from current fbo attachments
681 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
682 }
683
684 {
685 std::vector<tcu::Vec4> data;
686
687 getAttributeData(data);
688
689 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
690 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
691 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
692 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
693 }
694
695 // Iterations
696 for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx)
697 m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize));
698 }
699
deinit(void)700 void BBoxRenderCase::deinit (void)
701 {
702 m_program.clear();
703 m_vbo.clear();
704 m_fbo.clear();
705 }
706
iterate(void)707 BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void)
708 {
709 const tcu::ScopedLogSection section (m_testCtx.getLog(),
710 std::string() + "Iteration" + de::toString((int)m_iteration),
711 std::string() + "Iteration " + de::toString((int)m_iteration+1) + "/" + de::toString((int)m_iterationConfigs.size()));
712 const IterationConfig& config = m_iterationConfigs[m_iteration];
713
714 // default
715 if (m_iteration == 0)
716 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
717
718 renderTestPattern(config);
719 verifyRenderResult(config);
720
721 if (++m_iteration < (int)m_iterationConfigs.size())
722 return CONTINUE;
723
724 return STOP;
725 }
726
generateRandomConfig(int seed,const tcu::IVec2 & renderTargetSize) const727 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const
728 {
729 de::Random rnd (seed);
730 IterationConfig config;
731
732 // viewport config
733 config.viewportSize.x() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE));
734 config.viewportSize.y() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE));
735 config.viewportPos.x() = rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x());
736 config.viewportPos.y() = rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y());
737
738 // pattern location inside viewport
739 config.patternSize.x() = rnd.getFloat(0.4f, 1.4f);
740 config.patternSize.y() = rnd.getFloat(0.4f, 1.4f);
741 config.patternPos.x() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.x());
742 config.patternPos.y() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.y());
743
744 // accurate bounding box
745 config.bbox.min = tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f);
746 config.bbox.max = tcu::Vec4(config.patternPos.x() + config.patternSize.x(), config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f);
747
748 if (m_bboxSize == BBOXSIZE_LARGER)
749 {
750 // increase bbox size
751 config.bbox.min.x() -= rnd.getFloat() * 0.5f;
752 config.bbox.min.y() -= rnd.getFloat() * 0.5f;
753 config.bbox.min.z() -= rnd.getFloat() * 0.5f;
754
755 config.bbox.max.x() += rnd.getFloat() * 0.5f;
756 config.bbox.max.y() += rnd.getFloat() * 0.5f;
757 config.bbox.max.z() += rnd.getFloat() * 0.5f;
758 }
759 else if (m_bboxSize == BBOXSIZE_SMALLER)
760 {
761 // reduce bbox size
762 config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x();
763 config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y();
764
765 config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x();
766 config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y();
767 }
768
769 return config;
770 }
771
getViewportPatternArea(const tcu::Vec2 & patternPos,const tcu::Vec2 & patternSize,const tcu::IVec2 & viewportSize,AABBRoundDirection roundDir) const772 tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const
773 {
774 const float halfPixel = 0.5f;
775 tcu::Vec4 vertexBox;
776 tcu::IVec4 pixelBox;
777
778 vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x();
779 vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y();
780 vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x();
781 vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y();
782
783 if (roundDir == ROUND_INWARDS)
784 {
785 pixelBox.x() = (int)deFloatCeil(vertexBox.x()+halfPixel);
786 pixelBox.y() = (int)deFloatCeil(vertexBox.y()+halfPixel);
787 pixelBox.z() = (int)deFloatFloor(vertexBox.z()-halfPixel);
788 pixelBox.w() = (int)deFloatFloor(vertexBox.w()-halfPixel);
789 }
790 else
791 {
792 pixelBox.x() = (int)deFloatFloor(vertexBox.x()-halfPixel);
793 pixelBox.y() = (int)deFloatFloor(vertexBox.y()-halfPixel);
794 pixelBox.z() = (int)deFloatCeil(vertexBox.z()+halfPixel);
795 pixelBox.w() = (int)deFloatCeil(vertexBox.w()+halfPixel);
796 }
797
798 return pixelBox;
799 }
800
setupRender(const IterationConfig & config)801 void BBoxRenderCase::setupRender (const IterationConfig& config)
802 {
803 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
804 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
805 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_color");
806 const glw::GLint posScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_posScale");
807
808 TCU_CHECK(posLocation != -1);
809 TCU_CHECK(colLocation != -1);
810 TCU_CHECK(posScaleLocation != -1);
811
812 m_testCtx.getLog()
813 << tcu::TestLog::Message
814 << "Setting viewport to ("
815 << "x: " << config.viewportPos.x() << ", "
816 << "y: " << config.viewportPos.y() << ", "
817 << "w: " << config.viewportSize.x() << ", "
818 << "h: " << config.viewportSize.y() << ")\n"
819 << "Vertex coordinates are in range:\n"
820 << "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x()) << "]\n"
821 << "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y()) << "]\n"
822 << tcu::TestLog::EndMessage;
823
824 if (!m_calcPerPrimitiveBBox)
825 m_testCtx.getLog()
826 << tcu::TestLog::Message
827 << "Setting primitive bounding box to:\n"
828 << "\t" << config.bbox.min << "\n"
829 << "\t" << config.bbox.max << "\n"
830 << tcu::TestLog::EndMessage;
831
832 if (m_useGlobalState)
833 gl.primitiveBoundingBox(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(),
834 config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
835 else
836 // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
837 gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f,
838 -1.7f, -1.7f, 0.0f, 1.0f);
839
840 if (m_fbo)
841 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
842
843 gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y());
844 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
845 gl.clear(GL_COLOR_BUFFER_BIT);
846
847 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
848 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_POS_VEC_NDX);
849 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), (const float*)DE_NULL + 4 * VA_COL_VEC_NDX);
850 gl.enableVertexAttribArray(posLocation);
851 gl.enableVertexAttribArray(colLocation);
852 gl.useProgram(m_program->getProgram());
853 gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(), config.patternSize.y());
854
855 {
856 const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin");
857 const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax");
858
859 gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w());
860 gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
861 }
862
863 gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(), config.viewportPos.y());
864 gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(), config.viewportSize.y());
865
866 GLU_EXPECT_NO_ERROR(gl.getError(), "setup");
867 }
868
genShaderFunction(ShaderFunction func) const869 const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const
870 {
871 switch (func)
872 {
873 case SHADER_FUNC_MIRROR_X:
874 return "vec4 mirrorX(in highp vec4 p)\n"
875 "{\n"
876 " highp vec2 patternOffset = u_posScale.xy;\n"
877 " highp vec2 patternScale = u_posScale.zw;\n"
878 " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
879 " return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n"
880 "}\n";
881
882 case SHADER_FUNC_MIRROR_Y:
883 return "vec4 mirrorY(in highp vec4 p)\n"
884 "{\n"
885 " highp vec2 patternOffset = u_posScale.xy;\n"
886 " highp vec2 patternScale = u_posScale.zw;\n"
887 " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
888 " return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n"
889 "}\n";
890
891 case SHADER_FUNC_INSIDE_BBOX:
892 return "uniform highp ivec2 u_viewportPos;\n"
893 "uniform highp ivec2 u_viewportSize;\n"
894 "flat in highp float v_bbox_expansionSize;\n"
895 "flat in highp vec3 v_bbox_clipMin;\n"
896 "flat in highp vec3 v_bbox_clipMax;\n"
897 "\n"
898 "bool fragmentInsideTheBBox(in highp float depth)\n"
899 "{\n"
900 " highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - v_bbox_expansionSize/2.0),\n"
901 " floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - v_bbox_expansionSize/2.0),\n"
902 " ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + v_bbox_expansionSize/2.0),\n"
903 " ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + v_bbox_expansionSize/2.0));\n"
904 " if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + wc.z ||\n"
905 " gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + wc.w)\n"
906 " return false;\n"
907 " const highp float dEpsilon = 0.001;\n"
908 " if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n"
909 " return false;\n"
910 " return true;\n"
911 "}\n";
912 default:
913 DE_ASSERT(false);
914 return "";
915 }
916 }
917
918 class GridRenderCase : public BBoxRenderCase
919 {
920 public:
921 GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
922 ~GridRenderCase (void);
923
924 private:
925 void init (void);
926
927 std::string genVertexSource (void) const;
928 std::string genFragmentSource (void) const;
929 std::string genTessellationControlSource (void) const;
930 std::string genTessellationEvaluationSource (void) const;
931 std::string genGeometrySource (void) const;
932
933 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
934 void getAttributeData (std::vector<tcu::Vec4>& data) const;
935 void renderTestPattern (const IterationConfig& config);
936 void verifyRenderResult (const IterationConfig& config);
937
938 const int m_gridSize;
939 };
940
GridRenderCase(Context & context,const char * name,const char * description,deUint32 flags)941 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
942 : BBoxRenderCase (context, name, description, 12, flags)
943 , m_gridSize (24)
944 {
945 }
946
~GridRenderCase(void)947 GridRenderCase::~GridRenderCase (void)
948 {
949 }
950
init(void)951 void GridRenderCase::init (void)
952 {
953 m_testCtx.getLog()
954 << tcu::TestLog::Message
955 << "Rendering yellow-green grid to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
956 << "Grid cells are in random order, varying grid size and location for each iteration.\n"
957 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel."
958 << tcu::TestLog::EndMessage;
959
960 BBoxRenderCase::init();
961 }
962
genVertexSource(void) const963 std::string GridRenderCase::genVertexSource (void) const
964 {
965 std::ostringstream buf;
966
967 buf << "#version 310 es\n"
968 "in highp vec4 a_position;\n"
969 "in highp vec4 a_color;\n"
970 "out highp vec4 vtx_color;\n"
971 "uniform highp vec4 u_posScale;\n"
972 "\n";
973 if (!m_hasTessellationStage)
974 {
975 DE_ASSERT(m_useGlobalState);
976 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
977 "uniform highp vec4 u_primitiveBBoxMax;\n"
978 "\n"
979 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
980 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
981 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
982 "\n";
983 }
984
985 buf << "void main()\n"
986 "{\n"
987 " highp vec2 patternOffset = u_posScale.xy;\n"
988 " highp vec2 patternScale = u_posScale.zw;\n"
989 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
990 " vtx_color = a_color;\n";
991
992 if (!m_hasTessellationStage)
993 {
994 DE_ASSERT(m_useGlobalState);
995 buf << "\n"
996 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = 0.0;\n"
997 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
998 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
999 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1000 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1001 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1002 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1003 }
1004
1005 buf<< "}\n";
1006
1007 return buf.str();
1008 }
1009
genFragmentSource(void) const1010 std::string GridRenderCase::genFragmentSource (void) const
1011 {
1012 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1013 std::ostringstream buf;
1014
1015 buf << "#version 310 es\n"
1016 "in mediump vec4 " << colorInputName << ";\n"
1017 "layout(location = 0) out mediump vec4 o_color;\n"
1018 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1019 << "\n"
1020 "void main()\n"
1021 "{\n"
1022 " mediump vec4 baseColor = " << colorInputName << ";\n"
1023 " mediump float blueChannel;\n"
1024 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1025 " blueChannel = 0.0;\n"
1026 " else\n"
1027 " blueChannel = 1.0;\n"
1028 " o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n"
1029 "}\n";
1030
1031 return buf.str();
1032 }
1033
genTessellationControlSource(void) const1034 std::string GridRenderCase::genTessellationControlSource (void) const
1035 {
1036 std::ostringstream buf;
1037
1038 buf << "#version 310 es\n"
1039 "#extension GL_EXT_tessellation_shader : require\n"
1040 "#extension GL_EXT_primitive_bounding_box : require\n"
1041 "layout(vertices=3) out;\n"
1042 "\n"
1043 "in highp vec4 vtx_color[];\n"
1044 "out highp vec4 tess_ctrl_color[];\n"
1045 "uniform highp float u_tessellationLevel;\n"
1046 "uniform highp vec4 u_posScale;\n";
1047
1048 if (!m_calcPerPrimitiveBBox)
1049 {
1050 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1051 "uniform highp vec4 u_primitiveBBoxMax;\n";
1052 }
1053
1054 buf << "patch out highp float vp_bbox_expansionSize;\n"
1055 "patch out highp vec3 vp_bbox_clipMin;\n"
1056 "patch out highp vec3 vp_bbox_clipMax;\n";
1057
1058 if (m_calcPerPrimitiveBBox)
1059 {
1060 buf << "\n";
1061 if (m_hasGeometryStage)
1062 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1063 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1064
1065 buf << "vec4 transformVec(in highp vec4 p)\n"
1066 "{\n"
1067 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1068 "}\n";
1069 }
1070
1071 buf << "\n"
1072 "void main()\n"
1073 "{\n"
1074 " // convert to nonsensical coordinates, just in case\n"
1075 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1076 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1077 "\n"
1078 " gl_TessLevelOuter[0] = u_tessellationLevel;\n"
1079 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"
1080 " gl_TessLevelOuter[2] = u_tessellationLevel;\n"
1081 " gl_TessLevelInner[0] = u_tessellationLevel;\n";
1082
1083 if (m_calcPerPrimitiveBBox)
1084 {
1085 buf << "\n"
1086 " highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n"
1087 " transformVec(gl_in[1].gl_Position)),\n"
1088 " transformVec(gl_in[2].gl_Position));\n"
1089 " highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n"
1090 " transformVec(gl_in[1].gl_Position)),\n"
1091 " transformVec(gl_in[2].gl_Position));\n";
1092 }
1093 else
1094 {
1095 buf << "\n"
1096 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1097 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1098 }
1099
1100 if (!m_useGlobalState)
1101 buf << "\n"
1102 " gl_BoundingBoxEXT[0] = bboxMin;\n"
1103 " gl_BoundingBoxEXT[1] = bboxMax;\n";
1104
1105 buf << " vp_bbox_expansionSize = 0.0;\n"
1106 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1107 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1108 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1109 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1110 "}\n";
1111
1112 return buf.str();
1113 }
1114
genTessellationEvaluationSource(void) const1115 std::string GridRenderCase::genTessellationEvaluationSource (void) const
1116 {
1117 std::ostringstream buf;
1118
1119 buf << "#version 310 es\n"
1120 "#extension GL_EXT_tessellation_shader : require\n"
1121 "#extension GL_EXT_gpu_shader5 : require\n"
1122 "layout(triangles) in;\n"
1123 "\n"
1124 "in highp vec4 tess_ctrl_color[];\n"
1125 "out highp vec4 tess_color;\n"
1126 "uniform highp vec4 u_posScale;\n"
1127 "patch in highp float vp_bbox_expansionSize;\n"
1128 "patch in highp vec3 vp_bbox_clipMin;\n"
1129 "patch in highp vec3 vp_bbox_clipMax;\n"
1130 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1131 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1132 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1133 "\n"
1134 "precise gl_Position;\n"
1135 "\n"
1136 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1137 << "void main()\n"
1138 "{\n"
1139 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1140 " gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n"
1141 " gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n"
1142 " gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n"
1143 " tess_color = tess_ctrl_color[0];\n"
1144 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1145 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1146 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1147 "}\n";
1148
1149 return buf.str();
1150 }
1151
genGeometrySource(void) const1152 std::string GridRenderCase::genGeometrySource (void) const
1153 {
1154 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1155 std::ostringstream buf;
1156
1157 buf << "#version 310 es\n"
1158 "#extension GL_EXT_geometry_shader : require\n"
1159 "layout(triangles) in;\n"
1160 "layout(max_vertices=9, triangle_strip) out;\n"
1161 "\n"
1162 "in highp vec4 " << colorInputName << "[3];\n"
1163 "out highp vec4 geo_color;\n"
1164 "uniform highp vec4 u_posScale;\n"
1165 "\n"
1166 "flat in highp float v_geo_bbox_expansionSize[3];\n"
1167 "flat in highp vec3 v_geo_bbox_clipMin[3];\n"
1168 "flat in highp vec3 v_geo_bbox_clipMax[3];\n"
1169 "flat out highp vec3 v_bbox_clipMin;\n"
1170 "flat out highp vec3 v_bbox_clipMax;\n"
1171 "flat out highp float v_bbox_expansionSize;\n"
1172 << genShaderFunction(SHADER_FUNC_MIRROR_X)
1173 << "\n"
1174 "void setVisualizationVaryings()\n"
1175 "{\n"
1176 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1177 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1178 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1179 "}\n"
1180 "void main()\n"
1181 "{\n"
1182 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1183 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1184 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1185 " highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n"
1186 " highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n"
1187 " highp vec4 triangleColor = " << colorInputName << "[0];\n"
1188 "\n"
1189 " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1190 " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1191 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1192 " EndPrimitive();\n"
1193 "\n"
1194 " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1195 " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1196 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1197 " EndPrimitive();\n"
1198 "\n"
1199 " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1200 " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1201 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1202 " EndPrimitive();\n"
1203 "}\n";
1204
1205 return buf.str();
1206 }
1207
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1208 GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1209 {
1210 return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
1211 }
1212
getAttributeData(std::vector<tcu::Vec4> & data) const1213 void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1214 {
1215 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
1216 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f);
1217 std::vector<int> cellOrder (m_gridSize * m_gridSize);
1218 de::Random rnd (0xDE56789);
1219
1220 // generate grid with cells in random order
1221 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1222 cellOrder[ndx] = ndx;
1223 rnd.shuffle(cellOrder.begin(), cellOrder.end());
1224
1225 data.resize(m_gridSize * m_gridSize * 6 * 2);
1226 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1227 {
1228 const int cellNdx = cellOrder[ndx];
1229 const int cellX = cellNdx % m_gridSize;
1230 const int cellY = cellNdx / m_gridSize;
1231 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (green) : (yellow);
1232
1233 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1234 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1235 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1236 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1237 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1238 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1239 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1240 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1241 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1242 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1243 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1244 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1245 }
1246 }
1247
renderTestPattern(const IterationConfig & config)1248 void GridRenderCase::renderTestPattern (const IterationConfig& config)
1249 {
1250 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1251
1252 setupRender(config);
1253
1254 if (m_hasTessellationStage)
1255 {
1256 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1257 const glw::GLfloat tessLevel = 2.8f; // will be rounded up
1258
1259 TCU_CHECK(tessLevelPos != -1);
1260
1261 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1262
1263 gl.uniform1f(tessLevelPos, tessLevel);
1264 gl.patchParameteri(GL_PATCH_VERTICES, 3);
1265 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1266 }
1267
1268 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage;
1269
1270 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
1271 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1272 }
1273
verifyRenderResult(const IterationConfig & config)1274 void GridRenderCase::verifyRenderResult (const IterationConfig& config)
1275 {
1276 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1277 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
1278 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
1279 const tcu::IVec4 viewportGridOuterArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS);
1280 const tcu::IVec4 viewportGridInnerArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1281 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
1282 tcu::Surface errorMask (config.viewportSize.x(), config.viewportSize.y());
1283 bool anyError = false;
1284
1285 if (!m_calcPerPrimitiveBBox)
1286 m_testCtx.getLog()
1287 << tcu::TestLog::Message
1288 << "Projected bounding box: (clip space)\n"
1289 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1290 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1291 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1292 << "In viewport coordinates:\n"
1293 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1294 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1295 << "Verifying render results within the bounding box.\n"
1296 << tcu::TestLog::EndMessage;
1297 else
1298 m_testCtx.getLog()
1299 << tcu::TestLog::Message
1300 << "Verifying render result."
1301 << tcu::TestLog::EndMessage;
1302
1303 if (m_fbo)
1304 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1305 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1306
1307 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
1308
1309 for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y)
1310 for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x)
1311 {
1312 const tcu::RGBA pixel = viewportSurface.getPixel(x, y);
1313 const bool outsideGrid = x < viewportGridOuterArea.x() ||
1314 y < viewportGridOuterArea.y() ||
1315 x > viewportGridOuterArea.z() ||
1316 y > viewportGridOuterArea.w();
1317 const bool insideGrid = x > viewportGridInnerArea.x() &&
1318 y > viewportGridInnerArea.y() &&
1319 x < viewportGridInnerArea.z() &&
1320 y < viewportGridInnerArea.w();
1321
1322 bool error = false;
1323
1324 if (outsideGrid)
1325 {
1326 // expect black
1327 if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
1328 error = true;
1329 }
1330
1331 else if (insideGrid)
1332 {
1333 // expect green, yellow or a combination of these
1334 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
1335 error = true;
1336 }
1337 else
1338 {
1339 // boundary, allow anything
1340 }
1341
1342 if (error)
1343 {
1344 errorMask.setPixel(x, y, tcu::RGBA::red());
1345 anyError = true;
1346 }
1347 }
1348
1349 if (anyError)
1350 {
1351 m_testCtx.getLog()
1352 << tcu::TestLog::Message
1353 << "Image verification failed."
1354 << tcu::TestLog::EndMessage
1355 << tcu::TestLog::ImageSet("Images", "Image verification")
1356 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1357 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
1358 << tcu::TestLog::EndImageSet;
1359
1360 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1361 }
1362 else
1363 {
1364 m_testCtx.getLog()
1365 << tcu::TestLog::Message
1366 << "Result image ok."
1367 << tcu::TestLog::EndMessage
1368 << tcu::TestLog::ImageSet("Images", "Image verification")
1369 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1370 << tcu::TestLog::EndImageSet;
1371 }
1372 }
1373
1374 class LineRenderCase : public BBoxRenderCase
1375 {
1376 public:
1377 enum
1378 {
1379 LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide lines
1380 };
1381
1382 LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
1383 ~LineRenderCase (void);
1384
1385 private:
1386 enum
1387 {
1388 GREEN_COMPONENT_NDX = 1,
1389 BLUE_COMPONENT_NDX = 2,
1390
1391 SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line
1392 SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX,
1393 };
1394
1395 enum QueryDirection
1396 {
1397 DIRECTION_HORIZONTAL = 0,
1398 DIRECTION_VERTICAL,
1399 };
1400
1401 enum ScanResult
1402 {
1403 SCANRESULT_NUM_LINES_OK_BIT = (1 << 0),
1404 SCANRESULT_LINE_WIDTH_OK_BIT = (1 << 1),
1405 };
1406
1407 void init (void);
1408
1409 std::string genVertexSource (void) const;
1410 std::string genFragmentSource (void) const;
1411 std::string genTessellationControlSource (void) const;
1412 std::string genTessellationEvaluationSource (void) const;
1413 std::string genGeometrySource (void) const;
1414
1415 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
1416 void getAttributeData (std::vector<tcu::Vec4>& data) const;
1417 void renderTestPattern (const IterationConfig& config);
1418 void verifyRenderResult (const IterationConfig& config);
1419
1420 tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const;
1421 deUint8 scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1422 deUint8 scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1423 bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const;
1424 tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const;
1425 bool checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const;
1426 void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const;
1427
1428 const int m_patternSide;
1429 const bool m_isWideLineCase;
1430 const int m_wideLineLineWidth;
1431 };
1432
LineRenderCase(Context & context,const char * name,const char * description,deUint32 flags)1433 LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
1434 : BBoxRenderCase (context, name, description, 12, flags)
1435 , m_patternSide (12)
1436 , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0)
1437 , m_wideLineLineWidth (5)
1438 {
1439 }
1440
~LineRenderCase(void)1441 LineRenderCase::~LineRenderCase (void)
1442 {
1443 }
1444
init(void)1445 void LineRenderCase::init (void)
1446 {
1447 m_testCtx.getLog()
1448 << tcu::TestLog::Message
1449 << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1450 << "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1451 << "Line segments are in random order, varying pattern size and location for each iteration.\n"
1452 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1453 << tcu::TestLog::EndMessage;
1454
1455 if (m_isWideLineCase)
1456 {
1457 glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1458 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1459
1460 if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1461 throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1462 }
1463
1464 BBoxRenderCase::init();
1465 }
1466
genVertexSource(void) const1467 std::string LineRenderCase::genVertexSource (void) const
1468 {
1469 std::ostringstream buf;
1470
1471 buf << "#version 310 es\n"
1472 "in highp vec4 a_position;\n"
1473 "in highp vec4 a_color;\n"
1474 "out highp vec4 vtx_color;\n"
1475 "uniform highp vec4 u_posScale;\n"
1476 "uniform highp float u_lineWidth;\n"
1477 "\n";
1478 if (!m_hasTessellationStage)
1479 {
1480 DE_ASSERT(m_useGlobalState);
1481 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1482 "uniform highp vec4 u_primitiveBBoxMax;\n"
1483 "\n"
1484 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1485 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1486 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1487 "\n";
1488 }
1489 buf << "void main()\n"
1490 "{\n"
1491 " highp vec2 patternOffset = u_posScale.xy;\n"
1492 " highp vec2 patternScale = u_posScale.zw;\n"
1493 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1494 " vtx_color = a_color;\n";
1495 if (!m_hasTessellationStage)
1496 {
1497 DE_ASSERT(m_useGlobalState);
1498 buf << "\n"
1499 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n"
1500 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
1501 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
1502 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1503 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1504 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1505 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1506 }
1507 buf << "}\n";
1508
1509 return buf.str();
1510 }
1511
genFragmentSource(void) const1512 std::string LineRenderCase::genFragmentSource (void) const
1513 {
1514 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1515 std::ostringstream buf;
1516
1517 buf << "#version 310 es\n"
1518 "in mediump vec4 " << colorInputName << ";\n"
1519 "layout(location = 0) out mediump vec4 o_color;\n"
1520 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1521 << "\n"
1522 "void main()\n"
1523 "{\n"
1524 " mediump vec4 baseColor = " << colorInputName << ";\n"
1525 " mediump float redChannel;\n"
1526 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1527 " redChannel = 0.0;\n"
1528 " else\n"
1529 " redChannel = 1.0;\n"
1530 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1531 "}\n";
1532
1533 return buf.str();
1534 }
1535
genTessellationControlSource(void) const1536 std::string LineRenderCase::genTessellationControlSource (void) const
1537 {
1538 std::ostringstream buf;
1539
1540 buf << "#version 310 es\n"
1541 "#extension GL_EXT_tessellation_shader : require\n"
1542 "#extension GL_EXT_primitive_bounding_box : require\n"
1543 "layout(vertices=2) out;"
1544 "\n"
1545 "in highp vec4 vtx_color[];\n"
1546 "out highp vec4 tess_ctrl_color[];\n"
1547 "uniform highp float u_tessellationLevel;\n"
1548 "uniform highp vec4 u_posScale;\n"
1549 "uniform highp float u_lineWidth;\n";
1550
1551 if (!m_calcPerPrimitiveBBox)
1552 {
1553 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1554 "uniform highp vec4 u_primitiveBBoxMax;\n";
1555 }
1556
1557 buf << "patch out highp float vp_bbox_expansionSize;\n"
1558 "patch out highp vec3 vp_bbox_clipMin;\n"
1559 "patch out highp vec3 vp_bbox_clipMax;\n";
1560
1561 if (m_calcPerPrimitiveBBox)
1562 {
1563 buf << "\n";
1564 if (m_hasGeometryStage)
1565 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1566 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1567
1568 buf << "vec4 transformVec(in highp vec4 p)\n"
1569 "{\n"
1570 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1571 "}\n";
1572 }
1573
1574 buf << "\n"
1575 "void main()\n"
1576 "{\n"
1577 " // convert to nonsensical coordinates, just in case\n"
1578 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1579 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1580 "\n"
1581 " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1582 " gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1583
1584 if (m_calcPerPrimitiveBBox)
1585 {
1586 buf << "\n"
1587 " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1588 " transformVec(gl_in[1].gl_Position));\n"
1589 " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1590 " transformVec(gl_in[1].gl_Position));\n";
1591 }
1592 else
1593 {
1594 buf << "\n"
1595 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1596 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1597 }
1598
1599 if (!m_useGlobalState)
1600 buf << "\n"
1601 " gl_BoundingBoxEXT[0] = bboxMin;\n"
1602 " gl_BoundingBoxEXT[1] = bboxMax;\n";
1603
1604 buf << " vp_bbox_expansionSize = u_lineWidth;\n"
1605 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1606 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1607 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1608 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1609 "}\n";
1610
1611 return buf.str();
1612 }
1613
genTessellationEvaluationSource(void) const1614 std::string LineRenderCase::genTessellationEvaluationSource (void) const
1615 {
1616 std::ostringstream buf;
1617
1618 buf << "#version 310 es\n"
1619 "#extension GL_EXT_tessellation_shader : require\n"
1620 "layout(isolines) in;"
1621 "\n"
1622 "in highp vec4 tess_ctrl_color[];\n"
1623 "out highp vec4 tess_color;\n"
1624 "uniform highp vec4 u_posScale;\n"
1625 "\n"
1626 "patch in highp float vp_bbox_expansionSize;\n"
1627 "patch in highp vec3 vp_bbox_clipMin;\n"
1628 "patch in highp vec3 vp_bbox_clipMax;\n"
1629 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1630 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1631 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1632 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1633 << "void main()\n"
1634 "{\n"
1635 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1636 " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1637 " tess_color = tess_ctrl_color[0];\n"
1638 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1639 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1640 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1641 "}\n";
1642
1643 return buf.str();
1644 }
1645
genGeometrySource(void) const1646 std::string LineRenderCase::genGeometrySource (void) const
1647 {
1648 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1649 std::ostringstream buf;
1650
1651 buf << "#version 310 es\n"
1652 "#extension GL_EXT_geometry_shader : require\n"
1653 "layout(lines) in;\n"
1654 "layout(max_vertices=5, line_strip) out;\n"
1655 "\n"
1656 "in highp vec4 " << colorInputName << "[2];\n"
1657 "out highp vec4 geo_color;\n"
1658 "uniform highp vec4 u_posScale;\n"
1659 "\n"
1660 "\n"
1661 "flat in highp float v_geo_bbox_expansionSize[2];\n"
1662 "flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1663 "flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1664 "flat out highp vec3 v_bbox_clipMin;\n"
1665 "flat out highp vec3 v_bbox_clipMax;\n"
1666 "flat out highp float v_bbox_expansionSize;\n"
1667 << genShaderFunction(SHADER_FUNC_MIRROR_X)
1668 << "\n"
1669 "void setVisualizationVaryings()\n"
1670 "{\n"
1671 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1672 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1673 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1674 "}\n"
1675 "void main()\n"
1676 "{\n"
1677 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1678 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1679 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1680 " highp vec4 lineColor = " << colorInputName << "[0];\n"
1681 "\n"
1682 " // output two separate primitives, just in case\n"
1683 " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1684 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1685 " EndPrimitive();\n"
1686 "\n"
1687 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1688 " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1689 " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1690 " EndPrimitive();\n"
1691 "}\n";
1692
1693 return buf.str();
1694 }
1695
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1696 LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1697 {
1698 const int numMaxAttempts = 128;
1699
1700 // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1701 for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1702 {
1703 const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1704
1705 if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth &&
1706 (float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth)
1707 {
1708 return config;
1709 }
1710 }
1711
1712 DE_ASSERT(false);
1713 return IterationConfig();
1714 }
1715
getAttributeData(std::vector<tcu::Vec4> & data) const1716 void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1717 {
1718 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
1719 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1720 std::vector<int> cellOrder (m_patternSide * m_patternSide * 2);
1721 de::Random rnd (0xDE12345);
1722
1723 // generate crosshatch pattern with segments in random order
1724 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1725 cellOrder[ndx] = ndx;
1726 rnd.shuffle(cellOrder.begin(), cellOrder.end());
1727
1728 data.resize(cellOrder.size() * 4);
1729 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1730 {
1731 const int segmentID = cellOrder[ndx];
1732 const int direction = segmentID & 0x01;
1733 const int majorCoord = (segmentID >> 1) / m_patternSide;
1734 const int minorCoord = (segmentID >> 1) % m_patternSide;
1735
1736 if (direction)
1737 {
1738 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord) / float(m_patternSide), 0.0f, 1.0f);
1739 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1740 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f);
1741 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1742 }
1743 else
1744 {
1745 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1746 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1747 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord + 1) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1748 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1749 }
1750 }
1751 }
1752
renderTestPattern(const IterationConfig & config)1753 void LineRenderCase::renderTestPattern (const IterationConfig& config)
1754 {
1755 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1756
1757 setupRender(config);
1758
1759 if (m_hasTessellationStage)
1760 {
1761 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1762 const glw::GLfloat tessLevel = 2.8f; // will be rounded up
1763
1764 TCU_CHECK(tessLevelPos != -1);
1765
1766 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1767
1768 gl.uniform1f(tessLevelPos, tessLevel);
1769 gl.patchParameteri(GL_PATCH_VERTICES, 2);
1770 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1771 }
1772
1773 if (m_isWideLineCase)
1774 gl.lineWidth((float)m_wideLineLineWidth);
1775
1776 gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f));
1777
1778 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1779
1780 gl.enable(GL_BLEND);
1781 gl.blendFunc(GL_ONE, GL_ONE);
1782 gl.blendEquation(GL_FUNC_ADD);
1783
1784 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
1785 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1786 }
1787
verifyRenderResult(const IterationConfig & config)1788 void LineRenderCase::verifyRenderResult (const IterationConfig& config)
1789 {
1790 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1791 const bool isMsaa = m_context.getRenderTarget().getNumSamples() > 1;
1792 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
1793 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1794 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
1795 const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1796 const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL);
1797 const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL);
1798 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
1799 de::max(viewportBBoxArea.y(), 0),
1800 de::min(viewportBBoxArea.z(), config.viewportSize.x()),
1801 de::min(viewportBBoxArea.w(), config.viewportSize.y()));
1802
1803 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
1804 bool anyError = false;
1805 bool msaaRelaxationRequired = false;
1806 int messageLimitCounter = 8;
1807
1808 if (!m_calcPerPrimitiveBBox)
1809 m_testCtx.getLog()
1810 << tcu::TestLog::Message
1811 << "Projected bounding box: (clip space)\n"
1812 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1813 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1814 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1815 << "In viewport coordinates:\n"
1816 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1817 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1818 << "Verifying render results within the bounding box:\n"
1819 << tcu::TestLog::EndMessage;
1820 else
1821 m_testCtx.getLog()
1822 << tcu::TestLog::Message
1823 << "Verifying render result:"
1824 << tcu::TestLog::EndMessage;
1825
1826 m_testCtx.getLog()
1827 << tcu::TestLog::Message
1828 << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
1829 << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n"
1830 << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n"
1831 << tcu::TestLog::EndMessage;
1832
1833 if (m_fbo)
1834 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1835 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1836
1837 // scan rows
1838 for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
1839 {
1840 const deUint8 result = scanRow(viewportSurface.getAccess(),
1841 y,
1842 verificationArea.x(),
1843 verificationArea.z(),
1844 expectedVerticalLines,
1845 messageLimitCounter);
1846
1847 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1848 anyError = true;
1849 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1850 {
1851 if (m_isWideLineCase && isMsaa)
1852 {
1853 // multisampled wide lines might not be supported
1854 msaaRelaxationRequired = true;
1855 }
1856 else
1857 anyError = true;
1858 }
1859 }
1860
1861 // scan columns
1862 for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
1863 {
1864 const deUint8 result = scanColumn(viewportSurface.getAccess(),
1865 x,
1866 verificationArea.y(),
1867 verificationArea.w(),
1868 expectedHorizontalLines,
1869 messageLimitCounter);
1870
1871 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1872 anyError = true;
1873 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1874 {
1875 if (m_isWideLineCase && isMsaa)
1876 {
1877 // multisampled wide lines might not be supported
1878 msaaRelaxationRequired = true;
1879 }
1880 else
1881 anyError = true;
1882 }
1883 }
1884
1885 if (anyError || msaaRelaxationRequired)
1886 {
1887 if (messageLimitCounter < 0)
1888 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage;
1889
1890 m_testCtx.getLog()
1891 << tcu::TestLog::Message
1892 << "Image verification failed."
1893 << tcu::TestLog::EndMessage
1894 << tcu::TestLog::ImageSet("Images", "Image verification")
1895 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1896 << tcu::TestLog::EndImageSet;
1897
1898 if (anyError)
1899 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1900 else
1901 {
1902 // MSAA wide lines are optional
1903 m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed");
1904 }
1905 }
1906 else
1907 {
1908 m_testCtx.getLog()
1909 << tcu::TestLog::Message
1910 << "Result image ok."
1911 << tcu::TestLog::EndMessage
1912 << tcu::TestLog::ImageSet("Images", "Image verification")
1913 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1914 << tcu::TestLog::EndImageSet;
1915 }
1916 }
1917
getNumberOfLinesRange(int queryAreaBegin,int queryAreaEnd,float patternStart,float patternSize,int viewportArea,QueryDirection queryDir) const1918 tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const
1919 {
1920 // pattern is not symmetric due to mirroring
1921 const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0));
1922 const int patternEndNdx = patternStartNdx + m_patternSide;
1923
1924 int numLinesMin = 0;
1925 int numLinesMax = 0;
1926
1927 for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
1928 {
1929 const float linePos = (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
1930 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1931
1932 if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f &&
1933 linePos * (float)viewportArea < (float)queryAreaEnd - 1.0f)
1934 {
1935 // line center is within the area
1936 ++numLinesMin;
1937 ++numLinesMax;
1938 }
1939 else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f &&
1940 linePos * (float)viewportArea < (float)queryAreaEnd + lineWidth*0.5f + 1.0f)
1941 {
1942 // line could leak into area
1943 ++numLinesMax;
1944 }
1945 }
1946
1947 return tcu::IVec2(numLinesMin, numLinesMax);
1948 }
1949
scanRow(const tcu::ConstPixelBufferAccess & access,int row,int rowBegin,int rowEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const1950 deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1951 {
1952 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
1953 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
1954
1955 return (deUint8)((numLinesOk ? (deUint8)SCANRESULT_NUM_LINES_OK_BIT : 0u) |
1956 (lineWidthOk ? (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT : 0u));
1957 }
1958
scanColumn(const tcu::ConstPixelBufferAccess & access,int column,int columnBegin,int columnEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const1959 deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1960 {
1961 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
1962 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter);
1963
1964 return (deUint8)((numLinesOk ? (deUint8)SCANRESULT_NUM_LINES_OK_BIT : 0u) |
1965 (lineWidthOk ? (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT : 0u));
1966 }
1967
checkAreaNumLines(const tcu::ConstPixelBufferAccess & access,const tcu::IVec4 & area,int & messageLimitCounter,int componentNdx,const tcu::IVec2 & numLines) const1968 bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const
1969 {
1970 // Num maxima == num lines
1971 const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
1972 const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx);
1973 const int numMaxima = numMinimaMaxima.y();
1974
1975 // In valid range
1976 if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
1977 return true;
1978
1979 if (--messageLimitCounter < 0)
1980 return false;
1981
1982 if (area.z() == 1)
1983 m_testCtx.getLog()
1984 << tcu::TestLog::Message
1985 << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n"
1986 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1987 << tcu::TestLog::EndMessage;
1988 else
1989 m_testCtx.getLog()
1990 << tcu::TestLog::Message
1991 << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n"
1992 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1993 << tcu::TestLog::EndMessage;
1994
1995 return false;
1996 }
1997
getNumMinimaMaxima(const tcu::ConstPixelBufferAccess & access,int componentNdx) const1998 tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const
1999 {
2000 DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
2001
2002 int previousValue = -1;
2003 int previousSign = 0;
2004 int numMinima = 0;
2005 int numMaxima = 0;
2006
2007 for (int y = 0; y < access.getHeight(); ++y)
2008 for (int x = 0; x < access.getWidth(); ++x)
2009 {
2010 const int componentValue = access.getPixelInt(x, y)[componentNdx];
2011
2012 if (previousValue != -1)
2013 {
2014 const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0);
2015
2016 // local minima/maxima in sign changes (zero signless)
2017 if (sign != 0 && sign == -previousSign)
2018 {
2019 previousSign = sign;
2020
2021 if (sign > 0)
2022 ++numMinima;
2023 else
2024 ++numMaxima;
2025 }
2026 else if (sign != 0 && previousSign == 0)
2027 {
2028 previousSign = sign;
2029
2030 // local extreme at the start boundary
2031 if (sign > 0)
2032 ++numMinima;
2033 else
2034 ++numMaxima;
2035 }
2036 }
2037
2038 previousValue = componentValue;
2039 }
2040
2041 // local extreme at the end boundary
2042 if (previousSign > 0)
2043 ++numMaxima;
2044 else if (previousSign < 0)
2045 ++numMinima;
2046 else
2047 {
2048 ++numMaxima;
2049 ++numMinima;
2050 }
2051
2052 return tcu::IVec2(numMinima, numMaxima);
2053 }
2054
checkLineWidths(const tcu::ConstPixelBufferAccess & access,const tcu::IVec2 & begin,const tcu::IVec2 & end,int componentNdx,int & messageLimitCounter) const2055 bool LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const
2056 {
2057 const bool multisample = m_context.getRenderTarget().getNumSamples() > 1;
2058 const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2059 const tcu::IVec2 lineWidthRange = (multisample)
2060 ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel
2061 : (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2062
2063 int lineWidth = 0;
2064 bool bboxLimitedLine = false;
2065 bool anyError = false;
2066
2067 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2068
2069 // fragments before begin?
2070 if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2071 {
2072 bboxLimitedLine = true;
2073
2074 for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2075 {
2076 if (cursor.x() < 0 || cursor.y() < 0)
2077 {
2078 break;
2079 }
2080 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2081 {
2082 ++lineWidth;
2083 }
2084 else
2085 break;
2086 }
2087 }
2088
2089 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2090 {
2091 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2092
2093 if (hit)
2094 ++lineWidth;
2095 else if (lineWidth)
2096 {
2097 // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2098 const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2099
2100 if (incorrectLineWidth)
2101 {
2102 anyError = true;
2103 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2104 }
2105
2106 lineWidth = 0;
2107 bboxLimitedLine = false;
2108 }
2109 }
2110
2111 // fragments after end?
2112 if (lineWidth)
2113 {
2114 for (tcu::IVec2 cursor = end;; cursor += advance)
2115 {
2116 if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2117 {
2118 if (lineWidth > lineWidthRange.y())
2119 {
2120 anyError = true;
2121 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2122 }
2123
2124 break;
2125 }
2126 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2127 {
2128 ++lineWidth;
2129 }
2130 else if (lineWidth)
2131 {
2132 // only check that line width is not larger than expected. Line width may be smaller
2133 // since the scanning 'cursor' is now outside the bounding box.
2134 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2135
2136 if (incorrectLineWidth)
2137 {
2138 anyError = true;
2139 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2140 }
2141
2142 lineWidth = 0;
2143 }
2144 }
2145 }
2146
2147 return !anyError;
2148 }
2149
printLineWidthError(const tcu::IVec2 & pos,int detectedLineWidth,const tcu::IVec2 & lineWidthRange,bool isHorizontal,int & messageLimitCounter) const2150 void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const
2151 {
2152 if (--messageLimitCounter < 0)
2153 return;
2154
2155 m_testCtx.getLog()
2156 << tcu::TestLog::Message
2157 << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2158 << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth
2159 << tcu::TestLog::EndMessage;
2160 }
2161
2162 class PointRenderCase : public BBoxRenderCase
2163 {
2164 public:
2165 enum
2166 {
2167 POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points
2168 };
2169 struct GeneratedPoint
2170 {
2171 tcu::Vec2 center;
2172 int size;
2173 bool even;
2174 };
2175
2176 PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
2177 ~PointRenderCase (void);
2178
2179 private:
2180 enum ResultPointType
2181 {
2182 POINT_FULL = 0,
2183 POINT_PARTIAL
2184 };
2185
2186 void init (void);
2187 void deinit (void);
2188
2189 std::string genVertexSource (void) const;
2190 std::string genFragmentSource (void) const;
2191 std::string genTessellationControlSource (void) const;
2192 std::string genTessellationEvaluationSource (void) const;
2193 std::string genGeometrySource (void) const;
2194
2195 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
2196 void generateAttributeData (void);
2197 void getAttributeData (std::vector<tcu::Vec4>& data) const;
2198 void renderTestPattern (const IterationConfig& config);
2199 void verifyRenderResult (const IterationConfig& config);
2200
2201 void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const;
2202 bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2203 bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2204 bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter);
2205 bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter);
2206 tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const;
2207
2208 const int m_numStripes;
2209 const bool m_isWidePointCase;
2210 std::vector<tcu::Vec4> m_attribData;
2211 };
2212
PointRenderCase(Context & context,const char * name,const char * description,deUint32 flags)2213 PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
2214 : BBoxRenderCase (context, name, description, 12, flags)
2215 , m_numStripes (4)
2216 , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0)
2217 {
2218 }
2219
~PointRenderCase(void)2220 PointRenderCase::~PointRenderCase (void)
2221 {
2222 }
2223
init(void)2224 void PointRenderCase::init (void)
2225 {
2226 if (m_isWidePointCase)
2227 {
2228 // extensions
2229 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2230 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2231 if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2232 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2233
2234 // point size range
2235 {
2236 glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2237 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2238
2239 if (pointSizeRange[1] < 5.0f)
2240 throw tcu::NotSupportedError("Test requires point size 5.0");
2241 }
2242 }
2243
2244 m_testCtx.getLog()
2245 << tcu::TestLog::Message
2246 << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2247 << "Half of the points are green, half blue. Using additive blending.\n"
2248 << "Points are in random order, varying pattern size and location for each iteration.\n"
2249 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2250 << tcu::TestLog::EndMessage;
2251
2252 generateAttributeData();
2253
2254 BBoxRenderCase::init();
2255 }
2256
deinit(void)2257 void PointRenderCase::deinit (void)
2258 {
2259 // clear data
2260 m_attribData = std::vector<tcu::Vec4>();
2261
2262 // deinit parent
2263 BBoxRenderCase::deinit();
2264 }
2265
genVertexSource(void) const2266 std::string PointRenderCase::genVertexSource (void) const
2267 {
2268 std::ostringstream buf;
2269
2270 buf << "#version 310 es\n"
2271 "in highp vec4 a_position;\n"
2272 "in highp vec4 a_color;\n"
2273 "out highp vec4 vtx_color;\n"
2274 "uniform highp vec4 u_posScale;\n"
2275 "\n";
2276 if (!m_hasTessellationStage)
2277 {
2278 DE_ASSERT(m_useGlobalState);
2279 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2280 "uniform highp vec4 u_primitiveBBoxMax;\n"
2281 "\n"
2282 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
2283 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2284 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2285 "\n";
2286 }
2287
2288 buf << "void main()\n"
2289 "{\n"
2290 " highp vec2 patternOffset = u_posScale.xy;\n"
2291 " highp vec2 patternScale = u_posScale.zw;\n"
2292 " highp float pointSize = "
2293 << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2294 << ";\n"
2295 << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2296 " gl_PointSize = pointSize;\n"
2297 " vtx_color = a_color;\n";
2298
2299 if (!m_hasTessellationStage)
2300 {
2301 DE_ASSERT(m_useGlobalState);
2302 buf << "\n"
2303 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n"
2304 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
2305 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
2306 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
2307 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
2308 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
2309 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
2310 }
2311
2312 buf << "}\n";
2313 return buf.str();
2314 }
2315
genFragmentSource(void) const2316 std::string PointRenderCase::genFragmentSource (void) const
2317 {
2318 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2319 std::ostringstream buf;
2320
2321 buf << "#version 310 es\n"
2322 "in mediump vec4 " << colorInputName << ";\n"
2323 "layout(location = 0) out mediump vec4 o_color;\n"
2324 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2325 << "\n"
2326 "void main()\n"
2327 "{\n"
2328 " mediump vec4 baseColor = " << colorInputName << ";\n"
2329 " mediump float redChannel;\n"
2330 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2331 " redChannel = 0.0;\n"
2332 " else\n"
2333 " redChannel = 1.0;\n"
2334 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2335 "}\n";
2336
2337 return buf.str();
2338 }
2339
genTessellationControlSource(void) const2340 std::string PointRenderCase::genTessellationControlSource (void) const
2341 {
2342 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2343 std::ostringstream buf;
2344
2345 buf << "#version 310 es\n"
2346 "#extension GL_EXT_tessellation_shader : require\n"
2347 "#extension GL_EXT_primitive_bounding_box : require\n"
2348 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2349 << "layout(vertices=1) out;"
2350 "\n"
2351 "in highp vec4 vtx_color[];\n"
2352 "out highp vec4 tess_ctrl_color[];\n"
2353 "uniform highp float u_tessellationLevel;\n"
2354 "uniform highp vec4 u_posScale;\n";
2355
2356 if (!m_calcPerPrimitiveBBox)
2357 {
2358 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2359 "uniform highp vec4 u_primitiveBBoxMax;\n";
2360 }
2361
2362 buf << "patch out highp vec3 vp_bbox_clipMin;\n"
2363 "patch out highp vec3 vp_bbox_clipMax;\n";
2364
2365 if (m_calcPerPrimitiveBBox)
2366 {
2367 buf << "\n";
2368 if (m_hasGeometryStage)
2369 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2370 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2371
2372 buf << "vec4 transformVec(in highp vec4 p)\n"
2373 "{\n"
2374 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
2375 "}\n";
2376 }
2377
2378 buf << "\n"
2379 "void main()\n"
2380 "{\n"
2381 " // convert to nonsensical coordinates, just in case\n"
2382 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2383 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2384 "\n"
2385 " gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2386 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2387 " gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2388 " gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2389 " gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2390 " gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2391
2392 if (m_calcPerPrimitiveBBox)
2393 {
2394 buf << "\n";
2395
2396 if (m_hasGeometryStage)
2397 buf << " const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2398 " const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2399 else
2400 buf << " const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2401 " const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2402
2403 buf << " highp vec2 patternScale = u_posScale.zw;\n"
2404 " highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n"
2405 " highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n";
2406 }
2407 else
2408 {
2409 buf << "\n"
2410 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2411 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2412 }
2413 if (!m_useGlobalState)
2414 buf << "\n"
2415 " gl_BoundingBoxEXT[0] = bboxMin;\n"
2416 " gl_BoundingBoxEXT[1] = bboxMax;\n";
2417
2418 buf << " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2419 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2420 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2421 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2422 "}\n";
2423
2424 return buf.str();
2425 }
2426
genTessellationEvaluationSource(void) const2427 std::string PointRenderCase::genTessellationEvaluationSource (void) const
2428 {
2429 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2430 std::ostringstream buf;
2431
2432 buf << "#version 310 es\n"
2433 "#extension GL_EXT_tessellation_shader : require\n"
2434 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2435 << "layout(quads, point_mode) in;"
2436 "\n"
2437 "in highp vec4 tess_ctrl_color[];\n"
2438 "out highp vec4 tess_color;\n"
2439 "uniform highp vec4 u_posScale;\n"
2440 "\n"
2441 "patch in highp vec3 vp_bbox_clipMin;\n"
2442 "patch in highp vec3 vp_bbox_clipMax;\n"
2443 << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : (""))
2444 << "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2445 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2446 "\n"
2447 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
2448 << "void main()\n"
2449 "{\n"
2450 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2451 " highp vec2 patternScale = u_posScale.zw;\n"
2452 " highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2453 " highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n"
2454 " gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2455
2456 if (tessellationWidePoints)
2457 buf << " gl_PointSize = pointSize;\n";
2458
2459 buf << " tess_color = tess_ctrl_color[0];\n"
2460 << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : (""))
2461 << " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
2462 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
2463 "}\n";
2464
2465 return buf.str();
2466 }
2467
genGeometrySource(void) const2468 std::string PointRenderCase::genGeometrySource (void) const
2469 {
2470 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2471 std::ostringstream buf;
2472
2473 buf << "#version 310 es\n"
2474 "#extension GL_EXT_geometry_shader : require\n"
2475 << ((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : (""))
2476 << "layout(points) in;\n"
2477 "layout(max_vertices=3, points) out;\n"
2478 "\n"
2479 "in highp vec4 " << colorInputName << "[1];\n"
2480 "out highp vec4 geo_color;\n"
2481 "uniform highp vec4 u_posScale;\n"
2482 "\n"
2483 "flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2484 "flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2485 "flat out highp vec3 v_bbox_clipMin;\n"
2486 "flat out highp vec3 v_bbox_clipMax;\n"
2487 "flat out highp float v_bbox_expansionSize;\n"
2488 "\n"
2489 << genShaderFunction(SHADER_FUNC_MIRROR_X)
2490 << "\n"
2491 "void main()\n"
2492 "{\n"
2493 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2494 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2495 " highp vec4 pointColor = " << colorInputName << "[0];\n"
2496 " highp vec2 patternScale = u_posScale.zw;\n"
2497 " highp float pointSize = "
2498 << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2499 << ";\n"
2500 "\n"
2501 " highp vec4 offsets[3] =\n"
2502 " vec4[3](\n"
2503 " vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2504 " vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2505 " vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2506 " );\n"
2507 " for (int ndx = 0; ndx < 3; ++ndx)\n"
2508 " {\n"
2509 " gl_Position = p0 + offsets[ndx];\n";
2510
2511 if (m_isWidePointCase)
2512 buf << " gl_PointSize = pointSize;\n";
2513
2514 buf << " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2515 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2516 " v_bbox_expansionSize = pointSize;\n"
2517 " geo_color = pointColor;\n"
2518 " EmitVertex();\n"
2519 " }\n"
2520 "}\n";
2521
2522 return buf.str();
2523 }
2524
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const2525 PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
2526 {
2527 IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
2528
2529 // equal or larger -> expand according to shader expansion
2530 if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2531 {
2532 const tcu::Vec2 patternScale = config.patternSize;
2533
2534 if (m_hasTessellationStage)
2535 {
2536 config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2537 config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2538 }
2539 if (m_hasGeometryStage)
2540 {
2541 config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2542 config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2543 }
2544 }
2545
2546 return config;
2547 }
2548
generateAttributeData(void)2549 void PointRenderCase::generateAttributeData (void)
2550 {
2551 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
2552 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
2553 std::vector<int> cellOrder (m_numStripes * m_numStripes * 2);
2554 de::Random rnd (0xDE22446);
2555
2556 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2557 cellOrder[ndx] = ndx;
2558 rnd.shuffle(cellOrder.begin(), cellOrder.end());
2559
2560 m_attribData.resize(cellOrder.size() * 2);
2561 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2562 {
2563 const int pointID = cellOrder[ndx];
2564 const int direction = pointID & 0x01;
2565 const int majorCoord = (pointID >> 1) / m_numStripes;
2566 const int minorCoord = (pointID >> 1) % m_numStripes;
2567
2568 if (direction)
2569 {
2570 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_numStripes), float(majorCoord) / float(m_numStripes), 0.0f, 1.0f);
2571 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
2572 }
2573 else
2574 {
2575 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(((float)majorCoord + 0.5f) / float(m_numStripes), ((float)minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f);
2576 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
2577 }
2578 }
2579 }
2580
getAttributeData(std::vector<tcu::Vec4> & data) const2581 void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
2582 {
2583 data = m_attribData;
2584 }
2585
renderTestPattern(const IterationConfig & config)2586 void PointRenderCase::renderTestPattern (const IterationConfig& config)
2587 {
2588 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2589
2590 setupRender(config);
2591
2592 if (m_hasTessellationStage)
2593 {
2594 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
2595 const glw::GLfloat tessLevel = 0.8f; // will be rounded up
2596
2597 TCU_CHECK(tessLevelPos != -1);
2598
2599 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
2600
2601 gl.uniform1f(tessLevelPos, tessLevel);
2602 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2603 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
2604 }
2605
2606 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
2607
2608 gl.enable(GL_BLEND);
2609 gl.blendFunc(GL_ONE, GL_ONE);
2610 gl.blendEquation(GL_FUNC_ADD);
2611
2612 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
2613 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2614 }
2615
verifyRenderResult(const IterationConfig & config)2616 void PointRenderCase::verifyRenderResult (const IterationConfig& config)
2617 {
2618 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2619 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
2620 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
2621
2622 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
2623 int logFloodCounter = 8;
2624 bool anyError;
2625 std::vector<GeneratedPoint> refPoints;
2626
2627 if (!m_calcPerPrimitiveBBox)
2628 m_testCtx.getLog()
2629 << tcu::TestLog::Message
2630 << "Projected bounding box: (clip space)\n"
2631 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2632 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2633 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2634 << "In viewport coordinates:\n"
2635 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2636 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2637 << "Verifying render results within the bounding box:\n"
2638 << tcu::TestLog::EndMessage;
2639 else
2640 m_testCtx.getLog()
2641 << tcu::TestLog::Message
2642 << "Verifying render result:"
2643 << tcu::TestLog::EndMessage;
2644
2645 if (m_fbo)
2646 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2647 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
2648
2649 genReferencePointData(config, refPoints);
2650
2651 if (m_isWidePointCase)
2652 anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2653 else
2654 anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2655
2656 if (anyError)
2657 {
2658 if (logFloodCounter < 0)
2659 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage;
2660
2661 m_testCtx.getLog()
2662 << tcu::TestLog::Message
2663 << "Image verification failed."
2664 << tcu::TestLog::EndMessage
2665 << tcu::TestLog::ImageSet("Images", "Image verification")
2666 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2667 << tcu::TestLog::EndImageSet;
2668
2669 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2670 }
2671 else
2672 {
2673 m_testCtx.getLog()
2674 << tcu::TestLog::Message
2675 << "Result image ok."
2676 << tcu::TestLog::EndMessage
2677 << tcu::TestLog::ImageSet("Images", "Image verification")
2678 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2679 << tcu::TestLog::EndImageSet;
2680 }
2681 }
2682
2683 struct PointSorter
2684 {
operator ()deqp::gles31::Functional::__anon2babdd300111::PointSorter2685 bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const
2686 {
2687 if (a.center.y() < b.center.y())
2688 return true;
2689 else if (a.center.y() > b.center.y())
2690 return false;
2691 else
2692 return (a.center.x() < b.center.x());
2693 }
2694 };
2695
genReferencePointData(const IterationConfig & config,std::vector<GeneratedPoint> & data) const2696 void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const
2697 {
2698 std::vector<GeneratedPoint> currentPoints;
2699
2700 // vertex shader
2701 currentPoints.resize(m_attribData.size() / 2);
2702 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2703 {
2704 currentPoints[ndx].center = m_attribData[ndx*2].swizzle(0, 1);
2705 currentPoints[ndx].even = (m_attribData[ndx*2 + 1].y() == 1.0f); // is green
2706 currentPoints[ndx].size = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
2707 }
2708
2709 // tessellation
2710 if (m_hasTessellationStage)
2711 {
2712 std::vector<GeneratedPoint> tessellatedPoints;
2713
2714 tessellatedPoints.resize(currentPoints.size() * 4);
2715 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2716 {
2717 const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
2718
2719 tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f);
2720 tessellatedPoints[4 * ndx + 0].size = currentPoints[ndx].size;
2721 tessellatedPoints[4 * ndx + 0].even = currentPoints[ndx].even;
2722
2723 tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2( 0.07f, -0.07f);
2724 tessellatedPoints[4 * ndx + 1].size = currentPoints[ndx].size;
2725 tessellatedPoints[4 * ndx + 1].even = currentPoints[ndx].even;
2726
2727 tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2( 0.07f, 0.07f);
2728 tessellatedPoints[4 * ndx + 2].size = currentPoints[ndx].size;
2729 tessellatedPoints[4 * ndx + 2].even = currentPoints[ndx].even;
2730
2731 tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f);
2732 tessellatedPoints[4 * ndx + 3].size = currentPoints[ndx].size;
2733 tessellatedPoints[4 * ndx + 3].even = currentPoints[ndx].even;
2734 }
2735
2736 currentPoints.swap(tessellatedPoints);
2737 }
2738
2739 // geometry
2740 if (m_hasGeometryStage)
2741 {
2742 std::vector<GeneratedPoint> geometryShadedPoints;
2743
2744 geometryShadedPoints.resize(currentPoints.size() * 3);
2745 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2746 {
2747 const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
2748
2749 geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2( 0.05f, 0.03f);
2750 geometryShadedPoints[3 * ndx + 0].size = currentPoints[ndx].size;
2751 geometryShadedPoints[3 * ndx + 0].even = currentPoints[ndx].even;
2752
2753 geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f);
2754 geometryShadedPoints[3 * ndx + 1].size = currentPoints[ndx].size;
2755 geometryShadedPoints[3 * ndx + 1].even = currentPoints[ndx].even;
2756
2757 geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f);
2758 geometryShadedPoints[3 * ndx + 2].size = currentPoints[ndx].size;
2759 geometryShadedPoints[3 * ndx + 2].even = currentPoints[ndx].even;
2760 }
2761
2762 currentPoints.swap(geometryShadedPoints);
2763 }
2764
2765 // sort from left to right, top to bottom
2766 std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
2767
2768 // map to pattern space
2769 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2770 currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
2771
2772 currentPoints.swap(data);
2773 }
2774
verifyNarrowPointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)2775 bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2776 {
2777 bool anyError = false;
2778
2779 // check that there is something near each sample
2780 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2781 {
2782 const float epsilon = 1.0e-6f;
2783 const GeneratedPoint& refPoint = refPoints[pointNdx];
2784
2785 // skip points not in the the bbox, treat boundary as "in"
2786 if (refPoint.center.x() < bbox.min.x() - epsilon ||
2787 refPoint.center.y() < bbox.min.y() - epsilon ||
2788 refPoint.center.x() > bbox.max.x() + epsilon ||
2789 refPoint.center.y() > bbox.max.y() + epsilon)
2790 continue;
2791 else
2792 {
2793 // transform to viewport coords
2794 const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
2795 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
2796
2797 // find rasterized point in the result
2798 if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1)
2799 {
2800 // viewport boundary, assume point is fine
2801 }
2802 else
2803 {
2804 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel
2805 bool foundResult = false;
2806
2807 // check neighborhood
2808 for (int dy = -1; dy < 2 && !foundResult; ++dy)
2809 for (int dx = -1; dx < 2 && !foundResult; ++dx)
2810 {
2811 const tcu::IVec2 testPos (pixelCenter.x() + dx, pixelCenter.y() + dy);
2812 const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y());
2813
2814 if (color.toIVec()[componentNdx] > 0)
2815 foundResult = true;
2816 }
2817
2818 if (!foundResult)
2819 {
2820 anyError = true;
2821
2822 if (--logFloodCounter >= 0)
2823 {
2824 m_testCtx.getLog()
2825 << tcu::TestLog::Message
2826 << "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2827 << tcu::TestLog::EndMessage;
2828 }
2829 }
2830 }
2831 }
2832 }
2833
2834 return anyError;
2835 }
2836
verifyWidePointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)2837 bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2838 {
2839 bool anyError = false;
2840
2841 // check that there is something near each sample
2842 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2843 {
2844 const GeneratedPoint& refPoint = refPoints[pointNdx];
2845
2846 if (refPoint.center.x() >= bbox.min.x() &&
2847 refPoint.center.y() >= bbox.min.y() &&
2848 refPoint.center.x() <= bbox.max.x() &&
2849 refPoint.center.y() <= bbox.max.y())
2850 {
2851 // point fully in the bounding box
2852 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
2853 }
2854 else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f &&
2855 refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f &&
2856 refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f &&
2857 refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f)
2858 {
2859 // point leaks into bounding box
2860 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
2861 }
2862 }
2863
2864 return anyError;
2865 }
2866
verifyWidePoint(const tcu::Surface & viewport,const GeneratedPoint & refPoint,const ProjectedBBox & bbox,ResultPointType pointType,int & logFloodCounter)2867 bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter)
2868 {
2869 const int componentNdx = (refPoint.even) ? (1) : (2);
2870 const int halfPointSizeCeil = (refPoint.size + 1) / 2;
2871 const int halfPointSizeFloor = (refPoint.size + 1) / 2;
2872 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
2873 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
2874 de::max(viewportBBoxArea.y(), 0),
2875 de::min(viewportBBoxArea.z(), viewport.getWidth()),
2876 de::min(viewportBBoxArea.w(), viewport.getHeight()));
2877 const tcu::IVec2 pointPos = tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * (float)viewport.getWidth()),
2878 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * (float)viewport.getHeight()));
2879
2880 // find any fragment within the point that is inside the bbox, start search at the center
2881
2882 if (pointPos.x() >= verificationArea.x() &&
2883 pointPos.y() >= verificationArea.y() &&
2884 pointPos.x() < verificationArea.z() &&
2885 pointPos.y() < verificationArea.w())
2886 {
2887 if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
2888 return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2889 }
2890
2891 for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
2892 for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
2893 {
2894 const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
2895
2896 if (dx == 0 && dy == 0)
2897 continue;
2898
2899 if (testPos.x() >= verificationArea.x() &&
2900 testPos.y() >= verificationArea.y() &&
2901 testPos.x() < verificationArea.z() &&
2902 testPos.y() < verificationArea.w())
2903 {
2904 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
2905 return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2906 }
2907 }
2908
2909 // could not find point, this is only ok near boundaries
2910 if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 ||
2911 pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 ||
2912 pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
2913 pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
2914 return true;
2915
2916 if (--logFloodCounter >= 0)
2917 {
2918 m_testCtx.getLog()
2919 << tcu::TestLog::Message
2920 << "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2921 << tcu::TestLog::EndMessage;
2922 }
2923
2924 return false;
2925 }
2926
verifyWidePointAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,const GeneratedPoint & refPoint,const tcu::IVec4 & bbox,ResultPointType pointType,int componentNdx,int & logFloodCounter)2927 bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter)
2928 {
2929 const int expectedPointSize = refPoint.size;
2930 bool viewportClippedTop = false;
2931 bool viewportClippedBottom = false;
2932 bool primitiveClippedTop = false;
2933 bool primitiveClippedBottom = false;
2934 std::vector<tcu::IVec2> widthsUpwards;
2935 std::vector<tcu::IVec2> widthsDownwards;
2936 std::vector<tcu::IVec2> widths;
2937
2938 // search upwards
2939 for (int y = pointPos.y();; --y)
2940 {
2941 if (y < bbox.y() || y < 0)
2942 {
2943 if (y < bbox.y())
2944 primitiveClippedTop = true;
2945 if (y < 0)
2946 viewportClippedTop = true;
2947 break;
2948 }
2949 else if (pointPos.y() - y > expectedPointSize)
2950 {
2951 // no need to go further than point height
2952 break;
2953 }
2954 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2955 {
2956 break;
2957 }
2958 else
2959 {
2960 widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2961 }
2962 }
2963
2964 // top is clipped
2965 if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
2966 {
2967 const tcu::IVec2& range = widthsUpwards.back();
2968 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
2969 const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
2970
2971 if (squareFits || widthClipped)
2972 return true;
2973 }
2974
2975 // and downwards
2976 for (int y = pointPos.y()+1;; ++y)
2977 {
2978 if (y >= bbox.w() || y >= viewport.getHeight())
2979 {
2980 if (y >= bbox.w())
2981 primitiveClippedBottom = true;
2982 if (y >= viewport.getHeight())
2983 viewportClippedBottom = true;
2984 break;
2985 }
2986 else if (y - pointPos.y() > expectedPointSize)
2987 {
2988 // no need to go further than point height
2989 break;
2990 }
2991 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2992 {
2993 break;
2994 }
2995 else
2996 {
2997 widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2998 }
2999 }
3000
3001 // bottom is clipped
3002 if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty()))
3003 {
3004 const tcu::IVec2& range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
3005 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
3006 const bool bboxClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1);
3007 const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth()-1;
3008
3009 if (squareFits || bboxClipped || viewportClipped)
3010 return true;
3011 }
3012
3013 // would square point would fit into the rasterized area
3014
3015 for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
3016 widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
3017 for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
3018 widths.push_back(widthsDownwards[ndx]);
3019 DE_ASSERT(!widths.empty());
3020
3021 for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
3022 {
3023 tcu::IVec2 unionRange = widths[y];
3024
3025 for (int dy = 1; dy < expectedPointSize; ++dy)
3026 {
3027 unionRange.x() = de::max(unionRange.x(), widths[y+dy].x());
3028 unionRange.y() = de::min(unionRange.y(), widths[y+dy].y());
3029 }
3030
3031 // would a N x N block fit here?
3032 {
3033 const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
3034 const bool bboxClipped = (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1);
3035 const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1;
3036
3037 if (squareFits || bboxClipped || viewportClipped)
3038 return true;
3039 }
3040 }
3041
3042 if (--logFloodCounter >= 0)
3043 {
3044 m_testCtx.getLog()
3045 << tcu::TestLog::Message
3046 << "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3047 << tcu::TestLog::EndMessage;
3048 }
3049 return false;
3050 }
3051
scanPointWidthAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,int expectedPointSize,int componentNdx) const3052 tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const
3053 {
3054 int minX = pointPos.x();
3055 int maxX = pointPos.x();
3056
3057 // search horizontally for a point edges
3058 for (int x = pointPos.x()-1; x >= 0; --x)
3059 {
3060 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3061 break;
3062
3063 // no need to go further than point width
3064 if (pointPos.x() - x > expectedPointSize)
3065 break;
3066
3067 minX = x;
3068 }
3069 for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x)
3070 {
3071 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3072 break;
3073
3074 // no need to go further than point width
3075 if (x - pointPos.x() > expectedPointSize)
3076 break;
3077
3078 maxX = x;
3079 }
3080
3081 return tcu::IVec2(minX, maxX);
3082 }
3083
3084 class BlitFboCase : public TestCase
3085 {
3086 public:
3087 enum RenderTarget
3088 {
3089 TARGET_DEFAULT = 0,
3090 TARGET_FBO,
3091
3092 TARGET_LAST
3093 };
3094
3095 BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst);
3096 ~BlitFboCase (void);
3097
3098 private:
3099 enum
3100 {
3101 FBO_SIZE = 256,
3102 };
3103
3104 struct BlitArgs
3105 {
3106 tcu::IVec4 src;
3107 tcu::IVec4 dst;
3108 tcu::Vec4 bboxMin;
3109 tcu::Vec4 bboxMax;
3110 bool linear;
3111 };
3112
3113 void init (void);
3114 void deinit (void);
3115 IterateResult iterate (void);
3116
3117 void fillSourceWithPattern (void);
3118 bool verifyImage (const BlitArgs& args);
3119
3120 const RenderTarget m_src;
3121 const RenderTarget m_dst;
3122
3123 std::vector<BlitArgs> m_iterations;
3124 int m_iteration;
3125 de::MovePtr<glu::Framebuffer> m_srcFbo;
3126 de::MovePtr<glu::Framebuffer> m_dstFbo;
3127 de::MovePtr<glu::Renderbuffer> m_srcRbo;
3128 de::MovePtr<glu::Renderbuffer> m_dstRbo;
3129 de::MovePtr<glu::ShaderProgram> m_program;
3130 de::MovePtr<glu::Buffer> m_vbo;
3131 };
3132
BlitFboCase(Context & context,const char * name,const char * description,RenderTarget src,RenderTarget dst)3133 BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst)
3134 : TestCase (context, name, description)
3135 , m_src (src)
3136 , m_dst (dst)
3137 , m_iteration (0)
3138 {
3139 DE_ASSERT(src < TARGET_LAST);
3140 DE_ASSERT(dst < TARGET_LAST);
3141 }
3142
~BlitFboCase(void)3143 BlitFboCase::~BlitFboCase (void)
3144 {
3145 deinit();
3146 }
3147
init(void)3148 void BlitFboCase::init (void)
3149 {
3150 const int numIterations = 12;
3151 const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1);
3152 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3153 de::Random rnd (0xABC123);
3154
3155 m_testCtx.getLog()
3156 << tcu::TestLog::Message
3157 << "Using BlitFramebuffer to blit area from "
3158 << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3159 << " to "
3160 << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3161 << ".\n"
3162 << "Varying blit arguments and primitive bounding box between iterations.\n"
3163 << "Expecting bounding box to have no effect on blitting.\n"
3164 << "Source framebuffer is filled with green-yellow grid.\n"
3165 << tcu::TestLog::EndMessage;
3166
3167 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3168 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3169 if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3170 throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3171
3172 // resources
3173
3174 if (m_src == TARGET_FBO)
3175 {
3176 m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3177 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3178 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3179 GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3180
3181 m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3182 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3183 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3184 GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3185 }
3186
3187 if (m_dst == TARGET_FBO)
3188 {
3189 m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3190 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3191 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3192 GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3193
3194 m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3195 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3196 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3197 GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3198 }
3199
3200 {
3201 static const char* const s_vertexSource = "#version 310 es\n"
3202 "in highp vec4 a_position;\n"
3203 "out highp vec4 v_position;\n"
3204 "void main()\n"
3205 "{\n"
3206 " gl_Position = a_position;\n"
3207 " v_position = a_position;\n"
3208 "}\n";
3209 static const char* const s_fragmentSource = "#version 310 es\n"
3210 "in mediump vec4 v_position;\n"
3211 "layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3212 "void main()\n"
3213 "{\n"
3214 " const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3215 " const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3216 " dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3217 "}\n";
3218
3219 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource)));
3220
3221 if (!m_program->isOk())
3222 {
3223 m_testCtx.getLog() << *m_program;
3224 throw tcu::TestError("failed to build program");
3225 }
3226 }
3227
3228 {
3229 static const tcu::Vec4 s_quadCoords[] =
3230 {
3231 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3232 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
3233 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
3234 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
3235 };
3236
3237 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3238
3239 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3240 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3241 GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3242 }
3243
3244 // gen iterations
3245
3246 {
3247 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3248 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3249
3250 m_testCtx.getLog()
3251 << tcu::TestLog::Message
3252 << "srcSize = " << srcSize << "\n"
3253 << "dstSize = " << dstSize << "\n"
3254 << tcu::TestLog::EndMessage;
3255
3256 for (int ndx = 0; ndx < numIterations; ++ndx)
3257 {
3258 BlitArgs args;
3259
3260 if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3261 {
3262 const tcu::IVec2 unionSize = tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3263 const int srcWidth = rnd.getInt(1, unionSize.x());
3264 const int srcHeight = rnd.getInt(1, unionSize.y());
3265 const int srcX = rnd.getInt(0, unionSize.x() - srcWidth);
3266 const int srcY = rnd.getInt(0, unionSize.y() - srcHeight);
3267
3268 args.src.x() = srcX;
3269 args.src.y() = srcY;
3270 args.src.z() = srcX + srcWidth;
3271 args.src.w() = srcY + srcHeight;
3272
3273 args.dst = args.src;
3274 }
3275 else
3276 {
3277 const int srcWidth = rnd.getInt(1, srcSize.x());
3278 const int srcHeight = rnd.getInt(1, srcSize.y());
3279 const int srcX = rnd.getInt(0, srcSize.x() - srcWidth);
3280 const int srcY = rnd.getInt(0, srcSize.y() - srcHeight);
3281 const int dstWidth = rnd.getInt(1, dstSize.x());
3282 const int dstHeight = rnd.getInt(1, dstSize.y());
3283 const int dstX = rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2); // allow dst go out of bounds
3284 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1) / 2);
3285
3286 args.src.x() = srcX;
3287 args.src.y() = srcY;
3288 args.src.z() = srcX + srcWidth;
3289 args.src.w() = srcY + srcHeight;
3290 args.dst.x() = dstX;
3291 args.dst.y() = dstY;
3292 args.dst.z() = dstX + dstWidth;
3293 args.dst.w() = dstY + dstHeight;
3294 }
3295
3296 args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3297 args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3298 args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3299 args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f);
3300
3301 args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3302 args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3303 args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3304 args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f);
3305
3306 if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3307 std::swap(args.bboxMin.x(), args.bboxMax.x());
3308 if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3309 std::swap(args.bboxMin.y(), args.bboxMax.y());
3310 if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3311 std::swap(args.bboxMin.z(), args.bboxMax.z());
3312
3313 args.linear = rnd.getBool();
3314
3315 m_iterations.push_back(args);
3316 }
3317 }
3318 }
3319
deinit(void)3320 void BlitFboCase::deinit (void)
3321 {
3322 m_srcFbo.clear();
3323 m_srcRbo.clear();
3324 m_dstFbo.clear();
3325 m_dstRbo.clear();
3326 m_program.clear();
3327 m_vbo.clear();
3328 }
3329
iterate(void)3330 BlitFboCase::IterateResult BlitFboCase::iterate (void)
3331 {
3332 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size()));
3333 const BlitArgs& blitCfg = m_iterations[m_iteration];
3334 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3335
3336 if (m_iteration == 0)
3337 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3338
3339 // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3340 if (m_src == TARGET_DEFAULT || m_iteration == 0)
3341 fillSourceWithPattern();
3342
3343 m_testCtx.getLog()
3344 << tcu::TestLog::Message
3345 << "Set bounding box:\n"
3346 << "\tmin:" << blitCfg.bboxMin << "\n"
3347 << "\tmax:" << blitCfg.bboxMax << "\n"
3348 << "Blit:\n"
3349 << "\tsrc: " << blitCfg.src << "\n"
3350 << "\tdst: " << blitCfg.dst << "\n"
3351 << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest"))
3352 << tcu::TestLog::EndMessage;
3353
3354 gl.primitiveBoundingBox(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3355 blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
3356
3357 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3358 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3359 gl.clear(GL_COLOR_BUFFER_BIT);
3360
3361 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3362 gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(),
3363 blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(),
3364 GL_COLOR_BUFFER_BIT,
3365 ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3366 GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3367
3368 if (!verifyImage(blitCfg))
3369 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3370
3371 return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3372 }
3373
verifyImage(const BlitArgs & args)3374 bool BlitFboCase::verifyImage (const BlitArgs& args)
3375 {
3376 const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything
3377 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3378 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3379 tcu::Surface viewport (dstSize.x(), dstSize.y());
3380 tcu::Surface errorMask (dstSize.x(), dstSize.y());
3381 bool anyError = false;
3382
3383 m_testCtx.getLog()
3384 << tcu::TestLog::Message
3385 << "Verifying blit result"
3386 << tcu::TestLog::EndMessage;
3387
3388 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3389 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3390
3391 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3392
3393 for (int y = 0; y < dstSize.y(); ++y)
3394 for (int x = 0; x < dstSize.x(); ++x)
3395 {
3396 const tcu::RGBA color = viewport.getPixel(x, y);
3397 const bool inside = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3398 const bool error = (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold)
3399 : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold);
3400
3401 if (error)
3402 {
3403 anyError = true;
3404 errorMask.setPixel(x, y, tcu::RGBA::red());
3405 }
3406 }
3407
3408 if (anyError)
3409 {
3410 m_testCtx.getLog()
3411 << tcu::TestLog::Message
3412 << "Image verification failed."
3413 << tcu::TestLog::EndMessage
3414 << tcu::TestLog::ImageSet("Images", "Image verification")
3415 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3416 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3417 << tcu::TestLog::EndImageSet;
3418 return false;
3419 }
3420 else
3421 {
3422 m_testCtx.getLog()
3423 << tcu::TestLog::Message
3424 << "Result image ok."
3425 << tcu::TestLog::EndMessage
3426 << tcu::TestLog::ImageSet("Images", "Image verification")
3427 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3428 << tcu::TestLog::EndImageSet;
3429 return true;
3430 }
3431 }
3432
fillSourceWithPattern(void)3433 void BlitFboCase::fillSourceWithPattern (void)
3434 {
3435 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3436 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3437 const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3438
3439 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3440 gl.viewport(0, 0, srcSize.x(), srcSize.y());
3441 gl.useProgram(m_program->getProgram());
3442
3443 gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3444 gl.clear(GL_COLOR_BUFFER_BIT);
3445
3446 gl.enableVertexAttribArray(posLocation);
3447 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3448 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3449 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3450 }
3451
3452 class DepthDrawCase : public TestCase
3453 {
3454 public:
3455 enum DepthType
3456 {
3457 DEPTH_BUILTIN = 0,
3458 DEPTH_USER_DEFINED,
3459
3460 DEPTH_LAST
3461 };
3462 enum BBoxState
3463 {
3464 STATE_GLOBAL = 0,
3465 STATE_PER_PRIMITIVE,
3466
3467 STATE_LAST
3468 };
3469 enum BBoxSize
3470 {
3471 BBOX_EQUAL = 0,
3472 BBOX_LARGER,
3473
3474 BBOX_LAST
3475 };
3476
3477 DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize);
3478 ~DepthDrawCase (void);
3479
3480 private:
3481 void init (void);
3482 void deinit (void);
3483 IterateResult iterate (void);
3484
3485 std::string genVertexSource (void) const;
3486 std::string genFragmentSource (void) const;
3487 std::string genTessellationControlSource (void) const;
3488 std::string genTessellationEvaluationSource (void) const;
3489 void generateAttributeData (std::vector<tcu::Vec4>& data) const;
3490 bool verifyImage (const tcu::Surface& viewport) const;
3491
3492 enum
3493 {
3494 RENDER_AREA_SIZE = 256,
3495 };
3496
3497 struct LayerInfo
3498 {
3499 float zOffset;
3500 float zScale;
3501 tcu::Vec4 color1;
3502 tcu::Vec4 color2;
3503 };
3504
3505 const int m_numLayers;
3506 const int m_gridSize;
3507
3508 const DepthType m_depthType;
3509 const BBoxState m_state;
3510 const BBoxSize m_bboxSize;
3511
3512 de::MovePtr<glu::ShaderProgram> m_program;
3513 de::MovePtr<glu::Buffer> m_vbo;
3514 std::vector<LayerInfo> m_layers;
3515 };
3516
DepthDrawCase(Context & context,const char * name,const char * description,DepthType depthType,BBoxState state,BBoxSize bboxSize)3517 DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize)
3518 : TestCase (context, name, description)
3519 , m_numLayers (14)
3520 , m_gridSize (24)
3521 , m_depthType (depthType)
3522 , m_state (state)
3523 , m_bboxSize (bboxSize)
3524 {
3525 DE_ASSERT(depthType < DEPTH_LAST);
3526 DE_ASSERT(state < STATE_LAST);
3527 DE_ASSERT(bboxSize < BBOX_LAST);
3528 }
3529
~DepthDrawCase(void)3530 DepthDrawCase::~DepthDrawCase (void)
3531 {
3532 deinit();
3533 }
3534
init(void)3535 void DepthDrawCase::init (void)
3536 {
3537 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3538
3539 // requirements
3540
3541 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3542 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3543 if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3544 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3545 if (m_context.getRenderTarget().getDepthBits() == 0)
3546 throw tcu::NotSupportedError("Test requires depth buffer");
3547 if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
3548 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport");
3549
3550 // log
3551 m_testCtx.getLog()
3552 << tcu::TestLog::Message
3553 << "Rendering multiple triangle grids with with different z coordinates.\n"
3554 << "Topmost grid is green-yellow, other grids are blue-red.\n"
3555 << "Expecting only the green-yellow grid to be visible.\n"
3556 << "Setting primitive bounding box "
3557 << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
3558 << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
3559 << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding."))
3560 << "\n"
3561 << "Set bounding box using "
3562 << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3563 << "\n"
3564 << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : (""))
3565 << tcu::TestLog::EndMessage;
3566
3567 // resources
3568
3569 {
3570 glu::ProgramSources sources;
3571 sources << glu::VertexSource(genVertexSource());
3572 sources << glu::FragmentSource(genFragmentSource());
3573
3574 if (m_state == STATE_PER_PRIMITIVE)
3575 sources << glu::TessellationControlSource(genTessellationControlSource())
3576 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
3577
3578 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
3579 GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
3580
3581 {
3582 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
3583 m_testCtx.getLog() << *m_program;
3584 }
3585
3586 if (!m_program->isOk())
3587 throw tcu::TestError("failed to build program");
3588 }
3589
3590 {
3591 std::vector<tcu::Vec4> data;
3592
3593 generateAttributeData(data);
3594
3595 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3596 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3597 gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
3598 GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
3599 }
3600
3601 // gen layers
3602 {
3603 de::Random rnd(0x12345);
3604
3605 m_layers.resize(m_numLayers);
3606 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3607 {
3608 m_layers[layerNdx].zOffset = ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f;
3609 m_layers[layerNdx].zScale = (2.0f / (float)m_numLayers);
3610 m_layers[layerNdx].color1 = (layerNdx == 0) ? (tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
3611 m_layers[layerNdx].color2 = (layerNdx == 0) ? (tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f));
3612 }
3613 rnd.shuffle(m_layers.begin(), m_layers.end());
3614 }
3615 }
3616
deinit(void)3617 void DepthDrawCase::deinit (void)
3618 {
3619 m_program.clear();
3620 m_vbo.clear();
3621 }
3622
iterate(void)3623 DepthDrawCase::IterateResult DepthDrawCase::iterate (void)
3624 {
3625 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3626 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3627 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3628 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
3629 const glw::GLint depthBiasLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
3630 const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
3631 const glw::GLint color1Location = gl.getUniformLocation(m_program->getProgram(), "u_color1");
3632 const glw::GLint color2Location = gl.getUniformLocation(m_program->getProgram(), "u_color2");
3633
3634 tcu::Surface viewport (RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3635 de::Random rnd (0x213237);
3636
3637 TCU_CHECK(posLocation != -1);
3638 TCU_CHECK(colLocation != -1);
3639 TCU_CHECK(depthBiasLocation != -1);
3640 TCU_CHECK(depthScaleLocation != -1);
3641 TCU_CHECK(color1Location != -1);
3642 TCU_CHECK(color2Location != -1);
3643
3644 gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3645 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3646 gl.clearDepthf(1.0f);
3647 gl.depthFunc(GL_LESS);
3648 gl.enable(GL_DEPTH_TEST);
3649 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3650 GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
3651
3652 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3653 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL);
3654 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4);
3655 gl.enableVertexAttribArray(posLocation);
3656 gl.enableVertexAttribArray(colLocation);
3657 gl.useProgram(m_program->getProgram());
3658 GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
3659
3660 if (hasTessellation)
3661 gl.patchParameteri(GL_PATCH_VERTICES, 3);
3662
3663 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3664 {
3665 gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
3666 gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
3667 gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
3668 gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
3669
3670 if (m_state == STATE_GLOBAL)
3671 {
3672 const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3673 const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3674
3675 gl.primitiveBoundingBox(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f,
3676 1.0f, 1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
3677 }
3678
3679 gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
3680 }
3681
3682 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3683 GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
3684
3685 if (verifyImage(viewport))
3686 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3687 else
3688 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3689
3690 return STOP;
3691 }
3692
genVertexSource(void) const3693 std::string DepthDrawCase::genVertexSource (void) const
3694 {
3695 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3696 std::ostringstream buf;
3697
3698 buf << "#version 310 es\n"
3699 "in highp vec4 a_position;\n"
3700 "in highp vec4 a_colorMix;\n"
3701 "out highp vec4 vtx_colorMix;\n";
3702
3703 if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
3704 buf << "out highp float v_fragDepth;\n";
3705
3706 if (!hasTessellation)
3707 buf << "uniform highp float u_depthBias;\n"
3708 "uniform highp float u_depthScale;\n";
3709
3710 buf << "\n"
3711 "void main()\n"
3712 "{\n";
3713
3714 if (hasTessellation)
3715 buf << " gl_Position = a_position;\n";
3716 else if (m_depthType == DEPTH_USER_DEFINED)
3717 buf << " highp float dummyZ = a_position.z;\n"
3718 " highp float writtenZ = a_position.w;\n"
3719 " gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n"
3720 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3721 else
3722 buf << " highp float writtenZ = a_position.w;\n"
3723 " gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3724
3725 buf << " vtx_colorMix = a_colorMix;\n"
3726 "}\n";
3727
3728 return buf.str();
3729 }
3730
genFragmentSource(void) const3731 std::string DepthDrawCase::genFragmentSource (void) const
3732 {
3733 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3734 const char* const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
3735 std::ostringstream buf;
3736
3737 buf << "#version 310 es\n"
3738 "in mediump vec4 " << colorMixName << ";\n";
3739
3740 if (m_depthType == DEPTH_USER_DEFINED)
3741 buf << "in mediump float v_fragDepth;\n";
3742
3743 buf << "layout(location = 0) out mediump vec4 o_color;\n"
3744 "uniform highp vec4 u_color1;\n"
3745 "uniform highp vec4 u_color2;\n"
3746 "\n"
3747 "void main()\n"
3748 "{\n"
3749 " o_color = mix(u_color1, u_color2, " << colorMixName << ");\n";
3750
3751 if (m_depthType == DEPTH_USER_DEFINED)
3752 buf << " gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
3753
3754 buf << "}\n";
3755
3756 return buf.str();
3757 }
3758
genTessellationControlSource(void) const3759 std::string DepthDrawCase::genTessellationControlSource (void) const
3760 {
3761 std::ostringstream buf;
3762
3763 buf << "#version 310 es\n"
3764 "#extension GL_EXT_tessellation_shader : require\n"
3765 "#extension GL_EXT_primitive_bounding_box : require\n"
3766 "layout(vertices=3) out;\n"
3767 "\n"
3768 "uniform highp float u_depthBias;\n"
3769 "uniform highp float u_depthScale;\n"
3770 "\n"
3771 "in highp vec4 vtx_colorMix[];\n"
3772 "out highp vec4 tess_ctrl_colorMix[];\n"
3773 "\n"
3774 "void main()\n"
3775 "{\n"
3776 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3777 " tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
3778 "\n"
3779 " gl_TessLevelOuter[0] = 2.8;\n"
3780 " gl_TessLevelOuter[1] = 2.8;\n"
3781 " gl_TessLevelOuter[2] = 2.8;\n"
3782 " gl_TessLevelInner[0] = 2.8;\n"
3783 "\n"
3784 " // real Z stored in w component\n"
3785 " highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3786 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3787 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"
3788 " highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3789 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3790 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n";
3791
3792 if (m_bboxSize == BBOX_EQUAL)
3793 buf << " gl_BoundingBoxEXT[0] = minBound;\n"
3794 " gl_BoundingBoxEXT[1] = maxBound;\n";
3795 else
3796 buf << " highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
3797 " highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
3798 " gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
3799 " gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
3800
3801 buf << "}\n";
3802
3803 return buf.str();
3804 }
3805
genTessellationEvaluationSource(void) const3806 std::string DepthDrawCase::genTessellationEvaluationSource (void) const
3807 {
3808 std::ostringstream buf;
3809
3810 buf << "#version 310 es\n"
3811 "#extension GL_EXT_tessellation_shader : require\n"
3812 "#extension GL_EXT_gpu_shader5 : require\n"
3813 "layout(triangles) in;\n"
3814 "\n"
3815 "in highp vec4 tess_ctrl_colorMix[];\n"
3816 "out highp vec4 tess_eval_colorMix;\n";
3817
3818 if (m_depthType == DEPTH_USER_DEFINED)
3819 buf << "out highp float v_fragDepth;\n";
3820
3821 buf << "uniform highp float u_depthBias;\n"
3822 "uniform highp float u_depthScale;\n"
3823 "\n"
3824 "precise gl_Position;\n"
3825 "\n"
3826 "void main()\n"
3827 "{\n"
3828 " highp vec4 tessellatedPos = 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";
3829
3830 if (m_depthType == DEPTH_USER_DEFINED)
3831 buf << " highp float dummyZ = tessellatedPos.z;\n"
3832 " highp float writtenZ = tessellatedPos.w;\n"
3833 " gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n"
3834 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3835 else
3836 buf << " highp float writtenZ = tessellatedPos.w;\n"
3837 " gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3838
3839 buf << " tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
3840 "}\n";
3841
3842 return buf.str();
3843 }
3844
generateAttributeData(std::vector<tcu::Vec4> & data) const3845 void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const
3846 {
3847 const tcu::Vec4 color1 (0.0f, 0.0f, 0.0f, 0.0f); // mix weights
3848 const tcu::Vec4 color2 (1.0f, 1.0f, 1.0f, 1.0f);
3849 std::vector<int> cellOrder (m_gridSize * m_gridSize);
3850 de::Random rnd (0xAB54321);
3851
3852 // generate grid with cells in random order
3853 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3854 cellOrder[ndx] = ndx;
3855 rnd.shuffle(cellOrder.begin(), cellOrder.end());
3856
3857 data.resize(m_gridSize * m_gridSize * 6 * 2);
3858 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3859 {
3860 const int cellNdx = cellOrder[ndx];
3861 const int cellX = cellNdx % m_gridSize;
3862 const int cellY = cellNdx / m_gridSize;
3863 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (color1) : (color2);
3864
3865 data[ndx * 6 * 2 + 0] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 1] = cellColor;
3866 data[ndx * 6 * 2 + 2] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 3] = cellColor;
3867 data[ndx * 6 * 2 + 4] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 5] = cellColor;
3868 data[ndx * 6 * 2 + 6] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 7] = cellColor;
3869 data[ndx * 6 * 2 + 8] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 9] = cellColor;
3870 data[ndx * 6 * 2 + 10] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 11] = cellColor;
3871
3872 // Fill Z with random values (fake Z)
3873 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3874 data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
3875
3876 // Fill W with other random values (written Z)
3877 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3878 data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
3879 }
3880 }
3881
verifyImage(const tcu::Surface & viewport) const3882 bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const
3883 {
3884 tcu::Surface errorMask (viewport.getWidth(), viewport.getHeight());
3885 bool anyError = false;
3886
3887 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
3888
3889 for (int y = 0; y < viewport.getHeight(); ++y)
3890 for (int x = 0; x < viewport.getWidth(); ++x)
3891 {
3892 const tcu::RGBA pixel = viewport.getPixel(x, y);
3893 bool error = false;
3894
3895 // expect green, yellow or a combination of these
3896 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
3897 error = true;
3898
3899 if (error)
3900 {
3901 errorMask.setPixel(x, y, tcu::RGBA::red());
3902 anyError = true;
3903 }
3904 }
3905
3906 if (anyError)
3907 m_testCtx.getLog()
3908 << tcu::TestLog::Message
3909 << "Image verification failed."
3910 << tcu::TestLog::EndMessage
3911 << tcu::TestLog::ImageSet("Images", "Image verification")
3912 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3913 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3914 << tcu::TestLog::EndImageSet;
3915 else
3916 m_testCtx.getLog()
3917 << tcu::TestLog::Message
3918 << "Result image ok."
3919 << tcu::TestLog::EndMessage
3920 << tcu::TestLog::ImageSet("Images", "Image verification")
3921 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3922 << tcu::TestLog::EndImageSet;
3923
3924 return !anyError;
3925 }
3926
3927 class ClearCase : public TestCase
3928 {
3929 public:
3930 enum
3931 {
3932 SCISSOR_CLEAR_BIT = 1 << 0,
3933 DRAW_TRIANGLE_BIT = 1 << 1,
3934 PER_PRIMITIVE_BBOX_BIT = 1 << 2,
3935 FULLSCREEN_SCISSOR_BIT = 1 << 3,
3936 };
3937
3938 ClearCase (Context& context, const char* name, const char* description, deUint32 flags);
3939 ~ClearCase (void);
3940
3941 private:
3942 struct DrawObject
3943 {
3944 int firstNdx;
3945 int numVertices;
3946 };
3947
3948 void init (void);
3949 void deinit (void);
3950 IterateResult iterate (void);
3951
3952 void createVbo (void);
3953 void createProgram (void);
3954 void renderTo (tcu::Surface& dst, bool useBBox);
3955 bool verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox);
3956 bool verifyImageResultValid (const tcu::PixelBufferAccess& result);
3957
3958 std::string genVertexSource (void) const;
3959 std::string genFragmentSource (void) const;
3960 std::string genTessellationControlSource (bool setBBox) const;
3961 std::string genTessellationEvaluationSource (void) const;
3962
3963 const bool m_scissoredClear;
3964 const bool m_fullscreenScissor;
3965 const bool m_drawTriangles;
3966 const bool m_useGlobalState;
3967
3968 de::MovePtr<glu::Buffer> m_vbo;
3969 de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram;
3970 de::MovePtr<glu::ShaderProgram> m_basicProgram;
3971 std::vector<DrawObject> m_drawObjects;
3972 std::vector<tcu::Vec4> m_objectVertices;
3973 };
3974
ClearCase(Context & context,const char * name,const char * description,deUint32 flags)3975 ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags)
3976 : TestCase (context, name, description)
3977 , m_scissoredClear ((flags & SCISSOR_CLEAR_BIT) != 0)
3978 , m_fullscreenScissor ((flags & FULLSCREEN_SCISSOR_BIT) != 0)
3979 , m_drawTriangles ((flags & DRAW_TRIANGLE_BIT) != 0)
3980 , m_useGlobalState ((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
3981 {
3982 DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles
3983 DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
3984 }
3985
~ClearCase(void)3986 ClearCase::~ClearCase (void)
3987 {
3988 deinit();
3989 }
3990
init(void)3991 void ClearCase::init (void)
3992 {
3993 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3994 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3995 if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3996 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3997
3998 m_testCtx.getLog()
3999 << tcu::TestLog::Message
4000 << "Doing multiple"
4001 << ((m_scissoredClear) ? (" scissored") : (""))
4002 << " color buffer clears"
4003 << ((m_drawTriangles) ? (" and drawing some geometry between them") : (""))
4004 << ".\n"
4005 << ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : (""))
4006 << "Rendering with and without setting the bounding box.\n"
4007 << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
4008 << "Set bounding box using "
4009 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
4010 << ".\n"
4011 << "Clear color is green with yellowish shades.\n"
4012 << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
4013 << tcu::TestLog::EndMessage;
4014
4015 if (m_drawTriangles)
4016 {
4017 createVbo();
4018 createProgram();
4019 }
4020 }
4021
deinit(void)4022 void ClearCase::deinit (void)
4023 {
4024 m_vbo.clear();
4025 m_perPrimitiveProgram.clear();
4026 m_basicProgram.clear();
4027 m_drawObjects = std::vector<DrawObject>();
4028 m_objectVertices = std::vector<tcu::Vec4>();
4029 }
4030
iterate(void)4031 ClearCase::IterateResult ClearCase::iterate (void)
4032 {
4033 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4034 tcu::Surface resultWithoutBBox (renderTargetSize.x(), renderTargetSize.y());
4035 tcu::Surface resultWithBBox (renderTargetSize.x(), renderTargetSize.y());
4036
4037 // render with and without bbox set
4038 for (int passNdx = 0; passNdx < 2; ++passNdx)
4039 {
4040 const bool useBBox = (passNdx == 1);
4041 tcu::Surface& destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
4042
4043 renderTo(destination, useBBox);
4044 }
4045
4046 // Verify images are equal and that the image does not contain (trivially detectable) garbage
4047
4048 if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4049 {
4050 // verifyImagesEqual will print out the image and error mask
4051 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4052 }
4053 else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4054 {
4055 // verifyImageResultValid will print out the image and error mask
4056 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4057 }
4058 else
4059 {
4060 m_testCtx.getLog()
4061 << tcu::TestLog::Message
4062 << "Image comparison passed."
4063 << tcu::TestLog::EndMessage
4064 << tcu::TestLog::ImageSet("Images", "Image verification")
4065 << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4066 << tcu::TestLog::EndImageSet;
4067
4068 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4069 }
4070
4071 return STOP;
4072 }
4073
createVbo(void)4074 void ClearCase::createVbo (void)
4075 {
4076 const int numObjects = 16;
4077 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4078 de::Random rnd (deStringHash(getName()));
4079
4080 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4081
4082 for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4083 {
4084 const int numTriangles = rnd.getInt(1, 4);
4085 const float minX = rnd.getFloat(-1.2f, 0.8f);
4086 const float minY = rnd.getFloat(-1.2f, 0.8f);
4087 const float maxX = minX + rnd.getFloat(0.2f, 1.0f);
4088 const float maxY = minY + rnd.getFloat(0.2f, 1.0f);
4089
4090 DrawObject drawObject;
4091 drawObject.firstNdx = (int)m_objectVertices.size();
4092 drawObject.numVertices = numTriangles * 3;
4093
4094 m_drawObjects.push_back(drawObject);
4095
4096 for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4097 for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4098 {
4099 const float posX = rnd.getFloat(minX, maxX);
4100 const float posY = rnd.getFloat(minY, maxY);
4101 const float posZ = rnd.getFloat(-0.7f, 0.7f);
4102 const float posW = rnd.getFloat(0.9f, 1.1f);
4103
4104 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4105 }
4106 }
4107
4108 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4109 gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW);
4110 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4111 }
4112
createProgram(void)4113 void ClearCase::createProgram (void)
4114 {
4115 m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4116 glu::ProgramSources()
4117 << glu::VertexSource(genVertexSource())
4118 << glu::FragmentSource(genFragmentSource())
4119 << glu::TessellationControlSource(genTessellationControlSource(false))
4120 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4121
4122 m_testCtx.getLog()
4123 << tcu::TestLog::Section("Program", "Shader program")
4124 << *m_basicProgram
4125 << tcu::TestLog::EndSection;
4126
4127 if (!m_basicProgram->isOk())
4128 throw tcu::TestError("shader build failed");
4129
4130 if (!m_useGlobalState)
4131 {
4132 m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4133 glu::ProgramSources()
4134 << glu::VertexSource(genVertexSource())
4135 << glu::FragmentSource(genFragmentSource())
4136 << glu::TessellationControlSource(genTessellationControlSource(true))
4137 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4138
4139 m_testCtx.getLog()
4140 << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4141 << *m_perPrimitiveProgram
4142 << tcu::TestLog::EndSection;
4143
4144 if (!m_perPrimitiveProgram->isOk())
4145 throw tcu::TestError("shader build failed");
4146 }
4147 }
4148
renderTo(tcu::Surface & dst,bool useBBox)4149 void ClearCase::renderTo (tcu::Surface& dst, bool useBBox)
4150 {
4151 const int numOps = 45;
4152 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f);
4153 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
4154 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4155 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4156 de::Random rnd (deStringHash(getName()));
4157 glu::VertexArray vao (m_context.getRenderContext());
4158
4159 // always do the initial clear
4160 gl.disable(GL_SCISSOR_TEST);
4161 gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4162 gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4163 gl.clear(GL_COLOR_BUFFER_BIT);
4164 gl.finish();
4165
4166 // prepare draw
4167 if (m_scissoredClear)
4168 gl.enable(GL_SCISSOR_TEST);
4169
4170 if (m_drawTriangles)
4171 {
4172 const deUint32 programHandle = (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4173 const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position");
4174
4175 TCU_CHECK(positionAttribLoc != -1);
4176
4177 gl.useProgram(programHandle);
4178 gl.bindVertexArray(*vao);
4179 gl.enableVertexAttribArray(positionAttribLoc);
4180 gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4181 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4182 }
4183
4184 // do random scissor/clearldraw operations
4185 for (int opNdx = 0; opNdx < numOps; ++opNdx)
4186 {
4187 const int drawObjNdx = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4188 const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4189 const int objectVertexLength = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4190 tcu::Vec4 bboxMin;
4191 tcu::Vec4 bboxMax;
4192
4193 if (m_drawTriangles)
4194 {
4195 bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4196 bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4197
4198 // calc bbox
4199 for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx)
4200 for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4201 {
4202 bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4203 bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4204 }
4205 }
4206 else
4207 {
4208 // no geometry, just random something
4209 bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4210 bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4211 bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4212 bboxMin.w() = 1.0f;
4213 bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4214 bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4215 bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4216 bboxMax.w() = 1.0f;
4217 }
4218
4219 if (m_scissoredClear)
4220 {
4221 const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x()-1);
4222 const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y()-1);
4223 const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x()-scissorX);
4224 const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y()-scissorY);
4225
4226 gl.scissor(scissorX, scissorY, scissorW, scissorH);
4227 }
4228
4229 {
4230 const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4231 gl.clearColor(color.x(), color.y(), color.z(), color.w());
4232 gl.clear(GL_COLOR_BUFFER_BIT);
4233 }
4234
4235 if (useBBox)
4236 {
4237 DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4238 if (m_useGlobalState)
4239 gl.primitiveBoundingBox(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(),
4240 bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w());
4241 }
4242
4243 if (m_drawTriangles)
4244 gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4245 }
4246
4247 GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4248 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4249 }
4250
verifyImagesEqual(const tcu::PixelBufferAccess & withoutBBox,const tcu::PixelBufferAccess & withBBox)4251 bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox)
4252 {
4253 DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
4254 DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
4255
4256 tcu::Surface errorMask (withoutBBox.getWidth(), withoutBBox.getHeight());
4257 bool anyError = false;
4258
4259 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4260
4261 for (int y = 0; y < withoutBBox.getHeight(); ++y)
4262 for (int x = 0; x < withoutBBox.getWidth(); ++x)
4263 {
4264 if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4265 {
4266 errorMask.setPixel(x, y, tcu::RGBA::red());
4267 anyError = true;
4268 }
4269 }
4270
4271 if (anyError)
4272 {
4273 m_testCtx.getLog()
4274 << tcu::TestLog::Message
4275 << "Image comparison failed."
4276 << tcu::TestLog::EndMessage
4277 << tcu::TestLog::ImageSet("Images", "Image comparison")
4278 << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4279 << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4280 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4281 << tcu::TestLog::EndImageSet;
4282 }
4283
4284 return !anyError;
4285 }
4286
verifyImageResultValid(const tcu::PixelBufferAccess & result)4287 bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result)
4288 {
4289 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4290 bool anyError = false;
4291
4292 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4293
4294 for (int y = 0; y < result.getHeight(); ++y)
4295 for (int x = 0; x < result.getWidth(); ++x)
4296 {
4297 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4298
4299 // allow green, yellow and any shade between
4300 if (pixel[1] != 255 || pixel[2] != 0)
4301 {
4302 errorMask.setPixel(x, y, tcu::RGBA::red());
4303 anyError = true;
4304 }
4305 }
4306
4307 if (anyError)
4308 {
4309 m_testCtx.getLog()
4310 << tcu::TestLog::Message
4311 << "Image verification failed."
4312 << tcu::TestLog::EndMessage
4313 << tcu::TestLog::ImageSet("Images", "Image verification")
4314 << tcu::TestLog::Image("ResultImage", "Result image", result)
4315 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4316 << tcu::TestLog::EndImageSet;
4317 }
4318
4319 return !anyError;
4320 }
4321
4322 static const char* const s_yellowishPosOnlyVertexSource = "#version 310 es\n"
4323 "in highp vec4 a_position;\n"
4324 "out highp vec4 v_vertex_color;\n"
4325 "void main()\n"
4326 "{\n"
4327 " gl_Position = a_position;\n"
4328 " // yellowish shade\n"
4329 " highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4330 " v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4331 "}\n";
4332
4333 static const char* const s_basicColorFragmentSource = "#version 310 es\n"
4334 "in mediump vec4 v_color;\n"
4335 "layout(location = 0) out mediump vec4 o_color;\n"
4336 "void main()\n"
4337 "{\n"
4338 " o_color = v_color;\n"
4339 "}\n";
4340
4341
4342 static const char* const s_basicColorTessEvalSource = "#version 310 es\n"
4343 "#extension GL_EXT_tessellation_shader : require\n"
4344 "#extension GL_EXT_gpu_shader5 : require\n"
4345 "layout(triangles) in;\n"
4346 "in highp vec4 v_tess_eval_color[];\n"
4347 "out highp vec4 v_color;\n"
4348 "precise gl_Position;\n"
4349 "void main()\n"
4350 "{\n"
4351 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4352 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
4353 " + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4354 " v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4355 " + gl_TessCoord.y * v_tess_eval_color[1]\n"
4356 " + gl_TessCoord.z * v_tess_eval_color[2];\n"
4357 "}\n";
4358
genVertexSource(void) const4359 std::string ClearCase::genVertexSource (void) const
4360 {
4361 return s_yellowishPosOnlyVertexSource;
4362 }
4363
genFragmentSource(void) const4364 std::string ClearCase::genFragmentSource (void) const
4365 {
4366 return s_basicColorFragmentSource;
4367 }
4368
genTessellationControlSource(bool setBBox) const4369 std::string ClearCase::genTessellationControlSource (bool setBBox) const
4370 {
4371 std::ostringstream buf;
4372
4373 buf << "#version 310 es\n"
4374 "#extension GL_EXT_tessellation_shader : require\n";
4375
4376 if (setBBox)
4377 buf << "#extension GL_EXT_primitive_bounding_box : require\n";
4378
4379 buf << "layout(vertices=3) out;\n"
4380 "in highp vec4 v_vertex_color[];\n"
4381 "out highp vec4 v_tess_eval_color[];\n"
4382 "void main()\n"
4383 "{\n"
4384 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4385 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4386 " gl_TessLevelOuter[0] = 2.8;\n"
4387 " gl_TessLevelOuter[1] = 2.8;\n"
4388 " gl_TessLevelOuter[2] = 2.8;\n"
4389 " gl_TessLevelInner[0] = 2.8;\n";
4390
4391 if (setBBox)
4392 {
4393 buf << "\n"
4394 " gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n"
4395 " gl_in[1].gl_Position),\n"
4396 " gl_in[2].gl_Position);\n"
4397 " gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n"
4398 " gl_in[1].gl_Position),\n"
4399 " gl_in[2].gl_Position);\n";
4400 }
4401
4402 buf << "}\n";
4403 return buf.str();
4404 }
4405
genTessellationEvaluationSource(void) const4406 std::string ClearCase::genTessellationEvaluationSource (void) const
4407 {
4408 return s_basicColorTessEvalSource;
4409 }
4410
4411 class ViewportCallOrderCase : public TestCase
4412 {
4413 public:
4414 enum CallOrder
4415 {
4416 VIEWPORT_FIRST = 0,
4417 BBOX_FIRST,
4418
4419 ORDER_LAST
4420 };
4421
4422 ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder);
4423 ~ViewportCallOrderCase (void);
4424
4425 private:
4426 void init (void);
4427 void deinit (void);
4428 IterateResult iterate (void);
4429
4430 void genVbo (void);
4431 void genProgram (void);
4432 bool verifyImage (const tcu::PixelBufferAccess& result);
4433
4434 std::string genVertexSource (void) const;
4435 std::string genFragmentSource (void) const;
4436 std::string genTessellationControlSource (void) const;
4437 std::string genTessellationEvaluationSource (void) const;
4438
4439 const CallOrder m_callOrder;
4440
4441 de::MovePtr<glu::Buffer> m_vbo;
4442 de::MovePtr<glu::ShaderProgram> m_program;
4443 int m_numVertices;
4444 };
4445
ViewportCallOrderCase(Context & context,const char * name,const char * description,CallOrder callOrder)4446 ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder)
4447 : TestCase (context, name, description)
4448 , m_callOrder (callOrder)
4449 , m_numVertices (-1)
4450 {
4451 DE_ASSERT(m_callOrder < ORDER_LAST);
4452 }
4453
~ViewportCallOrderCase(void)4454 ViewportCallOrderCase::~ViewportCallOrderCase (void)
4455 {
4456 deinit();
4457 }
4458
init(void)4459 void ViewportCallOrderCase::init (void)
4460 {
4461 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4462 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4463
4464 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4465 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4466
4467 m_testCtx.getLog()
4468 << tcu::TestLog::Message
4469 << "Testing call order of state setting functions have no effect on the rendering.\n"
4470 << "Setting viewport and bounding box in the following order:\n"
4471 << ((m_callOrder == VIEWPORT_FIRST)
4472 ? ("\tFirst viewport with glViewport function.\n")
4473 : ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4474 << ((m_callOrder == VIEWPORT_FIRST)
4475 ? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
4476 : ("\tThen viewport with glViewport function.\n"))
4477 << "Verifying rendering result."
4478 << tcu::TestLog::EndMessage;
4479
4480 // resources
4481 genVbo();
4482 genProgram();
4483 }
4484
deinit(void)4485 void ViewportCallOrderCase::deinit (void)
4486 {
4487 m_vbo.clear();
4488 m_program.clear();
4489 }
4490
iterate(void)4491 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
4492 {
4493 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4494 const tcu::IVec2 viewportSize = tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4495 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
4496 tcu::Surface resultSurface (viewportSize.x(), viewportSize.y());
4497
4498 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4499 gl.clear(GL_COLOR_BUFFER_BIT);
4500
4501 // set state
4502 for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
4503 {
4504 if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
4505 (orderNdx == 1 && m_callOrder == BBOX_FIRST))
4506 {
4507 m_testCtx.getLog()
4508 << tcu::TestLog::Message
4509 << "Setting viewport to cover the left half of the render target.\n"
4510 << "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
4511 << tcu::TestLog::EndMessage;
4512
4513 gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
4514 }
4515 else
4516 {
4517 m_testCtx.getLog()
4518 << tcu::TestLog::Message
4519 << "Setting bounding box to cover the right half of the clip space.\n"
4520 << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
4521 << tcu::TestLog::EndMessage;
4522
4523 gl.primitiveBoundingBox(0.0f, -1.0f, -1.0f, 1.0f,
4524 1.0f, 1.0f, 1.0f, 1.0f);
4525 }
4526 }
4527
4528 m_testCtx.getLog()
4529 << tcu::TestLog::Message
4530 << "Rendering mesh covering the right half of the clip space."
4531 << tcu::TestLog::EndMessage;
4532
4533 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4534 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
4535 gl.enableVertexAttribArray(posLocation);
4536 gl.useProgram(m_program->getProgram());
4537 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4538 gl.drawArrays(GL_PATCHES, 0, m_numVertices);
4539 GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
4540
4541 m_testCtx.getLog()
4542 << tcu::TestLog::Message
4543 << "Verifying image"
4544 << tcu::TestLog::EndMessage;
4545 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
4546
4547 if (!verifyImage(resultSurface.getAccess()))
4548 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4549 else
4550 {
4551 m_testCtx.getLog()
4552 << tcu::TestLog::Message
4553 << "Result ok."
4554 << tcu::TestLog::EndMessage
4555 << tcu::TestLog::ImageSet("Images", "Image verification")
4556 << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
4557 << tcu::TestLog::EndImageSet;
4558
4559 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4560 }
4561 return STOP;
4562 }
4563
genVbo(void)4564 void ViewportCallOrderCase::genVbo (void)
4565 {
4566 const int gridSize = 6;
4567 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4568 std::vector<tcu::Vec4> data (gridSize * gridSize * 2 * 3);
4569 std::vector<int> cellOrder (gridSize * gridSize * 2);
4570 de::Random rnd (0x55443322);
4571
4572 // generate grid with triangles in random order
4573 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4574 cellOrder[ndx] = ndx;
4575 rnd.shuffle(cellOrder.begin(), cellOrder.end());
4576
4577 // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
4578 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4579 {
4580 const int cellNdx = cellOrder[ndx];
4581 const bool cellSide = ((cellNdx % 2) == 0);
4582 const int cellX = (cellNdx / 2) % gridSize;
4583 const int cellY = (cellNdx / 2) / gridSize;
4584
4585 if (cellSide)
4586 {
4587 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4588 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4589 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4590 }
4591 else
4592 {
4593 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4594 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4595 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4596 }
4597 }
4598
4599 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4600 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4601 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
4602 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
4603
4604 m_numVertices = (int)data.size();
4605 }
4606
genProgram(void)4607 void ViewportCallOrderCase::genProgram (void)
4608 {
4609 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4610 glu::ProgramSources()
4611 << glu::VertexSource(genVertexSource())
4612 << glu::FragmentSource(genFragmentSource())
4613 << glu::TessellationControlSource(genTessellationControlSource())
4614 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4615
4616 m_testCtx.getLog()
4617 << tcu::TestLog::Section("Program", "Shader program")
4618 << *m_program
4619 << tcu::TestLog::EndSection;
4620
4621 if (!m_program->isOk())
4622 throw tcu::TestError("shader build failed");
4623 }
4624
verifyImage(const tcu::PixelBufferAccess & result)4625 bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
4626 {
4627 const tcu::IVec2 insideBorder (deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
4628 const tcu::IVec2 outsideBorder (deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
4629 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4630 bool anyError = false;
4631
4632 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4633
4634 for (int y = 0; y < result.getHeight(); ++y)
4635 for (int x = 0; x < result.getWidth(); ++x)
4636 {
4637 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4638 const bool insideMeshArea = x >= insideBorder.x() && x <= insideBorder.x();
4639 const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
4640
4641 // inside mesh, allow green, yellow and any shade between
4642 // outside mesh, allow background (black) only
4643 // in the border area, allow anything
4644 if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
4645 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
4646 {
4647 errorMask.setPixel(x, y, tcu::RGBA::red());
4648 anyError = true;
4649 }
4650 }
4651
4652 if (anyError)
4653 {
4654 m_testCtx.getLog()
4655 << tcu::TestLog::Message
4656 << "Image verification failed."
4657 << tcu::TestLog::EndMessage
4658 << tcu::TestLog::ImageSet("Images", "Image verification")
4659 << tcu::TestLog::Image("ResultImage", "Result image", result)
4660 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4661 << tcu::TestLog::EndImageSet;
4662 }
4663
4664 return !anyError;
4665 }
4666
genVertexSource(void) const4667 std::string ViewportCallOrderCase::genVertexSource (void) const
4668 {
4669 return s_yellowishPosOnlyVertexSource;
4670 }
4671
genFragmentSource(void) const4672 std::string ViewportCallOrderCase::genFragmentSource (void) const
4673 {
4674 return s_basicColorFragmentSource;
4675 }
4676
genTessellationControlSource(void) const4677 std::string ViewportCallOrderCase::genTessellationControlSource (void) const
4678 {
4679 return "#version 310 es\n"
4680 "#extension GL_EXT_tessellation_shader : require\n"
4681 "layout(vertices=3) out;\n"
4682 "in highp vec4 v_vertex_color[];\n"
4683 "out highp vec4 v_tess_eval_color[];\n"
4684 "void main()\n"
4685 "{\n"
4686 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4687 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4688 " gl_TessLevelOuter[0] = 2.8;\n"
4689 " gl_TessLevelOuter[1] = 2.8;\n"
4690 " gl_TessLevelOuter[2] = 2.8;\n"
4691 " gl_TessLevelInner[0] = 2.8;\n"
4692 "}\n";
4693 }
4694
genTessellationEvaluationSource(void) const4695 std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
4696 {
4697 return s_basicColorTessEvalSource;
4698 }
4699
4700 } // anonymous
4701
PrimitiveBoundingBoxTests(Context & context)4702 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
4703 : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
4704 {
4705 }
4706
~PrimitiveBoundingBoxTests(void)4707 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
4708 {
4709 }
4710
init(void)4711 void PrimitiveBoundingBoxTests::init (void)
4712 {
4713 static const struct
4714 {
4715 const char* name;
4716 const char* description;
4717 deUint32 methodFlags;
4718 } stateSetMethods[] =
4719 {
4720 {
4721 "global_state",
4722 "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
4723 BBoxRenderCase::FLAG_SET_BBOX_STATE,
4724 },
4725 {
4726 "tessellation_set_per_draw",
4727 "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
4728 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
4729 },
4730 {
4731 "tessellation_set_per_primitive",
4732 "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
4733 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4734 },
4735 };
4736 static const struct
4737 {
4738 const char* name;
4739 const char* description;
4740 deUint32 stageFlags;
4741 } pipelineConfigs[] =
4742 {
4743 {
4744 "vertex_fragment",
4745 "Render with vertex-fragment program",
4746 0u
4747 },
4748 {
4749 "vertex_tessellation_fragment",
4750 "Render with vertex-tessellation{ctrl,eval}-fragment program",
4751 BBoxRenderCase::FLAG_TESSELLATION
4752 },
4753 {
4754 "vertex_geometry_fragment",
4755 "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
4756 BBoxRenderCase::FLAG_GEOMETRY
4757 },
4758 {
4759 "vertex_tessellation_geometry_fragment",
4760 "Render with vertex-geometry-fragment program",
4761 BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
4762 },
4763 };
4764 static const struct
4765 {
4766 const char* name;
4767 const char* description;
4768 deUint32 flags;
4769 deUint32 invalidFlags;
4770 deUint32 requiredFlags;
4771 } usageConfigs[] =
4772 {
4773 {
4774 "default_framebuffer_bbox_equal",
4775 "Render to default framebuffer, set tight bounding box",
4776 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4777 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4778 0
4779 },
4780 {
4781 "default_framebuffer_bbox_larger",
4782 "Render to default framebuffer, set padded bounding box",
4783 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4784 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4785 0
4786 },
4787 {
4788 "default_framebuffer_bbox_smaller",
4789 "Render to default framebuffer, set too small bounding box",
4790 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4791 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4792 0
4793 },
4794 {
4795 "fbo_bbox_equal",
4796 "Render to texture, set tight bounding box",
4797 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4798 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4799 0
4800 },
4801 {
4802 "fbo_bbox_larger",
4803 "Render to texture, set padded bounding box",
4804 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4805 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4806 0
4807 },
4808 {
4809 "fbo_bbox_smaller",
4810 "Render to texture, set too small bounding box",
4811 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4812 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4813 0
4814 },
4815 {
4816 "default_framebuffer",
4817 "Render to default framebuffer, set tight bounding box",
4818 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4819 0,
4820 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4821 },
4822 {
4823 "fbo",
4824 "Render to texture, set tight bounding box",
4825 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4826 0,
4827 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4828 },
4829 };
4830 enum PrimitiveRenderType
4831 {
4832 TYPE_TRIANGLE,
4833 TYPE_LINE,
4834 TYPE_POINT,
4835 };
4836 const struct
4837 {
4838 const char* name;
4839 const char* description;
4840 PrimitiveRenderType type;
4841 deUint32 flags;
4842 } primitiveTypes[] =
4843 {
4844 {
4845 "triangles",
4846 "Triangle render tests",
4847 TYPE_TRIANGLE,
4848 0
4849 },
4850 {
4851 "lines",
4852 "Line render tests",
4853 TYPE_LINE,
4854 0
4855 },
4856 {
4857 "points",
4858 "Point render tests",
4859 TYPE_POINT,
4860 0
4861 },
4862 {
4863 "wide_lines",
4864 "Wide line render tests",
4865 TYPE_LINE,
4866 LineRenderCase::LINEFLAG_WIDE
4867 },
4868 {
4869 "wide_points",
4870 "Wide point render tests",
4871 TYPE_POINT,
4872 PointRenderCase::POINTFLAG_WIDE
4873 },
4874 };
4875
4876 // .state_query
4877 {
4878 tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
4879 addChild(stateQueryGroup);
4880
4881 stateQueryGroup->addChild(new InitialValueCase (m_context, "initial_value", "Initial value case"));
4882 stateQueryGroup->addChild(new QueryCase (m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT));
4883 stateQueryGroup->addChild(new QueryCase (m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN));
4884 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT));
4885 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64));
4886 }
4887
4888 // .triangles
4889 // .(wide_)lines
4890 // .(wide_)points
4891 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
4892 {
4893 tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
4894 addChild(primitiveGroup);
4895
4896 for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
4897 {
4898 tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
4899 primitiveGroup->addChild(methodGroup);
4900
4901 for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
4902 {
4903 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
4904 (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0)
4905 {
4906 // invalid config combination
4907 }
4908 else
4909 {
4910 tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
4911 methodGroup->addChild(pipelineGroup);
4912
4913 for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
4914 {
4915 const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags |
4916 stateSetMethods[stateSetMethodNdx].methodFlags |
4917 pipelineConfigs[pipelineConfigNdx].stageFlags |
4918 usageConfigs[usageNdx].flags;
4919
4920 if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
4921 continue;
4922 if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
4923 continue;
4924
4925 switch (primitiveTypes[primitiveTypeNdx].type)
4926 {
4927 case TYPE_TRIANGLE:
4928 pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4929 break;
4930 case TYPE_LINE:
4931 pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4932 break;
4933 case TYPE_POINT:
4934 pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4935 break;
4936 default:
4937 DE_ASSERT(false);
4938 }
4939 }
4940 }
4941 }
4942 }
4943 }
4944
4945 // .depth
4946 {
4947 static const struct
4948 {
4949 const char* name;
4950 const char* description;
4951 DepthDrawCase::DepthType depthMethod;
4952 } depthMethods[] =
4953 {
4954 {
4955 "builtin_depth",
4956 "Fragment depth not modified in fragment shader",
4957 DepthDrawCase::DEPTH_BUILTIN
4958 },
4959 {
4960 "user_defined_depth",
4961 "Fragment depth is defined in the fragment shader",
4962 DepthDrawCase::DEPTH_USER_DEFINED
4963 },
4964 };
4965 static const struct
4966 {
4967 const char* name;
4968 const char* description;
4969 DepthDrawCase::BBoxState bboxState;
4970 DepthDrawCase::BBoxSize bboxSize;
4971 } depthCases[] =
4972 {
4973 {
4974 "global_state_bbox_equal",
4975 "Test tight bounding box with global bbox state",
4976 DepthDrawCase::STATE_GLOBAL,
4977 DepthDrawCase::BBOX_EQUAL,
4978 },
4979 {
4980 "global_state_bbox_larger",
4981 "Test padded bounding box with global bbox state",
4982 DepthDrawCase::STATE_GLOBAL,
4983 DepthDrawCase::BBOX_LARGER,
4984 },
4985 {
4986 "per_primitive_bbox_equal",
4987 "Test tight bounding box with tessellation output bbox",
4988 DepthDrawCase::STATE_PER_PRIMITIVE,
4989 DepthDrawCase::BBOX_EQUAL,
4990 },
4991 {
4992 "per_primitive_bbox_larger",
4993 "Test padded bounding box with tessellation output bbox",
4994 DepthDrawCase::STATE_PER_PRIMITIVE,
4995 DepthDrawCase::BBOX_LARGER,
4996 },
4997 };
4998
4999 tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
5000 addChild(depthGroup);
5001
5002 // .builtin_depth
5003 // .user_defined_depth
5004 for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
5005 {
5006 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
5007 depthGroup->addChild(group);
5008
5009 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
5010 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
5011 }
5012 }
5013
5014 // .blit_fbo
5015 {
5016 tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
5017 addChild(blitFboGroup);
5018
5019 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
5020 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT));
5021 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO));
5022 }
5023
5024 // .clear
5025 {
5026 tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
5027 addChild(clearGroup);
5028
5029 clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0));
5030 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT));
5031 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles_per_primitive_bbox", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5032 clearGroup->addChild(new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT));
5033 clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles", "Do scissored clears and render some geometry", ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT));
5034 clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles_per_primitive_bbox", "Do scissored clears and render some geometry", ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5035 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
5036 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles", "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT));
5037 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles_per_primitive_bbox", "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5038 }
5039
5040 // .call_order (Khronos bug #13262)
5041 {
5042 tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
5043 addChild(callOrderGroup);
5044
5045 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
5046 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));
5047 }
5048 }
5049
5050 } // Functional
5051 } // gles31
5052 } // deqp
5053