1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */ /*!
20  * \file
21  * \brief
22  */ /*-------------------------------------------------------------------*/
23 
24 /**
25  * \file  gl4GPUShaderFP64Tests.cpp
26  * \brief Implements conformance tests for "GPU Shader FP64" functionality.
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "gl4cClipControlTests.hpp"
30 
31 #include "deSharedPtr.hpp"
32 
33 #include "gluContextInfo.hpp"
34 #include "gluDefs.hpp"
35 #include "gluPixelTransfer.hpp"
36 #include "gluShaderProgram.hpp"
37 
38 #include "tcuFuzzyImageCompare.hpp"
39 #include "tcuImageCompare.hpp"
40 #include "tcuRenderTarget.hpp"
41 #include "tcuSurface.hpp"
42 #include "tcuTestLog.hpp"
43 
44 #include "glw.h"
45 #include "glwFunctions.hpp"
46 
47 #include <cmath>
48 
49 namespace gl4cts
50 {
51 
52 class ClipControlApi
53 {
54 public:
ClipControlApi(deqp::Context & context,ClipControlTests::API api)55 	ClipControlApi(deqp::Context& context, ClipControlTests::API api) : m_context(context)
56 	{
57 		if (!Supported(m_context, api))
58 		{
59 			throw tcu::NotSupportedError("Required clip_control extension is not supported");
60 		}
61 
62 		switch (api)
63 		{
64 		case ClipControlTests::API_GL_ARB_clip_control:
65 		case ClipControlTests::API_GL_45core: //fall through
66 			clipControl = context.getRenderContext().getFunctions().clipControl;
67 			break;
68 		}
69 	}
70 
Supported(deqp::Context & context,ClipControlTests::API api)71 	static bool Supported(deqp::Context& context, ClipControlTests::API api)
72 	{
73 		return (api == ClipControlTests::API_GL_ARB_clip_control &&
74 				context.getContextInfo().isExtensionSupported("GL_ARB_clip_control")) ||
75 			   api == ClipControlTests::API_GL_45core;
76 	}
77 
78 	glw::glClipControlFunc clipControl;
79 
80 private:
81 	deqp::Context& m_context;
82 };
83 
84 class ClipControlBaseTest : public deqp::TestCase
85 {
86 protected:
ClipControlBaseTest(deqp::Context & context,ClipControlTests::API api,const char * name,const char * description)87 	ClipControlBaseTest(deqp::Context& context, ClipControlTests::API api, const char* name, const char* description)
88 		: TestCase(context, name, description), m_api(api)
89 	{
90 	}
91 
init()92 	void init()
93 	{
94 		ClipControlApi(m_context, m_api);
95 	}
96 
verifyState(glw::GLenum origin,glw::GLenum depth)97 	bool verifyState(glw::GLenum origin, glw::GLenum depth)
98 	{
99 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
100 
101 		bool ret = true;
102 
103 		glw::GLint retI;
104 		gl.getIntegerv(GL_CLIP_ORIGIN, &retI);
105 		GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_CLIP_ORIGIN");
106 
107 		ret &= (static_cast<glw::GLenum>(retI) == origin);
108 
109 		gl.getIntegerv(GL_CLIP_DEPTH_MODE, &retI);
110 		GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_CLIP_DEPTH_MODE");
111 
112 		ret &= (static_cast<glw::GLenum>(retI) == depth);
113 
114 		return ret;
115 	}
116 
117 protected:
118 	const ClipControlTests::API m_api;
119 };
120 
121 class ClipControlRenderBaseTest : public ClipControlBaseTest
122 {
123 protected:
ClipControlRenderBaseTest(deqp::Context & context,ClipControlTests::API api,const char * name,const char * description)124 	ClipControlRenderBaseTest(deqp::Context& context, ClipControlTests::API api, const char* name,
125 							  const char* description)
126 		: ClipControlBaseTest(context, api, name, description), m_fbo(0), m_rboC(0), m_rboD(0)
127 	{
128 	}
129 
fsh()130 	const char* fsh()
131 	{
132 		return "#version 400"
133 			   "\n"
134 			   "out vec4 FragColor;"
135 			   "\n"
136 			   "void main() {"
137 			   "\n"
138 			   "    FragColor = vec4(0.0, 1.0, 0.0, 1.0);"
139 			   "\n"
140 			   "}";
141 	}
142 
fuzzyDepthCompare(tcu::TestLog & log,const char * imageSetName,const char * imageSetDesc,const tcu::TextureLevel & reference,const tcu::TextureLevel & result,float threshold,const tcu::TextureLevel * importanceMask=NULL)143 	bool fuzzyDepthCompare(tcu::TestLog& log, const char* imageSetName, const char* imageSetDesc,
144 						   const tcu::TextureLevel& reference, const tcu::TextureLevel& result, float threshold,
145 						   const tcu::TextureLevel* importanceMask = NULL)
146 	{
147 		(void)imageSetName;
148 		(void)imageSetDesc;
149 		bool  depthOk	= true;
150 		float difference = 0.0f;
151 
152 		for (int y = 0; y < result.getHeight() && depthOk; y++)
153 		{
154 			for (int x = 0; x < result.getWidth() && depthOk; x++)
155 			{
156 				float ref  = reference.getAccess().getPixDepth(x, y);
157 				float res  = result.getAccess().getPixDepth(x, y);
158 				difference = std::abs(ref - res);
159 				if (importanceMask)
160 				{
161 					difference *= importanceMask->getAccess().getPixDepth(x, y);
162 				}
163 				depthOk &= (difference < threshold);
164 			}
165 		}
166 
167 		if (!depthOk)
168 			log << tcu::TestLog::Message << "Image comparison failed: difference = " << difference
169 				<< ", threshold = " << threshold << tcu::TestLog::EndMessage;
170 		tcu::Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
171 		tcu::Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
172 		log << tcu::TestLog::ImageSet("Result", "Depth image comparison result")
173 			<< tcu::TestLog::Image("Result", "Result", result.getAccess(), pixelScale, pixelBias)
174 			<< tcu::TestLog::Image("Reference", "Reference", reference.getAccess(), pixelScale, pixelBias);
175 		if (importanceMask)
176 		{
177 			log << tcu::TestLog::Image("Importance mask", "mask", importanceMask->getAccess(), pixelScale, pixelBias);
178 		}
179 		log << tcu::TestLog::EndImageSet;
180 
181 		return depthOk;
182 	}
183 
init(void)184 	virtual void init(void)
185 	{
186 		const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
187 		glw::GLuint				 viewportW	= renderTarget.getWidth();
188 		glw::GLuint				 viewportH	= renderTarget.getHeight();
189 		const glw::Functions&	gl			  = m_context.getRenderContext().getFunctions();
190 
191 		gl.genFramebuffers(1, &m_fbo);
192 		gl.genRenderbuffers(1, &m_rboC);
193 		gl.genRenderbuffers(1, &m_rboD);
194 
195 		gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboC);
196 		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, viewportW, viewportH);
197 		gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboD);
198 		gl.renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, viewportW, viewportH);
199 
200 		gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
201 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboC);
202 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_rboD);
203 	}
204 
deinit(void)205 	virtual void deinit(void)
206 	{
207 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
208 		gl.deleteFramebuffers(1, &m_fbo);
209 		gl.deleteRenderbuffers(1, &m_rboC);
210 		gl.deleteRenderbuffers(1, &m_rboD);
211 		gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
212 	}
213 
214 private:
215 	GLuint m_fbo, m_rboC, m_rboD;
216 };
217 
218 /*
219  Verify the following state values are implemented and return a valid
220  initial value by calling GetIntegerv:
221 
222  Get Value                                 Initial Value
223  -------------------------------------------------------
224  CLIP_ORIGIN                                  LOWER_LEFT
225  CLIP_DEPTH_MODE                     NEGATIVE_ONE_TO_ONE
226 
227  Verify no GL error is generated.
228  */
229 class ClipControlInitialState : public ClipControlBaseTest
230 {
231 public:
ClipControlInitialState(deqp::Context & context,ClipControlTests::API api,const char * name)232 	ClipControlInitialState(deqp::Context& context, ClipControlTests::API api, const char* name)
233 		: ClipControlBaseTest(context, api, name, "Verify initial state")
234 	{
235 	}
236 
iterate()237 	IterateResult iterate()
238 	{
239 		if (!verifyState(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE))
240 		{
241 			TCU_FAIL("Wrong intitial state: GL_CLIP_ORIGIN should be GL_LOWER_LEFT,"
242 					 " GL_CLIP_ORIGIN should be NEGATIVE_ONE_TO_ONE");
243 		}
244 
245 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
246 		return STOP;
247 	}
248 };
249 
250 /*
251  Modify the state to each of the following combinations and after each
252  state change verify the state values:
253 
254  ClipControl(UPPER_LEFT, ZERO_TO_ONE)
255  ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
256  ClipControl(LOWER_LEFT, ZERO_TO_ONE)
257  ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
258 
259  Verify no GL error is generated.
260 
261  */
262 class ClipControlModifyGetState : public ClipControlBaseTest
263 {
264 public:
ClipControlModifyGetState(deqp::Context & context,ClipControlTests::API api,const char * name)265 	ClipControlModifyGetState(deqp::Context& context, ClipControlTests::API api, const char* name)
266 		: ClipControlBaseTest(context, api, name, "Verify initial state")
267 	{
268 	}
269 
deinit()270 	void deinit()
271 	{
272 		if (ClipControlApi::Supported(m_context, m_api))
273 		{
274 			ClipControlApi cc(m_context, m_api);
275 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
276 		}
277 	}
278 
iterate()279 	IterateResult iterate()
280 	{
281 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
282 		ClipControlApi		  cc(m_context, m_api);
283 
284 		GLenum cases[4][2] = {
285 			{ GL_UPPER_LEFT, GL_ZERO_TO_ONE },
286 			{ GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE },
287 			{ GL_LOWER_LEFT, GL_ZERO_TO_ONE },
288 			{ GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE },
289 		};
290 
291 		for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cases); i++)
292 		{
293 			cc.clipControl(cases[i][0], cases[i][1]);
294 			GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
295 			if (!verifyState(cases[i][0], cases[i][1]))
296 			{
297 				TCU_FAIL("Wrong ClipControl state after ClipControl() call");
298 			}
299 		}
300 
301 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
302 		return STOP;
303 	}
304 };
305 
306 /*
307  Check that ClipControl generate an GL_INVALID_ENUM error if origin is
308  not GL_LOWER_LEFT or GL_UPPER_LEFT.
309 
310  Check that ClipControl generate an GL_INVALID_ENUM error if depth is
311  not GL_NEGATIVE_ONE_TO_ONE or GL_ZERO_TO_ONE.
312 
313  Test is based on OpenGL 4.5 Core Profile Specification May 28th Section
314  13.5 Primitive Clipping:
315  "An INVALID_ENUM error is generated if origin is not LOWER_LEFT or
316  UPPER_LEFT.
317  An INVALID_ENUM error is generated if depth is not NEGATIVE_ONE_-
318  TO_ONE or ZERO_TO_ONE."
319  */
320 class ClipControlErrors : public ClipControlBaseTest
321 {
322 public:
ClipControlErrors(deqp::Context & context,ClipControlTests::API api,const char * name)323 	ClipControlErrors(deqp::Context& context, ClipControlTests::API api, const char* name)
324 		: ClipControlBaseTest(context, api, name, "Verify that proper errors are generated when using ClipControl.")
325 	{
326 	}
327 
deinit()328 	void deinit()
329 	{
330 		if (ClipControlApi::Supported(m_context, m_api))
331 		{
332 			ClipControlApi cc(m_context, m_api);
333 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
334 		}
335 	}
336 
iterate()337 	IterateResult iterate()
338 	{
339 		/* API query */
340 		tcu::TestLog&		  log = m_testCtx.getLog();
341 		const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
342 		ClipControlApi		  cc(m_context, m_api);
343 
344 		/* Finding improper value. */
345 		GLenum improper_value = GL_NONE;
346 
347 		while ((GL_UPPER_LEFT == improper_value) || (GL_LOWER_LEFT == improper_value) ||
348 			   (GL_ZERO_TO_ONE == improper_value) || (GL_NEGATIVE_ONE_TO_ONE == improper_value))
349 		{
350 			++improper_value;
351 		}
352 
353 		/* Test setup. */
354 		GLenum cases[5][2] = { { GL_UPPER_LEFT, improper_value },
355 							   { GL_LOWER_LEFT, improper_value },
356 							   { improper_value, GL_ZERO_TO_ONE },
357 							   { improper_value, GL_NEGATIVE_ONE_TO_ONE },
358 							   { improper_value, improper_value } };
359 
360 		/* Test iterations. */
361 		for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(cases); i++)
362 		{
363 			cc.clipControl(cases[i][0], cases[i][1]);
364 
365 			if (GL_INVALID_ENUM != gl.getError())
366 			{
367 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, qpGetTestResultName(QP_TEST_RESULT_FAIL));
368 
369 				log << tcu::TestLog::Message
370 					<< "ClipControl have not generated GL_INVALID_ENUM error when called with invalid value ("
371 					<< cases[i][0] << ", " << cases[i][1] << ")." << tcu::TestLog::EndMessage;
372 			}
373 		}
374 
375 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, qpGetTestResultName(QP_TEST_RESULT_PASS));
376 		return STOP;
377 	}
378 };
379 
380 /*
381  Clip Control Origin Test
382 
383  * Basic <origin> behavior can be tested by rendering to a viewport with
384  clip coordinates where -1.0 <= x_c <= 0.0 and -1.0 <= y_c <= 0.0.
385  When <origin> is LOWER_LEFT the "bottom left" portion of the window
386  is rendered and when UPPER_LEFT is used the "top left" portion of the
387  window is rendered. The default framebuffer should be bound. Here is the
388  basic outline of the test:
389 
390  - Clear the default framebuffer to red (1,0,0).
391  - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
392  - Render a triangle fan covering (-1.0, -1.0) to (0.0, 0.0) and
393  write a pixel value of green (0,1,0).
394  - Read back the default framebuffer with ReadPixels
395  - Verify the green pixels at the top and red at the bottom.
396 
397  Repeat the above test with LOWER_LEFT and verify green at the bottom
398  and red at the top.
399  */
400 class ClipControlOriginTest : public ClipControlRenderBaseTest
401 {
402 public:
ClipControlOriginTest(deqp::Context & context,ClipControlTests::API api,const char * name)403 	ClipControlOriginTest(deqp::Context& context, ClipControlTests::API api, const char* name)
404 		: ClipControlRenderBaseTest(context, api, name, "Clip Control Origin Test"), m_vao(0), m_vbo(0)
405 	{
406 	}
407 
deinit()408 	void deinit()
409 	{
410 		ClipControlRenderBaseTest::deinit();
411 
412 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
413 		if (ClipControlApi::Supported(m_context, m_api))
414 		{
415 			ClipControlApi cc(m_context, m_api);
416 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
417 		}
418 
419 		gl.clearColor(0.0, 0.0, 0.0, 0.0);
420 		if (m_vao)
421 		{
422 			gl.deleteVertexArrays(1, &m_vao);
423 		}
424 		if (m_vbo)
425 		{
426 			gl.deleteBuffers(1, &m_vbo);
427 		}
428 	}
429 
iterate()430 	IterateResult iterate()
431 	{
432 
433 		tcu::TestLog&		  log = m_testCtx.getLog();
434 		const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
435 		ClipControlApi		  cc(m_context, m_api);
436 
437 		//Render a triangle fan covering(-1.0, -1.0) to(1.0, 0.0) and
438 		//write a pixel value of green(0, 1, 0).
439 
440 		de::SharedPtr<glu::ShaderProgram> program(
441 			new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
442 
443 		log << (*program);
444 		if (!program->isOk())
445 		{
446 			TCU_FAIL("Program compilation failed");
447 		}
448 
449 		gl.genVertexArrays(1, &m_vao);
450 		gl.bindVertexArray(m_vao);
451 
452 		gl.genBuffers(1, &m_vbo);
453 
454 		const float vertex_data0[] = { -1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0 };
455 
456 		gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
457 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
458 
459 		gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
460 		gl.enableVertexAttribArray(0);
461 
462 		gl.useProgram(program->getProgram());
463 
464 		glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
465 
466 		qpTestResult result = QP_TEST_RESULT_PASS;
467 
468 		for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
469 		{
470 			//Clear the default framebuffer to red(1, 0, 0).
471 			gl.clearColor(1.0, 0.0, 0.0, 1.0);
472 			gl.clear(GL_COLOR_BUFFER_BIT);
473 
474 			//Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
475 			cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
476 			GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
477 
478 			//test method modification: use GL_TRIANGLE_STRIP, not FAN.
479 			gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
480 
481 			//Read back the default framebuffer with ReadPixels
482 			//Verify the green pixels at the top and red at the bottom.
483 			qpTestResult loopResult = ValidateFramebuffer(m_context, origins[orig]);
484 			if (loopResult != QP_TEST_RESULT_PASS)
485 			{
486 				result = loopResult;
487 			}
488 		}
489 
490 		m_testCtx.setTestResult(result, qpGetTestResultName(result));
491 
492 		return STOP;
493 	}
494 
vsh()495 	const char* vsh()
496 	{
497 		return "#version 400"
498 			   "\n"
499 			   "in vec2 Position;"
500 			   "\n"
501 			   "void main() {"
502 			   "\n"
503 			   "    gl_Position = vec4(Position, 0.0, 1.0);"
504 			   "\n"
505 			   "}";
506 	}
507 
ValidateFramebuffer(deqp::Context & context,glw::GLenum origin)508 	qpTestResult ValidateFramebuffer(deqp::Context& context, glw::GLenum origin)
509 	{
510 		const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
511 		glw::GLuint				 viewportW	= renderTarget.getWidth();
512 		glw::GLuint				 viewportH	= renderTarget.getHeight();
513 		tcu::Surface			 renderedFrame(viewportW, viewportH);
514 		tcu::Surface			 referenceFrame(viewportW, viewportH);
515 
516 		tcu::TestLog& log = context.getTestContext().getLog();
517 
518 		for (int y = 0; y < renderedFrame.getHeight(); y++)
519 		{
520 			float yCoord = (float)(y) / (float)renderedFrame.getHeight();
521 
522 			for (int x = 0; x < renderedFrame.getWidth(); x++)
523 			{
524 
525 				float xCoord = (float)(x) / (float)renderedFrame.getWidth();
526 
527 				bool greenQuadrant;
528 
529 				if (origin == GL_UPPER_LEFT)
530 				{
531 					greenQuadrant = (yCoord > 0.5 && xCoord <= 0.5);
532 				}
533 				else
534 				{
535 					greenQuadrant = (yCoord <= 0.5 && xCoord <= 0.5);
536 				}
537 
538 				if (greenQuadrant)
539 				{
540 					referenceFrame.setPixel(x, y, tcu::RGBA::green());
541 				}
542 				else
543 				{
544 					referenceFrame.setPixel(x, y, tcu::RGBA::red());
545 				}
546 			}
547 		}
548 
549 		glu::readPixels(context.getRenderContext(), 0, 0, renderedFrame.getAccess());
550 
551 		if (tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
552 							  tcu::COMPARE_LOG_RESULT))
553 		{
554 			return QP_TEST_RESULT_PASS;
555 		}
556 		else
557 		{
558 			return QP_TEST_RESULT_FAIL;
559 		}
560 	}
561 
562 	glw::GLuint m_vao, m_vbo;
563 };
564 
565 /*   Depth Mode Test
566 
567  * Basic <depth> behavior can be tested by writing specific z_c (z
568  clip coordinates) and observing its clipping and transformation.
569  Create and bind a framebuffer object with a floating-point depth
570  buffer attachment. Make sure depth clamping is disabled. The best
571  steps for verifying the correct depth mode:
572 
573  - Clear the depth buffer to 0.5.
574  - Set ClipControl(LOWER_LEFT, ZERO_TO_ONE)
575  - Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
576  - Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
577  - Read back the floating-point depth buffer with ReadPixels
578  - Verify that the pixels with a Z clip coordinate less than 0.0 are
579  clipped and those coordinates from 0.0 to 1.0 update the depth
580  buffer with values 0.0 to 1.0.
581  */
582 class ClipControlDepthModeZeroToOneTest : public ClipControlRenderBaseTest
583 {
584 public:
ClipControlDepthModeZeroToOneTest(deqp::Context & context,ClipControlTests::API api,const char * name)585 	ClipControlDepthModeZeroToOneTest(deqp::Context& context, ClipControlTests::API api, const char* name)
586 		: ClipControlRenderBaseTest(context, api, name, "Depth Mode Test, ZERO_TO_ONE"), m_vao(0), m_vbo(0)
587 	{
588 	}
589 
deinit()590 	void deinit()
591 	{
592 		ClipControlRenderBaseTest::deinit();
593 
594 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
595 
596 		if (ClipControlApi::Supported(m_context, m_api))
597 		{
598 			ClipControlApi cc(m_context, m_api);
599 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
600 		}
601 
602 		gl.clearDepth(0.0);
603 		gl.clearColor(0.0, 0.0, 0.0, 0.0);
604 
605 		gl.disable(GL_DEPTH_TEST);
606 		gl.depthFunc(GL_LESS);
607 
608 		if (m_vao)
609 		{
610 			gl.deleteVertexArrays(1, &m_vao);
611 		}
612 		if (m_vbo)
613 		{
614 			gl.deleteBuffers(1, &m_vbo);
615 		}
616 	}
617 
iterate()618 	IterateResult iterate()
619 	{
620 
621 		tcu::TestLog&		  log = m_testCtx.getLog();
622 		const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
623 		ClipControlApi		  cc(m_context, m_api);
624 
625 		gl.clearColor(1.0, 0.0, 0.0, 1.0);
626 		gl.clear(GL_COLOR_BUFFER_BIT);
627 
628 		//Clear the depth buffer to 0.5.
629 		gl.clearDepth(0.5);
630 		gl.clear(GL_DEPTH_BUFFER_BIT);
631 
632 		//Set ClipControl(LOWER_LEFT, ZERO_TO_ONE)
633 		cc.clipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
634 		GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
635 
636 		//Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
637 		gl.enable(GL_DEPTH_TEST);
638 		gl.depthFunc(GL_ALWAYS);
639 
640 		//Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
641 		de::SharedPtr<glu::ShaderProgram> program(
642 			new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
643 
644 		log << (*program);
645 		if (!program->isOk())
646 		{
647 			TCU_FAIL("Program compilation failed");
648 		}
649 
650 		gl.genVertexArrays(1, &m_vao);
651 		gl.bindVertexArray(m_vao);
652 
653 		gl.genBuffers(1, &m_vbo);
654 
655 		const float vertex_data0[] = {
656 			-1.0, -1.0, -1.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
657 		};
658 
659 		gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
660 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
661 
662 		gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
663 		gl.enableVertexAttribArray(0);
664 
665 		gl.useProgram(program->getProgram());
666 
667 		//test method modification: use GL_TRIANGLE_STRIP, not FAN.
668 		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
669 
670 		//Read back the floating-point depth buffer with ReadPixels
671 		//Verify that the pixels with a Z clip coordinate less than 0.0 are
672 		//  clipped and those coordinates from 0.0 to 1.0 update the depth
673 		//  buffer with values 0.0 to 1.0.
674 		qpTestResult result = ValidateFramebuffer(m_context);
675 		m_testCtx.setTestResult(result, qpGetTestResultName(result));
676 
677 		return STOP;
678 	}
679 
vsh()680 	const char* vsh()
681 	{
682 		return "#version 400"
683 			   "\n"
684 			   "in vec3 Position;"
685 			   "\n"
686 			   "void main() {"
687 			   "\n"
688 			   "    gl_Position = vec4(Position, 1.0);"
689 			   "\n"
690 			   "}";
691 	}
692 
ValidateFramebuffer(deqp::Context & context)693 	qpTestResult ValidateFramebuffer(deqp::Context& context)
694 	{
695 		const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
696 		glw::GLuint				 viewportW	= renderTarget.getWidth();
697 		glw::GLuint				 viewportH	= renderTarget.getHeight();
698 		tcu::Surface			 renderedColorFrame(viewportW, viewportH);
699 		tcu::Surface			 referenceColorFrame(viewportW, viewportH);
700 		tcu::TextureFormat		 depthFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
701 		tcu::TextureLevel		 renderedDepthFrame(depthFormat, viewportW, viewportH);
702 		tcu::TextureLevel		 referenceDepthFrame(depthFormat, viewportW, viewportH);
703 		tcu::TextureLevel		 importanceMaskFrame(depthFormat, viewportW, viewportH);
704 
705 		tcu::TestLog& log = context.getTestContext().getLog();
706 
707 		const float rasterizationError =
708 			2.0f / (float)renderedColorFrame.getHeight() + 2.0f / (float)renderedColorFrame.getWidth();
709 
710 		for (int y = 0; y < renderedColorFrame.getHeight(); y++)
711 		{
712 			float yCoord = ((float)(y) + 0.5f) / (float)renderedColorFrame.getHeight();
713 
714 			for (int x = 0; x < renderedColorFrame.getWidth(); x++)
715 			{
716 				float xCoord = ((float)(x) + 0.5f) / (float)renderedColorFrame.getWidth();
717 
718 				if (yCoord >= 1.0 - xCoord - rasterizationError && yCoord <= 1.0 - xCoord + rasterizationError)
719 				{
720 					importanceMaskFrame.getAccess().setPixDepth(0.0f, x, y);
721 				}
722 				else
723 				{
724 					importanceMaskFrame.getAccess().setPixDepth(1.0f, x, y);
725 				}
726 
727 				if (yCoord < 1.0 - xCoord)
728 				{
729 					referenceColorFrame.setPixel(x, y, tcu::RGBA::red());
730 					referenceDepthFrame.getAccess().setPixDepth(0.5f, x, y);
731 				}
732 				else
733 				{
734 					referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
735 
736 					referenceDepthFrame.getAccess().setPixDepth(-1.0f + xCoord + yCoord, x, y);
737 				}
738 			}
739 		}
740 
741 		glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
742 		if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
743 							   0.05f, tcu::COMPARE_LOG_RESULT))
744 		{
745 
746 			return QP_TEST_RESULT_FAIL;
747 		}
748 
749 		glu::readPixels(context.getRenderContext(), 0, 0, renderedDepthFrame.getAccess());
750 		if (!fuzzyDepthCompare(log, "Result", "Depth image comparison result", referenceDepthFrame, renderedDepthFrame,
751 							   0.05f, &importanceMaskFrame))
752 		{
753 			return QP_TEST_RESULT_FAIL;
754 		}
755 		return QP_TEST_RESULT_PASS;
756 	}
757 
758 	glw::GLuint m_vao, m_vbo;
759 };
760 
761 /*
762  Do the same as above, but use the default NEGATIVE_ONE_TO_ONE depth mode:
763 
764  - Clear the depth buffer to 0.5.
765  - Set ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
766  - Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
767  - Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
768  - Read back the floating-point depth buffer with ReadPixels
769  - Verify that no pixels are clipped and the depth buffer contains
770  values from 0.0 to 1.0.
771  */
772 class ClipControlDepthModeOneToOneTest : public ClipControlRenderBaseTest
773 {
774 public:
ClipControlDepthModeOneToOneTest(deqp::Context & context,ClipControlTests::API api,const char * name)775 	ClipControlDepthModeOneToOneTest(deqp::Context& context, ClipControlTests::API api, const char* name)
776 		: ClipControlRenderBaseTest(context, api, name, "Depth Mode Test, ZERO_TO_ONE"), m_vao(0), m_vbo(0)
777 	{
778 	}
779 
deinit()780 	void deinit()
781 	{
782 		ClipControlRenderBaseTest::deinit();
783 
784 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
785 
786 		if (ClipControlApi::Supported(m_context, m_api))
787 		{
788 			ClipControlApi cc(m_context, m_api);
789 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
790 		}
791 
792 		gl.clearDepth(0.0);
793 		gl.clearColor(0.0, 0.0, 0.0, 0.0);
794 
795 		gl.disable(GL_DEPTH_TEST);
796 		gl.depthFunc(GL_LESS);
797 
798 		if (m_vao)
799 		{
800 			gl.deleteVertexArrays(1, &m_vao);
801 		}
802 		if (m_vbo)
803 		{
804 			gl.deleteBuffers(1, &m_vbo);
805 		}
806 	}
807 
iterate()808 	IterateResult iterate()
809 	{
810 		tcu::TestLog&		  log = m_testCtx.getLog();
811 		const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
812 		ClipControlApi		  cc(m_context, m_api);
813 
814 		gl.clearColor(1.0, 0.0, 0.0, 1.0);
815 		gl.clear(GL_COLOR_BUFFER_BIT);
816 
817 		//Clear the depth buffer to 0.5.
818 		gl.clearDepth(0.5);
819 		gl.clear(GL_DEPTH_BUFFER_BIT);
820 
821 		//Set ClipControl(LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE)
822 		cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
823 		GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
824 
825 		//Enable(DEPTH_TEST) with DepthFunc(ALWAYS)
826 		gl.enable(GL_DEPTH_TEST);
827 		gl.depthFunc(GL_ALWAYS);
828 
829 		//Render a triangle fan coverage (-1.0,-1.0,-1.0) to (1.0,1.0,1.0).
830 		de::SharedPtr<glu::ShaderProgram> program(
831 			new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
832 
833 		log << (*program);
834 		if (!program->isOk())
835 		{
836 			TCU_FAIL("Program compilation failed");
837 		}
838 
839 		gl.genVertexArrays(1, &m_vao);
840 		gl.bindVertexArray(m_vao);
841 
842 		gl.genBuffers(1, &m_vbo);
843 
844 		const float vertex_data0[] = {
845 			-1.0, -1.0, -1.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
846 		};
847 
848 		gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
849 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
850 
851 		gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
852 		gl.enableVertexAttribArray(0);
853 
854 		gl.useProgram(program->getProgram());
855 
856 		//test method modification: use GL_TRIANGLE_STRIP, not FAN.
857 		gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
858 
859 		//Read back the floating-point depth buffer with ReadPixels
860 		//Verify that the pixels with a Z clip coordinate less than 0.0 are
861 		//  clipped and those coordinates from 0.0 to 1.0 update the depth
862 		//  buffer with values 0.0 to 1.0.
863 		qpTestResult result = ValidateFramebuffer(m_context);
864 		m_testCtx.setTestResult(result, qpGetTestResultName(result));
865 
866 		return STOP;
867 	}
868 
vsh()869 	const char* vsh()
870 	{
871 		return "#version 400"
872 			   "\n"
873 			   "in vec3 Position;"
874 			   "\n"
875 			   "void main() {"
876 			   "\n"
877 			   "    gl_Position = vec4(Position, 1.0);"
878 			   "\n"
879 			   "}";
880 	}
881 
ValidateFramebuffer(deqp::Context & context)882 	qpTestResult ValidateFramebuffer(deqp::Context& context)
883 	{
884 		const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
885 		glw::GLuint				 viewportW	= renderTarget.getWidth();
886 		glw::GLuint				 viewportH	= renderTarget.getHeight();
887 		tcu::Surface			 renderedColorFrame(viewportW, viewportH);
888 		tcu::Surface			 referenceColorFrame(viewportW, viewportH);
889 		tcu::TextureFormat		 depthFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
890 		tcu::TextureLevel		 renderedDepthFrame(depthFormat, viewportW, viewportH);
891 		tcu::TextureLevel		 referenceDepthFrame(depthFormat, viewportW, viewportH);
892 
893 		tcu::TestLog& log = context.getTestContext().getLog();
894 
895 		for (int y = 0; y < renderedColorFrame.getHeight(); y++)
896 		{
897 			float yCoord = (float)(y) / (float)renderedColorFrame.getHeight();
898 			for (int x = 0; x < renderedColorFrame.getWidth(); x++)
899 			{
900 				float xCoord = (float)(x) / (float)renderedColorFrame.getWidth();
901 
902 				referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
903 				referenceDepthFrame.getAccess().setPixDepth((xCoord + yCoord) * 0.5f, x, y);
904 			}
905 		}
906 
907 		glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
908 		if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
909 							   0.05f, tcu::COMPARE_LOG_RESULT))
910 		{
911 
912 			return QP_TEST_RESULT_FAIL;
913 		}
914 		glu::readPixels(context.getRenderContext(), 0, 0, renderedDepthFrame.getAccess());
915 		if (!fuzzyDepthCompare(log, "Result", "Depth image comparison result", referenceDepthFrame, renderedDepthFrame,
916 							   0.05f))
917 		{
918 
919 			return QP_TEST_RESULT_FAIL;
920 		}
921 
922 		return QP_TEST_RESULT_PASS;
923 	}
924 
925 	glw::GLuint m_vao, m_vbo;
926 };
927 
928 /*
929  Clip Control Origin With Face Culling Test
930 
931  * Face culling should be tested with both <origin> settings.
932  The reason for that is, when doing Y-inversion, implementation
933  should not flip the calculated area sign for the triangle.
934  In other words, culling of CCW and CW triangles should
935  be orthogonal to used <origin> mode. Both triangle windings
936  and both <origin> modes should be tested. Here is the basic
937  outline of the test:
938 
939  - Clear the framebuffer to red (1,0,0).
940  - Enable GL_CULL_FACE, leave default front face & cull face (CCW, BACK)
941  - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
942  - Render a counter-clockwise triangles covering
943  (-1.0, -1.0) to (0.0, 1.0) and write a pixel value of green (0,1,0).
944  - Render a clockwise triangles covering
945  (0.0, -1.0) to (1.0, 1.0) and write a pixel value of green (0,1,0).
946  - Read back the framebuffer with ReadPixels
947  - Verify the green pixels at the left and red at the right.
948 
949  Repeat above test for ClipControl(LOWER_LEFT, NEGATIVE_ONE_TO_ONE)
950  */
951 class ClipControlFaceCulling : public ClipControlRenderBaseTest
952 {
953 public:
ClipControlFaceCulling(deqp::Context & context,ClipControlTests::API api,const char * name)954 	ClipControlFaceCulling(deqp::Context& context, ClipControlTests::API api, const char* name)
955 		: ClipControlRenderBaseTest(context, api, name, "Face culling test, both origins"), m_vao(0), m_vbo(0)
956 	{
957 	}
958 
deinit()959 	void deinit()
960 	{
961 		ClipControlRenderBaseTest::deinit();
962 
963 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
964 
965 		if (ClipControlApi::Supported(m_context, m_api))
966 		{
967 			ClipControlApi cc(m_context, m_api);
968 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
969 		}
970 
971 		gl.disable(GL_CULL_FACE);
972 
973 		gl.clearDepth(0.0);
974 		gl.clearColor(0.0, 0.0, 0.0, 0.0);
975 
976 		gl.disable(GL_DEPTH_TEST);
977 		gl.depthFunc(GL_LESS);
978 
979 		if (m_vao)
980 		{
981 			gl.deleteVertexArrays(1, &m_vao);
982 		}
983 		if (m_vbo)
984 		{
985 			gl.deleteBuffers(1, &m_vbo);
986 		}
987 	}
988 
iterate()989 	IterateResult iterate()
990 	{
991 
992 		tcu::TestLog&		  log = m_testCtx.getLog();
993 		const glw::Functions& gl  = m_context.getRenderContext().getFunctions();
994 		ClipControlApi		  cc(m_context, m_api);
995 
996 		//Enable GL_CULL_FACE, leave default front face & cull face(CCW, BACK)
997 		gl.enable(GL_CULL_FACE);
998 
999 		//Render a counter-clockwise triangles covering
1000 		//(-1.0, -1.0) to(0.0, 1.0) and write a pixel value of green(0, 1, 0).
1001 		//Render a clockwise triangles covering
1002 		//(0.0, -1.0) to(1.0, 1.0) and write a pixel value of green(0, 1, 0).
1003 		de::SharedPtr<glu::ShaderProgram> program(
1004 			new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
1005 
1006 		log << (*program);
1007 		if (!program->isOk())
1008 		{
1009 			TCU_FAIL("Program compilation failed");
1010 		}
1011 
1012 		gl.genVertexArrays(1, &m_vao);
1013 		gl.bindVertexArray(m_vao);
1014 
1015 		gl.genBuffers(1, &m_vbo);
1016 
1017 		const float vertex_data0[] = {
1018 			//CCW
1019 			-1.0, -1.0, 0.0, -1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 1.0, -1.0, 1.0,
1020 			//CW
1021 			0.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
1022 		};
1023 
1024 		gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
1025 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
1026 
1027 		gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
1028 		gl.enableVertexAttribArray(0);
1029 
1030 		gl.useProgram(program->getProgram());
1031 
1032 		glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
1033 
1034 		qpTestResult result = QP_TEST_RESULT_PASS;
1035 
1036 		for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
1037 		{
1038 			//Clear the framebuffer to red (1,0,0).
1039 			gl.clearColor(1.0, 0.0, 0.0, 1.0);
1040 			gl.clear(GL_COLOR_BUFFER_BIT);
1041 
1042 			gl.drawArrays(GL_TRIANGLES, 0, 12);
1043 
1044 			//Set ClipControl(<origin>, NEGATIVE_ONE_TO_ONE)
1045 			cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
1046 			GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
1047 
1048 			//Read back the framebuffer with ReadPixels
1049 			//Verify the green pixels at the left and red at the right.
1050 			qpTestResult loopResult = ValidateFramebuffer(m_context);
1051 			if (loopResult != QP_TEST_RESULT_PASS)
1052 			{
1053 				result = loopResult;
1054 			}
1055 		}
1056 		m_testCtx.setTestResult(result, qpGetTestResultName(result));
1057 
1058 		return STOP;
1059 	}
1060 
vsh()1061 	const char* vsh()
1062 	{
1063 		return "#version 400"
1064 			   "\n"
1065 			   "in vec3 Position;"
1066 			   "\n"
1067 			   "void main() {"
1068 			   "\n"
1069 			   "    gl_Position = vec4(Position, 1.0);"
1070 			   "\n"
1071 			   "}";
1072 	}
1073 
ValidateFramebuffer(deqp::Context & context)1074 	qpTestResult ValidateFramebuffer(deqp::Context& context)
1075 	{
1076 		const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
1077 		glw::GLuint				 viewportW	= renderTarget.getWidth();
1078 		glw::GLuint				 viewportH	= renderTarget.getHeight();
1079 		tcu::Surface			 renderedColorFrame(viewportW, viewportH);
1080 		tcu::Surface			 referenceColorFrame(viewportW, viewportH);
1081 		tcu::TestLog&			 log = context.getTestContext().getLog();
1082 
1083 		for (int y = 0; y < renderedColorFrame.getHeight(); y++)
1084 		{
1085 			for (int x = 0; x < renderedColorFrame.getWidth(); x++)
1086 			{
1087 				float xCoord = (float)(x) / (float)renderedColorFrame.getWidth();
1088 
1089 				if (xCoord < 0.5)
1090 				{
1091 					referenceColorFrame.setPixel(x, y, tcu::RGBA::green());
1092 				}
1093 				else
1094 				{
1095 					referenceColorFrame.setPixel(x, y, tcu::RGBA::red());
1096 				}
1097 			}
1098 		}
1099 
1100 		glu::readPixels(context.getRenderContext(), 0, 0, renderedColorFrame.getAccess());
1101 		if (!tcu::fuzzyCompare(log, "Result", "Color image comparison result", referenceColorFrame, renderedColorFrame,
1102 							   0.05f, tcu::COMPARE_LOG_RESULT))
1103 		{
1104 
1105 			return QP_TEST_RESULT_FAIL;
1106 		}
1107 		return QP_TEST_RESULT_PASS;
1108 	}
1109 
1110 	glw::GLuint m_vao, m_vbo;
1111 };
1112 
1113 /*
1114  Viewport Bounds Test
1115 
1116  * Viewport bounds should be tested, to ensure that rendering with flipped
1117  origin affects only viewport area.
1118 
1119  This can be done by clearing the window to blue, making viewport
1120  a non-symmetric-in-any-way subset of the window, than rendering
1121  full-viewport multiple color quad. The (-1.0, -1.0)..(0.0, 0.0) quadrant
1122  of a quad is red, the rest is green.
1123  Whatever the origin is, the area outside of the viewport should stay blue.
1124  If origin is LOWER_LEFT the "lower left" portion of the viewport is red,
1125  if origin is UPPER_LEFT the "top left" portion of the viewport is red
1126  (and in both cases the rest of viewport is green).
1127 
1128  Here is the basic outline of the test:
1129 
1130  - Clear the default framebuffer to blue (0,0,1).
1131  - Set viewport to A = (x, y, w, h) = (1/8, 1/4, 1/2, 1/4)  in terms of proportional window size
1132  - Set ClipControl(UPPER_LEFT, NEGATIVE_ONE_TO_ONE)
1133  - Render a triangle strip covering (-1.0, -1.0) to (1.0, 1.0).
1134  Write a pixel value of red (0,1,0) to (-1.0, -1.0)..(0.0, 0.0), other parts are green
1135  - Reset viewport to defaults
1136  - Read back the default framebuffer with ReadPixels
1137  - Verify:
1138  - regions outside A viewport are green
1139  - Inside A viewport upper upper left portion is red, rest is green.
1140 
1141  Repeat the above test with LOWER_LEFT origin and lower left portion of A is red,
1142  rest is green.
1143  */
1144 class ClipControlViewportBounds : public ClipControlRenderBaseTest
1145 {
1146 public:
ClipControlViewportBounds(deqp::Context & context,ClipControlTests::API api,const char * name)1147 	ClipControlViewportBounds(deqp::Context& context, ClipControlTests::API api, const char* name)
1148 		: ClipControlRenderBaseTest(context, api, name, "Clip Control Origin Test"), m_vao(0), m_vbo(0)
1149 	{
1150 	}
1151 
deinit()1152 	void deinit()
1153 	{
1154 		ClipControlRenderBaseTest::deinit();
1155 
1156 		const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
1157 		glw::GLuint				 windowW	  = renderTarget.getWidth();
1158 		glw::GLuint				 windowH	  = renderTarget.getHeight();
1159 		const glw::Functions&	gl			  = m_context.getRenderContext().getFunctions();
1160 
1161 		if (ClipControlApi::Supported(m_context, m_api))
1162 		{
1163 			ClipControlApi cc(m_context, m_api);
1164 			cc.clipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
1165 		}
1166 
1167 		gl.clearColor(0.0, 0.0, 0.0, 0.0);
1168 		gl.viewport(0, 0, windowW, windowH);
1169 
1170 		if (m_vao)
1171 		{
1172 			gl.deleteVertexArrays(1, &m_vao);
1173 		}
1174 		if (m_vbo)
1175 		{
1176 			gl.deleteBuffers(1, &m_vbo);
1177 		}
1178 	}
1179 
iterate()1180 	IterateResult iterate()
1181 	{
1182 		tcu::TestLog&			 log		  = m_testCtx.getLog();
1183 		const glw::Functions&	gl			  = m_context.getRenderContext().getFunctions();
1184 		const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
1185 		glw::GLuint				 windowW	  = renderTarget.getWidth();
1186 		glw::GLuint				 windowH	  = renderTarget.getHeight();
1187 		ClipControlApi			 cc(m_context, m_api);
1188 
1189 		//Clear the default framebuffer to blue (0,0,1).
1190 		gl.clearColor(0.0, 0.0, 1.0, 1.0);
1191 		gl.clear(GL_COLOR_BUFFER_BIT);
1192 
1193 		de::SharedPtr<glu::ShaderProgram> program(
1194 			new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
1195 
1196 		log << (*program);
1197 		if (!program->isOk())
1198 		{
1199 			TCU_FAIL("Program compilation failed");
1200 		}
1201 		gl.genVertexArrays(1, &m_vao);
1202 		gl.bindVertexArray(m_vao);
1203 
1204 		gl.genBuffers(1, &m_vbo);
1205 
1206 		const float vertex_data0[] = { -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0 };
1207 
1208 		gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
1209 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertex_data0), vertex_data0, GL_STATIC_DRAW);
1210 
1211 		gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
1212 		gl.enableVertexAttribArray(0);
1213 
1214 		gl.useProgram(program->getProgram());
1215 
1216 		glw::GLenum origins[] = { GL_UPPER_LEFT, GL_LOWER_LEFT };
1217 
1218 		qpTestResult result = QP_TEST_RESULT_PASS;
1219 
1220 		for (size_t orig = 0; orig < DE_LENGTH_OF_ARRAY(origins); orig++)
1221 		{
1222 			//Set viewport to A = (x, y, w, h) = (1/8, 1/4, 1/2, 1/4) in terms of proportional window size
1223 			gl.viewport((glw::GLint)(0.125f * (float)windowW), (glw::GLint)(0.25f * (float)windowH),
1224 						(glw::GLsizei)(0.5f * (float)windowW), (glw::GLsizei)(0.25f * (float)windowH));
1225 
1226 			//Set ClipControl(<origin>, NEGATIVE_ONE_TO_ONE)
1227 			cc.clipControl(origins[orig], GL_NEGATIVE_ONE_TO_ONE);
1228 			GLU_EXPECT_NO_ERROR(gl.getError(), "ClipControl()");
1229 
1230 			//Render a triangle strip covering (-1.0, -1.0) to (1.0, 1.0).
1231 			//Write a pixel value of red (0,1,0) to (-1.0, -1.0)..(0.0, 0.0), other parts are green
1232 			gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
1233 
1234 			gl.viewport(0, 0, windowW, windowH);
1235 
1236 			//Read back the default framebuffer with ReadPixels
1237 			//Verify the green pixels at the top and red at the bottom.
1238 			qpTestResult loopResult = ValidateFramebuffer(m_context, origins[orig]);
1239 			if (loopResult != QP_TEST_RESULT_PASS)
1240 			{
1241 				result = loopResult;
1242 			}
1243 		}
1244 		m_testCtx.setTestResult(result, qpGetTestResultName(result));
1245 		return STOP;
1246 	}
1247 
vsh()1248 	const char* vsh()
1249 	{
1250 		return "#version 400"
1251 			   "\n"
1252 			   "in  vec2 Position;"
1253 			   "\n"
1254 			   "out vec2 PositionOut;"
1255 			   "\n"
1256 			   "void main() {"
1257 			   "\n"
1258 			   "    gl_Position = vec4(Position, 0.0, 1.0);"
1259 			   "\n"
1260 			   "    PositionOut = Position;"
1261 			   "\n"
1262 			   "}";
1263 	}
1264 
fsh()1265 	const char* fsh()
1266 	{
1267 		return "#version 400"
1268 			   "\n"
1269 			   "in  vec2 PositionOut;"
1270 			   "\n"
1271 			   "out vec4 FragColor;"
1272 			   "\n"
1273 			   "void main() {"
1274 			   "\n"
1275 			   "    if (PositionOut.x < 0.0 && PositionOut.y < 0.0)"
1276 			   "\n"
1277 			   "       FragColor = vec4(0.0, 1.0, 0.0, 1.0);"
1278 			   "\n"
1279 			   "    else"
1280 			   "\n"
1281 			   "       FragColor = vec4(1.0, 0.0, 0.0, 1.0);"
1282 			   "\n"
1283 			   "}";
1284 	}
1285 
ValidateFramebuffer(deqp::Context & context,glw::GLenum origin)1286 	qpTestResult ValidateFramebuffer(deqp::Context& context, glw::GLenum origin)
1287 	{
1288 		const tcu::RenderTarget& renderTarget = context.getRenderContext().getRenderTarget();
1289 		glw::GLuint				 windowW	  = renderTarget.getWidth();
1290 		glw::GLuint				 windowH	  = renderTarget.getHeight();
1291 		tcu::Surface			 renderedFrame(windowW, windowH);
1292 		tcu::Surface			 referenceFrame(windowW, windowH);
1293 
1294 		tcu::TestLog& log = context.getTestContext().getLog();
1295 
1296 		for (int y = 0; y < renderedFrame.getHeight(); y++)
1297 		{
1298 			float yCoord   = (float)(y) / (float)renderedFrame.getHeight();
1299 			float yVPCoord = (yCoord - 0.25f) * 4.0f;
1300 
1301 			for (int x = 0; x < renderedFrame.getWidth(); x++)
1302 			{
1303 				float xCoord   = (float)(x) / (float)renderedFrame.getWidth();
1304 				float xVPCoord = (xCoord - 0.125f) * 2.0f;
1305 
1306 				if (xVPCoord > 0.0f && xVPCoord < 1.0f && yVPCoord > 0.0f && yVPCoord < 1.0f)
1307 				{
1308 
1309 					bool greenQuadrant;
1310 
1311 					//inside viewport
1312 					if (origin == GL_UPPER_LEFT)
1313 					{
1314 						greenQuadrant = (yVPCoord > 0.5f && xVPCoord <= 0.5f);
1315 					}
1316 					else
1317 					{
1318 						greenQuadrant = (yVPCoord <= 0.5f && xVPCoord <= 0.5f);
1319 					}
1320 
1321 					if (greenQuadrant)
1322 					{
1323 						referenceFrame.setPixel(x, y, tcu::RGBA::green());
1324 					}
1325 					else
1326 					{
1327 						referenceFrame.setPixel(x, y, tcu::RGBA::red());
1328 					}
1329 				}
1330 				else
1331 				{
1332 					//outside viewport
1333 					referenceFrame.setPixel(x, y, tcu::RGBA::blue());
1334 				}
1335 			}
1336 		}
1337 
1338 		glu::readPixels(context.getRenderContext(), 0, 0, renderedFrame.getAccess());
1339 
1340 		if (tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
1341 							  tcu::COMPARE_LOG_RESULT))
1342 		{
1343 			return QP_TEST_RESULT_PASS;
1344 		}
1345 		else
1346 		{
1347 			return QP_TEST_RESULT_FAIL;
1348 		}
1349 	}
1350 
1351 	glw::GLuint m_vao, m_vbo;
1352 };
1353 
apiToTestName(ClipControlTests::API api)1354 const char* apiToTestName(ClipControlTests::API api)
1355 {
1356 	switch (api)
1357 	{
1358 	case ClipControlTests::API_GL_45core:
1359 		return "clip_control";
1360 	case ClipControlTests::API_GL_ARB_clip_control:
1361 		return "clip_control_ARB";
1362 	}
1363 	DE_ASSERT(0);
1364 	return "";
1365 }
1366 
1367 /** Constructor.
1368  *
1369  *  @param context Rendering context.
1370  **/
ClipControlTests(deqp::Context & context,API api)1371 ClipControlTests::ClipControlTests(deqp::Context& context, API api)
1372 	: TestCaseGroup(context, apiToTestName(api), "Verifies \"clip_control\" functionality"), m_api(api)
1373 {
1374 	/* Left blank on purpose */
1375 }
1376 
1377 /** Destructor.
1378  *
1379  **/
~ClipControlTests()1380 ClipControlTests::~ClipControlTests()
1381 {
1382 }
1383 
1384 /** Initializes a texture_storage_multisample test group.
1385  *
1386  **/
init(void)1387 void ClipControlTests::init(void)
1388 {
1389 	addChild(new ClipControlInitialState(m_context, m_api, "initial"));
1390 	addChild(new ClipControlModifyGetState(m_context, m_api, "modify_get"));
1391 	addChild(new ClipControlErrors(m_context, m_api, "errors"));
1392 	addChild(new ClipControlOriginTest(m_context, m_api, "origin"));
1393 	addChild(new ClipControlDepthModeZeroToOneTest(m_context, m_api, "depth_mode_zero_to_one"));
1394 	addChild(new ClipControlDepthModeOneToOneTest(m_context, m_api, "depth_mode_one_to_one"));
1395 	addChild(new ClipControlFaceCulling(m_context, m_api, "face_culling"));
1396 	addChild(new ClipControlViewportBounds(m_context, m_api, "viewport_bounds"));
1397 }
1398 } /* deqp namespace */
1399