1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
5 * Copyright 2015 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Primitive bounding box tests.
22 *//*--------------------------------------------------------------------*/
24 #include "es31fPrimitiveBoundingBoxTests.hpp"
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"
58 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
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;
73 float& BoundingBox::getComponentAccess (int ndx)
75 DE_ASSERT(ndx >= 0 && ndx < 8);
82 const float& BoundingBox::getComponentAccess (int ndx) const
84 return const_cast<BoundingBox*>(this)->getComponentAccess(ndx);
93 static ProjectedBBox projectBoundingBox (const BoundingBox& bbox)
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());
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);
106 static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f)
111 vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * viewportSize.x();
112 vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * viewportSize.y();
113 vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * viewportSize.x();
114 vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * viewportSize.y();
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);
124 class InitialValueCase : public TestCase
127 InitialValueCase (Context& context, const char* name, const char* desc);
130 IterateResult iterate (void);
133 InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc)
134 : TestCase(context, name, desc)
138 void InitialValueCase::init (void)
140 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
141 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
144 InitialValueCase::IterateResult InitialValueCase::iterate (void)
146 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
147 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
149 gl.enableLogging(true);
152 << tcu::TestLog::Message
153 << "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)"
154 << tcu::TestLog::EndMessage;
156 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
157 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
159 if (!state.verifyValidity(m_testCtx))
163 << tcu::TestLog::Message
164 << "Got " << tcu::formatArray(&state[0], &state[8])
165 << tcu::TestLog::EndMessage;
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))
171 << tcu::TestLog::Message
172 << "Error, unexpected value"
173 << tcu::TestLog::EndMessage;
175 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value");
178 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
183 class QueryCase : public TestCase
196 QueryCase (Context& context, const char* name, const char* desc, QueryMethod method);
200 IterateResult iterate (void);
202 bool verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const;
204 const QueryMethod m_method;
207 QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method)
208 : TestCase (context, name, desc)
211 DE_ASSERT(method < QUERY_LAST);
214 void QueryCase::init (void)
216 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
217 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
220 QueryCase::IterateResult QueryCase::iterate (void)
222 static const BoundingBox fixedCases[] =
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) },
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;
237 cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases));
238 for (int ndx = 0; ndx < numRandomCases; ++ndx)
240 BoundingBox boundingBox;
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);
246 cases.push_back(boundingBox);
249 gl.enableLogging(true);
250 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
252 for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx)
254 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1));
255 const BoundingBox& boundingBox = cases[caseNdx];
257 gl.glPrimitiveBoundingBoxEXT(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());
260 if (!verifyState(gl, boundingBox))
261 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
267 bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const
273 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
276 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
277 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
279 if (!state.verifyValidity(m_testCtx))
283 << tcu::TestLog::Message
284 << "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8])
285 << tcu::TestLog::EndMessage;
287 for (int ndx = 0; ndx < 8; ++ndx)
288 if (state[ndx] != bbox.getComponentAccess(ndx))
294 << tcu::TestLog::Message
295 << "Error, unexpected value\n"
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;
307 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]> state;
310 gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
311 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
313 if (!state.verifyValidity(m_testCtx))
317 << tcu::TestLog::Message
318 << "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8])
319 << tcu::TestLog::EndMessage;
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)))
328 tcu::MessageBuilder builder(&m_testCtx.getLog());
330 builder << "Error, unexpected value\n"
333 for (int ndx = 0; ndx < 8; ++ndx)
335 const glw::GLint roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx));
336 const glw::GLint roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx));
341 if (roundDown == roundUp)
342 builder << roundDown;
344 builder << "{" << roundDown << ", " << roundUp << "}";
348 << tcu::TestLog::EndMessage;
356 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]> state;
359 gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
360 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
362 if (!state.verifyValidity(m_testCtx))
366 << tcu::TestLog::Message
367 << "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8])
368 << tcu::TestLog::EndMessage;
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)))
377 tcu::MessageBuilder builder(&m_testCtx.getLog());
379 builder << "Error, unexpected value\n"
382 for (int ndx = 0; ndx < 8; ++ndx)
384 const glw::GLint64 roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx));
385 const glw::GLint64 roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx));
390 if (roundDown == roundUp)
391 builder << roundDown;
393 builder << "{" << roundDown << ", " << roundUp << "}";
397 << tcu::TestLog::EndMessage;
405 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]> state;
408 gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
409 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
411 if (!state.verifyValidity(m_testCtx))
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;
421 for (int ndx = 0; ndx < 8; ++ndx)
422 if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE)))
427 tcu::MessageBuilder builder(&m_testCtx.getLog());
429 builder << "Error, unexpected value\n"
432 for (int ndx = 0; ndx < 8; ++ndx)
437 builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE"));
441 << tcu::TestLog::EndMessage;
453 class BBoxRenderCase : public TestCase
458 FLAG_RENDERTARGET_DEFAULT = 1u << 0, //!< render to default renderbuffer
459 FLAG_RENDERTARGET_FBO = 1u << 1, //!< render to framebuffer object
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
465 FLAG_TESSELLATION = 1u << 5, //!< use tessellation shader
466 FLAG_GEOMETRY = 1u << 6, //!< use geometry shader
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
472 FLAGBIT_USER_BIT = 10u //!< bits N and and up are reserved for subclasses
475 BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags);
476 ~BBoxRenderCase (void);
481 RENDERTARGET_DEFAULT,
493 RENDER_TARGET_MIN_SIZE = 256,
495 MIN_VIEWPORT_SIZE = 256,
496 MAX_VIEWPORT_SIZE = 512,
498 DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE);
504 VA_NUM_ATTRIB_VECS = 2,
507 enum AABBRoundDirection
513 struct IterationConfig
515 tcu::IVec2 viewportPos;
516 tcu::IVec2 viewportSize;
517 tcu::Vec2 patternPos; //!< in NDC
518 tcu::Vec2 patternSize; //!< in NDC
522 virtual void init (void);
523 virtual void deinit (void);
524 IterateResult iterate (void);
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;
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;
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;
540 void setupRender (const IterationConfig& config);
544 SHADER_FUNC_MIRROR_X,
545 SHADER_FUNC_MIRROR_Y,
546 SHADER_FUNC_INSIDE_BBOX,
549 const char* genShaderFunction (ShaderFunction func) const;
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;
559 de::MovePtr<glu::ShaderProgram> m_program;
560 de::MovePtr<glu::Buffer> m_vbo;
561 de::MovePtr<glu::Framebuffer> m_fbo;
564 std::vector<IterationConfig> m_iterationConfigs;
568 BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags)
569 : TestCase (context, name, description)
570 , m_renderTarget ((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO))
571 , m_bboxSize ((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER))
572 , m_hasTessellationStage ((flags & FLAG_TESSELLATION) != 0)
573 , m_hasGeometryStage ((flags & FLAG_GEOMETRY) != 0)
574 , m_useGlobalState ((flags & FLAG_SET_BBOX_STATE) != 0)
575 , m_calcPerPrimitiveBBox ((flags & FLAG_PER_PRIMITIVE_BBOX) != 0)
576 , m_numIterations (numIterations)
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)));
591 DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation
593 if (m_calcPerPrimitiveBBox)
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
600 BBoxRenderCase::~BBoxRenderCase (void)
605 void BBoxRenderCase::init (void)
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));
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");
622 // log case specifics
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")
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"))
639 << "Verifying rendering results are valid within the bounding box."
640 << tcu::TestLog::EndMessage;
645 glu::ProgramSources sources;
646 sources << glu::VertexSource(genVertexSource());
647 sources << glu::FragmentSource(genFragmentSource());
649 if (m_hasTessellationStage)
650 sources << glu::TessellationControlSource(genTessellationControlSource())
651 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
652 if (m_hasGeometryStage)
653 sources << glu::GeometrySource(genGeometrySource());
655 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
656 GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
659 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
660 m_testCtx.getLog() << *m_program;
663 if (!m_program->isOk())
664 throw tcu::TestError("failed to build program");
667 if (m_renderTarget == RENDERTARGET_FBO)
669 glu::Texture colorAttachment(m_context.getRenderContext());
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");
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");
680 // unbind to prevent texture name deletion from removing it from current fbo attachments
681 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
685 std::vector<tcu::Vec4> data;
687 getAttributeData(data);
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");
696 for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx)
697 m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize));
700 void BBoxRenderCase::deinit (void)
707 BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void)
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];
715 if (m_iteration == 0)
716 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
718 renderTestPattern(config);
719 verifyRenderResult(config);
721 if (++m_iteration < (int)m_iterationConfigs.size())
727 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const
729 de::Random rnd (seed);
730 IterationConfig 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());
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());
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);
748 if (m_bboxSize == BBOXSIZE_LARGER)
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;
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;
759 else if (m_bboxSize == BBOXSIZE_SMALLER)
762 config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x();
763 config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y();
765 config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x();
766 config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y();
772 tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const
774 const float halfPixel = 0.5f;
778 vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * viewportSize.x();
779 vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * viewportSize.y();
780 vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * viewportSize.x();
781 vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * viewportSize.y();
783 if (roundDir == ROUND_INWARDS)
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);
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);
801 void BBoxRenderCase::setupRender (const IterationConfig& config)
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");
808 TCU_CHECK(posLocation != -1);
809 TCU_CHECK(colLocation != -1);
810 TCU_CHECK(posScaleLocation != -1);
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;
824 if (!m_calcPerPrimitiveBBox)
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;
832 if (m_useGlobalState)
833 gl.primitiveBoundingBoxEXT(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());
836 // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
837 gl.primitiveBoundingBoxEXT(-2.0f, -2.0f, 0.0f, 1.0f,
838 -1.7f, -1.7f, 0.0f, 1.0f);
841 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
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);
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());
856 const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin");
857 const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax");
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());
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());
866 GLU_EXPECT_NO_ERROR(gl.getError(), "setup");
869 const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const
873 case SHADER_FUNC_MIRROR_X:
874 return "vec4 mirrorX(in highp vec4 p)\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"
882 case SHADER_FUNC_MIRROR_Y:
883 return "vec4 mirrorY(in highp vec4 p)\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"
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"
898 "bool fragmentInsideTheBBox(in highp float depth)\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"
907 " const highp float dEpsilon = 0.001;\n"
908 " if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n"
918 class GridRenderCase : public BBoxRenderCase
921 GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
922 ~GridRenderCase (void);
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;
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);
938 const int m_gridSize;
941 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
942 : BBoxRenderCase (context, name, description, 12, flags)
947 GridRenderCase::~GridRenderCase (void)
951 void GridRenderCase::init (void)
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;
960 BBoxRenderCase::init();
963 std::string GridRenderCase::genVertexSource (void) const
965 std::ostringstream buf;
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"
973 if (!m_hasTessellationStage)
975 DE_ASSERT(m_useGlobalState);
976 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
977 "uniform highp vec4 u_primitiveBBoxMax;\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"
985 buf << "void main()\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";
992 if (!m_hasTessellationStage)
994 DE_ASSERT(m_useGlobalState);
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";
1010 std::string GridRenderCase::genFragmentSource (void) const
1012 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1013 std::ostringstream buf;
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)
1022 " mediump vec4 baseColor = " << colorInputName << ";\n"
1023 " mediump float blueChannel;\n"
1024 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1025 " blueChannel = 0.0;\n"
1027 " blueChannel = 1.0;\n"
1028 " o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n"
1034 std::string GridRenderCase::genTessellationControlSource (void) const
1036 std::ostringstream buf;
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"
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";
1048 if (!m_calcPerPrimitiveBBox)
1050 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1051 "uniform highp vec4 u_primitiveBBoxMax;\n";
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";
1058 if (m_calcPerPrimitiveBBox)
1061 if (m_hasGeometryStage)
1062 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1063 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1065 buf << "vec4 transformVec(in highp vec4 p)\n"
1067 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\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"
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";
1083 if (m_calcPerPrimitiveBBox)
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";
1096 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1097 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1100 if (!m_useGlobalState)
1102 " gl_BoundingBoxEXT[0] = bboxMin;\n"
1103 " gl_BoundingBoxEXT[1] = bboxMax;\n";
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"
1115 std::string GridRenderCase::genTessellationEvaluationSource (void) const
1117 std::ostringstream buf;
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"
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"
1134 "precise gl_Position;\n"
1136 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
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"
1152 std::string GridRenderCase::genGeometrySource (void) const
1154 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1155 std::ostringstream buf;
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"
1162 "in highp vec4 " << colorInputName << "[3];\n"
1163 "out highp vec4 geo_color;\n"
1164 "uniform highp vec4 u_posScale;\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)
1174 "void setVisualizationVaryings()\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"
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"
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"
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"
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"
1208 GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1210 return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
1213 void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
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);
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());
1225 data.resize(m_gridSize * m_gridSize * 6 * 2);
1226 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
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);
1233 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+0) / float(m_gridSize), (cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1234 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1235 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+1) / float(m_gridSize), (cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1236 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1237 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+0) / float(m_gridSize), (cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1238 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1239 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+0) / float(m_gridSize), (cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1240 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1241 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+1) / float(m_gridSize), (cellY+0) / float(m_gridSize), 0.0f, 1.0f);
1242 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1243 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((cellX+1) / float(m_gridSize), (cellY+1) / float(m_gridSize), 0.0f, 1.0f);
1244 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1248 void GridRenderCase::renderTestPattern (const IterationConfig& config)
1250 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1252 setupRender(config);
1254 if (m_hasTessellationStage)
1256 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1257 const glw::GLfloat tessLevel = 2.8f; // will be rounded up
1259 TCU_CHECK(tessLevelPos != -1);
1261 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1263 gl.uniform1f(tessLevelPos, tessLevel);
1264 gl.patchParameteri(GL_PATCH_VERTICES, 3);
1265 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1268 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage;
1270 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
1271 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1274 void GridRenderCase::verifyRenderResult (const IterationConfig& config)
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;
1285 if (!m_calcPerPrimitiveBBox)
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;
1299 << tcu::TestLog::Message
1300 << "Verifying render result."
1301 << tcu::TestLog::EndMessage;
1304 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1305 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1307 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
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)
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();
1327 if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
1331 else if (insideGrid)
1333 // expect green, yellow or a combination of these
1334 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
1339 // boundary, allow anything
1344 errorMask.setPixel(x, y, tcu::RGBA::red);
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;
1360 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
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;
1374 class LineRenderCase : public BBoxRenderCase
1379 LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide lines
1382 LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
1383 ~LineRenderCase (void);
1388 GREEN_COMPONENT_NDX = 1,
1389 BLUE_COMPONENT_NDX = 2,
1391 SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line
1392 SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX,
1397 DIRECTION_HORIZONTAL = 0,
1403 std::string genVertexSource (void) const;
1404 std::string genFragmentSource (void) const;
1405 std::string genTessellationControlSource (void) const;
1406 std::string genTessellationEvaluationSource (void) const;
1407 std::string genGeometrySource (void) const;
1409 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
1410 void getAttributeData (std::vector<tcu::Vec4>& data) const;
1411 void renderTestPattern (const IterationConfig& config);
1412 void verifyRenderResult (const IterationConfig& config);
1414 tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const;
1415 bool scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1416 bool scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const;
1417 bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const;
1418 tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const;
1419 bool checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const;
1420 void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const;
1422 const int m_patternSide;
1423 const bool m_isWideLineCase;
1424 const int m_wideLineLineWidth;
1427 LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
1428 : BBoxRenderCase (context, name, description, 12, flags)
1429 , m_patternSide (12)
1430 , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0)
1431 , m_wideLineLineWidth (5)
1435 LineRenderCase::~LineRenderCase (void)
1439 void LineRenderCase::init (void)
1442 << tcu::TestLog::Message
1443 << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1444 << "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1445 << "Line segments are in random order, varying pattern size and location for each iteration.\n"
1446 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1447 << tcu::TestLog::EndMessage;
1449 if (m_isWideLineCase)
1451 glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1452 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1454 if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1455 throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1458 BBoxRenderCase::init();
1461 std::string LineRenderCase::genVertexSource (void) const
1463 std::ostringstream buf;
1465 buf << "#version 310 es\n"
1466 "in highp vec4 a_position;\n"
1467 "in highp vec4 a_color;\n"
1468 "out highp vec4 vtx_color;\n"
1469 "uniform highp vec4 u_posScale;\n"
1470 "uniform highp float u_lineWidth;\n"
1472 if (!m_hasTessellationStage)
1474 DE_ASSERT(m_useGlobalState);
1475 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1476 "uniform highp vec4 u_primitiveBBoxMax;\n"
1478 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1479 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1480 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1483 buf << "void main()\n"
1485 " highp vec2 patternOffset = u_posScale.xy;\n"
1486 " highp vec2 patternScale = u_posScale.zw;\n"
1487 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1488 " vtx_color = a_color;\n";
1489 if (!m_hasTessellationStage)
1491 DE_ASSERT(m_useGlobalState);
1493 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n"
1494 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
1495 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
1496 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
1497 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
1498 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
1499 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
1506 std::string LineRenderCase::genFragmentSource (void) const
1508 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1509 std::ostringstream buf;
1511 buf << "#version 310 es\n"
1512 "in mediump vec4 " << colorInputName << ";\n"
1513 "layout(location = 0) out mediump vec4 o_color;\n"
1514 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1518 " mediump vec4 baseColor = " << colorInputName << ";\n"
1519 " mediump float redChannel;\n"
1520 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1521 " redChannel = 0.0;\n"
1523 " redChannel = 1.0;\n"
1524 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1530 std::string LineRenderCase::genTessellationControlSource (void) const
1532 std::ostringstream buf;
1534 buf << "#version 310 es\n"
1535 "#extension GL_EXT_tessellation_shader : require\n"
1536 "#extension GL_EXT_primitive_bounding_box : require\n"
1537 "layout(vertices=2) out;"
1539 "in highp vec4 vtx_color[];\n"
1540 "out highp vec4 tess_ctrl_color[];\n"
1541 "uniform highp float u_tessellationLevel;\n"
1542 "uniform highp vec4 u_posScale;\n"
1543 "uniform highp float u_lineWidth;\n";
1545 if (!m_calcPerPrimitiveBBox)
1547 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1548 "uniform highp vec4 u_primitiveBBoxMax;\n";
1551 buf << "patch out highp float vp_bbox_expansionSize;\n"
1552 "patch out highp vec3 vp_bbox_clipMin;\n"
1553 "patch out highp vec3 vp_bbox_clipMax;\n";
1555 if (m_calcPerPrimitiveBBox)
1558 if (m_hasGeometryStage)
1559 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1560 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1562 buf << "vec4 transformVec(in highp vec4 p)\n"
1564 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
1571 " // convert to nonsensical coordinates, just in case\n"
1572 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1573 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1575 " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1576 " gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1578 if (m_calcPerPrimitiveBBox)
1581 " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1582 " transformVec(gl_in[1].gl_Position));\n"
1583 " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1584 " transformVec(gl_in[1].gl_Position));\n";
1589 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1590 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1593 if (!m_useGlobalState)
1595 " gl_BoundingBoxEXT[0] = bboxMin;\n"
1596 " gl_BoundingBoxEXT[1] = bboxMax;\n";
1598 buf << " vp_bbox_expansionSize = u_lineWidth;\n"
1599 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1600 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1601 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1602 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1608 std::string LineRenderCase::genTessellationEvaluationSource (void) const
1610 std::ostringstream buf;
1612 buf << "#version 310 es\n"
1613 "#extension GL_EXT_tessellation_shader : require\n"
1614 "layout(isolines) in;"
1616 "in highp vec4 tess_ctrl_color[];\n"
1617 "out highp vec4 tess_color;\n"
1618 "uniform highp vec4 u_posScale;\n"
1620 "patch in highp float vp_bbox_expansionSize;\n"
1621 "patch in highp vec3 vp_bbox_clipMin;\n"
1622 "patch in highp vec3 vp_bbox_clipMax;\n"
1623 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
1624 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
1625 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1626 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1629 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1630 " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1631 " tess_color = tess_ctrl_color[0];\n"
1632 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1633 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
1634 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
1640 std::string LineRenderCase::genGeometrySource (void) const
1642 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1643 std::ostringstream buf;
1645 buf << "#version 310 es\n"
1646 "#extension GL_EXT_geometry_shader : require\n"
1647 "layout(lines) in;\n"
1648 "layout(max_vertices=5, line_strip) out;\n"
1650 "in highp vec4 " << colorInputName << "[2];\n"
1651 "out highp vec4 geo_color;\n"
1652 "uniform highp vec4 u_posScale;\n"
1655 "flat in highp float v_geo_bbox_expansionSize[2];\n"
1656 "flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1657 "flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1658 "flat out highp vec3 v_bbox_clipMin;\n"
1659 "flat out highp vec3 v_bbox_clipMax;\n"
1660 "flat out highp float v_bbox_expansionSize;\n"
1661 << genShaderFunction(SHADER_FUNC_MIRROR_X)
1663 "void setVisualizationVaryings()\n"
1665 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1666 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1667 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1671 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1672 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1673 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1674 " highp vec4 lineColor = " << colorInputName << "[0];\n"
1676 " // output two separate primitives, just in case\n"
1677 " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1678 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1679 " EndPrimitive();\n"
1681 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1682 " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1683 " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1684 " EndPrimitive();\n"
1690 LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
1692 const int numMaxAttempts = 128;
1694 // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1695 for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1697 const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1699 if (config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * m_patternSide * m_wideLineLineWidth &&
1700 config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * m_patternSide * m_wideLineLineWidth)
1707 return IterationConfig();
1710 void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
1712 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
1713 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
1714 std::vector<int> cellOrder (m_patternSide * m_patternSide * 2);
1715 de::Random rnd (0xDE12345);
1717 // generate crosshatch pattern with segments in random order
1718 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1719 cellOrder[ndx] = ndx;
1720 rnd.shuffle(cellOrder.begin(), cellOrder.end());
1722 data.resize(cellOrder.size() * 4);
1723 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1725 const int segmentID = cellOrder[ndx];
1726 const int direction = segmentID & 0x01;
1727 const int majorCoord = (segmentID >> 1) / m_patternSide;
1728 const int minorCoord = (segmentID >> 1) % m_patternSide;
1732 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_patternSide), majorCoord / float(m_patternSide), 0.0f, 1.0f);
1733 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1734 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_patternSide), (majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f);
1735 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1739 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(majorCoord / float(m_patternSide), minorCoord / float(m_patternSide), 0.0f, 1.0f);
1740 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1741 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((majorCoord + 1) / float(m_patternSide), minorCoord / float(m_patternSide), 0.0f, 1.0f);
1742 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1747 void LineRenderCase::renderTestPattern (const IterationConfig& config)
1749 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1751 setupRender(config);
1753 if (m_hasTessellationStage)
1755 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1756 const glw::GLfloat tessLevel = 2.8f; // will be rounded up
1758 TCU_CHECK(tessLevelPos != -1);
1760 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
1762 gl.uniform1f(tessLevelPos, tessLevel);
1763 gl.patchParameteri(GL_PATCH_VERTICES, 2);
1764 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1767 if (m_isWideLineCase)
1768 gl.lineWidth((float)m_wideLineLineWidth);
1770 gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f));
1772 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1774 gl.enable(GL_BLEND);
1775 gl.blendFunc(GL_ONE, GL_ONE);
1776 gl.blendEquation(GL_FUNC_ADD);
1778 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
1779 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1782 void LineRenderCase::verifyRenderResult (const IterationConfig& config)
1784 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1785 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
1786 const float lineWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f);
1787 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
1788 const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1789 const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL);
1790 const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL);
1791 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
1792 de::max(viewportBBoxArea.y(), 0),
1793 de::min(viewportBBoxArea.z(), config.viewportSize.x()),
1794 de::min(viewportBBoxArea.w(), config.viewportSize.y()));
1796 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
1797 bool anyError = false;
1798 int messageLimitCounter = 8;
1800 if (!m_calcPerPrimitiveBBox)
1802 << tcu::TestLog::Message
1803 << "Projected bounding box: (clip space)\n"
1804 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1805 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1806 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1807 << "In viewport coordinates:\n"
1808 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1809 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1810 << "Verifying render results within the bounding box:\n"
1811 << tcu::TestLog::EndMessage;
1814 << tcu::TestLog::Message
1815 << "Verifying render result:"
1816 << tcu::TestLog::EndMessage;
1819 << tcu::TestLog::Message
1820 << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
1821 << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n"
1822 << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n"
1823 << tcu::TestLog::EndMessage;
1826 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1827 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
1830 for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
1831 anyError |= !scanRow(viewportSurface.getAccess(),
1833 verificationArea.x(),
1834 verificationArea.z(),
1835 expectedVerticalLines,
1836 messageLimitCounter);
1839 for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
1840 anyError |= !scanColumn(viewportSurface.getAccess(),
1842 verificationArea.y(),
1843 verificationArea.w(),
1844 expectedHorizontalLines,
1845 messageLimitCounter);
1849 if (messageLimitCounter < 0)
1850 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage;
1853 << tcu::TestLog::Message
1854 << "Image verification failed."
1855 << tcu::TestLog::EndMessage
1856 << tcu::TestLog::ImageSet("Images", "Image verification")
1857 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1858 << tcu::TestLog::EndImageSet;
1860 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1865 << tcu::TestLog::Message
1866 << "Result image ok."
1867 << tcu::TestLog::EndMessage
1868 << tcu::TestLog::ImageSet("Images", "Image verification")
1869 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1870 << tcu::TestLog::EndImageSet;
1874 tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const
1876 // pattern is not symmetric due to mirroring
1877 const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0));
1878 const int patternEndNdx = patternStartNdx + m_patternSide;
1880 int numLinesMin = 0;
1881 int numLinesMax = 0;
1883 for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
1885 const float linePos = (patternStart + (lineNdx / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
1886 const float lineWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f);
1888 if (linePos * viewportArea > queryAreaBegin + 1.0f &&
1889 linePos * viewportArea < queryAreaEnd - 1.0f)
1891 // line center is within the area
1895 else if (linePos * viewportArea > queryAreaBegin - lineWidth*0.5f - 1.0f &&
1896 linePos * viewportArea < queryAreaEnd + lineWidth*0.5f + 1.0f)
1898 // line could leak into area
1903 return tcu::IVec2(numLinesMin, numLinesMax);
1906 bool LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1908 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
1909 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
1910 return numLinesOk && lineWidthOk;
1913 bool LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const
1915 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
1916 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter);
1917 return numLinesOk && lineWidthOk;
1920 bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const
1922 // Num maxima == num lines
1923 const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
1924 const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx);
1925 const int numMaxima = numMinimaMaxima.y();
1928 if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
1931 if (--messageLimitCounter < 0)
1936 << tcu::TestLog::Message
1937 << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n"
1938 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1939 << tcu::TestLog::EndMessage;
1942 << tcu::TestLog::Message
1943 << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n"
1944 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima
1945 << tcu::TestLog::EndMessage;
1950 tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const
1952 DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
1954 int previousValue = -1;
1955 int previousSign = 0;
1959 for (int y = 0; y < access.getHeight(); ++y)
1960 for (int x = 0; x < access.getWidth(); ++x)
1962 const int componentValue = access.getPixelInt(x, y)[componentNdx];
1964 if (previousValue != -1)
1966 const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0);
1968 // local minima/maxima in sign changes (zero signless)
1969 if (sign != 0 && sign == -previousSign)
1971 previousSign = sign;
1978 else if (sign != 0 && previousSign == 0)
1980 previousSign = sign;
1982 // local extreme at the start boundary
1990 previousValue = componentValue;
1993 // local extreme at the end boundary
1994 if (previousSign > 0)
1996 else if (previousSign < 0)
2004 return tcu::IVec2(numMinima, numMaxima);
2007 bool LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const
2009 const bool multisample = m_context.getRenderTarget().getNumSamples() > 1;
2010 const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2011 const tcu::IVec2 lineWidthRange = (multisample)
2012 ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel
2013 : (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2016 bool bboxLimitedLine = false;
2017 bool anyError = false;
2019 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2021 // fragments before begin?
2022 if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2024 bboxLimitedLine = true;
2026 for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2028 if (cursor.x() < 0 || cursor.y() < 0)
2032 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2041 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2043 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2049 // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2050 const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2052 if (incorrectLineWidth)
2055 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2059 bboxLimitedLine = false;
2063 // fragments after end?
2066 for (tcu::IVec2 cursor = end;; cursor += advance)
2068 if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2070 if (lineWidth > lineWidthRange.y())
2073 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2078 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2084 // only check that line width is not larger than expected. Line width may be smaller
2085 // since the scanning 'cursor' is now outside the bounding box.
2086 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2088 if (incorrectLineWidth)
2091 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2102 void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const
2104 if (--messageLimitCounter < 0)
2108 << tcu::TestLog::Message
2109 << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2110 << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth
2111 << tcu::TestLog::EndMessage;
2114 class PointRenderCase : public BBoxRenderCase
2119 POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points
2121 struct GeneratedPoint
2128 PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags);
2129 ~PointRenderCase (void);
2132 enum ResultPointType
2141 std::string genVertexSource (void) const;
2142 std::string genFragmentSource (void) const;
2143 std::string genTessellationControlSource (void) const;
2144 std::string genTessellationEvaluationSource (void) const;
2145 std::string genGeometrySource (void) const;
2147 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const;
2148 void generateAttributeData (void);
2149 void getAttributeData (std::vector<tcu::Vec4>& data) const;
2150 void renderTestPattern (const IterationConfig& config);
2151 void verifyRenderResult (const IterationConfig& config);
2153 void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const;
2154 bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2155 bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter);
2156 bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter);
2157 bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter);
2158 tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const;
2160 const int m_numStripes;
2161 const bool m_isWidePointCase;
2162 std::vector<tcu::Vec4> m_attribData;
2165 PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags)
2166 : BBoxRenderCase (context, name, description, 12, flags)
2168 , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0)
2172 PointRenderCase::~PointRenderCase (void)
2176 void PointRenderCase::init (void)
2178 if (m_isWidePointCase)
2181 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2182 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2183 if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2184 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2188 glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2189 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2191 if (pointSizeRange[1] < 5.0f)
2192 throw tcu::NotSupportedError("Test requires point size 5.0");
2197 << tcu::TestLog::Message
2198 << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2199 << "Half of the points are green, half blue. Using additive blending.\n"
2200 << "Points are in random order, varying pattern size and location for each iteration.\n"
2201 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2202 << tcu::TestLog::EndMessage;
2204 generateAttributeData();
2206 BBoxRenderCase::init();
2209 void PointRenderCase::deinit (void)
2212 m_attribData = std::vector<tcu::Vec4>();
2215 BBoxRenderCase::deinit();
2218 std::string PointRenderCase::genVertexSource (void) const
2220 std::ostringstream buf;
2222 buf << "#version 310 es\n"
2223 "in highp vec4 a_position;\n"
2224 "in highp vec4 a_color;\n"
2225 "out highp vec4 vtx_color;\n"
2226 "uniform highp vec4 u_posScale;\n"
2228 if (!m_hasTessellationStage)
2230 DE_ASSERT(m_useGlobalState);
2231 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2232 "uniform highp vec4 u_primitiveBBoxMax;\n"
2234 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n"
2235 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2236 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2240 buf << "void main()\n"
2242 " highp vec2 patternOffset = u_posScale.xy;\n"
2243 " highp vec2 patternScale = u_posScale.zw;\n"
2244 " highp float pointSize = "
2245 << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2247 << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2248 " gl_PointSize = pointSize;\n"
2249 " vtx_color = a_color;\n";
2251 if (!m_hasTessellationStage)
2253 DE_ASSERT(m_useGlobalState);
2255 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n"
2256 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n"
2257 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n"
2258 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n"
2259 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n"
2260 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n"
2261 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n";
2268 std::string PointRenderCase::genFragmentSource (void) const
2270 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2271 std::ostringstream buf;
2273 buf << "#version 310 es\n"
2274 "in mediump vec4 " << colorInputName << ";\n"
2275 "layout(location = 0) out mediump vec4 o_color;\n"
2276 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2280 " mediump vec4 baseColor = " << colorInputName << ";\n"
2281 " mediump float redChannel;\n"
2282 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2283 " redChannel = 0.0;\n"
2285 " redChannel = 1.0;\n"
2286 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2292 std::string PointRenderCase::genTessellationControlSource (void) const
2294 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2295 std::ostringstream buf;
2297 buf << "#version 310 es\n"
2298 "#extension GL_EXT_tessellation_shader : require\n"
2299 "#extension GL_EXT_primitive_bounding_box : require\n"
2300 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2301 << "layout(vertices=1) out;"
2303 "in highp vec4 vtx_color[];\n"
2304 "out highp vec4 tess_ctrl_color[];\n"
2305 "uniform highp float u_tessellationLevel;\n"
2306 "uniform highp vec4 u_posScale;\n";
2308 if (!m_calcPerPrimitiveBBox)
2310 buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2311 "uniform highp vec4 u_primitiveBBoxMax;\n";
2314 buf << "patch out highp vec3 vp_bbox_clipMin;\n"
2315 "patch out highp vec3 vp_bbox_clipMax;\n";
2317 if (m_calcPerPrimitiveBBox)
2320 if (m_hasGeometryStage)
2321 buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2322 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2324 buf << "vec4 transformVec(in highp vec4 p)\n"
2326 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n"
2333 " // convert to nonsensical coordinates, just in case\n"
2334 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2335 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2337 " gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2338 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2339 " gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2340 " gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2341 " gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2342 " gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2344 if (m_calcPerPrimitiveBBox)
2348 if (m_hasGeometryStage)
2349 buf << " const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2350 " const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2352 buf << " const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2353 " const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2355 buf << " highp vec2 patternScale = u_posScale.zw;\n"
2356 " highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n"
2357 " highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n";
2362 " highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2363 " highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2365 if (!m_useGlobalState)
2367 " gl_BoundingBoxEXT[0] = bboxMin;\n"
2368 " gl_BoundingBoxEXT[1] = bboxMax;\n";
2370 buf << " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2371 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2372 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2373 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2379 std::string PointRenderCase::genTessellationEvaluationSource (void) const
2381 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2382 std::ostringstream buf;
2384 buf << "#version 310 es\n"
2385 "#extension GL_EXT_tessellation_shader : require\n"
2386 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : (""))
2387 << "layout(quads, point_mode) in;"
2389 "in highp vec4 tess_ctrl_color[];\n"
2390 "out highp vec4 tess_color;\n"
2391 "uniform highp vec4 u_posScale;\n"
2393 "patch in highp vec3 vp_bbox_clipMin;\n"
2394 "patch in highp vec3 vp_bbox_clipMax;\n"
2395 << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : (""))
2396 << "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n"
2397 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
2399 << genShaderFunction(SHADER_FUNC_MIRROR_Y)
2402 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2403 " highp vec2 patternScale = u_posScale.zw;\n"
2404 " highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2405 " highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n"
2406 " gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2408 if (tessellationWidePoints)
2409 buf << " gl_PointSize = pointSize;\n";
2411 buf << " tess_color = tess_ctrl_color[0];\n"
2412 << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : (""))
2413 << " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n"
2414 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n"
2420 std::string PointRenderCase::genGeometrySource (void) const
2422 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2423 std::ostringstream buf;
2425 buf << "#version 310 es\n"
2426 "#extension GL_EXT_geometry_shader : require\n"
2427 << ((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : (""))
2428 << "layout(points) in;\n"
2429 "layout(max_vertices=3, points) out;\n"
2431 "in highp vec4 " << colorInputName << "[1];\n"
2432 "out highp vec4 geo_color;\n"
2433 "uniform highp vec4 u_posScale;\n"
2435 "flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2436 "flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2437 "flat out highp vec3 v_bbox_clipMin;\n"
2438 "flat out highp vec3 v_bbox_clipMax;\n"
2439 "flat out highp float v_bbox_expansionSize;\n"
2441 << genShaderFunction(SHADER_FUNC_MIRROR_X)
2445 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2446 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2447 " highp vec4 pointColor = " << colorInputName << "[0];\n"
2448 " highp vec2 patternScale = u_posScale.zw;\n"
2449 " highp float pointSize = "
2450 << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2453 " highp vec4 offsets[3] =\n"
2455 " vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2456 " vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2457 " vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2459 " for (int ndx = 0; ndx < 3; ++ndx)\n"
2461 " gl_Position = p0 + offsets[ndx];\n";
2463 if (m_isWidePointCase)
2464 buf << " gl_PointSize = pointSize;\n";
2466 buf << " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2467 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2468 " v_bbox_expansionSize = pointSize;\n"
2469 " geo_color = pointColor;\n"
2477 PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const
2479 IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize);
2481 // equal or larger -> expand according to shader expansion
2482 if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2484 const tcu::Vec2 patternScale = config.patternSize;
2486 if (m_hasTessellationStage)
2488 config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2489 config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2491 if (m_hasGeometryStage)
2493 config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2494 config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2501 void PointRenderCase::generateAttributeData (void)
2503 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
2504 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f);
2505 std::vector<int> cellOrder (m_numStripes * m_numStripes * 2);
2506 de::Random rnd (0xDE22446);
2508 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2509 cellOrder[ndx] = ndx;
2510 rnd.shuffle(cellOrder.begin(), cellOrder.end());
2512 m_attribData.resize(cellOrder.size() * 2);
2513 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
2515 const int pointID = cellOrder[ndx];
2516 const int direction = pointID & 0x01;
2517 const int majorCoord = (pointID >> 1) / m_numStripes;
2518 const int minorCoord = (pointID >> 1) % m_numStripes;
2522 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_numStripes), majorCoord / float(m_numStripes), 0.0f, 1.0f);
2523 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
2527 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((majorCoord + 0.5f) / float(m_numStripes), (minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f);
2528 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
2533 void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const
2535 data = m_attribData;
2538 void PointRenderCase::renderTestPattern (const IterationConfig& config)
2540 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2542 setupRender(config);
2544 if (m_hasTessellationStage)
2546 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
2547 const glw::GLfloat tessLevel = 0.8f; // will be rounded up
2549 TCU_CHECK(tessLevelPos != -1);
2551 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage;
2553 gl.uniform1f(tessLevelPos, tessLevel);
2554 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2555 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
2558 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
2560 gl.enable(GL_BLEND);
2561 gl.blendFunc(GL_ONE, GL_ONE);
2562 gl.blendEquation(GL_FUNC_ADD);
2564 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
2565 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2568 void PointRenderCase::verifyRenderResult (const IterationConfig& config)
2570 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2571 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
2572 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
2574 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y());
2575 int logFloodCounter = 8;
2577 std::vector<GeneratedPoint> refPoints;
2579 if (!m_calcPerPrimitiveBBox)
2581 << tcu::TestLog::Message
2582 << "Projected bounding box: (clip space)\n"
2583 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2584 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2585 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2586 << "In viewport coordinates:\n"
2587 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2588 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2589 << "Verifying render results within the bounding box:\n"
2590 << tcu::TestLog::EndMessage;
2593 << tcu::TestLog::Message
2594 << "Verifying render result:"
2595 << tcu::TestLog::EndMessage;
2598 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2599 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess());
2601 genReferencePointData(config, refPoints);
2603 if (m_isWidePointCase)
2604 anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2606 anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
2610 if (logFloodCounter < 0)
2611 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage;
2614 << tcu::TestLog::Message
2615 << "Image verification failed."
2616 << tcu::TestLog::EndMessage
2617 << tcu::TestLog::ImageSet("Images", "Image verification")
2618 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2619 << tcu::TestLog::EndImageSet;
2621 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2626 << tcu::TestLog::Message
2627 << "Result image ok."
2628 << tcu::TestLog::EndMessage
2629 << tcu::TestLog::ImageSet("Images", "Image verification")
2630 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2631 << tcu::TestLog::EndImageSet;
2637 bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const
2639 if (a.center.y() < b.center.y())
2641 else if (a.center.y() > b.center.y())
2644 return (a.center.x() < b.center.x());
2648 void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const
2650 std::vector<GeneratedPoint> currentPoints;
2653 currentPoints.resize(m_attribData.size() / 2);
2654 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2656 currentPoints[ndx].center = m_attribData[ndx*2].swizzle(0, 1);
2657 currentPoints[ndx].even = (m_attribData[ndx*2 + 1].y() == 1.0f); // is green
2658 currentPoints[ndx].size = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
2662 if (m_hasTessellationStage)
2664 std::vector<GeneratedPoint> tessellatedPoints;
2666 tessellatedPoints.resize(currentPoints.size() * 4);
2667 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2669 const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
2671 tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f);
2672 tessellatedPoints[4 * ndx + 0].size = currentPoints[ndx].size;
2673 tessellatedPoints[4 * ndx + 0].even = currentPoints[ndx].even;
2675 tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2( 0.07f, -0.07f);
2676 tessellatedPoints[4 * ndx + 1].size = currentPoints[ndx].size;
2677 tessellatedPoints[4 * ndx + 1].even = currentPoints[ndx].even;
2679 tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2( 0.07f, 0.07f);
2680 tessellatedPoints[4 * ndx + 2].size = currentPoints[ndx].size;
2681 tessellatedPoints[4 * ndx + 2].even = currentPoints[ndx].even;
2683 tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f);
2684 tessellatedPoints[4 * ndx + 3].size = currentPoints[ndx].size;
2685 tessellatedPoints[4 * ndx + 3].even = currentPoints[ndx].even;
2688 currentPoints.swap(tessellatedPoints);
2692 if (m_hasGeometryStage)
2694 std::vector<GeneratedPoint> geometryShadedPoints;
2696 geometryShadedPoints.resize(currentPoints.size() * 3);
2697 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2699 const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
2701 geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2( 0.05f, 0.03f);
2702 geometryShadedPoints[3 * ndx + 0].size = currentPoints[ndx].size;
2703 geometryShadedPoints[3 * ndx + 0].even = currentPoints[ndx].even;
2705 geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f);
2706 geometryShadedPoints[3 * ndx + 1].size = currentPoints[ndx].size;
2707 geometryShadedPoints[3 * ndx + 1].even = currentPoints[ndx].even;
2709 geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f);
2710 geometryShadedPoints[3 * ndx + 2].size = currentPoints[ndx].size;
2711 geometryShadedPoints[3 * ndx + 2].even = currentPoints[ndx].even;
2714 currentPoints.swap(geometryShadedPoints);
2717 // sort from left to right, top to bottom
2718 std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
2720 // map to pattern space
2721 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
2722 currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
2724 currentPoints.swap(data);
2727 bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2729 bool anyError = false;
2731 // check that there is something near each sample
2732 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2734 const float epsilon = 1.0e-6f;
2735 const GeneratedPoint& refPoint = refPoints[pointNdx];
2737 // skip points not in the the bbox, treat boundary as "in"
2738 if (refPoint.center.x() < bbox.min.x() - epsilon ||
2739 refPoint.center.y() < bbox.min.y() - epsilon ||
2740 refPoint.center.x() > bbox.max.x() + epsilon ||
2741 refPoint.center.y() > bbox.max.y() + epsilon)
2745 // transform to viewport coords
2746 const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * viewport.getWidth()), deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * viewport.getHeight()));
2748 // find rasterized point in the result
2749 if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1)
2751 // viewport boundary, assume point is fine
2755 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel
2756 bool foundResult = false;
2758 // check neighborhood
2759 for (int dy = -1; dy < 2 && !foundResult; ++dy)
2760 for (int dx = -1; dx < 2 && !foundResult; ++dx)
2762 const tcu::IVec2 testPos (pixelCenter.x() + dx, pixelCenter.y() + dy);
2763 const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y());
2765 if (color.toIVec()[componentNdx] > 0)
2773 if (--logFloodCounter >= 0)
2776 << tcu::TestLog::Message
2777 << "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2778 << tcu::TestLog::EndMessage;
2788 bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter)
2790 bool anyError = false;
2792 // check that there is something near each sample
2793 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
2795 const GeneratedPoint& refPoint = refPoints[pointNdx];
2797 if (refPoint.center.x() >= bbox.min.x() &&
2798 refPoint.center.y() >= bbox.min.y() &&
2799 refPoint.center.x() <= bbox.max.x() &&
2800 refPoint.center.y() <= bbox.max.y())
2802 // point fully in the bounding box
2803 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
2805 else if (refPoint.center.x() >= bbox.min.x() + refPoint.size / 2.0f &&
2806 refPoint.center.y() >= bbox.min.y() - refPoint.size / 2.0f &&
2807 refPoint.center.x() <= bbox.max.x() + refPoint.size / 2.0f &&
2808 refPoint.center.y() <= bbox.max.y() - refPoint.size / 2.0f)
2810 // point leaks into bounding box
2811 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
2818 bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter)
2820 const int componentNdx = (refPoint.even) ? (1) : (2);
2821 const int halfPointSizeCeil = (refPoint.size + 1) / 2;
2822 const int halfPointSizeFloor = (refPoint.size + 1) / 2;
2823 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
2824 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0),
2825 de::max(viewportBBoxArea.y(), 0),
2826 de::min(viewportBBoxArea.z(), viewport.getWidth()),
2827 de::min(viewportBBoxArea.w(), viewport.getHeight()));
2828 const tcu::IVec2 pointPos = tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * viewport.getWidth()),
2829 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * viewport.getHeight()));
2831 // find any fragment within the point that is inside the bbox, start search at the center
2833 if (pointPos.x() >= verificationArea.x() &&
2834 pointPos.y() >= verificationArea.y() &&
2835 pointPos.x() < verificationArea.z() &&
2836 pointPos.y() < verificationArea.w())
2838 if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
2839 return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2842 for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
2843 for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
2845 const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
2847 if (dx == 0 && dy == 0)
2850 if (testPos.x() >= verificationArea.x() &&
2851 testPos.y() >= verificationArea.y() &&
2852 testPos.x() < verificationArea.z() &&
2853 testPos.y() < verificationArea.w())
2855 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
2856 return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter);
2860 // could not find point, this is only ok near boundaries
2861 if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 ||
2862 pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 ||
2863 pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
2864 pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
2867 if (--logFloodCounter >= 0)
2870 << tcu::TestLog::Message
2871 << "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2872 << tcu::TestLog::EndMessage;
2878 bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter)
2880 const int expectedPointSize = refPoint.size;
2881 bool viewportClippedTop = false;
2882 bool viewportClippedBottom = false;
2883 bool primitiveClippedTop = false;
2884 bool primitiveClippedBottom = false;
2885 std::vector<tcu::IVec2> widthsUpwards;
2886 std::vector<tcu::IVec2> widthsDownwards;
2887 std::vector<tcu::IVec2> widths;
2890 for (int y = pointPos.y();; --y)
2892 if (y < bbox.y() || y < 0)
2895 primitiveClippedTop = true;
2897 viewportClippedTop = true;
2900 else if (pointPos.y() - y > expectedPointSize)
2902 // no need to go further than point height
2905 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2911 widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2916 if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
2918 const tcu::IVec2& range = widthsUpwards.back();
2919 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
2920 const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
2922 if (squareFits || widthClipped)
2927 for (int y = pointPos.y()+1;; ++y)
2929 if (y >= bbox.w() || y >= viewport.getHeight())
2932 primitiveClippedBottom = true;
2933 if (y >= viewport.getHeight())
2934 viewportClippedBottom = true;
2937 else if (y - pointPos.y() > expectedPointSize)
2939 // no need to go further than point height
2942 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
2948 widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
2952 // bottom is clipped
2953 if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty()))
2955 const tcu::IVec2& range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
2956 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize;
2957 const bool bboxClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1);
2958 const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth()-1;
2960 if (squareFits || bboxClipped || viewportClipped)
2964 // would square point would fit into the rasterized area
2966 for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
2967 widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
2968 for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
2969 widths.push_back(widthsDownwards[ndx]);
2970 DE_ASSERT(!widths.empty());
2972 for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
2974 tcu::IVec2 unionRange = widths[y];
2976 for (int dy = 1; dy < expectedPointSize; ++dy)
2978 unionRange.x() = de::max(unionRange.x(), widths[y+dy].x());
2979 unionRange.y() = de::min(unionRange.y(), widths[y+dy].y());
2982 // would a N x N block fit here?
2984 const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
2985 const bool bboxClipped = (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1);
2986 const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1;
2988 if (squareFits || bboxClipped || viewportClipped)
2993 if (--logFloodCounter >= 0)
2996 << tcu::TestLog::Message
2997 << "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
2998 << tcu::TestLog::EndMessage;
3003 tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const
3005 int minX = pointPos.x();
3006 int maxX = pointPos.x();
3008 // search horizontally for a point edges
3009 for (int x = pointPos.x()-1; x >= 0; --x)
3011 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3014 // no need to go further than point width
3015 if (pointPos.x() - x > expectedPointSize)
3020 for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x)
3022 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3025 // no need to go further than point width
3026 if (x - pointPos.x() > expectedPointSize)
3032 return tcu::IVec2(minX, maxX);
3035 class BlitFboCase : public TestCase
3046 BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst);
3047 ~BlitFboCase (void);
3066 IterateResult iterate (void);
3068 void fillSourceWithPattern (void);
3069 bool verifyImage (const BlitArgs& args);
3071 const RenderTarget m_src;
3072 const RenderTarget m_dst;
3074 std::vector<BlitArgs> m_iterations;
3076 de::MovePtr<glu::Framebuffer> m_srcFbo;
3077 de::MovePtr<glu::Framebuffer> m_dstFbo;
3078 de::MovePtr<glu::Renderbuffer> m_srcRbo;
3079 de::MovePtr<glu::Renderbuffer> m_dstRbo;
3080 de::MovePtr<glu::ShaderProgram> m_program;
3081 de::MovePtr<glu::Buffer> m_vbo;
3084 BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst)
3085 : TestCase (context, name, description)
3090 DE_ASSERT(src < TARGET_LAST);
3091 DE_ASSERT(dst < TARGET_LAST);
3094 BlitFboCase::~BlitFboCase (void)
3099 void BlitFboCase::init (void)
3101 const int numIterations = 12;
3102 const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1);
3103 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3104 de::Random rnd (0xABC123);
3107 << tcu::TestLog::Message
3108 << "Using BlitFramebuffer to blit area from "
3109 << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3111 << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo"))
3113 << "Varying blit arguments and primitive bounding box between iterations.\n"
3114 << "Expecting bounding box to have no effect on blitting.\n"
3115 << "Source framebuffer is filled with green-yellow grid.\n"
3116 << tcu::TestLog::EndMessage;
3118 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3119 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3120 if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3121 throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3125 if (m_src == TARGET_FBO)
3127 m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3128 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3129 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3130 GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3132 m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3133 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3134 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3135 GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3138 if (m_dst == TARGET_FBO)
3140 m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3141 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3142 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3143 GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3145 m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3146 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3147 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3148 GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3152 static const char* const s_vertexSource = "#version 310 es\n"
3153 "in highp vec4 a_position;\n"
3154 "out highp vec4 v_position;\n"
3157 " gl_Position = a_position;\n"
3158 " v_position = a_position;\n"
3160 static const char* const s_fragmentSource = "#version 310 es\n"
3161 "in mediump vec4 v_position;\n"
3162 "layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3165 " const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3166 " const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3167 " dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3170 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource)));
3172 if (!m_program->isOk())
3174 m_testCtx.getLog() << *m_program;
3175 throw tcu::TestError("failed to build program");
3180 static const tcu::Vec4 s_quadCoords[] =
3182 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3183 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
3184 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
3185 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
3188 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3190 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3191 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3192 GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3198 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3199 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3202 << tcu::TestLog::Message
3203 << "srcSize = " << srcSize << "\n"
3204 << "dstSize = " << dstSize << "\n"
3205 << tcu::TestLog::EndMessage;
3207 for (int ndx = 0; ndx < numIterations; ++ndx)
3211 if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3213 const tcu::IVec2 unionSize = tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3214 const int srcWidth = rnd.getInt(1, unionSize.x());
3215 const int srcHeight = rnd.getInt(1, unionSize.y());
3216 const int srcX = rnd.getInt(0, unionSize.x() - srcWidth);
3217 const int srcY = rnd.getInt(0, unionSize.y() - srcHeight);
3219 args.src.x() = srcX;
3220 args.src.y() = srcY;
3221 args.src.z() = srcX + srcWidth;
3222 args.src.w() = srcY + srcHeight;
3224 args.dst = args.src;
3228 const int srcWidth = rnd.getInt(1, srcSize.x());
3229 const int srcHeight = rnd.getInt(1, srcSize.y());
3230 const int srcX = rnd.getInt(0, srcSize.x() - srcWidth);
3231 const int srcY = rnd.getInt(0, srcSize.y() - srcHeight);
3232 const int dstWidth = rnd.getInt(1, dstSize.x());
3233 const int dstHeight = rnd.getInt(1, dstSize.y());
3234 const int dstX = rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2); // allow dst go out of bounds
3235 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1) / 2);
3237 args.src.x() = srcX;
3238 args.src.y() = srcY;
3239 args.src.z() = srcX + srcWidth;
3240 args.src.w() = srcY + srcHeight;
3241 args.dst.x() = dstX;
3242 args.dst.y() = dstY;
3243 args.dst.z() = dstX + dstWidth;
3244 args.dst.w() = dstY + dstHeight;
3247 args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3248 args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3249 args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3250 args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f);
3252 args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3253 args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3254 args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3255 args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f);
3257 if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3258 std::swap(args.bboxMin.x(), args.bboxMax.x());
3259 if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3260 std::swap(args.bboxMin.y(), args.bboxMax.y());
3261 if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3262 std::swap(args.bboxMin.z(), args.bboxMax.z());
3264 args.linear = rnd.getBool();
3266 m_iterations.push_back(args);
3271 void BlitFboCase::deinit (void)
3281 BlitFboCase::IterateResult BlitFboCase::iterate (void)
3283 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size()));
3284 const BlitArgs& blitCfg = m_iterations[m_iteration];
3285 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3287 if (m_iteration == 0)
3288 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3290 // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3291 if (m_src == TARGET_DEFAULT || m_iteration == 0)
3292 fillSourceWithPattern();
3295 << tcu::TestLog::Message
3296 << "Set bounding box:\n"
3297 << "\tmin:" << blitCfg.bboxMin << "\n"
3298 << "\tmax:" << blitCfg.bboxMax << "\n"
3300 << "\tsrc: " << blitCfg.src << "\n"
3301 << "\tdst: " << blitCfg.dst << "\n"
3302 << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest"))
3303 << tcu::TestLog::EndMessage;
3305 gl.primitiveBoundingBoxEXT(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3306 blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
3308 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3309 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3310 gl.clear(GL_COLOR_BUFFER_BIT);
3312 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3313 gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(),
3314 blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(),
3315 GL_COLOR_BUFFER_BIT,
3316 ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3317 GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3319 if (!verifyImage(blitCfg))
3320 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3322 return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3325 bool BlitFboCase::verifyImage (const BlitArgs& args)
3327 const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything
3328 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3329 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3330 tcu::Surface viewport (dstSize.x(), dstSize.y());
3331 tcu::Surface errorMask (dstSize.x(), dstSize.y());
3332 bool anyError = false;
3335 << tcu::TestLog::Message
3336 << "Verifying blit result"
3337 << tcu::TestLog::EndMessage;
3339 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3340 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3342 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3344 for (int y = 0; y < dstSize.y(); ++y)
3345 for (int x = 0; x < dstSize.x(); ++x)
3347 const tcu::RGBA color = viewport.getPixel(x, y);
3348 const bool inside = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3349 const bool error = (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold)
3350 : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold);
3355 errorMask.setPixel(x, y, tcu::RGBA::red);
3362 << tcu::TestLog::Message
3363 << "Image verification failed."
3364 << tcu::TestLog::EndMessage
3365 << tcu::TestLog::ImageSet("Images", "Image verification")
3366 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3367 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3368 << tcu::TestLog::EndImageSet;
3374 << tcu::TestLog::Message
3375 << "Result image ok."
3376 << tcu::TestLog::EndMessage
3377 << tcu::TestLog::ImageSet("Images", "Image verification")
3378 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3379 << tcu::TestLog::EndImageSet;
3384 void BlitFboCase::fillSourceWithPattern (void)
3386 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3387 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3388 const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3390 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3391 gl.viewport(0, 0, srcSize.x(), srcSize.y());
3392 gl.useProgram(m_program->getProgram());
3394 gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3395 gl.clear(GL_COLOR_BUFFER_BIT);
3397 gl.enableVertexAttribArray(posLocation);
3398 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3399 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3400 GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3403 class DepthDrawCase : public TestCase
3416 STATE_PER_PRIMITIVE,
3428 DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize);
3429 ~DepthDrawCase (void);
3434 IterateResult iterate (void);
3436 std::string genVertexSource (void) const;
3437 std::string genFragmentSource (void) const;
3438 std::string genTessellationControlSource (void) const;
3439 std::string genTessellationEvaluationSource (void) const;
3440 void generateAttributeData (std::vector<tcu::Vec4>& data) const;
3441 bool verifyImage (const tcu::Surface& viewport) const;
3445 RENDER_AREA_SIZE = 256,
3456 const int m_numLayers;
3457 const int m_gridSize;
3459 const DepthType m_depthType;
3460 const BBoxState m_state;
3461 const BBoxSize m_bboxSize;
3463 de::MovePtr<glu::ShaderProgram> m_program;
3464 de::MovePtr<glu::Buffer> m_vbo;
3465 std::vector<LayerInfo> m_layers;
3468 DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize)
3469 : TestCase (context, name, description)
3472 , m_depthType (depthType)
3474 , m_bboxSize (bboxSize)
3476 DE_ASSERT(depthType < DEPTH_LAST);
3477 DE_ASSERT(state < STATE_LAST);
3478 DE_ASSERT(bboxSize < BBOX_LAST);
3481 DepthDrawCase::~DepthDrawCase (void)
3486 void DepthDrawCase::init (void)
3488 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3492 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3493 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3494 if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3495 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3496 if (m_context.getRenderTarget().getDepthBits() == 0)
3497 throw tcu::NotSupportedError("Test requires depth buffer");
3498 if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
3499 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport");
3503 << tcu::TestLog::Message
3504 << "Rendering multiple triangle grids with with different z coordinates.\n"
3505 << "Topmost grid is green-yellow, other grids are blue-red.\n"
3506 << "Expecting only the green-yellow grid to be visible.\n"
3507 << "Setting primitive bounding box "
3508 << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
3509 << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
3510 << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding."))
3512 << "Set bounding box using "
3513 << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3515 << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : (""))
3516 << tcu::TestLog::EndMessage;
3521 glu::ProgramSources sources;
3522 sources << glu::VertexSource(genVertexSource());
3523 sources << glu::FragmentSource(genFragmentSource());
3525 if (m_state == STATE_PER_PRIMITIVE)
3526 sources << glu::TessellationControlSource(genTessellationControlSource())
3527 << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
3529 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
3530 GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
3533 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
3534 m_testCtx.getLog() << *m_program;
3537 if (!m_program->isOk())
3538 throw tcu::TestError("failed to build program");
3542 std::vector<tcu::Vec4> data;
3544 generateAttributeData(data);
3546 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3547 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3548 gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
3549 GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
3554 de::Random rnd(0x12345);
3556 m_layers.resize(m_numLayers);
3557 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3559 m_layers[layerNdx].zOffset = ((float)layerNdx / m_numLayers) * 2.0f - 1.0f;
3560 m_layers[layerNdx].zScale = (2.0f / m_numLayers);
3561 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));
3562 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));
3564 rnd.shuffle(m_layers.begin(), m_layers.end());
3568 void DepthDrawCase::deinit (void)
3574 DepthDrawCase::IterateResult DepthDrawCase::iterate (void)
3576 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3577 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
3578 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3579 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
3580 const glw::GLint depthBiasLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
3581 const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
3582 const glw::GLint color1Location = gl.getUniformLocation(m_program->getProgram(), "u_color1");
3583 const glw::GLint color2Location = gl.getUniformLocation(m_program->getProgram(), "u_color2");
3585 tcu::Surface viewport (RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3586 de::Random rnd (0x213237);
3588 TCU_CHECK(posLocation != -1);
3589 TCU_CHECK(colLocation != -1);
3590 TCU_CHECK(depthBiasLocation != -1);
3591 TCU_CHECK(depthScaleLocation != -1);
3592 TCU_CHECK(color1Location != -1);
3593 TCU_CHECK(color2Location != -1);
3595 gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
3596 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3597 gl.clearDepthf(1.0f);
3598 gl.depthFunc(GL_LESS);
3599 gl.enable(GL_DEPTH_TEST);
3600 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3601 GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
3603 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3604 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL);
3605 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4);
3606 gl.enableVertexAttribArray(posLocation);
3607 gl.enableVertexAttribArray(colLocation);
3608 gl.useProgram(m_program->getProgram());
3609 GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
3611 if (hasTessellation)
3612 gl.patchParameteri(GL_PATCH_VERTICES, 3);
3614 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
3616 gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
3617 gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
3618 gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
3619 gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
3621 if (m_state == STATE_GLOBAL)
3623 const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3624 const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
3626 gl.primitiveBoundingBoxEXT(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f,
3627 1.0f, 1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
3630 gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
3633 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3634 GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
3636 if (verifyImage(viewport))
3637 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3639 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3644 std::string DepthDrawCase::genVertexSource (void) const
3646 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3647 std::ostringstream buf;
3649 buf << "#version 310 es\n"
3650 "in highp vec4 a_position;\n"
3651 "in highp vec4 a_colorMix;\n"
3652 "out highp vec4 vtx_colorMix;\n";
3654 if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
3655 buf << "out highp float v_fragDepth;\n";
3657 if (!hasTessellation)
3658 buf << "uniform highp float u_depthBias;\n"
3659 "uniform highp float u_depthScale;\n";
3665 if (hasTessellation)
3666 buf << " gl_Position = a_position;\n";
3667 else if (m_depthType == DEPTH_USER_DEFINED)
3668 buf << " highp float dummyZ = a_position.z;\n"
3669 " highp float writtenZ = a_position.w;\n"
3670 " gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n"
3671 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3673 buf << " highp float writtenZ = a_position.w;\n"
3674 " gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3676 buf << " vtx_colorMix = a_colorMix;\n"
3682 std::string DepthDrawCase::genFragmentSource (void) const
3684 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
3685 const char* const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
3686 std::ostringstream buf;
3688 buf << "#version 310 es\n"
3689 "in mediump vec4 " << colorMixName << ";\n";
3691 if (m_depthType == DEPTH_USER_DEFINED)
3692 buf << "in mediump float v_fragDepth;\n";
3694 buf << "layout(location = 0) out mediump vec4 o_color;\n"
3695 "uniform highp vec4 u_color1;\n"
3696 "uniform highp vec4 u_color2;\n"
3700 " o_color = mix(u_color1, u_color2, " << colorMixName << ");\n";
3702 if (m_depthType == DEPTH_USER_DEFINED)
3703 buf << " gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
3710 std::string DepthDrawCase::genTessellationControlSource (void) const
3712 std::ostringstream buf;
3714 buf << "#version 310 es\n"
3715 "#extension GL_EXT_tessellation_shader : require\n"
3716 "#extension GL_EXT_primitive_bounding_box : require\n"
3717 "layout(vertices=3) out;\n"
3719 "uniform highp float u_depthBias;\n"
3720 "uniform highp float u_depthScale;\n"
3722 "in highp vec4 vtx_colorMix[];\n"
3723 "out highp vec4 tess_ctrl_colorMix[];\n"
3727 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3728 " tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
3730 " gl_TessLevelOuter[0] = 2.8;\n"
3731 " gl_TessLevelOuter[1] = 2.8;\n"
3732 " gl_TessLevelOuter[2] = 2.8;\n"
3733 " gl_TessLevelInner[0] = 2.8;\n"
3735 " // real Z stored in w component\n"
3736 " highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3737 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3738 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"
3739 " highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n"
3740 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n"
3741 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n";
3743 if (m_bboxSize == BBOX_EQUAL)
3744 buf << " gl_BoundingBoxEXT[0] = minBound;\n"
3745 " gl_BoundingBoxEXT[1] = maxBound;\n";
3747 buf << " highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
3748 " highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
3749 " gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
3750 " gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
3757 std::string DepthDrawCase::genTessellationEvaluationSource (void) const
3759 std::ostringstream buf;
3761 buf << "#version 310 es\n"
3762 "#extension GL_EXT_tessellation_shader : require\n"
3763 "#extension GL_EXT_gpu_shader5 : require\n"
3764 "layout(triangles) in;\n"
3766 "in highp vec4 tess_ctrl_colorMix[];\n"
3767 "out highp vec4 tess_eval_colorMix;\n";
3769 if (m_depthType == DEPTH_USER_DEFINED)
3770 buf << "out highp float v_fragDepth;\n";
3772 buf << "uniform highp float u_depthBias;\n"
3773 "uniform highp float u_depthScale;\n"
3775 "precise gl_Position;\n"
3779 " 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";
3781 if (m_depthType == DEPTH_USER_DEFINED)
3782 buf << " highp float dummyZ = tessellatedPos.z;\n"
3783 " highp float writtenZ = tessellatedPos.w;\n"
3784 " gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n"
3785 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
3787 buf << " highp float writtenZ = tessellatedPos.w;\n"
3788 " gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
3790 buf << " tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
3796 void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const
3798 const tcu::Vec4 color1 (0.0f, 0.0f, 0.0f, 0.0f); // mix weights
3799 const tcu::Vec4 color2 (1.0f, 1.0f, 1.0f, 1.0f);
3800 std::vector<int> cellOrder (m_gridSize * m_gridSize);
3801 de::Random rnd (0xAB54321);
3803 // generate grid with cells in random order
3804 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3805 cellOrder[ndx] = ndx;
3806 rnd.shuffle(cellOrder.begin(), cellOrder.end());
3808 data.resize(m_gridSize * m_gridSize * 6 * 2);
3809 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3811 const int cellNdx = cellOrder[ndx];
3812 const int cellX = cellNdx % m_gridSize;
3813 const int cellY = cellNdx / m_gridSize;
3814 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (color1) : (color2);
3816 data[ndx * 6 * 2 + 0] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 1] = cellColor;
3817 data[ndx * 6 * 2 + 2] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 3] = cellColor;
3818 data[ndx * 6 * 2 + 4] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 5] = cellColor;
3819 data[ndx * 6 * 2 + 6] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 7] = cellColor;
3820 data[ndx * 6 * 2 + 8] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 9] = cellColor;
3821 data[ndx * 6 * 2 + 10] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 11] = cellColor;
3823 // Fill Z with random values (fake Z)
3824 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3825 data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
3827 // Fill W with other random values (written Z)
3828 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
3829 data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
3833 bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const
3835 tcu::Surface errorMask (viewport.getWidth(), viewport.getHeight());
3836 bool anyError = false;
3838 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255));
3840 for (int y = 0; y < viewport.getHeight(); ++y)
3841 for (int x = 0; x < viewport.getWidth(); ++x)
3843 const tcu::RGBA pixel = viewport.getPixel(x, y);
3846 // expect green, yellow or a combination of these
3847 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
3852 errorMask.setPixel(x, y, tcu::RGBA::red);
3859 << tcu::TestLog::Message
3860 << "Image verification failed."
3861 << tcu::TestLog::EndMessage
3862 << tcu::TestLog::ImageSet("Images", "Image verification")
3863 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3864 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3865 << tcu::TestLog::EndImageSet;
3868 << tcu::TestLog::Message
3869 << "Result image ok."
3870 << tcu::TestLog::EndMessage
3871 << tcu::TestLog::ImageSet("Images", "Image verification")
3872 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3873 << tcu::TestLog::EndImageSet;
3878 class ClearCase : public TestCase
3883 SCISSOR_CLEAR_BIT = 1 << 0,
3884 DRAW_TRIANGLE_BIT = 1 << 1,
3885 PER_PRIMITIVE_BBOX_BIT = 1 << 2,
3886 FULLSCREEN_SCISSOR_BIT = 1 << 3,
3889 ClearCase (Context& context, const char* name, const char* description, deUint32 flags);
3901 IterateResult iterate (void);
3903 void createVbo (void);
3904 void createProgram (void);
3905 void renderTo (tcu::Surface& dst, bool useBBox);
3906 bool verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox);
3907 bool verifyImageResultValid (const tcu::PixelBufferAccess& result);
3909 std::string genVertexSource (void) const;
3910 std::string genFragmentSource (void) const;
3911 std::string genTessellationControlSource (bool setBBox) const;
3912 std::string genTessellationEvaluationSource (void) const;
3914 const bool m_scissoredClear;
3915 const bool m_fullscreenScissor;
3916 const bool m_drawTriangles;
3917 const bool m_useGlobalState;
3919 de::MovePtr<glu::Buffer> m_vbo;
3920 de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram;
3921 de::MovePtr<glu::ShaderProgram> m_basicProgram;
3922 std::vector<DrawObject> m_drawObjects;
3923 std::vector<tcu::Vec4> m_objectVertices;
3926 ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags)
3927 : TestCase (context, name, description)
3928 , m_scissoredClear ((flags & SCISSOR_CLEAR_BIT) != 0)
3929 , m_fullscreenScissor ((flags & FULLSCREEN_SCISSOR_BIT) != 0)
3930 , m_drawTriangles ((flags & DRAW_TRIANGLE_BIT) != 0)
3931 , m_useGlobalState ((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
3933 DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles
3934 DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
3937 ClearCase::~ClearCase (void)
3942 void ClearCase::init (void)
3944 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
3945 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3946 if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
3947 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
3950 << tcu::TestLog::Message
3952 << ((m_scissoredClear) ? (" scissored") : (""))
3953 << " color buffer clears"
3954 << ((m_drawTriangles) ? (" and drawing some geometry between them") : (""))
3956 << ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : (""))
3957 << "Rendering with and without setting the bounding box.\n"
3958 << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
3959 << "Set bounding box using "
3960 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
3962 << "Clear color is green with yellowish shades.\n"
3963 << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
3964 << tcu::TestLog::EndMessage;
3966 if (m_drawTriangles)
3973 void ClearCase::deinit (void)
3976 m_perPrimitiveProgram.clear();
3977 m_basicProgram.clear();
3978 m_drawObjects = std::vector<DrawObject>();
3979 m_objectVertices = std::vector<tcu::Vec4>();
3982 ClearCase::IterateResult ClearCase::iterate (void)
3984 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
3985 tcu::Surface resultWithoutBBox (renderTargetSize.x(), renderTargetSize.y());
3986 tcu::Surface resultWithBBox (renderTargetSize.x(), renderTargetSize.y());
3988 // render with and without bbox set
3989 for (int passNdx = 0; passNdx < 2; ++passNdx)
3991 const bool useBBox = (passNdx == 1);
3992 tcu::Surface& destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
3994 renderTo(destination, useBBox);
3997 // Verify images are equal and that the image does not contain (trivially detectable) garbage
3999 if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4001 // verifyImagesEqual will print out the image and error mask
4002 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4004 else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4006 // verifyImageResultValid will print out the image and error mask
4007 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4012 << tcu::TestLog::Message
4013 << "Image comparison passed."
4014 << tcu::TestLog::EndMessage
4015 << tcu::TestLog::ImageSet("Images", "Image verification")
4016 << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4017 << tcu::TestLog::EndImageSet;
4019 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4025 void ClearCase::createVbo (void)
4027 const int numObjects = 16;
4028 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4029 de::Random rnd (deStringHash(getName()));
4031 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4033 for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4035 const int numTriangles = rnd.getInt(1, 4);
4036 const float minX = rnd.getFloat(-1.2f, 0.8f);
4037 const float minY = rnd.getFloat(-1.2f, 0.8f);
4038 const float maxX = minX + rnd.getFloat(0.2f, 1.0f);
4039 const float maxY = minY + rnd.getFloat(0.2f, 1.0f);
4041 DrawObject drawObject;
4042 drawObject.firstNdx = (int)m_objectVertices.size();
4043 drawObject.numVertices = numTriangles * 3;
4045 m_drawObjects.push_back(drawObject);
4047 for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4048 for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4050 const float posX = rnd.getFloat(minX, maxX);
4051 const float posY = rnd.getFloat(minY, maxY);
4052 const float posZ = rnd.getFloat(-0.7f, 0.7f);
4053 const float posW = rnd.getFloat(0.9f, 1.1f);
4055 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4059 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4060 gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW);
4061 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4064 void ClearCase::createProgram (void)
4066 m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4067 glu::ProgramSources()
4068 << glu::VertexSource(genVertexSource())
4069 << glu::FragmentSource(genFragmentSource())
4070 << glu::TessellationControlSource(genTessellationControlSource(false))
4071 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4074 << tcu::TestLog::Section("Program", "Shader program")
4076 << tcu::TestLog::EndSection;
4078 if (!m_basicProgram->isOk())
4079 throw tcu::TestError("shader build failed");
4081 if (!m_useGlobalState)
4083 m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4084 glu::ProgramSources()
4085 << glu::VertexSource(genVertexSource())
4086 << glu::FragmentSource(genFragmentSource())
4087 << glu::TessellationControlSource(genTessellationControlSource(true))
4088 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4091 << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4092 << *m_perPrimitiveProgram
4093 << tcu::TestLog::EndSection;
4095 if (!m_perPrimitiveProgram->isOk())
4096 throw tcu::TestError("shader build failed");
4100 void ClearCase::renderTo (tcu::Surface& dst, bool useBBox)
4102 const int numOps = 45;
4103 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f);
4104 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
4105 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4106 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4107 de::Random rnd (deStringHash(getName()));
4108 glu::VertexArray vao (m_context.getRenderContext());
4110 // always do the initial clear
4111 gl.disable(GL_SCISSOR_TEST);
4112 gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4113 gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4114 gl.clear(GL_COLOR_BUFFER_BIT);
4118 if (m_scissoredClear)
4119 gl.enable(GL_SCISSOR_TEST);
4121 if (m_drawTriangles)
4123 const deUint32 programHandle = (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4124 const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position");
4126 TCU_CHECK(positionAttribLoc != -1);
4128 gl.useProgram(programHandle);
4129 gl.bindVertexArray(*vao);
4130 gl.enableVertexAttribArray(positionAttribLoc);
4131 gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4132 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4135 // do random scissor/clearldraw operations
4136 for (int opNdx = 0; opNdx < numOps; ++opNdx)
4138 const int drawObjNdx = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4139 const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4140 const int objectVertexLength = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4144 if (m_drawTriangles)
4146 bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4147 bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4150 for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx)
4151 for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4153 bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4154 bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4159 // no geometry, just random something
4160 bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4161 bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4162 bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4164 bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4165 bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4166 bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4170 if (m_scissoredClear)
4172 const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x()-1);
4173 const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y()-1);
4174 const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x()-scissorX);
4175 const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y()-scissorY);
4177 gl.scissor(scissorX, scissorY, scissorW, scissorH);
4181 const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4182 gl.clearColor(color.x(), color.y(), color.z(), color.w());
4183 gl.clear(GL_COLOR_BUFFER_BIT);
4188 DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4189 if (m_useGlobalState)
4190 gl.primitiveBoundingBoxEXT(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(),
4191 bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w());
4194 if (m_drawTriangles)
4195 gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4198 GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4199 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4202 bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox)
4204 DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
4205 DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
4207 tcu::Surface errorMask (withoutBBox.getWidth(), withoutBBox.getHeight());
4208 bool anyError = false;
4210 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec());
4212 for (int y = 0; y < withoutBBox.getHeight(); ++y)
4213 for (int x = 0; x < withoutBBox.getWidth(); ++x)
4215 if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4217 errorMask.setPixel(x, y, tcu::RGBA::red);
4225 << tcu::TestLog::Message
4226 << "Image comparison failed."
4227 << tcu::TestLog::EndMessage
4228 << tcu::TestLog::ImageSet("Images", "Image comparison")
4229 << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4230 << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4231 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4232 << tcu::TestLog::EndImageSet;
4238 bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result)
4240 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4241 bool anyError = false;
4243 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec());
4245 for (int y = 0; y < result.getHeight(); ++y)
4246 for (int x = 0; x < result.getWidth(); ++x)
4248 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4250 // allow green, yellow and any shade between
4251 if (pixel[1] != 255 || pixel[2] != 0)
4253 errorMask.setPixel(x, y, tcu::RGBA::red);
4261 << tcu::TestLog::Message
4262 << "Image verification failed."
4263 << tcu::TestLog::EndMessage
4264 << tcu::TestLog::ImageSet("Images", "Image verification")
4265 << tcu::TestLog::Image("ResultImage", "Result image", result)
4266 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4267 << tcu::TestLog::EndImageSet;
4273 static const char* const s_yellowishPosOnlyVertexSource = "#version 310 es\n"
4274 "in highp vec4 a_position;\n"
4275 "out highp vec4 v_vertex_color;\n"
4278 " gl_Position = a_position;\n"
4279 " // yellowish shade\n"
4280 " highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4281 " v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4284 static const char* const s_basicColorFragmentSource = "#version 310 es\n"
4285 "in mediump vec4 v_color;\n"
4286 "layout(location = 0) out mediump vec4 o_color;\n"
4289 " o_color = v_color;\n"
4293 static const char* const s_basicColorTessEvalSource = "#version 310 es\n"
4294 "#extension GL_EXT_tessellation_shader : require\n"
4295 "#extension GL_EXT_gpu_shader5 : require\n"
4296 "layout(triangles) in;\n"
4297 "in highp vec4 v_tess_eval_color[];\n"
4298 "out highp vec4 v_color;\n"
4299 "precise gl_Position;\n"
4302 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4303 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
4304 " + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4305 " v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4306 " + gl_TessCoord.y * v_tess_eval_color[1]\n"
4307 " + gl_TessCoord.z * v_tess_eval_color[2];\n"
4310 std::string ClearCase::genVertexSource (void) const
4312 return s_yellowishPosOnlyVertexSource;
4315 std::string ClearCase::genFragmentSource (void) const
4317 return s_basicColorFragmentSource;
4320 std::string ClearCase::genTessellationControlSource (bool setBBox) const
4322 std::ostringstream buf;
4324 buf << "#version 310 es\n"
4325 "#extension GL_EXT_tessellation_shader : require\n";
4328 buf << "#extension GL_EXT_primitive_bounding_box : require\n";
4330 buf << "layout(vertices=3) out;\n"
4331 "in highp vec4 v_vertex_color[];\n"
4332 "out highp vec4 v_tess_eval_color[];\n"
4335 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4336 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4337 " gl_TessLevelOuter[0] = 2.8;\n"
4338 " gl_TessLevelOuter[1] = 2.8;\n"
4339 " gl_TessLevelOuter[2] = 2.8;\n"
4340 " gl_TessLevelInner[0] = 2.8;\n";
4345 " gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n"
4346 " gl_in[1].gl_Position),\n"
4347 " gl_in[2].gl_Position);\n"
4348 " gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n"
4349 " gl_in[1].gl_Position),\n"
4350 " gl_in[2].gl_Position);\n";
4357 std::string ClearCase::genTessellationEvaluationSource (void) const
4359 return s_basicColorTessEvalSource;
4362 class ViewportCallOrderCase : public TestCase
4373 ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder);
4374 ~ViewportCallOrderCase (void);
4379 IterateResult iterate (void);
4382 void genProgram (void);
4383 bool verifyImage (const tcu::PixelBufferAccess& result);
4385 std::string genVertexSource (void) const;
4386 std::string genFragmentSource (void) const;
4387 std::string genTessellationControlSource (void) const;
4388 std::string genTessellationEvaluationSource (void) const;
4390 const CallOrder m_callOrder;
4392 de::MovePtr<glu::Buffer> m_vbo;
4393 de::MovePtr<glu::ShaderProgram> m_program;
4397 ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder)
4398 : TestCase (context, name, description)
4399 , m_callOrder (callOrder)
4400 , m_numVertices (-1)
4402 DE_ASSERT(m_callOrder < ORDER_LAST);
4405 ViewportCallOrderCase::~ViewportCallOrderCase (void)
4410 void ViewportCallOrderCase::init (void)
4412 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"))
4413 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4416 << tcu::TestLog::Message
4417 << "Testing call order of state setting functions have no effect on the rendering.\n"
4418 << "Setting viewport and bounding box in the following order:\n"
4419 << ((m_callOrder == VIEWPORT_FIRST)
4420 ? ("\tFirst viewport with glViewport function.\n")
4421 : ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4422 << ((m_callOrder == VIEWPORT_FIRST)
4423 ? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n")
4424 : ("\tThen viewport with glViewport function.\n"))
4425 << "Verifying rendering result."
4426 << tcu::TestLog::EndMessage;
4433 void ViewportCallOrderCase::deinit (void)
4439 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void)
4441 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4442 const tcu::IVec2 viewportSize = tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4443 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
4444 tcu::Surface resultSurface (viewportSize.x(), viewportSize.y());
4446 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4447 gl.clear(GL_COLOR_BUFFER_BIT);
4450 for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
4452 if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) ||
4453 (orderNdx == 1 && m_callOrder == BBOX_FIRST))
4456 << tcu::TestLog::Message
4457 << "Setting viewport to cover the left half of the render target.\n"
4458 << "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")"
4459 << tcu::TestLog::EndMessage;
4461 gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y());
4466 << tcu::TestLog::Message
4467 << "Setting bounding box to cover the right half of the clip space.\n"
4468 << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)"
4469 << tcu::TestLog::EndMessage;
4471 gl.primitiveBoundingBoxEXT(0.0f, -1.0f, -1.0f, 1.0f,
4472 1.0f, 1.0f, 1.0f, 1.0f);
4477 << tcu::TestLog::Message
4478 << "Rendering mesh covering the right half of the clip space."
4479 << tcu::TestLog::EndMessage;
4481 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4482 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL);
4483 gl.enableVertexAttribArray(posLocation);
4484 gl.useProgram(m_program->getProgram());
4485 gl.patchParameteri(GL_PATCH_VERTICES, 3);
4486 gl.drawArrays(GL_PATCHES, 0, m_numVertices);
4487 GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
4490 << tcu::TestLog::Message
4491 << "Verifying image"
4492 << tcu::TestLog::EndMessage;
4493 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
4495 if (!verifyImage(resultSurface.getAccess()))
4496 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4500 << tcu::TestLog::Message
4502 << tcu::TestLog::EndMessage
4503 << tcu::TestLog::ImageSet("Images", "Image verification")
4504 << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
4505 << tcu::TestLog::EndImageSet;
4507 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4512 void ViewportCallOrderCase::genVbo (void)
4514 const int gridSize = 6;
4515 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
4516 std::vector<tcu::Vec4> data (gridSize * gridSize * 2 * 3);
4517 std::vector<int> cellOrder (gridSize * gridSize * 2);
4518 de::Random rnd (0x55443322);
4520 // generate grid with triangles in random order
4521 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4522 cellOrder[ndx] = ndx;
4523 rnd.shuffle(cellOrder.begin(), cellOrder.end());
4525 // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
4526 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4528 const int cellNdx = cellOrder[ndx];
4529 const bool cellSide = ((cellNdx % 2) == 0);
4530 const int cellX = (cellNdx / 2) % gridSize;
4531 const int cellY = (cellNdx / 2) / gridSize;
4535 data[ndx * 3 + 0] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4536 data[ndx * 3 + 1] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4537 data[ndx * 3 + 2] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4541 data[ndx * 3 + 0] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4542 data[ndx * 3 + 1] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4543 data[ndx * 3 + 2] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
4547 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4548 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4549 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
4550 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
4552 m_numVertices = (int)data.size();
4555 void ViewportCallOrderCase::genProgram (void)
4557 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(),
4558 glu::ProgramSources()
4559 << glu::VertexSource(genVertexSource())
4560 << glu::FragmentSource(genFragmentSource())
4561 << glu::TessellationControlSource(genTessellationControlSource())
4562 << glu::TessellationEvaluationSource(genTessellationEvaluationSource())));
4565 << tcu::TestLog::Section("Program", "Shader program")
4567 << tcu::TestLog::EndSection;
4569 if (!m_program->isOk())
4570 throw tcu::TestError("shader build failed");
4573 bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result)
4575 const tcu::IVec2 insideBorder (deCeilFloatToInt32(0.25f * result.getWidth()) + 1, deFloorFloatToInt32(0.5f * result.getWidth()) - 1);
4576 const tcu::IVec2 outsideBorder (deFloorFloatToInt32(0.25f * result.getWidth()) - 1, deCeilFloatToInt32(0.5f * result.getWidth()) + 1);
4577 tcu::Surface errorMask (result.getWidth(), result.getHeight());
4578 bool anyError = false;
4580 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec());
4582 for (int y = 0; y < result.getHeight(); ++y)
4583 for (int x = 0; x < result.getWidth(); ++x)
4585 const tcu::IVec4 pixel = result.getPixelInt(x, y);
4586 const bool insideMeshArea = x >= insideBorder.x() && x <= insideBorder.x();
4587 const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x();
4589 // inside mesh, allow green, yellow and any shade between
4590 // outside mesh, allow background (black) only
4591 // in the border area, allow anything
4592 if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
4593 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
4595 errorMask.setPixel(x, y, tcu::RGBA::red);
4603 << tcu::TestLog::Message
4604 << "Image verification failed."
4605 << tcu::TestLog::EndMessage
4606 << tcu::TestLog::ImageSet("Images", "Image verification")
4607 << tcu::TestLog::Image("ResultImage", "Result image", result)
4608 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask)
4609 << tcu::TestLog::EndImageSet;
4615 std::string ViewportCallOrderCase::genVertexSource (void) const
4617 return s_yellowishPosOnlyVertexSource;
4620 std::string ViewportCallOrderCase::genFragmentSource (void) const
4622 return s_basicColorFragmentSource;
4625 std::string ViewportCallOrderCase::genTessellationControlSource (void) const
4627 return "#version 310 es\n"
4628 "#extension GL_EXT_tessellation_shader : require\n"
4629 "layout(vertices=3) out;\n"
4630 "in highp vec4 v_vertex_color[];\n"
4631 "out highp vec4 v_tess_eval_color[];\n"
4634 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4635 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4636 " gl_TessLevelOuter[0] = 2.8;\n"
4637 " gl_TessLevelOuter[1] = 2.8;\n"
4638 " gl_TessLevelOuter[2] = 2.8;\n"
4639 " gl_TessLevelInner[0] = 2.8;\n"
4643 std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const
4645 return s_basicColorTessEvalSource;
4650 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context)
4651 : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
4655 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void)
4659 void PrimitiveBoundingBoxTests::init (void)
4664 const char* description;
4665 deUint32 methodFlags;
4666 } stateSetMethods[] =
4670 "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
4671 BBoxRenderCase::FLAG_SET_BBOX_STATE,
4674 "tessellation_set_per_draw",
4675 "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
4676 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
4679 "tessellation_set_per_primitive",
4680 "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
4681 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4687 const char* description;
4688 deUint32 stageFlags;
4689 } pipelineConfigs[] =
4693 "Render with vertex-fragment program",
4697 "vertex_tessellation_fragment",
4698 "Render with vertex-tessellation{ctrl,eval}-fragment program",
4699 BBoxRenderCase::FLAG_TESSELLATION
4702 "vertex_geometry_fragment",
4703 "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
4704 BBoxRenderCase::FLAG_GEOMETRY
4707 "vertex_tessellation_geometry_fragment",
4708 "Render with vertex-geometry-fragment program",
4709 BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY
4715 const char* description;
4717 deUint32 invalidFlags;
4718 deUint32 requiredFlags;
4722 "default_framebuffer_bbox_equal",
4723 "Render to default framebuffer, set tight bounding box",
4724 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4725 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4729 "default_framebuffer_bbox_larger",
4730 "Render to default framebuffer, set padded bounding box",
4731 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4732 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4736 "default_framebuffer_bbox_smaller",
4737 "Render to default framebuffer, set too small bounding box",
4738 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4739 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4744 "Render to texture, set tight bounding box",
4745 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4746 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4751 "Render to texture, set padded bounding box",
4752 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
4753 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4758 "Render to texture, set too small bounding box",
4759 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
4760 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
4764 "default_framebuffer",
4765 "Render to default framebuffer, set tight bounding box",
4766 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4768 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4772 "Render to texture, set tight bounding box",
4773 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
4775 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX
4778 enum PrimitiveRenderType
4787 const char* description;
4788 PrimitiveRenderType type;
4790 } primitiveTypes[] =
4794 "Triangle render tests",
4800 "Line render tests",
4806 "Point render tests",
4812 "Wide line render tests",
4814 LineRenderCase::LINEFLAG_WIDE
4818 "Wide point render tests",
4820 PointRenderCase::POINTFLAG_WIDE
4826 tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
4827 addChild(stateQueryGroup);
4829 stateQueryGroup->addChild(new InitialValueCase (m_context, "initial_value", "Initial value case"));
4830 stateQueryGroup->addChild(new QueryCase (m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT));
4831 stateQueryGroup->addChild(new QueryCase (m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN));
4832 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT));
4833 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64));
4839 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
4841 tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
4842 addChild(primitiveGroup);
4844 for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
4846 tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
4847 primitiveGroup->addChild(methodGroup);
4849 for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx)
4851 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
4852 (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0)
4854 // invalid config combination
4858 tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description);
4859 methodGroup->addChild(pipelineGroup);
4861 for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
4863 const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags |
4864 stateSetMethods[stateSetMethodNdx].methodFlags |
4865 pipelineConfigs[pipelineConfigNdx].stageFlags |
4866 usageConfigs[usageNdx].flags;
4868 if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
4870 if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
4873 switch (primitiveTypes[primitiveTypeNdx].type)
4876 pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4879 pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4882 pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags));
4898 const char* description;
4899 DepthDrawCase::DepthType depthMethod;
4904 "Fragment depth not modified in fragment shader",
4905 DepthDrawCase::DEPTH_BUILTIN
4908 "user_defined_depth",
4909 "Fragment depth is defined in the fragment shader",
4910 DepthDrawCase::DEPTH_USER_DEFINED
4916 const char* description;
4917 DepthDrawCase::BBoxState bboxState;
4918 DepthDrawCase::BBoxSize bboxSize;
4922 "global_state_bbox_equal",
4923 "Test tight bounding box with global bbox state",
4924 DepthDrawCase::STATE_GLOBAL,
4925 DepthDrawCase::BBOX_EQUAL,
4928 "global_state_bbox_larger",
4929 "Test padded bounding box with global bbox state",
4930 DepthDrawCase::STATE_GLOBAL,
4931 DepthDrawCase::BBOX_LARGER,
4934 "per_primitive_bbox_equal",
4935 "Test tight bounding box with tessellation output bbox",
4936 DepthDrawCase::STATE_PER_PRIMITIVE,
4937 DepthDrawCase::BBOX_EQUAL,
4940 "per_primitive_bbox_larger",
4941 "Test padded bounding box with tessellation output bbox",
4942 DepthDrawCase::STATE_PER_PRIMITIVE,
4943 DepthDrawCase::BBOX_LARGER,
4947 tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
4948 addChild(depthGroup);
4951 // .user_defined_depth
4952 for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
4954 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
4955 depthGroup->addChild(group);
4957 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
4958 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize));
4964 tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
4965 addChild(blitFboGroup);
4967 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
4968 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT));
4969 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO));
4974 tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
4975 addChild(clearGroup);
4977 clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0));
4978 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT));
4979 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));
4980 clearGroup->addChild(new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT));
4981 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));
4982 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));
4983 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
4984 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));
4985 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));
4988 // .call_order (Khronos bug #13262)
4990 tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
4991 addChild(callOrderGroup);
4993 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST));
4994 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST));