blob: 7f07d369be0eefad59a89766ee9f90b59d8ab47c [file] [log] [blame]
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001/*-------------------------------------------------------------------------
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
49namespace deqp
50{
51namespace gles31
52{
53namespace Functional
54{
55namespace
56{
57
58namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
59
60struct 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
73float& 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
82const float& BoundingBox::getComponentAccess (int ndx) const
83{
84 return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
85}
86
87struct ProjectedBBox
88{
89 tcu::Vec3 min;
90 tcu::Vec3 max;
91};
92
93static 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
106static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f)
107{
108 tcu::Vec4 vertexBox;
109 tcu::IVec4 pixelBox;
110
Jarkko Pöyry7a161d22015-05-19 20:44:07 -0700111 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();
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800115
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
124class InitialValueCase : public TestCase
125{
126public:
127 InitialValueCase (Context& context, const char* name, const char* desc);
128
129 void init (void);
130 IterateResult iterate (void);
131};
132
133InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
134 : TestCase(context, name, desc)
135{
136}
137
138void 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
144InitialValueCase::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
183class QueryCase : public TestCase
184{
185public:
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
198private:
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
207QueryCase::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
214void 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
220QueryCase::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 {
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -0800254 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1));
255 const BoundingBox& boundingBox = cases[caseNdx];
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800256
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -0600257 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());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800259
260 if (!verifyState(gl, boundingBox))
261 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
262 }
263
264 return STOP;
265}
266
267bool 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
453class BBoxRenderCase : public TestCase
454{
455public:
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
478protected:
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
563private:
564 std::vector<IterationConfig> m_iterationConfigs;
565 int m_iteration;
566};
567
568BBoxRenderCase::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
600BBoxRenderCase::~BBoxRenderCase (void)
601{
602 deinit();
603}
604
605void 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
700void BBoxRenderCase::deinit (void)
701{
702 m_program.clear();
703 m_vbo.clear();
704 m_fbo.clear();
705}
706
707BBoxRenderCase::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
727BBoxRenderCase::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
772tcu::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
Jarkko Pöyry7a161d22015-05-19 20:44:07 -0700778 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();
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800782
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
801void 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)
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -0600833 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());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800835 else
836 // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -0600837 gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f,
838 -1.7f, -1.7f, 0.0f, 1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800839
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
869const 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"
Jarkko Pöyry26e8fab2015-02-09 21:32:19 -0800907 " 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"
Jarkko Pöyry1f99d692014-11-17 14:21:54 -0800909 " return false;\n"
910 " return true;\n"
911 "}\n";
912 default:
913 DE_ASSERT(false);
914 return "";
915 }
916}
917
918class GridRenderCase : public BBoxRenderCase
919{
920public:
921 GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
922 ~GridRenderCase (void);
923
924private:
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
941GridRenderCase::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
947GridRenderCase::~GridRenderCase (void)
948{
949}
950
951void 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
963std::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
1010std::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
1034std::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
1115std::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
1152std::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
1208GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1209{
1210 return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
1211}
1212
1213void 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
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001233 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001234 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001235 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001236 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001237 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001238 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001239 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001240 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001241 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001242 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001243 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001244 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1245 }
1246}
1247
1248void 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
1274void 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 {
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05001344 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001345 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
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -08001363 {
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 }
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001372}
1373
1374class LineRenderCase : public BBoxRenderCase
1375{
1376public:
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
1385private:
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
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001401 enum ScanResult
1402 {
1403 SCANRESULT_NUM_LINES_OK_BIT = (1 << 0),
1404 SCANRESULT_LINE_WIDTH_OK_BIT = (1 << 1),
Pyry Haulosba455912016-11-15 10:28:48 -08001405 SCANRESULT_LINE_WIDTH_WARN_BIT = (1 << 2),
1406 SCANRESULT_LINE_WIDTH_ERR_BIT = (1 << 3),
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001407 };
1408
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001409 void init (void);
1410
1411 std::string genVertexSource (void) const;
1412 std::string genFragmentSource (void) const;
1413 std::string genTessellationControlSource (void) const;
1414 std::string genTessellationEvaluationSource (void) const;
1415 std::string genGeometrySource (void) const;
1416
1417 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
1418 void getAttributeData (std::vector<tcu::Vec4>& data) const;
1419 void renderTestPattern (const IterationConfig& config);
1420 void verifyRenderResult (const IterationConfig& config);
1421
1422 tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const;
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001423 deUint8 scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1424 deUint8 scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001425 bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const;
1426 tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const;
Pyry Haulosba455912016-11-15 10:28:48 -08001427 deUint8 checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001428 void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const;
1429
1430 const int m_patternSide;
1431 const bool m_isWideLineCase;
1432 const int m_wideLineLineWidth;
1433};
1434
1435LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
1436 : BBoxRenderCase (context, name, description, 12, flags)
1437 , m_patternSide (12)
1438 , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0)
1439 , m_wideLineLineWidth (5)
1440{
1441}
1442
1443LineRenderCase::~LineRenderCase (void)
1444{
1445}
1446
1447void LineRenderCase::init (void)
1448{
1449 m_testCtx.getLog()
1450 << tcu::TestLog::Message
1451 << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1452 << "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1453 << "Line segments are in random order, varying pattern size and location for each iteration.\n"
1454 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1455 << tcu::TestLog::EndMessage;
1456
1457 if (m_isWideLineCase)
1458 {
1459 glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1460 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1461
1462 if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1463 throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1464 }
1465
1466 BBoxRenderCase::init();
1467}
1468
1469std::string LineRenderCase::genVertexSource (void) const
1470{
1471 std::ostringstream buf;
1472
1473 buf << "#version 310 es\n"
1474 "in highp vec4 a_position;\n"
1475 "in highp vec4 a_color;\n"
1476 "out highp vec4 vtx_color;\n"
1477 "uniform highp vec4 u_posScale;\n"
1478 "uniform highp float u_lineWidth;\n"
1479 "\n";
1480 if (!m_hasTessellationStage)
1481 {
1482 DE_ASSERT(m_useGlobalState);
1483 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1484 "uniform highp vec4 u_primitiveBBoxMax;\n"
1485 "\n"
1486 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1487 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1488 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1489 "\n";
1490 }
1491 buf << "void main()\n"
1492 "{\n"
1493 " highp vec2 patternOffset = u_posScale.xy;\n"
1494 " highp vec2 patternScale = u_posScale.zw;\n"
1495 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1496 " vtx_color = a_color;\n";
1497 if (!m_hasTessellationStage)
1498 {
1499 DE_ASSERT(m_useGlobalState);
1500 buf << "\n"
1501 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n"
1502 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
1503 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
1504 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1505 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1506 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1507 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1508 }
1509 buf << "}\n";
1510
1511 return buf.str();
1512}
1513
1514std::string LineRenderCase::genFragmentSource (void) const
1515{
1516 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1517 std::ostringstream buf;
1518
1519 buf << "#version 310 es\n"
1520 "in mediump vec4 " << colorInputName << ";\n"
1521 "layout(location = 0) out mediump vec4 o_color;\n"
1522 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1523 << "\n"
1524 "void main()\n"
1525 "{\n"
1526 " mediump vec4 baseColor = " << colorInputName << ";\n"
1527 " mediump float redChannel;\n"
1528 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1529 " redChannel = 0.0;\n"
1530 " else\n"
1531 " redChannel = 1.0;\n"
1532 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1533 "}\n";
1534
1535 return buf.str();
1536}
1537
1538std::string LineRenderCase::genTessellationControlSource (void) const
1539{
1540 std::ostringstream buf;
1541
1542 buf << "#version 310 es\n"
1543 "#extension GL_EXT_tessellation_shader : require\n"
1544 "#extension GL_EXT_primitive_bounding_box : require\n"
1545 "layout(vertices=2) out;"
1546 "\n"
1547 "in highp vec4 vtx_color[];\n"
1548 "out highp vec4 tess_ctrl_color[];\n"
1549 "uniform highp float u_tessellationLevel;\n"
1550 "uniform highp vec4 u_posScale;\n"
1551 "uniform highp float u_lineWidth;\n";
1552
1553 if (!m_calcPerPrimitiveBBox)
1554 {
1555 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1556 "uniform highp vec4 u_primitiveBBoxMax;\n";
1557 }
1558
1559 buf << "patch out highp float vp_bbox_expansionSize;\n"
1560 "patch out highp vec3 vp_bbox_clipMin;\n"
1561 "patch out highp vec3 vp_bbox_clipMax;\n";
1562
1563 if (m_calcPerPrimitiveBBox)
1564 {
1565 buf << "\n";
1566 if (m_hasGeometryStage)
1567 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1568 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1569
1570 buf << "vec4 transformVec(in highp vec4 p)\n"
1571 "{\n"
1572 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1573 "}\n";
1574 }
1575
1576 buf << "\n"
1577 "void main()\n"
1578 "{\n"
1579 " // convert to nonsensical coordinates, just in case\n"
1580 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1581 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1582 "\n"
1583 " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1584 " gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1585
1586 if (m_calcPerPrimitiveBBox)
1587 {
1588 buf << "\n"
1589 " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1590 " transformVec(gl_in[1].gl_Position));\n"
1591 " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1592 " transformVec(gl_in[1].gl_Position));\n";
1593 }
1594 else
1595 {
1596 buf << "\n"
1597 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1598 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1599 }
1600
1601 if (!m_useGlobalState)
1602 buf << "\n"
1603 " gl_BoundingBoxEXT[0] = bboxMin;\n"
1604 " gl_BoundingBoxEXT[1] = bboxMax;\n";
1605
1606 buf << " vp_bbox_expansionSize = u_lineWidth;\n"
1607 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1608 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1609 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1610 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1611 "}\n";
1612
1613 return buf.str();
1614}
1615
1616std::string LineRenderCase::genTessellationEvaluationSource (void) const
1617{
1618 std::ostringstream buf;
1619
1620 buf << "#version 310 es\n"
1621 "#extension GL_EXT_tessellation_shader : require\n"
1622 "layout(isolines) in;"
1623 "\n"
1624 "in highp vec4 tess_ctrl_color[];\n"
1625 "out highp vec4 tess_color;\n"
1626 "uniform highp vec4 u_posScale;\n"
1627 "\n"
1628 "patch in highp float vp_bbox_expansionSize;\n"
1629 "patch in highp vec3 vp_bbox_clipMin;\n"
1630 "patch in highp vec3 vp_bbox_clipMax;\n"
1631 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1632 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1633 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1634 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1635 << "void main()\n"
1636 "{\n"
1637 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1638 " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1639 " tess_color = tess_ctrl_color[0];\n"
1640 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1641 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1642 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1643 "}\n";
1644
1645 return buf.str();
1646}
1647
1648std::string LineRenderCase::genGeometrySource (void) const
1649{
1650 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1651 std::ostringstream buf;
1652
1653 buf << "#version 310 es\n"
1654 "#extension GL_EXT_geometry_shader : require\n"
1655 "layout(lines) in;\n"
1656 "layout(max_vertices=5, line_strip) out;\n"
1657 "\n"
1658 "in highp vec4 " << colorInputName << "[2];\n"
1659 "out highp vec4 geo_color;\n"
1660 "uniform highp vec4 u_posScale;\n"
1661 "\n"
1662 "\n"
1663 "flat in highp float v_geo_bbox_expansionSize[2];\n"
1664 "flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1665 "flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1666 "flat out highp vec3 v_bbox_clipMin;\n"
1667 "flat out highp vec3 v_bbox_clipMax;\n"
1668 "flat out highp float v_bbox_expansionSize;\n"
1669 << genShaderFunction(SHADER_FUNC_MIRROR_X)
1670 << "\n"
1671 "void setVisualizationVaryings()\n"
1672 "{\n"
1673 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1674 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1675 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1676 "}\n"
1677 "void main()\n"
1678 "{\n"
1679 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1680 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1681 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1682 " highp vec4 lineColor = " << colorInputName << "[0];\n"
1683 "\n"
1684 " // output two separate primitives, just in case\n"
1685 " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1686 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1687 " EndPrimitive();\n"
1688 "\n"
1689 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1690 " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1691 " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1692 " EndPrimitive();\n"
1693 "}\n";
1694
1695 return buf.str();
1696}
1697
1698LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1699{
1700 const int numMaxAttempts = 128;
1701
1702 // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1703 for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1704 {
1705 const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1706
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001707 if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth &&
1708 (float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth)
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001709 {
1710 return config;
1711 }
1712 }
1713
1714 DE_ASSERT(false);
1715 return IterationConfig();
1716}
1717
1718void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1719{
1720 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
1721 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1722 std::vector<int> cellOrder (m_patternSide * m_patternSide * 2);
1723 de::Random rnd (0xDE12345);
1724
1725 // generate crosshatch pattern with segments in random order
1726 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1727 cellOrder[ndx] = ndx;
1728 rnd.shuffle(cellOrder.begin(), cellOrder.end());
1729
1730 data.resize(cellOrder.size() * 4);
1731 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1732 {
1733 const int segmentID = cellOrder[ndx];
1734 const int direction = segmentID & 0x01;
1735 const int majorCoord = (segmentID >> 1) / m_patternSide;
1736 const int minorCoord = (segmentID >> 1) % m_patternSide;
1737
1738 if (direction)
1739 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001740 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001741 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001742 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001743 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1744 }
1745 else
1746 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001747 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001748 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001749 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001750 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1751 }
1752 }
1753}
1754
1755void LineRenderCase::renderTestPattern (const IterationConfig& config)
1756{
1757 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1758
1759 setupRender(config);
1760
1761 if (m_hasTessellationStage)
1762 {
1763 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1764 const glw::GLfloat tessLevel = 2.8f; // will be rounded up
1765
1766 TCU_CHECK(tessLevelPos != -1);
1767
1768 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1769
1770 gl.uniform1f(tessLevelPos, tessLevel);
1771 gl.patchParameteri(GL_PATCH_VERTICES, 2);
1772 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1773 }
1774
1775 if (m_isWideLineCase)
1776 gl.lineWidth((float)m_wideLineLineWidth);
1777
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001778 gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001779
1780 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1781
1782 gl.enable(GL_BLEND);
1783 gl.blendFunc(GL_ONE, GL_ONE);
1784 gl.blendEquation(GL_FUNC_ADD);
1785
1786 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
1787 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1788}
1789
1790void LineRenderCase::verifyRenderResult (const IterationConfig& config)
1791{
Pyry Haulosba455912016-11-15 10:28:48 -08001792 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1793 const bool isMsaa = m_context.getRenderTarget().getNumSamples() > 1;
1794 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
1795 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
1796 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
1797 const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1798 const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL);
1799 const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL);
1800 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
1801 de::max(viewportBBoxArea.y(), 0),
1802 de::min(viewportBBoxArea.z(), config.viewportSize.x()),
1803 de::min(viewportBBoxArea.w(), config.viewportSize.y()));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001804
Pyry Haulosba455912016-11-15 10:28:48 -08001805 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
1806 bool anyError = false;
1807 bool msaaRelaxationRequired = false;
1808 bool hwIssueRelaxationRequired = false;
1809 int messageLimitCounter = 8;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001810
1811 if (!m_calcPerPrimitiveBBox)
1812 m_testCtx.getLog()
1813 << tcu::TestLog::Message
1814 << "Projected bounding box: (clip space)\n"
1815 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1816 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1817 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1818 << "In viewport coordinates:\n"
1819 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1820 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1821 << "Verifying render results within the bounding box:\n"
1822 << tcu::TestLog::EndMessage;
1823 else
1824 m_testCtx.getLog()
1825 << tcu::TestLog::Message
1826 << "Verifying render result:"
1827 << tcu::TestLog::EndMessage;
1828
1829 m_testCtx.getLog()
1830 << tcu::TestLog::Message
1831 << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
1832 << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n"
1833 << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n"
1834 << tcu::TestLog::EndMessage;
1835
1836 if (m_fbo)
1837 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1838 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1839
1840 // scan rows
1841 for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001842 {
Jarkko Pöyry25ed53b2015-05-05 10:53:21 -07001843 const deUint8 result = scanRow(viewportSurface.getAccess(),
1844 y,
1845 verificationArea.x(),
1846 verificationArea.z(),
1847 expectedVerticalLines,
1848 messageLimitCounter);
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001849
1850 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1851 anyError = true;
1852 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1853 {
1854 if (m_isWideLineCase && isMsaa)
1855 {
1856 // multisampled wide lines might not be supported
1857 msaaRelaxationRequired = true;
1858 }
Pyry Haulosba455912016-11-15 10:28:48 -08001859 else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 &&
1860 (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
1861 {
1862 hwIssueRelaxationRequired = true;
1863 }
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001864 else
1865 anyError = true;
1866 }
1867 }
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001868
1869 // scan columns
1870 for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001871 {
Jarkko Pöyry25ed53b2015-05-05 10:53:21 -07001872 const deUint8 result = scanColumn(viewportSurface.getAccess(),
1873 x,
1874 verificationArea.y(),
1875 verificationArea.w(),
1876 expectedHorizontalLines,
1877 messageLimitCounter);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001878
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001879 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
1880 anyError = true;
1881 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
1882 {
1883 if (m_isWideLineCase && isMsaa)
1884 {
1885 // multisampled wide lines might not be supported
1886 msaaRelaxationRequired = true;
1887 }
Pyry Haulosba455912016-11-15 10:28:48 -08001888 else if ((result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
1889 {
1890 hwIssueRelaxationRequired = true;
1891 }
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001892 else
1893 anyError = true;
1894 }
1895 }
1896
Pyry Haulosba455912016-11-15 10:28:48 -08001897 if (anyError || msaaRelaxationRequired || hwIssueRelaxationRequired)
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001898 {
1899 if (messageLimitCounter < 0)
1900 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage;
1901
1902 m_testCtx.getLog()
1903 << tcu::TestLog::Message
1904 << "Image verification failed."
1905 << tcu::TestLog::EndMessage
1906 << tcu::TestLog::ImageSet("Images", "Image verification")
1907 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1908 << tcu::TestLog::EndImageSet;
1909
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001910 if (anyError)
1911 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
Pyry Haulosba455912016-11-15 10:28:48 -08001912 else if (hwIssueRelaxationRequired)
1913 {
1914 // Line width hw issue
1915 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Line width verification failed");
1916 }
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001917 else
1918 {
1919 // MSAA wide lines are optional
1920 m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed");
1921 }
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001922 }
1923 else
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -08001924 {
1925 m_testCtx.getLog()
1926 << tcu::TestLog::Message
1927 << "Result image ok."
1928 << tcu::TestLog::EndMessage
1929 << tcu::TestLog::ImageSet("Images", "Image verification")
1930 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1931 << tcu::TestLog::EndImageSet;
1932 }
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001933}
1934
1935tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const
1936{
1937 // pattern is not symmetric due to mirroring
1938 const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0));
1939 const int patternEndNdx = patternStartNdx + m_patternSide;
1940
1941 int numLinesMin = 0;
1942 int numLinesMax = 0;
1943
1944 for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
1945 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001946 const float linePos = (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
1947 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001948
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001949 if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f &&
1950 linePos * (float)viewportArea < (float)queryAreaEnd - 1.0f)
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001951 {
1952 // line center is within the area
1953 ++numLinesMin;
1954 ++numLinesMax;
1955 }
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07001956 else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f &&
1957 linePos * (float)viewportArea < (float)queryAreaEnd + lineWidth*0.5f + 1.0f)
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001958 {
1959 // line could leak into area
1960 ++numLinesMax;
1961 }
1962 }
1963
1964 return tcu::IVec2(numLinesMin, numLinesMax);
1965}
1966
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001967deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001968{
Pyry Haulosba455912016-11-15 10:28:48 -08001969 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
1970 const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
1971 deUint8 result = 0;
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001972
Pyry Haulosba455912016-11-15 10:28:48 -08001973 if (numLinesOk)
1974 result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT;
1975
1976 if (lineWidthRes == 0)
1977 result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT;
1978 else
1979 result |= lineWidthRes;
1980
1981 return result;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001982}
1983
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001984deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001985{
Pyry Haulosba455912016-11-15 10:28:48 -08001986 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
1987 const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter);
1988 deUint8 result = 0;
Jarkko Pöyryf0ca13a2015-04-27 16:20:50 -07001989
Pyry Haulosba455912016-11-15 10:28:48 -08001990 if (numLinesOk)
1991 result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT;
1992
1993 if (lineWidthRes == 0)
1994 result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT;
1995 else
1996 result |= lineWidthRes;
1997
1998 return result;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08001999}
2000
2001bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const
2002{
2003 // Num maxima == num lines
2004 const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
2005 const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx);
2006 const int numMaxima = numMinimaMaxima.y();
2007
2008 // In valid range
2009 if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
2010 return true;
2011
2012 if (--messageLimitCounter < 0)
2013 return false;
2014
2015 if (area.z() == 1)
2016 m_testCtx.getLog()
2017 << tcu::TestLog::Message
2018 << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n"
2019 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
2020 << tcu::TestLog::EndMessage;
2021 else
2022 m_testCtx.getLog()
2023 << tcu::TestLog::Message
2024 << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n"
2025 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
2026 << tcu::TestLog::EndMessage;
2027
2028 return false;
2029}
2030
2031tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const
2032{
2033 DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
2034
2035 int previousValue = -1;
2036 int previousSign = 0;
2037 int numMinima = 0;
2038 int numMaxima = 0;
2039
2040 for (int y = 0; y < access.getHeight(); ++y)
2041 for (int x = 0; x < access.getWidth(); ++x)
2042 {
2043 const int componentValue = access.getPixelInt(x, y)[componentNdx];
2044
2045 if (previousValue != -1)
2046 {
2047 const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0);
2048
2049 // local minima/maxima in sign changes (zero signless)
2050 if (sign != 0 && sign == -previousSign)
2051 {
2052 previousSign = sign;
2053
2054 if (sign > 0)
2055 ++numMinima;
2056 else
2057 ++numMaxima;
2058 }
2059 else if (sign != 0 && previousSign == 0)
2060 {
2061 previousSign = sign;
2062
2063 // local extreme at the start boundary
2064 if (sign > 0)
2065 ++numMinima;
2066 else
2067 ++numMaxima;
2068 }
2069 }
2070
2071 previousValue = componentValue;
2072 }
2073
2074 // local extreme at the end boundary
2075 if (previousSign > 0)
2076 ++numMaxima;
2077 else if (previousSign < 0)
2078 ++numMinima;
2079 else
2080 {
2081 ++numMaxima;
2082 ++numMinima;
2083 }
2084
2085 return tcu::IVec2(numMinima, numMaxima);
2086}
2087
Pyry Haulosba455912016-11-15 10:28:48 -08002088deUint8 LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002089{
Pyry Haulosba455912016-11-15 10:28:48 -08002090 const bool multisample = m_context.getRenderTarget().getNumSamples() > 1;
2091 const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2092 const tcu::IVec2 lineWidthRange = (multisample)
2093 ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel
2094 : (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2095 const tcu::IVec2 relaxedLineWidthRange = (tcu::IVec2(lineRenderWidth-1, lineRenderWidth+1));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002096
Pyry Haulosba455912016-11-15 10:28:48 -08002097 int lineWidth = 0;
2098 bool bboxLimitedLine = false;
2099 deUint8 errorMask = 0;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002100
Pyry Haulosba455912016-11-15 10:28:48 -08002101 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002102
2103 // fragments before begin?
2104 if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2105 {
2106 bboxLimitedLine = true;
2107
2108 for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2109 {
2110 if (cursor.x() < 0 || cursor.y() < 0)
2111 {
2112 break;
2113 }
2114 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2115 {
2116 ++lineWidth;
2117 }
2118 else
2119 break;
2120 }
2121 }
2122
2123 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2124 {
2125 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2126
2127 if (hit)
2128 ++lineWidth;
2129 else if (lineWidth)
2130 {
2131 // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2132 const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2133
2134 if (incorrectLineWidth)
2135 {
Pyry Haulosba455912016-11-15 10:28:48 -08002136 const bool incorrectRelaxedLineWidth = (lineWidth < relaxedLineWidthRange.x() && !bboxLimitedLine) || (lineWidth > relaxedLineWidthRange.y());
2137
2138 if (incorrectRelaxedLineWidth)
2139 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2140 else
2141 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2142
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002143 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2144 }
2145
2146 lineWidth = 0;
2147 bboxLimitedLine = false;
2148 }
2149 }
2150
2151 // fragments after end?
2152 if (lineWidth)
2153 {
2154 for (tcu::IVec2 cursor = end;; cursor += advance)
2155 {
2156 if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2157 {
2158 if (lineWidth > lineWidthRange.y())
2159 {
Pyry Haulosba455912016-11-15 10:28:48 -08002160 if (lineWidth > relaxedLineWidthRange.y())
2161 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2162 else
2163 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2164
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002165 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2166 }
2167
2168 break;
2169 }
2170 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2171 {
2172 ++lineWidth;
2173 }
2174 else if (lineWidth)
2175 {
2176 // only check that line width is not larger than expected. Line width may be smaller
2177 // since the scanning 'cursor' is now outside the bounding box.
2178 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2179
2180 if (incorrectLineWidth)
2181 {
Pyry Haulosba455912016-11-15 10:28:48 -08002182 const bool incorrectRelaxedLineWidth = (lineWidth > relaxedLineWidthRange.y());
2183
2184 if (incorrectRelaxedLineWidth)
2185 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2186 else
2187 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2188
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002189 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2190 }
2191
2192 lineWidth = 0;
2193 }
2194 }
2195 }
2196
Pyry Haulosba455912016-11-15 10:28:48 -08002197 return errorMask;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002198}
2199
2200void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const
2201{
2202 if (--messageLimitCounter < 0)
2203 return;
2204
2205 m_testCtx.getLog()
2206 << tcu::TestLog::Message
2207 << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2208 << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth
2209 << tcu::TestLog::EndMessage;
2210}
2211
2212class PointRenderCase : public BBoxRenderCase
2213{
2214public:
2215 enum
2216 {
2217 POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points
2218 };
2219 struct GeneratedPoint
2220 {
2221 tcu::Vec2 center;
2222 int size;
2223 bool even;
2224 };
2225
2226 PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
2227 ~PointRenderCase (void);
2228
2229private:
2230 enum ResultPointType
2231 {
2232 POINT_FULL = 0,
2233 POINT_PARTIAL
2234 };
2235
2236 void init (void);
2237 void deinit (void);
2238
2239 std::string genVertexSource (void) const;
2240 std::string genFragmentSource (void) const;
2241 std::string genTessellationControlSource (void) const;
2242 std::string genTessellationEvaluationSource (void) const;
2243 std::string genGeometrySource (void) const;
2244
2245 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
2246 void generateAttributeData (void);
2247 void getAttributeData (std::vector<tcu::Vec4>& data) const;
2248 void renderTestPattern (const IterationConfig& config);
2249 void verifyRenderResult (const IterationConfig& config);
2250
2251 void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const;
2252 bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2253 bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2254 bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter);
2255 bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter);
2256 tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const;
2257
2258 const int m_numStripes;
2259 const bool m_isWidePointCase;
2260 std::vector<tcu::Vec4> m_attribData;
2261};
2262
2263PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
2264 : BBoxRenderCase (context, name, description, 12, flags)
2265 , m_numStripes (4)
2266 , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0)
2267{
2268}
2269
2270PointRenderCase::~PointRenderCase (void)
2271{
2272}
2273
2274void PointRenderCase::init (void)
2275{
2276 if (m_isWidePointCase)
2277 {
2278 // extensions
2279 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2280 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2281 if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2282 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2283
2284 // point size range
2285 {
2286 glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2287 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2288
2289 if (pointSizeRange[1] < 5.0f)
2290 throw tcu::NotSupportedError("Test requires point size 5.0");
2291 }
2292 }
2293
2294 m_testCtx.getLog()
2295 << tcu::TestLog::Message
2296 << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2297 << "Half of the points are green, half blue. Using additive blending.\n"
2298 << "Points are in random order, varying pattern size and location for each iteration.\n"
2299 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2300 << tcu::TestLog::EndMessage;
2301
2302 generateAttributeData();
2303
2304 BBoxRenderCase::init();
2305}
2306
2307void PointRenderCase::deinit (void)
2308{
2309 // clear data
2310 m_attribData = std::vector<tcu::Vec4>();
2311
2312 // deinit parent
2313 BBoxRenderCase::deinit();
2314}
2315
2316std::string PointRenderCase::genVertexSource (void) const
2317{
2318 std::ostringstream buf;
2319
2320 buf << "#version 310 es\n"
2321 "in highp vec4 a_position;\n"
2322 "in highp vec4 a_color;\n"
2323 "out highp vec4 vtx_color;\n"
2324 "uniform highp vec4 u_posScale;\n"
2325 "\n";
2326 if (!m_hasTessellationStage)
2327 {
2328 DE_ASSERT(m_useGlobalState);
2329 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2330 "uniform highp vec4 u_primitiveBBoxMax;\n"
2331 "\n"
2332 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
2333 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2334 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2335 "\n";
2336 }
2337
2338 buf << "void main()\n"
2339 "{\n"
2340 " highp vec2 patternOffset = u_posScale.xy;\n"
2341 " highp vec2 patternScale = u_posScale.zw;\n"
2342 " highp float pointSize = "
2343 << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2344 << ";\n"
2345 << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2346 " gl_PointSize = pointSize;\n"
2347 " vtx_color = a_color;\n";
2348
2349 if (!m_hasTessellationStage)
2350 {
2351 DE_ASSERT(m_useGlobalState);
2352 buf << "\n"
2353 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n"
2354 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
2355 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
2356 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
2357 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
2358 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
2359 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
2360 }
2361
2362 buf << "}\n";
2363 return buf.str();
2364}
2365
2366std::string PointRenderCase::genFragmentSource (void) const
2367{
2368 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2369 std::ostringstream buf;
2370
2371 buf << "#version 310 es\n"
2372 "in mediump vec4 " << colorInputName << ";\n"
2373 "layout(location = 0) out mediump vec4 o_color;\n"
2374 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2375 << "\n"
2376 "void main()\n"
2377 "{\n"
2378 " mediump vec4 baseColor = " << colorInputName << ";\n"
2379 " mediump float redChannel;\n"
2380 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2381 " redChannel = 0.0;\n"
2382 " else\n"
2383 " redChannel = 1.0;\n"
2384 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2385 "}\n";
2386
2387 return buf.str();
2388}
2389
2390std::string PointRenderCase::genTessellationControlSource (void) const
2391{
2392 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2393 std::ostringstream buf;
2394
2395 buf << "#version 310 es\n"
2396 "#extension GL_EXT_tessellation_shader : require\n"
2397 "#extension GL_EXT_primitive_bounding_box : require\n"
2398 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2399 << "layout(vertices=1) out;"
2400 "\n"
2401 "in highp vec4 vtx_color[];\n"
2402 "out highp vec4 tess_ctrl_color[];\n"
2403 "uniform highp float u_tessellationLevel;\n"
2404 "uniform highp vec4 u_posScale;\n";
2405
2406 if (!m_calcPerPrimitiveBBox)
2407 {
2408 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2409 "uniform highp vec4 u_primitiveBBoxMax;\n";
2410 }
2411
2412 buf << "patch out highp vec3 vp_bbox_clipMin;\n"
2413 "patch out highp vec3 vp_bbox_clipMax;\n";
2414
2415 if (m_calcPerPrimitiveBBox)
2416 {
2417 buf << "\n";
2418 if (m_hasGeometryStage)
2419 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2420 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2421
2422 buf << "vec4 transformVec(in highp vec4 p)\n"
2423 "{\n"
2424 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
2425 "}\n";
2426 }
2427
2428 buf << "\n"
2429 "void main()\n"
2430 "{\n"
2431 " // convert to nonsensical coordinates, just in case\n"
2432 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2433 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2434 "\n"
2435 " gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2436 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2437 " gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2438 " gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2439 " gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2440 " gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2441
2442 if (m_calcPerPrimitiveBBox)
2443 {
2444 buf << "\n";
2445
2446 if (m_hasGeometryStage)
2447 buf << " const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2448 " const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2449 else
2450 buf << " const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2451 " const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2452
2453 buf << " highp vec2 patternScale = u_posScale.zw;\n"
2454 " highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n"
2455 " highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n";
2456 }
2457 else
2458 {
2459 buf << "\n"
2460 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2461 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2462 }
2463 if (!m_useGlobalState)
2464 buf << "\n"
2465 " gl_BoundingBoxEXT[0] = bboxMin;\n"
2466 " gl_BoundingBoxEXT[1] = bboxMax;\n";
2467
2468 buf << " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2469 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2470 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2471 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2472 "}\n";
2473
2474 return buf.str();
2475}
2476
2477std::string PointRenderCase::genTessellationEvaluationSource (void) const
2478{
2479 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2480 std::ostringstream buf;
2481
2482 buf << "#version 310 es\n"
2483 "#extension GL_EXT_tessellation_shader : require\n"
2484 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2485 << "layout(quads, point_mode) in;"
2486 "\n"
2487 "in highp vec4 tess_ctrl_color[];\n"
2488 "out highp vec4 tess_color;\n"
2489 "uniform highp vec4 u_posScale;\n"
2490 "\n"
2491 "patch in highp vec3 vp_bbox_clipMin;\n"
2492 "patch in highp vec3 vp_bbox_clipMax;\n"
2493 << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : (""))
2494 << "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2495 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2496 "\n"
2497 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
2498 << "void main()\n"
2499 "{\n"
2500 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2501 " highp vec2 patternScale = u_posScale.zw;\n"
2502 " highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2503 " highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n"
2504 " gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2505
2506 if (tessellationWidePoints)
2507 buf << " gl_PointSize = pointSize;\n";
2508
2509 buf << " tess_color = tess_ctrl_color[0];\n"
2510 << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : (""))
2511 << " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
2512 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
2513 "}\n";
2514
2515 return buf.str();
2516}
2517
2518std::string PointRenderCase::genGeometrySource (void) const
2519{
2520 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2521 std::ostringstream buf;
2522
2523 buf << "#version 310 es\n"
2524 "#extension GL_EXT_geometry_shader : require\n"
2525 << ((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : (""))
2526 << "layout(points) in;\n"
2527 "layout(max_vertices=3, points) out;\n"
2528 "\n"
2529 "in highp vec4 " << colorInputName << "[1];\n"
2530 "out highp vec4 geo_color;\n"
2531 "uniform highp vec4 u_posScale;\n"
2532 "\n"
2533 "flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2534 "flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2535 "flat out highp vec3 v_bbox_clipMin;\n"
2536 "flat out highp vec3 v_bbox_clipMax;\n"
2537 "flat out highp float v_bbox_expansionSize;\n"
2538 "\n"
2539 << genShaderFunction(SHADER_FUNC_MIRROR_X)
2540 << "\n"
2541 "void main()\n"
2542 "{\n"
2543 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2544 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2545 " highp vec4 pointColor = " << colorInputName << "[0];\n"
2546 " highp vec2 patternScale = u_posScale.zw;\n"
2547 " highp float pointSize = "
2548 << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2549 << ";\n"
2550 "\n"
2551 " highp vec4 offsets[3] =\n"
Jarkko Pöyrycdde9ab2015-03-06 13:50:37 -08002552 " vec4[3](\n"
2553 " vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2554 " vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2555 " vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2556 " );\n"
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002557 " for (int ndx = 0; ndx < 3; ++ndx)\n"
2558 " {\n"
2559 " gl_Position = p0 + offsets[ndx];\n";
2560
2561 if (m_isWidePointCase)
2562 buf << " gl_PointSize = pointSize;\n";
2563
2564 buf << " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2565 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2566 " v_bbox_expansionSize = pointSize;\n"
2567 " geo_color = pointColor;\n"
2568 " EmitVertex();\n"
2569 " }\n"
2570 "}\n";
2571
2572 return buf.str();
2573}
2574
2575PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
2576{
2577 IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
2578
2579 // equal or larger -> expand according to shader expansion
2580 if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2581 {
2582 const tcu::Vec2 patternScale = config.patternSize;
2583
2584 if (m_hasTessellationStage)
2585 {
2586 config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2587 config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2588 }
2589 if (m_hasGeometryStage)
2590 {
2591 config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2592 config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2593 }
2594 }
2595
2596 return config;
2597}
2598
2599void PointRenderCase::generateAttributeData (void)
2600{
2601 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
2602 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
2603 std::vector<int> cellOrder (m_numStripes * m_numStripes * 2);
2604 de::Random rnd (0xDE22446);
2605
2606 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2607 cellOrder[ndx] = ndx;
2608 rnd.shuffle(cellOrder.begin(), cellOrder.end());
2609
2610 m_attribData.resize(cellOrder.size() * 2);
2611 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2612 {
2613 const int pointID = cellOrder[ndx];
2614 const int direction = pointID & 0x01;
2615 const int majorCoord = (pointID >> 1) / m_numStripes;
2616 const int minorCoord = (pointID >> 1) % m_numStripes;
2617
2618 if (direction)
2619 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07002620 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002621 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
2622 }
2623 else
2624 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07002625 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);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002626 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
2627 }
2628 }
2629}
2630
2631void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
2632{
2633 data = m_attribData;
2634}
2635
2636void PointRenderCase::renderTestPattern (const IterationConfig& config)
2637{
2638 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2639
2640 setupRender(config);
2641
2642 if (m_hasTessellationStage)
2643 {
2644 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
2645 const glw::GLfloat tessLevel = 0.8f; // will be rounded up
2646
2647 TCU_CHECK(tessLevelPos != -1);
2648
2649 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
2650
2651 gl.uniform1f(tessLevelPos, tessLevel);
2652 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2653 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
2654 }
2655
2656 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
2657
2658 gl.enable(GL_BLEND);
2659 gl.blendFunc(GL_ONE, GL_ONE);
2660 gl.blendEquation(GL_FUNC_ADD);
2661
2662 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
2663 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2664}
2665
2666void PointRenderCase::verifyRenderResult (const IterationConfig& config)
2667{
2668 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2669 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
2670 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
2671
2672 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
2673 int logFloodCounter = 8;
2674 bool anyError;
2675 std::vector<GeneratedPoint> refPoints;
2676
2677 if (!m_calcPerPrimitiveBBox)
2678 m_testCtx.getLog()
2679 << tcu::TestLog::Message
2680 << "Projected bounding box: (clip space)\n"
2681 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2682 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2683 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2684 << "In viewport coordinates:\n"
2685 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2686 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2687 << "Verifying render results within the bounding box:\n"
2688 << tcu::TestLog::EndMessage;
2689 else
2690 m_testCtx.getLog()
2691 << tcu::TestLog::Message
2692 << "Verifying render result:"
2693 << tcu::TestLog::EndMessage;
2694
2695 if (m_fbo)
2696 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2697 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
2698
2699 genReferencePointData(config, refPoints);
2700
2701 if (m_isWidePointCase)
2702 anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2703 else
2704 anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2705
2706 if (anyError)
2707 {
2708 if (logFloodCounter < 0)
2709 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage;
2710
2711 m_testCtx.getLog()
2712 << tcu::TestLog::Message
2713 << "Image verification failed."
2714 << tcu::TestLog::EndMessage
2715 << tcu::TestLog::ImageSet("Images", "Image verification")
2716 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2717 << tcu::TestLog::EndImageSet;
2718
2719 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2720 }
2721 else
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -08002722 {
2723 m_testCtx.getLog()
2724 << tcu::TestLog::Message
2725 << "Result image ok."
2726 << tcu::TestLog::EndMessage
2727 << tcu::TestLog::ImageSet("Images", "Image verification")
2728 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2729 << tcu::TestLog::EndImageSet;
2730 }
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002731}
2732
2733struct PointSorter
2734{
2735 bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const
2736 {
2737 if (a.center.y() < b.center.y())
2738 return true;
2739 else if (a.center.y() > b.center.y())
2740 return false;
2741 else
2742 return (a.center.x() < b.center.x());
2743 }
2744};
2745
2746void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const
2747{
2748 std::vector<GeneratedPoint> currentPoints;
2749
2750 // vertex shader
2751 currentPoints.resize(m_attribData.size() / 2);
2752 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2753 {
2754 currentPoints[ndx].center = m_attribData[ndx*2].swizzle(0, 1);
2755 currentPoints[ndx].even = (m_attribData[ndx*2 + 1].y() == 1.0f); // is green
Pyry Haulos7d6ad9e2015-02-03 10:41:01 -08002756 currentPoints[ndx].size = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002757 }
2758
2759 // tessellation
2760 if (m_hasTessellationStage)
2761 {
2762 std::vector<GeneratedPoint> tessellatedPoints;
2763
2764 tessellatedPoints.resize(currentPoints.size() * 4);
2765 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2766 {
2767 const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
2768
2769 tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f);
2770 tessellatedPoints[4 * ndx + 0].size = currentPoints[ndx].size;
2771 tessellatedPoints[4 * ndx + 0].even = currentPoints[ndx].even;
2772
2773 tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2( 0.07f, -0.07f);
2774 tessellatedPoints[4 * ndx + 1].size = currentPoints[ndx].size;
2775 tessellatedPoints[4 * ndx + 1].even = currentPoints[ndx].even;
2776
2777 tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2( 0.07f, 0.07f);
2778 tessellatedPoints[4 * ndx + 2].size = currentPoints[ndx].size;
2779 tessellatedPoints[4 * ndx + 2].even = currentPoints[ndx].even;
2780
2781 tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f);
2782 tessellatedPoints[4 * ndx + 3].size = currentPoints[ndx].size;
2783 tessellatedPoints[4 * ndx + 3].even = currentPoints[ndx].even;
2784 }
2785
2786 currentPoints.swap(tessellatedPoints);
2787 }
2788
2789 // geometry
2790 if (m_hasGeometryStage)
2791 {
2792 std::vector<GeneratedPoint> geometryShadedPoints;
2793
2794 geometryShadedPoints.resize(currentPoints.size() * 3);
2795 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2796 {
2797 const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
2798
2799 geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2( 0.05f, 0.03f);
2800 geometryShadedPoints[3 * ndx + 0].size = currentPoints[ndx].size;
2801 geometryShadedPoints[3 * ndx + 0].even = currentPoints[ndx].even;
2802
2803 geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f);
2804 geometryShadedPoints[3 * ndx + 1].size = currentPoints[ndx].size;
2805 geometryShadedPoints[3 * ndx + 1].even = currentPoints[ndx].even;
2806
2807 geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f);
2808 geometryShadedPoints[3 * ndx + 2].size = currentPoints[ndx].size;
2809 geometryShadedPoints[3 * ndx + 2].even = currentPoints[ndx].even;
2810 }
2811
2812 currentPoints.swap(geometryShadedPoints);
2813 }
2814
2815 // sort from left to right, top to bottom
2816 std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
2817
2818 // map to pattern space
2819 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2820 currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
2821
2822 currentPoints.swap(data);
2823}
2824
2825bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2826{
2827 bool anyError = false;
2828
2829 // check that there is something near each sample
2830 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2831 {
2832 const float epsilon = 1.0e-6f;
2833 const GeneratedPoint& refPoint = refPoints[pointNdx];
2834
2835 // skip points not in the the bbox, treat boundary as "in"
2836 if (refPoint.center.x() < bbox.min.x() - epsilon ||
2837 refPoint.center.y() < bbox.min.y() - epsilon ||
2838 refPoint.center.x() > bbox.max.x() + epsilon ||
2839 refPoint.center.y() > bbox.max.y() + epsilon)
2840 continue;
2841 else
2842 {
2843 // transform to viewport coords
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07002844 const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
2845 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002846
2847 // find rasterized point in the result
2848 if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1)
2849 {
2850 // viewport boundary, assume point is fine
2851 }
2852 else
2853 {
2854 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel
2855 bool foundResult = false;
2856
2857 // check neighborhood
2858 for (int dy = -1; dy < 2 && !foundResult; ++dy)
2859 for (int dx = -1; dx < 2 && !foundResult; ++dx)
2860 {
2861 const tcu::IVec2 testPos (pixelCenter.x() + dx, pixelCenter.y() + dy);
2862 const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y());
2863
2864 if (color.toIVec()[componentNdx] > 0)
2865 foundResult = true;
2866 }
2867
2868 if (!foundResult)
2869 {
2870 anyError = true;
2871
2872 if (--logFloodCounter >= 0)
2873 {
2874 m_testCtx.getLog()
2875 << tcu::TestLog::Message
2876 << "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2877 << tcu::TestLog::EndMessage;
2878 }
2879 }
2880 }
2881 }
2882 }
2883
2884 return anyError;
2885}
2886
2887bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2888{
2889 bool anyError = false;
2890
2891 // check that there is something near each sample
2892 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2893 {
2894 const GeneratedPoint& refPoint = refPoints[pointNdx];
2895
2896 if (refPoint.center.x() >= bbox.min.x() &&
2897 refPoint.center.y() >= bbox.min.y() &&
2898 refPoint.center.x() <= bbox.max.x() &&
2899 refPoint.center.y() <= bbox.max.y())
2900 {
2901 // point fully in the bounding box
2902 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
2903 }
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07002904 else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f &&
2905 refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f &&
2906 refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f &&
2907 refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f)
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002908 {
2909 // point leaks into bounding box
2910 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
2911 }
2912 }
2913
2914 return anyError;
2915}
2916
2917bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter)
2918{
2919 const int componentNdx = (refPoint.even) ? (1) : (2);
2920 const int halfPointSizeCeil = (refPoint.size + 1) / 2;
2921 const int halfPointSizeFloor = (refPoint.size + 1) / 2;
Pyry Haulos7d6ad9e2015-02-03 10:41:01 -08002922 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002923 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
2924 de::max(viewportBBoxArea.y(), 0),
2925 de::min(viewportBBoxArea.z(), viewport.getWidth()),
2926 de::min(viewportBBoxArea.w(), viewport.getHeight()));
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07002927 const tcu::IVec2 pointPos = tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * (float)viewport.getWidth()),
2928 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * (float)viewport.getHeight()));
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08002929
2930 // find any fragment within the point that is inside the bbox, start search at the center
2931
2932 if (pointPos.x() >= verificationArea.x() &&
2933 pointPos.y() >= verificationArea.y() &&
2934 pointPos.x() < verificationArea.z() &&
2935 pointPos.y() < verificationArea.w())
2936 {
2937 if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
2938 return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2939 }
2940
2941 for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
2942 for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
2943 {
2944 const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
2945
2946 if (dx == 0 && dy == 0)
2947 continue;
2948
2949 if (testPos.x() >= verificationArea.x() &&
2950 testPos.y() >= verificationArea.y() &&
2951 testPos.x() < verificationArea.z() &&
2952 testPos.y() < verificationArea.w())
2953 {
2954 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
2955 return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2956 }
2957 }
2958
2959 // could not find point, this is only ok near boundaries
2960 if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 ||
2961 pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 ||
2962 pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
2963 pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
2964 return true;
2965
2966 if (--logFloodCounter >= 0)
2967 {
2968 m_testCtx.getLog()
2969 << tcu::TestLog::Message
2970 << "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2971 << tcu::TestLog::EndMessage;
2972 }
2973
2974 return false;
2975}
2976
2977bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter)
2978{
2979 const int expectedPointSize = refPoint.size;
2980 bool viewportClippedTop = false;
2981 bool viewportClippedBottom = false;
2982 bool primitiveClippedTop = false;
2983 bool primitiveClippedBottom = false;
2984 std::vector<tcu::IVec2> widthsUpwards;
2985 std::vector<tcu::IVec2> widthsDownwards;
2986 std::vector<tcu::IVec2> widths;
2987
2988 // search upwards
2989 for (int y = pointPos.y();; --y)
2990 {
2991 if (y < bbox.y() || y < 0)
2992 {
2993 if (y < bbox.y())
2994 primitiveClippedTop = true;
2995 if (y < 0)
2996 viewportClippedTop = true;
2997 break;
2998 }
2999 else if (pointPos.y() - y > expectedPointSize)
3000 {
3001 // no need to go further than point height
3002 break;
3003 }
3004 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3005 {
3006 break;
3007 }
3008 else
3009 {
3010 widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3011 }
3012 }
3013
3014 // top is clipped
3015 if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
3016 {
3017 const tcu::IVec2& range = widthsUpwards.back();
3018 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
3019 const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
3020
3021 if (squareFits || widthClipped)
3022 return true;
3023 }
3024
3025 // and downwards
3026 for (int y = pointPos.y()+1;; ++y)
3027 {
3028 if (y >= bbox.w() || y >= viewport.getHeight())
3029 {
3030 if (y >= bbox.w())
3031 primitiveClippedBottom = true;
3032 if (y >= viewport.getHeight())
3033 viewportClippedBottom = true;
3034 break;
3035 }
3036 else if (y - pointPos.y() > expectedPointSize)
3037 {
3038 // no need to go further than point height
3039 break;
3040 }
3041 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3042 {
3043 break;
3044 }
3045 else
3046 {
3047 widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3048 }
3049 }
3050
3051 // bottom is clipped
3052 if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty()))
3053 {
3054 const tcu::IVec2& range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
3055 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
3056 const bool bboxClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1);
3057 const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth()-1;
3058
3059 if (squareFits || bboxClipped || viewportClipped)
3060 return true;
3061 }
3062
3063 // would square point would fit into the rasterized area
3064
3065 for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
3066 widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
3067 for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
3068 widths.push_back(widthsDownwards[ndx]);
3069 DE_ASSERT(!widths.empty());
3070
3071 for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
3072 {
3073 tcu::IVec2 unionRange = widths[y];
3074
3075 for (int dy = 1; dy < expectedPointSize; ++dy)
3076 {
3077 unionRange.x() = de::max(unionRange.x(), widths[y+dy].x());
3078 unionRange.y() = de::min(unionRange.y(), widths[y+dy].y());
3079 }
3080
3081 // would a N x N block fit here?
3082 {
3083 const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
3084 const bool bboxClipped = (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1);
3085 const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1;
3086
3087 if (squareFits || bboxClipped || viewportClipped)
3088 return true;
3089 }
3090 }
3091
3092 if (--logFloodCounter >= 0)
3093 {
3094 m_testCtx.getLog()
3095 << tcu::TestLog::Message
3096 << "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3097 << tcu::TestLog::EndMessage;
3098 }
3099 return false;
3100}
3101
3102tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const
3103{
3104 int minX = pointPos.x();
3105 int maxX = pointPos.x();
3106
3107 // search horizontally for a point edges
3108 for (int x = pointPos.x()-1; x >= 0; --x)
3109 {
3110 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3111 break;
3112
3113 // no need to go further than point width
3114 if (pointPos.x() - x > expectedPointSize)
3115 break;
3116
3117 minX = x;
3118 }
3119 for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x)
3120 {
3121 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3122 break;
3123
3124 // no need to go further than point width
3125 if (x - pointPos.x() > expectedPointSize)
3126 break;
3127
3128 maxX = x;
3129 }
3130
3131 return tcu::IVec2(minX, maxX);
3132}
3133
3134class BlitFboCase : public TestCase
3135{
3136public:
3137 enum RenderTarget
3138 {
3139 TARGET_DEFAULT = 0,
3140 TARGET_FBO,
3141
3142 TARGET_LAST
3143 };
3144
3145 BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst);
3146 ~BlitFboCase (void);
3147
3148private:
3149 enum
3150 {
3151 FBO_SIZE = 256,
3152 };
3153
3154 struct BlitArgs
3155 {
3156 tcu::IVec4 src;
3157 tcu::IVec4 dst;
3158 tcu::Vec4 bboxMin;
3159 tcu::Vec4 bboxMax;
3160 bool linear;
3161 };
3162
3163 void init (void);
3164 void deinit (void);
3165 IterateResult iterate (void);
3166
3167 void fillSourceWithPattern (void);
3168 bool verifyImage (const BlitArgs& args);
3169
3170 const RenderTarget m_src;
3171 const RenderTarget m_dst;
3172
3173 std::vector<BlitArgs> m_iterations;
3174 int m_iteration;
3175 de::MovePtr<glu::Framebuffer> m_srcFbo;
3176 de::MovePtr<glu::Framebuffer> m_dstFbo;
3177 de::MovePtr<glu::Renderbuffer> m_srcRbo;
3178 de::MovePtr<glu::Renderbuffer> m_dstRbo;
3179 de::MovePtr<glu::ShaderProgram> m_program;
3180 de::MovePtr<glu::Buffer> m_vbo;
3181};
3182
3183BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst)
3184 : TestCase (context, name, description)
3185 , m_src (src)
3186 , m_dst (dst)
3187 , m_iteration (0)
3188{
3189 DE_ASSERT(src < TARGET_LAST);
3190 DE_ASSERT(dst < TARGET_LAST);
3191}
3192
3193BlitFboCase::~BlitFboCase (void)
3194{
3195 deinit();
3196}
3197
3198void BlitFboCase::init (void)
3199{
3200 const int numIterations = 12;
3201 const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1);
3202 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3203 de::Random rnd (0xABC123);
3204
3205 m_testCtx.getLog()
3206 << tcu::TestLog::Message
3207 << "Using BlitFramebuffer to blit area from "
3208 << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3209 << " to "
3210 << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3211 << ".\n"
3212 << "Varying blit arguments and primitive bounding box between iterations.\n"
3213 << "Expecting bounding box to have no effect on blitting.\n"
3214 << "Source framebuffer is filled with green-yellow grid.\n"
3215 << tcu::TestLog::EndMessage;
3216
3217 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3218 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3219 if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3220 throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3221
3222 // resources
3223
3224 if (m_src == TARGET_FBO)
3225 {
3226 m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3227 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3228 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3229 GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3230
3231 m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3232 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3233 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3234 GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3235 }
3236
3237 if (m_dst == TARGET_FBO)
3238 {
3239 m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3240 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3241 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3242 GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3243
3244 m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3245 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3246 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3247 GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3248 }
3249
3250 {
3251 static const char* const s_vertexSource = "#version 310 es\n"
3252 "in highp vec4 a_position;\n"
3253 "out highp vec4 v_position;\n"
3254 "void main()\n"
3255 "{\n"
3256 " gl_Position = a_position;\n"
3257 " v_position = a_position;\n"
3258 "}\n";
3259 static const char* const s_fragmentSource = "#version 310 es\n"
3260 "in mediump vec4 v_position;\n"
3261 "layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3262 "void main()\n"
3263 "{\n"
3264 " const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3265 " const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3266 " dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3267 "}\n";
3268
3269 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource)));
3270
3271 if (!m_program->isOk())
3272 {
3273 m_testCtx.getLog() << *m_program;
3274 throw tcu::TestError("failed to build program");
3275 }
3276 }
3277
3278 {
3279 static const tcu::Vec4 s_quadCoords[] =
3280 {
3281 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3282 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
3283 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
3284 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
3285 };
3286
3287 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3288
3289 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3290 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3291 GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3292 }
3293
3294 // gen iterations
3295
3296 {
3297 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3298 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3299
3300 m_testCtx.getLog()
3301 << tcu::TestLog::Message
3302 << "srcSize = " << srcSize << "\n"
3303 << "dstSize = " << dstSize << "\n"
3304 << tcu::TestLog::EndMessage;
3305
3306 for (int ndx = 0; ndx < numIterations; ++ndx)
3307 {
3308 BlitArgs args;
3309
3310 if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3311 {
3312 const tcu::IVec2 unionSize = tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3313 const int srcWidth = rnd.getInt(1, unionSize.x());
3314 const int srcHeight = rnd.getInt(1, unionSize.y());
3315 const int srcX = rnd.getInt(0, unionSize.x() - srcWidth);
3316 const int srcY = rnd.getInt(0, unionSize.y() - srcHeight);
3317
3318 args.src.x() = srcX;
3319 args.src.y() = srcY;
3320 args.src.z() = srcX + srcWidth;
3321 args.src.w() = srcY + srcHeight;
3322
3323 args.dst = args.src;
3324 }
3325 else
3326 {
3327 const int srcWidth = rnd.getInt(1, srcSize.x());
3328 const int srcHeight = rnd.getInt(1, srcSize.y());
3329 const int srcX = rnd.getInt(0, srcSize.x() - srcWidth);
3330 const int srcY = rnd.getInt(0, srcSize.y() - srcHeight);
3331 const int dstWidth = rnd.getInt(1, dstSize.x());
3332 const int dstHeight = rnd.getInt(1, dstSize.y());
3333 const int dstX = rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2); // allow dst go out of bounds
3334 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1) / 2);
3335
3336 args.src.x() = srcX;
3337 args.src.y() = srcY;
3338 args.src.z() = srcX + srcWidth;
3339 args.src.w() = srcY + srcHeight;
3340 args.dst.x() = dstX;
3341 args.dst.y() = dstY;
3342 args.dst.z() = dstX + dstWidth;
3343 args.dst.w() = dstY + dstHeight;
3344 }
3345
3346 args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3347 args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3348 args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3349 args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f);
3350
3351 args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3352 args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3353 args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3354 args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f);
3355
3356 if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3357 std::swap(args.bboxMin.x(), args.bboxMax.x());
3358 if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3359 std::swap(args.bboxMin.y(), args.bboxMax.y());
3360 if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3361 std::swap(args.bboxMin.z(), args.bboxMax.z());
3362
3363 args.linear = rnd.getBool();
3364
3365 m_iterations.push_back(args);
3366 }
3367 }
3368}
3369
3370void BlitFboCase::deinit (void)
3371{
3372 m_srcFbo.clear();
3373 m_srcRbo.clear();
3374 m_dstFbo.clear();
3375 m_dstRbo.clear();
3376 m_program.clear();
3377 m_vbo.clear();
3378}
3379
3380BlitFboCase::IterateResult BlitFboCase::iterate (void)
3381{
3382 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size()));
3383 const BlitArgs& blitCfg = m_iterations[m_iteration];
3384 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3385
3386 if (m_iteration == 0)
3387 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3388
3389 // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3390 if (m_src == TARGET_DEFAULT || m_iteration == 0)
3391 fillSourceWithPattern();
3392
3393 m_testCtx.getLog()
3394 << tcu::TestLog::Message
3395 << "Set bounding box:\n"
3396 << "\tmin:" << blitCfg.bboxMin << "\n"
3397 << "\tmax:" << blitCfg.bboxMax << "\n"
3398 << "Blit:\n"
3399 << "\tsrc: " << blitCfg.src << "\n"
3400 << "\tdst: " << blitCfg.dst << "\n"
3401 << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest"))
3402 << tcu::TestLog::EndMessage;
3403
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -06003404 gl.primitiveBoundingBox(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3405 blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003406
3407 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3408 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3409 gl.clear(GL_COLOR_BUFFER_BIT);
3410
3411 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3412 gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(),
3413 blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(),
3414 GL_COLOR_BUFFER_BIT,
3415 ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3416 GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3417
3418 if (!verifyImage(blitCfg))
3419 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3420
3421 return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3422}
3423
3424bool BlitFboCase::verifyImage (const BlitArgs& args)
3425{
3426 const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything
3427 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3428 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3429 tcu::Surface viewport (dstSize.x(), dstSize.y());
3430 tcu::Surface errorMask (dstSize.x(), dstSize.y());
3431 bool anyError = false;
3432
3433 m_testCtx.getLog()
3434 << tcu::TestLog::Message
3435 << "Verifying blit result"
3436 << tcu::TestLog::EndMessage;
3437
3438 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3439 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3440
3441 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3442
3443 for (int y = 0; y < dstSize.y(); ++y)
3444 for (int x = 0; x < dstSize.x(); ++x)
3445 {
3446 const tcu::RGBA color = viewport.getPixel(x, y);
3447 const bool inside = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3448 const bool error = (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold)
3449 : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold);
3450
3451 if (error)
3452 {
3453 anyError = true;
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05003454 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003455 }
3456 }
3457
3458 if (anyError)
3459 {
3460 m_testCtx.getLog()
3461 << tcu::TestLog::Message
3462 << "Image verification failed."
3463 << tcu::TestLog::EndMessage
3464 << tcu::TestLog::ImageSet("Images", "Image verification")
3465 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3466 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3467 << tcu::TestLog::EndImageSet;
3468 return false;
3469 }
3470 else
3471 {
3472 m_testCtx.getLog()
3473 << tcu::TestLog::Message
3474 << "Result image ok."
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -08003475 << tcu::TestLog::EndMessage
3476 << tcu::TestLog::ImageSet("Images", "Image verification")
3477 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3478 << tcu::TestLog::EndImageSet;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003479 return true;
3480 }
3481}
3482
3483void BlitFboCase::fillSourceWithPattern (void)
3484{
3485 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3486 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3487 const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3488
3489 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3490 gl.viewport(0, 0, srcSize.x(), srcSize.y());
3491 gl.useProgram(m_program->getProgram());
3492
3493 gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3494 gl.clear(GL_COLOR_BUFFER_BIT);
3495
3496 gl.enableVertexAttribArray(posLocation);
3497 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3498 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3499 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3500}
3501
3502class DepthDrawCase : public TestCase
3503{
3504public:
3505 enum DepthType
3506 {
3507 DEPTH_BUILTIN = 0,
3508 DEPTH_USER_DEFINED,
3509
3510 DEPTH_LAST
3511 };
3512 enum BBoxState
3513 {
3514 STATE_GLOBAL = 0,
3515 STATE_PER_PRIMITIVE,
3516
3517 STATE_LAST
3518 };
3519 enum BBoxSize
3520 {
3521 BBOX_EQUAL = 0,
3522 BBOX_LARGER,
3523
3524 BBOX_LAST
3525 };
3526
3527 DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize);
3528 ~DepthDrawCase (void);
3529
3530private:
3531 void init (void);
3532 void deinit (void);
3533 IterateResult iterate (void);
3534
3535 std::string genVertexSource (void) const;
3536 std::string genFragmentSource (void) const;
3537 std::string genTessellationControlSource (void) const;
3538 std::string genTessellationEvaluationSource (void) const;
3539 void generateAttributeData (std::vector<tcu::Vec4>& data) const;
3540 bool verifyImage (const tcu::Surface& viewport) const;
3541
3542 enum
3543 {
3544 RENDER_AREA_SIZE = 256,
3545 };
3546
3547 struct LayerInfo
3548 {
3549 float zOffset;
3550 float zScale;
3551 tcu::Vec4 color1;
3552 tcu::Vec4 color2;
3553 };
3554
3555 const int m_numLayers;
3556 const int m_gridSize;
3557
3558 const DepthType m_depthType;
3559 const BBoxState m_state;
3560 const BBoxSize m_bboxSize;
3561
3562 de::MovePtr<glu::ShaderProgram> m_program;
3563 de::MovePtr<glu::Buffer> m_vbo;
3564 std::vector<LayerInfo> m_layers;
3565};
3566
3567DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize)
3568 : TestCase (context, name, description)
3569 , m_numLayers (14)
3570 , m_gridSize (24)
3571 , m_depthType (depthType)
3572 , m_state (state)
3573 , m_bboxSize (bboxSize)
3574{
3575 DE_ASSERT(depthType < DEPTH_LAST);
3576 DE_ASSERT(state < STATE_LAST);
3577 DE_ASSERT(bboxSize < BBOX_LAST);
3578}
3579
3580DepthDrawCase::~DepthDrawCase (void)
3581{
3582 deinit();
3583}
3584
3585void DepthDrawCase::init (void)
3586{
3587 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3588
3589 // requirements
3590
3591 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3592 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3593 if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3594 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3595 if (m_context.getRenderTarget().getDepthBits() == 0)
3596 throw tcu::NotSupportedError("Test requires depth buffer");
3597 if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
3598 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport");
3599
3600 // log
3601 m_testCtx.getLog()
3602 << tcu::TestLog::Message
3603 << "Rendering multiple triangle grids with with different z coordinates.\n"
3604 << "Topmost grid is green-yellow, other grids are blue-red.\n"
3605 << "Expecting only the green-yellow grid to be visible.\n"
3606 << "Setting primitive bounding box "
3607 << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
3608 << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
3609 << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding."))
3610 << "\n"
3611 << "Set bounding box using "
3612 << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3613 << "\n"
3614 << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : (""))
3615 << tcu::TestLog::EndMessage;
3616
3617 // resources
3618
3619 {
3620 glu::ProgramSources sources;
3621 sources << glu::VertexSource(genVertexSource());
3622 sources << glu::FragmentSource(genFragmentSource());
3623
3624 if (m_state == STATE_PER_PRIMITIVE)
3625 sources << glu::TessellationControlSource(genTessellationControlSource())
3626 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
3627
3628 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
3629 GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
3630
3631 {
3632 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
3633 m_testCtx.getLog() << *m_program;
3634 }
3635
3636 if (!m_program->isOk())
3637 throw tcu::TestError("failed to build program");
3638 }
3639
3640 {
3641 std::vector<tcu::Vec4> data;
3642
3643 generateAttributeData(data);
3644
3645 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3646 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3647 gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
3648 GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
3649 }
3650
3651 // gen layers
3652 {
3653 de::Random rnd(0x12345);
3654
3655 m_layers.resize(m_numLayers);
3656 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3657 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07003658 m_layers[layerNdx].zOffset = ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f;
3659 m_layers[layerNdx].zScale = (2.0f / (float)m_numLayers);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003660 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));
3661 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));
3662 }
3663 rnd.shuffle(m_layers.begin(), m_layers.end());
3664 }
3665}
3666
3667void DepthDrawCase::deinit (void)
3668{
3669 m_program.clear();
3670 m_vbo.clear();
3671}
3672
3673DepthDrawCase::IterateResult DepthDrawCase::iterate (void)
3674{
3675 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3676 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3677 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3678 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
3679 const glw::GLint depthBiasLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
3680 const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
3681 const glw::GLint color1Location = gl.getUniformLocation(m_program->getProgram(), "u_color1");
3682 const glw::GLint color2Location = gl.getUniformLocation(m_program->getProgram(), "u_color2");
3683
3684 tcu::Surface viewport (RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3685 de::Random rnd (0x213237);
3686
3687 TCU_CHECK(posLocation != -1);
3688 TCU_CHECK(colLocation != -1);
3689 TCU_CHECK(depthBiasLocation != -1);
3690 TCU_CHECK(depthScaleLocation != -1);
3691 TCU_CHECK(color1Location != -1);
3692 TCU_CHECK(color2Location != -1);
3693
3694 gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3695 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3696 gl.clearDepthf(1.0f);
3697 gl.depthFunc(GL_LESS);
3698 gl.enable(GL_DEPTH_TEST);
3699 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3700 GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
3701
3702 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3703 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL);
3704 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4);
3705 gl.enableVertexAttribArray(posLocation);
3706 gl.enableVertexAttribArray(colLocation);
3707 gl.useProgram(m_program->getProgram());
3708 GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
3709
3710 if (hasTessellation)
3711 gl.patchParameteri(GL_PATCH_VERTICES, 3);
3712
3713 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3714 {
3715 gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
3716 gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
3717 gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
3718 gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
3719
3720 if (m_state == STATE_GLOBAL)
3721 {
3722 const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3723 const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3724
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -06003725 gl.primitiveBoundingBox(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f,
3726 1.0f, 1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003727 }
3728
3729 gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
3730 }
3731
3732 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3733 GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
3734
3735 if (verifyImage(viewport))
3736 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3737 else
3738 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3739
3740 return STOP;
3741}
3742
3743std::string DepthDrawCase::genVertexSource (void) const
3744{
3745 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3746 std::ostringstream buf;
3747
3748 buf << "#version 310 es\n"
3749 "in highp vec4 a_position;\n"
3750 "in highp vec4 a_colorMix;\n"
3751 "out highp vec4 vtx_colorMix;\n";
3752
3753 if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
3754 buf << "out highp float v_fragDepth;\n";
3755
3756 if (!hasTessellation)
3757 buf << "uniform highp float u_depthBias;\n"
3758 "uniform highp float u_depthScale;\n";
3759
3760 buf << "\n"
3761 "void main()\n"
3762 "{\n";
3763
3764 if (hasTessellation)
3765 buf << " gl_Position = a_position;\n";
3766 else if (m_depthType == DEPTH_USER_DEFINED)
3767 buf << " highp float dummyZ = a_position.z;\n"
3768 " highp float writtenZ = a_position.w;\n"
3769 " gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n"
3770 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3771 else
3772 buf << " highp float writtenZ = a_position.w;\n"
3773 " gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3774
3775 buf << " vtx_colorMix = a_colorMix;\n"
3776 "}\n";
3777
3778 return buf.str();
3779}
3780
3781std::string DepthDrawCase::genFragmentSource (void) const
3782{
3783 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3784 const char* const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
3785 std::ostringstream buf;
3786
3787 buf << "#version 310 es\n"
3788 "in mediump vec4 " << colorMixName << ";\n";
3789
3790 if (m_depthType == DEPTH_USER_DEFINED)
3791 buf << "in mediump float v_fragDepth;\n";
3792
3793 buf << "layout(location = 0) out mediump vec4 o_color;\n"
3794 "uniform highp vec4 u_color1;\n"
3795 "uniform highp vec4 u_color2;\n"
3796 "\n"
3797 "void main()\n"
3798 "{\n"
3799 " o_color = mix(u_color1, u_color2, " << colorMixName << ");\n";
3800
3801 if (m_depthType == DEPTH_USER_DEFINED)
3802 buf << " gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
3803
3804 buf << "}\n";
3805
3806 return buf.str();
3807}
3808
3809std::string DepthDrawCase::genTessellationControlSource (void) const
3810{
3811 std::ostringstream buf;
3812
3813 buf << "#version 310 es\n"
3814 "#extension GL_EXT_tessellation_shader : require\n"
3815 "#extension GL_EXT_primitive_bounding_box : require\n"
3816 "layout(vertices=3) out;\n"
3817 "\n"
3818 "uniform highp float u_depthBias;\n"
3819 "uniform highp float u_depthScale;\n"
3820 "\n"
3821 "in highp vec4 vtx_colorMix[];\n"
3822 "out highp vec4 tess_ctrl_colorMix[];\n"
3823 "\n"
3824 "void main()\n"
3825 "{\n"
3826 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3827 " tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
3828 "\n"
3829 " gl_TessLevelOuter[0] = 2.8;\n"
3830 " gl_TessLevelOuter[1] = 2.8;\n"
3831 " gl_TessLevelOuter[2] = 2.8;\n"
3832 " gl_TessLevelInner[0] = 2.8;\n"
3833 "\n"
3834 " // real Z stored in w component\n"
3835 " highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3836 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3837 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"
3838 " highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3839 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3840 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n";
3841
3842 if (m_bboxSize == BBOX_EQUAL)
3843 buf << " gl_BoundingBoxEXT[0] = minBound;\n"
3844 " gl_BoundingBoxEXT[1] = maxBound;\n";
3845 else
3846 buf << " highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
3847 " highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
3848 " gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
3849 " gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
3850
3851 buf << "}\n";
3852
3853 return buf.str();
3854}
3855
3856std::string DepthDrawCase::genTessellationEvaluationSource (void) const
3857{
3858 std::ostringstream buf;
3859
3860 buf << "#version 310 es\n"
3861 "#extension GL_EXT_tessellation_shader : require\n"
3862 "#extension GL_EXT_gpu_shader5 : require\n"
3863 "layout(triangles) in;\n"
3864 "\n"
3865 "in highp vec4 tess_ctrl_colorMix[];\n"
3866 "out highp vec4 tess_eval_colorMix;\n";
3867
3868 if (m_depthType == DEPTH_USER_DEFINED)
3869 buf << "out highp float v_fragDepth;\n";
3870
3871 buf << "uniform highp float u_depthBias;\n"
3872 "uniform highp float u_depthScale;\n"
3873 "\n"
3874 "precise gl_Position;\n"
3875 "\n"
3876 "void main()\n"
3877 "{\n"
3878 " 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";
3879
3880 if (m_depthType == DEPTH_USER_DEFINED)
3881 buf << " highp float dummyZ = tessellatedPos.z;\n"
3882 " highp float writtenZ = tessellatedPos.w;\n"
3883 " gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n"
3884 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3885 else
3886 buf << " highp float writtenZ = tessellatedPos.w;\n"
3887 " gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3888
3889 buf << " tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
3890 "}\n";
3891
3892 return buf.str();
3893}
3894
3895void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const
3896{
3897 const tcu::Vec4 color1 (0.0f, 0.0f, 0.0f, 0.0f); // mix weights
3898 const tcu::Vec4 color2 (1.0f, 1.0f, 1.0f, 1.0f);
3899 std::vector<int> cellOrder (m_gridSize * m_gridSize);
3900 de::Random rnd (0xAB54321);
3901
3902 // generate grid with cells in random order
3903 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3904 cellOrder[ndx] = ndx;
3905 rnd.shuffle(cellOrder.begin(), cellOrder.end());
3906
3907 data.resize(m_gridSize * m_gridSize * 6 * 2);
3908 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3909 {
3910 const int cellNdx = cellOrder[ndx];
3911 const int cellX = cellNdx % m_gridSize;
3912 const int cellY = cellNdx / m_gridSize;
3913 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (color1) : (color2);
3914
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07003915 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;
3916 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;
3917 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;
3918 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;
3919 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;
3920 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;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003921
3922 // Fill Z with random values (fake Z)
3923 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3924 data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
3925
3926 // Fill W with other random values (written Z)
3927 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3928 data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
3929 }
3930}
3931
3932bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const
3933{
3934 tcu::Surface errorMask (viewport.getWidth(), viewport.getHeight());
3935 bool anyError = false;
3936
3937 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
3938
3939 for (int y = 0; y < viewport.getHeight(); ++y)
3940 for (int x = 0; x < viewport.getWidth(); ++x)
3941 {
3942 const tcu::RGBA pixel = viewport.getPixel(x, y);
3943 bool error = false;
3944
3945 // expect green, yellow or a combination of these
3946 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
3947 error = true;
3948
3949 if (error)
3950 {
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05003951 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003952 anyError = true;
3953 }
3954 }
3955
3956 if (anyError)
3957 m_testCtx.getLog()
3958 << tcu::TestLog::Message
3959 << "Image verification failed."
3960 << tcu::TestLog::EndMessage
3961 << tcu::TestLog::ImageSet("Images", "Image verification")
3962 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3963 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3964 << tcu::TestLog::EndImageSet;
3965 else
3966 m_testCtx.getLog()
3967 << tcu::TestLog::Message
3968 << "Result image ok."
Jarkko Pöyry30fde0c2015-02-12 14:07:42 -08003969 << tcu::TestLog::EndMessage
3970 << tcu::TestLog::ImageSet("Images", "Image verification")
3971 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3972 << tcu::TestLog::EndImageSet;
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08003973
3974 return !anyError;
3975}
3976
3977class ClearCase : public TestCase
3978{
3979public:
3980 enum
3981 {
3982 SCISSOR_CLEAR_BIT = 1 << 0,
3983 DRAW_TRIANGLE_BIT = 1 << 1,
3984 PER_PRIMITIVE_BBOX_BIT = 1 << 2,
3985 FULLSCREEN_SCISSOR_BIT = 1 << 3,
3986 };
3987
3988 ClearCase (Context& context, const char* name, const char* description, deUint32 flags);
3989 ~ClearCase (void);
3990
3991private:
3992 struct DrawObject
3993 {
3994 int firstNdx;
3995 int numVertices;
3996 };
3997
3998 void init (void);
3999 void deinit (void);
4000 IterateResult iterate (void);
4001
4002 void createVbo (void);
4003 void createProgram (void);
4004 void renderTo (tcu::Surface& dst, bool useBBox);
4005 bool verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox);
4006 bool verifyImageResultValid (const tcu::PixelBufferAccess& result);
4007
4008 std::string genVertexSource (void) const;
4009 std::string genFragmentSource (void) const;
4010 std::string genTessellationControlSource (bool setBBox) const;
4011 std::string genTessellationEvaluationSource (void) const;
4012
4013 const bool m_scissoredClear;
4014 const bool m_fullscreenScissor;
4015 const bool m_drawTriangles;
4016 const bool m_useGlobalState;
4017
4018 de::MovePtr<glu::Buffer> m_vbo;
4019 de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram;
4020 de::MovePtr<glu::ShaderProgram> m_basicProgram;
4021 std::vector<DrawObject> m_drawObjects;
4022 std::vector<tcu::Vec4> m_objectVertices;
4023};
4024
4025ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags)
4026 : TestCase (context, name, description)
4027 , m_scissoredClear ((flags & SCISSOR_CLEAR_BIT) != 0)
4028 , m_fullscreenScissor ((flags & FULLSCREEN_SCISSOR_BIT) != 0)
4029 , m_drawTriangles ((flags & DRAW_TRIANGLE_BIT) != 0)
4030 , m_useGlobalState ((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
4031{
4032 DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles
4033 DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
4034}
4035
4036ClearCase::~ClearCase (void)
4037{
4038 deinit();
4039}
4040
4041void ClearCase::init (void)
4042{
4043 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4044 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4045 if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4046 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4047
4048 m_testCtx.getLog()
4049 << tcu::TestLog::Message
4050 << "Doing multiple"
4051 << ((m_scissoredClear) ? (" scissored") : (""))
4052 << " color buffer clears"
4053 << ((m_drawTriangles) ? (" and drawing some geometry between them") : (""))
4054 << ".\n"
4055 << ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : (""))
4056 << "Rendering with and without setting the bounding box.\n"
4057 << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
4058 << "Set bounding box using "
4059 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
4060 << ".\n"
4061 << "Clear color is green with yellowish shades.\n"
4062 << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
4063 << tcu::TestLog::EndMessage;
4064
4065 if (m_drawTriangles)
4066 {
4067 createVbo();
4068 createProgram();
4069 }
4070}
4071
4072void ClearCase::deinit (void)
4073{
4074 m_vbo.clear();
4075 m_perPrimitiveProgram.clear();
4076 m_basicProgram.clear();
4077 m_drawObjects = std::vector<DrawObject>();
4078 m_objectVertices = std::vector<tcu::Vec4>();
4079}
4080
4081ClearCase::IterateResult ClearCase::iterate (void)
4082{
4083 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4084 tcu::Surface resultWithoutBBox (renderTargetSize.x(), renderTargetSize.y());
4085 tcu::Surface resultWithBBox (renderTargetSize.x(), renderTargetSize.y());
4086
4087 // render with and without bbox set
4088 for (int passNdx = 0; passNdx < 2; ++passNdx)
4089 {
4090 const bool useBBox = (passNdx == 1);
4091 tcu::Surface& destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
4092
4093 renderTo(destination, useBBox);
4094 }
4095
4096 // Verify images are equal and that the image does not contain (trivially detectable) garbage
4097
4098 if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4099 {
4100 // verifyImagesEqual will print out the image and error mask
4101 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4102 }
4103 else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4104 {
4105 // verifyImageResultValid will print out the image and error mask
4106 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4107 }
4108 else
4109 {
4110 m_testCtx.getLog()
4111 << tcu::TestLog::Message
4112 << "Image comparison passed."
4113 << tcu::TestLog::EndMessage
4114 << tcu::TestLog::ImageSet("Images", "Image verification")
4115 << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4116 << tcu::TestLog::EndImageSet;
4117
4118 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4119 }
4120
4121 return STOP;
4122}
4123
4124void ClearCase::createVbo (void)
4125{
4126 const int numObjects = 16;
4127 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4128 de::Random rnd (deStringHash(getName()));
4129
4130 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4131
4132 for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4133 {
4134 const int numTriangles = rnd.getInt(1, 4);
4135 const float minX = rnd.getFloat(-1.2f, 0.8f);
4136 const float minY = rnd.getFloat(-1.2f, 0.8f);
4137 const float maxX = minX + rnd.getFloat(0.2f, 1.0f);
4138 const float maxY = minY + rnd.getFloat(0.2f, 1.0f);
4139
4140 DrawObject drawObject;
4141 drawObject.firstNdx = (int)m_objectVertices.size();
4142 drawObject.numVertices = numTriangles * 3;
4143
4144 m_drawObjects.push_back(drawObject);
4145
4146 for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4147 for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4148 {
4149 const float posX = rnd.getFloat(minX, maxX);
4150 const float posY = rnd.getFloat(minY, maxY);
4151 const float posZ = rnd.getFloat(-0.7f, 0.7f);
4152 const float posW = rnd.getFloat(0.9f, 1.1f);
4153
4154 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4155 }
4156 }
4157
4158 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4159 gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW);
4160 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4161}
4162
4163void ClearCase::createProgram (void)
4164{
4165 m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4166 glu::ProgramSources()
4167 << glu::VertexSource(genVertexSource())
4168 << glu::FragmentSource(genFragmentSource())
4169 << glu::TessellationControlSource(genTessellationControlSource(false))
4170 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4171
4172 m_testCtx.getLog()
4173 << tcu::TestLog::Section("Program", "Shader program")
4174 << *m_basicProgram
4175 << tcu::TestLog::EndSection;
4176
4177 if (!m_basicProgram->isOk())
4178 throw tcu::TestError("shader build failed");
4179
4180 if (!m_useGlobalState)
4181 {
4182 m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4183 glu::ProgramSources()
4184 << glu::VertexSource(genVertexSource())
4185 << glu::FragmentSource(genFragmentSource())
4186 << glu::TessellationControlSource(genTessellationControlSource(true))
4187 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4188
4189 m_testCtx.getLog()
4190 << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4191 << *m_perPrimitiveProgram
4192 << tcu::TestLog::EndSection;
4193
4194 if (!m_perPrimitiveProgram->isOk())
4195 throw tcu::TestError("shader build failed");
4196 }
4197}
4198
4199void ClearCase::renderTo (tcu::Surface& dst, bool useBBox)
4200{
4201 const int numOps = 45;
4202 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f);
4203 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
4204 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4205 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4206 de::Random rnd (deStringHash(getName()));
4207 glu::VertexArray vao (m_context.getRenderContext());
4208
4209 // always do the initial clear
4210 gl.disable(GL_SCISSOR_TEST);
4211 gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4212 gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4213 gl.clear(GL_COLOR_BUFFER_BIT);
4214 gl.finish();
4215
4216 // prepare draw
4217 if (m_scissoredClear)
4218 gl.enable(GL_SCISSOR_TEST);
4219
4220 if (m_drawTriangles)
4221 {
4222 const deUint32 programHandle = (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4223 const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position");
4224
4225 TCU_CHECK(positionAttribLoc != -1);
4226
4227 gl.useProgram(programHandle);
4228 gl.bindVertexArray(*vao);
4229 gl.enableVertexAttribArray(positionAttribLoc);
4230 gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4231 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4232 }
4233
4234 // do random scissor/clearldraw operations
4235 for (int opNdx = 0; opNdx < numOps; ++opNdx)
4236 {
4237 const int drawObjNdx = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4238 const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4239 const int objectVertexLength = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4240 tcu::Vec4 bboxMin;
4241 tcu::Vec4 bboxMax;
4242
4243 if (m_drawTriangles)
4244 {
4245 bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4246 bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4247
4248 // calc bbox
4249 for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx)
4250 for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4251 {
4252 bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4253 bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4254 }
4255 }
4256 else
4257 {
4258 // no geometry, just random something
4259 bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4260 bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4261 bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4262 bboxMin.w() = 1.0f;
4263 bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4264 bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4265 bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4266 bboxMax.w() = 1.0f;
4267 }
4268
4269 if (m_scissoredClear)
4270 {
4271 const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x()-1);
4272 const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y()-1);
4273 const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x()-scissorX);
4274 const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y()-scissorY);
4275
4276 gl.scissor(scissorX, scissorY, scissorW, scissorH);
4277 }
4278
4279 {
4280 const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4281 gl.clearColor(color.x(), color.y(), color.z(), color.w());
4282 gl.clear(GL_COLOR_BUFFER_BIT);
4283 }
4284
4285 if (useBBox)
4286 {
4287 DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4288 if (m_useGlobalState)
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -06004289 gl.primitiveBoundingBox(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(),
4290 bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004291 }
4292
4293 if (m_drawTriangles)
4294 gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4295 }
4296
4297 GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4298 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4299}
4300
4301bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox)
4302{
4303 DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
Jarkko Pöyry26e8fab2015-02-09 21:32:19 -08004304 DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004305
4306 tcu::Surface errorMask (withoutBBox.getWidth(), withoutBBox.getHeight());
4307 bool anyError = false;
4308
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004309 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004310
4311 for (int y = 0; y < withoutBBox.getHeight(); ++y)
4312 for (int x = 0; x < withoutBBox.getWidth(); ++x)
4313 {
4314 if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4315 {
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004316 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004317 anyError = true;
4318 }
4319 }
4320
4321 if (anyError)
4322 {
4323 m_testCtx.getLog()
4324 << tcu::TestLog::Message
4325 << "Image comparison failed."
4326 << tcu::TestLog::EndMessage
4327 << tcu::TestLog::ImageSet("Images", "Image comparison")
4328 << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4329 << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4330 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4331 << tcu::TestLog::EndImageSet;
4332 }
4333
4334 return !anyError;
4335}
4336
4337bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result)
4338{
4339 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4340 bool anyError = false;
4341
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004342 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004343
4344 for (int y = 0; y < result.getHeight(); ++y)
4345 for (int x = 0; x < result.getWidth(); ++x)
4346 {
4347 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4348
4349 // allow green, yellow and any shade between
4350 if (pixel[1] != 255 || pixel[2] != 0)
4351 {
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004352 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004353 anyError = true;
4354 }
4355 }
4356
4357 if (anyError)
4358 {
4359 m_testCtx.getLog()
4360 << tcu::TestLog::Message
4361 << "Image verification failed."
4362 << tcu::TestLog::EndMessage
4363 << tcu::TestLog::ImageSet("Images", "Image verification")
4364 << tcu::TestLog::Image("ResultImage", "Result image", result)
4365 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4366 << tcu::TestLog::EndImageSet;
4367 }
4368
4369 return !anyError;
4370}
4371
4372static const char* const s_yellowishPosOnlyVertexSource = "#version 310 es\n"
4373 "in highp vec4 a_position;\n"
4374 "out highp vec4 v_vertex_color;\n"
4375 "void main()\n"
4376 "{\n"
4377 " gl_Position = a_position;\n"
4378 " // yellowish shade\n"
4379 " highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4380 " v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4381 "}\n";
4382
4383static const char* const s_basicColorFragmentSource = "#version 310 es\n"
4384 "in mediump vec4 v_color;\n"
4385 "layout(location = 0) out mediump vec4 o_color;\n"
4386 "void main()\n"
4387 "{\n"
4388 " o_color = v_color;\n"
4389 "}\n";
4390
4391
4392static const char* const s_basicColorTessEvalSource = "#version 310 es\n"
4393 "#extension GL_EXT_tessellation_shader : require\n"
4394 "#extension GL_EXT_gpu_shader5 : require\n"
4395 "layout(triangles) in;\n"
4396 "in highp vec4 v_tess_eval_color[];\n"
4397 "out highp vec4 v_color;\n"
4398 "precise gl_Position;\n"
4399 "void main()\n"
4400 "{\n"
4401 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4402 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
4403 " + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4404 " v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4405 " + gl_TessCoord.y * v_tess_eval_color[1]\n"
4406 " + gl_TessCoord.z * v_tess_eval_color[2];\n"
4407 "}\n";
4408
4409std::string ClearCase::genVertexSource (void) const
4410{
4411 return s_yellowishPosOnlyVertexSource;
4412}
4413
4414std::string ClearCase::genFragmentSource (void) const
4415{
4416 return s_basicColorFragmentSource;
4417}
4418
4419std::string ClearCase::genTessellationControlSource (bool setBBox) const
4420{
4421 std::ostringstream buf;
4422
4423 buf << "#version 310 es\n"
4424 "#extension GL_EXT_tessellation_shader : require\n";
4425
4426 if (setBBox)
4427 buf << "#extension GL_EXT_primitive_bounding_box : require\n";
4428
4429 buf << "layout(vertices=3) out;\n"
4430 "in highp vec4 v_vertex_color[];\n"
4431 "out highp vec4 v_tess_eval_color[];\n"
4432 "void main()\n"
4433 "{\n"
4434 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4435 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4436 " gl_TessLevelOuter[0] = 2.8;\n"
4437 " gl_TessLevelOuter[1] = 2.8;\n"
4438 " gl_TessLevelOuter[2] = 2.8;\n"
4439 " gl_TessLevelInner[0] = 2.8;\n";
4440
4441 if (setBBox)
4442 {
4443 buf << "\n"
4444 " gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n"
4445 " gl_in[1].gl_Position),\n"
4446 " gl_in[2].gl_Position);\n"
4447 " gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n"
4448 " gl_in[1].gl_Position),\n"
4449 " gl_in[2].gl_Position);\n";
4450 }
4451
4452 buf << "}\n";
4453 return buf.str();
4454}
4455
4456std::string ClearCase::genTessellationEvaluationSource (void) const
4457{
4458 return s_basicColorTessEvalSource;
4459}
4460
4461class ViewportCallOrderCase : public TestCase
4462{
4463public:
4464 enum CallOrder
4465 {
4466 VIEWPORT_FIRST = 0,
4467 BBOX_FIRST,
4468
4469 ORDER_LAST
4470 };
4471
4472 ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder);
4473 ~ViewportCallOrderCase (void);
4474
4475private:
4476 void init (void);
4477 void deinit (void);
4478 IterateResult iterate (void);
4479
4480 void genVbo (void);
4481 void genProgram (void);
4482 bool verifyImage (const tcu::PixelBufferAccess& result);
4483
4484 std::string genVertexSource (void) const;
4485 std::string genFragmentSource (void) const;
4486 std::string genTessellationControlSource (void) const;
4487 std::string genTessellationEvaluationSource (void) const;
4488
4489 const CallOrder m_callOrder;
4490
4491 de::MovePtr<glu::Buffer> m_vbo;
4492 de::MovePtr<glu::ShaderProgram> m_program;
4493 int m_numVertices;
4494};
4495
4496ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder)
4497 : TestCase (context, name, description)
4498 , m_callOrder (callOrder)
4499 , m_numVertices (-1)
4500{
4501 DE_ASSERT(m_callOrder < ORDER_LAST);
4502}
4503
4504ViewportCallOrderCase::~ViewportCallOrderCase (void)
4505{
4506 deinit();
4507}
4508
4509void ViewportCallOrderCase::init (void)
4510{
4511 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4512 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4513
Mika Isojärvic22e16e2016-01-21 15:28:21 -08004514 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4515 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4516
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004517 m_testCtx.getLog()
4518 << tcu::TestLog::Message
4519 << "Testing call order of state setting functions have no effect on the rendering.\n"
4520 << "Setting viewport and bounding box in the following order:\n"
4521 << ((m_callOrder == VIEWPORT_FIRST)
4522 ? ("\tFirst viewport with glViewport function.\n")
4523 : ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4524 << ((m_callOrder == VIEWPORT_FIRST)
4525 ? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
4526 : ("\tThen viewport with glViewport function.\n"))
4527 << "Verifying rendering result."
4528 << tcu::TestLog::EndMessage;
4529
4530 // resources
4531 genVbo();
4532 genProgram();
4533}
4534
4535void ViewportCallOrderCase::deinit (void)
4536{
4537 m_vbo.clear();
4538 m_program.clear();
4539}
4540
4541ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
4542{
4543 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4544 const tcu::IVec2 viewportSize = tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4545 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
4546 tcu::Surface resultSurface (viewportSize.x(), viewportSize.y());
4547
4548 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4549 gl.clear(GL_COLOR_BUFFER_BIT);
4550
4551 // set state
4552 for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
4553 {
4554 if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
4555 (orderNdx == 1 && m_callOrder == BBOX_FIRST))
4556 {
4557 m_testCtx.getLog()
4558 << tcu::TestLog::Message
4559 << "Setting viewport to cover the left half of the render target.\n"
4560 << "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
4561 << tcu::TestLog::EndMessage;
4562
4563 gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
4564 }
4565 else
4566 {
4567 m_testCtx.getLog()
4568 << tcu::TestLog::Message
4569 << "Setting bounding box to cover the right half of the clip space.\n"
4570 << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
4571 << tcu::TestLog::EndMessage;
4572
Daniel Andrade Groppe485a2d12015-12-02 15:45:41 -06004573 gl.primitiveBoundingBox(0.0f, -1.0f, -1.0f, 1.0f,
4574 1.0f, 1.0f, 1.0f, 1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004575 }
4576 }
4577
4578 m_testCtx.getLog()
4579 << tcu::TestLog::Message
4580 << "Rendering mesh covering the right half of the clip space."
4581 << tcu::TestLog::EndMessage;
4582
4583 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4584 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
4585 gl.enableVertexAttribArray(posLocation);
4586 gl.useProgram(m_program->getProgram());
4587 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4588 gl.drawArrays(GL_PATCHES, 0, m_numVertices);
4589 GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
4590
4591 m_testCtx.getLog()
4592 << tcu::TestLog::Message
4593 << "Verifying image"
4594 << tcu::TestLog::EndMessage;
4595 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
4596
4597 if (!verifyImage(resultSurface.getAccess()))
4598 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4599 else
4600 {
4601 m_testCtx.getLog()
4602 << tcu::TestLog::Message
4603 << "Result ok."
4604 << tcu::TestLog::EndMessage
4605 << tcu::TestLog::ImageSet("Images", "Image verification")
4606 << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
4607 << tcu::TestLog::EndImageSet;
4608
4609 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4610 }
4611 return STOP;
4612}
4613
4614void ViewportCallOrderCase::genVbo (void)
4615{
4616 const int gridSize = 6;
4617 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4618 std::vector<tcu::Vec4> data (gridSize * gridSize * 2 * 3);
4619 std::vector<int> cellOrder (gridSize * gridSize * 2);
4620 de::Random rnd (0x55443322);
4621
4622 // generate grid with triangles in random order
4623 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4624 cellOrder[ndx] = ndx;
4625 rnd.shuffle(cellOrder.begin(), cellOrder.end());
4626
4627 // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
4628 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4629 {
4630 const int cellNdx = cellOrder[ndx];
4631 const bool cellSide = ((cellNdx % 2) == 0);
4632 const int cellX = (cellNdx / 2) % gridSize;
4633 const int cellY = (cellNdx / 2) / gridSize;
4634
4635 if (cellSide)
4636 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07004637 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4638 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4639 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004640 }
4641 else
4642 {
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07004643 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4644 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4645 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004646 }
4647 }
4648
4649 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4650 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4651 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
4652 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
4653
4654 m_numVertices = (int)data.size();
4655}
4656
4657void ViewportCallOrderCase::genProgram (void)
4658{
4659 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4660 glu::ProgramSources()
4661 << glu::VertexSource(genVertexSource())
4662 << glu::FragmentSource(genFragmentSource())
4663 << glu::TessellationControlSource(genTessellationControlSource())
4664 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4665
4666 m_testCtx.getLog()
4667 << tcu::TestLog::Section("Program", "Shader program")
4668 << *m_program
4669 << tcu::TestLog::EndSection;
4670
4671 if (!m_program->isOk())
4672 throw tcu::TestError("shader build failed");
4673}
4674
4675bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
4676{
Jarkko Pöyry7a161d22015-05-19 20:44:07 -07004677 const tcu::IVec2 insideBorder (deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
4678 const tcu::IVec2 outsideBorder (deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004679 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4680 bool anyError = false;
4681
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004682 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004683
4684 for (int y = 0; y < result.getHeight(); ++y)
4685 for (int x = 0; x < result.getWidth(); ++x)
4686 {
4687 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4688 const bool insideMeshArea = x >= insideBorder.x() && x <= insideBorder.x();
4689 const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
4690
4691 // inside mesh, allow green, yellow and any shade between
4692 // outside mesh, allow background (black) only
4693 // in the border area, allow anything
4694 if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
4695 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
4696 {
Dejan Mircevskic215aaa2015-11-06 17:08:44 -05004697 errorMask.setPixel(x, y, tcu::RGBA::red());
Jarkko Pöyry1f99d692014-11-17 14:21:54 -08004698 anyError = true;
4699 }
4700 }
4701
4702 if (anyError)
4703 {
4704 m_testCtx.getLog()
4705 << tcu::TestLog::Message
4706 << "Image verification failed."
4707 << tcu::TestLog::EndMessage
4708 << tcu::TestLog::ImageSet("Images", "Image verification")
4709 << tcu::TestLog::Image("ResultImage", "Result image", result)
4710 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4711 << tcu::TestLog::EndImageSet;
4712 }
4713
4714 return !anyError;
4715}
4716
4717std::string ViewportCallOrderCase::genVertexSource (void) const
4718{
4719 return s_yellowishPosOnlyVertexSource;
4720}
4721
4722std::string ViewportCallOrderCase::genFragmentSource (void) const
4723{
4724 return s_basicColorFragmentSource;
4725}
4726
4727std::string ViewportCallOrderCase::genTessellationControlSource (void) const
4728{
4729 return "#version 310 es\n"
4730 "#extension GL_EXT_tessellation_shader : require\n"
4731 "layout(vertices=3) out;\n"
4732 "in highp vec4 v_vertex_color[];\n"
4733 "out highp vec4 v_tess_eval_color[];\n"
4734 "void main()\n"
4735 "{\n"
4736 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4737 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4738 " gl_TessLevelOuter[0] = 2.8;\n"
4739 " gl_TessLevelOuter[1] = 2.8;\n"
4740 " gl_TessLevelOuter[2] = 2.8;\n"
4741 " gl_TessLevelInner[0] = 2.8;\n"
4742 "}\n";
4743}
4744
4745std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
4746{
4747 return s_basicColorTessEvalSource;
4748}
4749
4750} // anonymous
4751
4752PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
4753 : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
4754{
4755}
4756
4757PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
4758{
4759}
4760
4761void PrimitiveBoundingBoxTests::init (void)
4762{
4763 static const struct
4764 {
4765 const char* name;
4766 const char* description;
4767 deUint32 methodFlags;
4768 } stateSetMethods[] =
4769 {
4770 {
4771 "global_state",
4772 "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
4773 BBoxRenderCase::FLAG_SET_BBOX_STATE,
4774 },
4775 {
4776 "tessellation_set_per_draw",
4777 "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
4778 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
4779 },
4780 {
4781 "tessellation_set_per_primitive",
4782 "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
4783 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4784 },
4785 };
4786 static const struct
4787 {
4788 const char* name;
4789 const char* description;
4790 deUint32 stageFlags;
4791 } pipelineConfigs[] =
4792 {
4793 {
4794 "vertex_fragment",
4795 "Render with vertex-fragment program",
4796 0u
4797 },
4798 {
4799 "vertex_tessellation_fragment",
4800 "Render with vertex-tessellation{ctrl,eval}-fragment program",
4801 BBoxRenderCase::FLAG_TESSELLATION
4802 },
4803 {
4804 "vertex_geometry_fragment",
4805 "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
4806 BBoxRenderCase::FLAG_GEOMETRY
4807 },
4808 {
4809 "vertex_tessellation_geometry_fragment",
4810 "Render with vertex-geometry-fragment program",
4811 BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
4812 },
4813 };
4814 static const struct
4815 {
4816 const char* name;
4817 const char* description;
4818 deUint32 flags;
4819 deUint32 invalidFlags;
4820 deUint32 requiredFlags;
4821 } usageConfigs[] =
4822 {
4823 {
4824 "default_framebuffer_bbox_equal",
4825 "Render to default framebuffer, set tight bounding box",
4826 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4827 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4828 0
4829 },
4830 {
4831 "default_framebuffer_bbox_larger",
4832 "Render to default framebuffer, set padded bounding box",
4833 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4834 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4835 0
4836 },
4837 {
4838 "default_framebuffer_bbox_smaller",
4839 "Render to default framebuffer, set too small bounding box",
4840 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4841 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4842 0
4843 },
4844 {
4845 "fbo_bbox_equal",
4846 "Render to texture, set tight bounding box",
4847 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4848 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4849 0
4850 },
4851 {
4852 "fbo_bbox_larger",
4853 "Render to texture, set padded bounding box",
4854 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4855 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4856 0
4857 },
4858 {
4859 "fbo_bbox_smaller",
4860 "Render to texture, set too small bounding box",
4861 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4862 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4863 0
4864 },
4865 {
4866 "default_framebuffer",
4867 "Render to default framebuffer, set tight bounding box",
4868 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4869 0,
4870 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4871 },
4872 {
4873 "fbo",
4874 "Render to texture, set tight bounding box",
4875 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4876 0,
4877 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4878 },
4879 };
4880 enum PrimitiveRenderType
4881 {
4882 TYPE_TRIANGLE,
4883 TYPE_LINE,
4884 TYPE_POINT,
4885 };
4886 const struct
4887 {
4888 const char* name;
4889 const char* description;
4890 PrimitiveRenderType type;
4891 deUint32 flags;
4892 } primitiveTypes[] =
4893 {
4894 {
4895 "triangles",
4896 "Triangle render tests",
4897 TYPE_TRIANGLE,
4898 0
4899 },
4900 {
4901 "lines",
4902 "Line render tests",
4903 TYPE_LINE,
4904 0
4905 },
4906 {
4907 "points",
4908 "Point render tests",
4909 TYPE_POINT,
4910 0
4911 },
4912 {
4913 "wide_lines",
4914 "Wide line render tests",
4915 TYPE_LINE,
4916 LineRenderCase::LINEFLAG_WIDE
4917 },
4918 {
4919 "wide_points",
4920 "Wide point render tests",
4921 TYPE_POINT,
4922 PointRenderCase::POINTFLAG_WIDE
4923 },
4924 };
4925
4926 // .state_query
4927 {
4928 tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
4929 addChild(stateQueryGroup);
4930
4931 stateQueryGroup->addChild(new InitialValueCase (m_context, "initial_value", "Initial value case"));
4932 stateQueryGroup->addChild(new QueryCase (m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT));
4933 stateQueryGroup->addChild(new QueryCase (m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN));
4934 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT));
4935 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64));
4936 }
4937
4938 // .triangles
4939 // .(wide_)lines
4940 // .(wide_)points
4941 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
4942 {
4943 tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
4944 addChild(primitiveGroup);
4945
4946 for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
4947 {
4948 tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
4949 primitiveGroup->addChild(methodGroup);
4950
4951 for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
4952 {
4953 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
4954 (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0)
4955 {
4956 // invalid config combination
4957 }
4958 else
4959 {
4960 tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
4961 methodGroup->addChild(pipelineGroup);
4962
4963 for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
4964 {
4965 const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags |
4966 stateSetMethods[stateSetMethodNdx].methodFlags |
4967 pipelineConfigs[pipelineConfigNdx].stageFlags |
4968 usageConfigs[usageNdx].flags;
4969
4970 if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
4971 continue;
4972 if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
4973 continue;
4974
4975 switch (primitiveTypes[primitiveTypeNdx].type)
4976 {
4977 case TYPE_TRIANGLE:
4978 pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4979 break;
4980 case TYPE_LINE:
4981 pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4982 break;
4983 case TYPE_POINT:
4984 pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4985 break;
4986 default:
4987 DE_ASSERT(false);
4988 }
4989 }
4990 }
4991 }
4992 }
4993 }
4994
4995 // .depth
4996 {
4997 static const struct
4998 {
4999 const char* name;
5000 const char* description;
5001 DepthDrawCase::DepthType depthMethod;
5002 } depthMethods[] =
5003 {
5004 {
5005 "builtin_depth",
5006 "Fragment depth not modified in fragment shader",
5007 DepthDrawCase::DEPTH_BUILTIN
5008 },
5009 {
5010 "user_defined_depth",
5011 "Fragment depth is defined in the fragment shader",
5012 DepthDrawCase::DEPTH_USER_DEFINED
5013 },
5014 };
5015 static const struct
5016 {
5017 const char* name;
5018 const char* description;
5019 DepthDrawCase::BBoxState bboxState;
5020 DepthDrawCase::BBoxSize bboxSize;
5021 } depthCases[] =
5022 {
5023 {
5024 "global_state_bbox_equal",
5025 "Test tight bounding box with global bbox state",
5026 DepthDrawCase::STATE_GLOBAL,
5027 DepthDrawCase::BBOX_EQUAL,
5028 },
5029 {
5030 "global_state_bbox_larger",
5031 "Test padded bounding box with global bbox state",
5032 DepthDrawCase::STATE_GLOBAL,
5033 DepthDrawCase::BBOX_LARGER,
5034 },
5035 {
5036 "per_primitive_bbox_equal",
5037 "Test tight bounding box with tessellation output bbox",
5038 DepthDrawCase::STATE_PER_PRIMITIVE,
5039 DepthDrawCase::BBOX_EQUAL,
5040 },
5041 {
5042 "per_primitive_bbox_larger",
5043 "Test padded bounding box with tessellation output bbox",
5044 DepthDrawCase::STATE_PER_PRIMITIVE,
5045 DepthDrawCase::BBOX_LARGER,
5046 },
5047 };
5048
5049 tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
5050 addChild(depthGroup);
5051
5052 // .builtin_depth
5053 // .user_defined_depth
5054 for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
5055 {
5056 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
5057 depthGroup->addChild(group);
5058
5059 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
5060 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
5061 }
5062 }
5063
5064 // .blit_fbo
5065 {
5066 tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
5067 addChild(blitFboGroup);
5068
5069 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
5070 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT));
5071 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO));
5072 }
5073
5074 // .clear
5075 {
5076 tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
5077 addChild(clearGroup);
5078
5079 clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0));
5080 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT));
5081 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));
5082 clearGroup->addChild(new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT));
5083 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));
5084 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));
5085 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
5086 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));
5087 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));
5088 }
5089
5090 // .call_order (Khronos bug #13262)
5091 {
5092 tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
5093 addChild(callOrderGroup);
5094
5095 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
5096 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));
5097 }
5098}
5099
5100} // Functional
5101} // gles31
5102} // deqp