• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2017 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 esextcTessellationShaderWinding.cpp
21  * \brief Test winding order with tessellation shaders
22  */ /*-------------------------------------------------------------------*/
23 
24 #include "esextcTessellationShaderWinding.hpp"
25 #include "deSharedPtr.hpp"
26 #include "esextcTessellationShaderUtils.hpp"
27 #include "gluContextInfo.hpp"
28 #include "gluDefs.hpp"
29 #include "gluPixelTransfer.hpp"
30 #include "gluShaderProgram.hpp"
31 #include "glwEnums.hpp"
32 #include "glwFunctions.hpp"
33 #include "tcuRGBA.hpp"
34 #include "tcuSurface.hpp"
35 #include "tcuTestLog.hpp"
36 #include <string>
37 
38 namespace glcts
39 {
40 
41 class WindingCase : public TestCaseBase
42 {
43 public:
44 	WindingCase(glcts::Context& context, const ExtParameters& extParams, std::string name, std::string primitiveType,
45 				std::string winding);
46 
47 	void		  init(void);
48 	void		  deinit(void);
49 	IterateResult iterate(void);
50 	void prepareFramebuffer();
51 
52 private:
53 	static const int RENDER_SIZE = 64;
54 
55 	de::SharedPtr<const glu::ShaderProgram> m_program;
56 	glw::GLuint m_rbo;
57 	glw::GLuint m_fbo;
58 };
59 
WindingCase(glcts::Context & context,const ExtParameters & extParams,std::string name,std::string primitiveType,std::string winding)60 WindingCase::WindingCase(glcts::Context& context, const ExtParameters& extParams, std::string name,
61 						 std::string primitiveType, std::string winding)
62 	: TestCaseBase(context, extParams, name.c_str(), "")
63 {
64 	DE_ASSERT((primitiveType.compare("triangles") == 0) || (primitiveType.compare("quads") == 0));
65 	DE_ASSERT((winding.compare("cw") == 0) || (winding.compare("ccw") == 0));
66 
67 	m_specializationMap["PRIMITIVE_TYPE"] = primitiveType;
68 	m_specializationMap["WINDING"]        = winding;
69 	m_rbo                                 = 0;
70 	m_fbo                                 = 0;
71 }
72 
init(void)73 void WindingCase::init(void)
74 {
75 	TestCaseBase::init();
76 	if (!m_is_tessellation_shader_supported)
77 	{
78 		TCU_THROW(NotSupportedError, TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
79 	}
80 
81 	TestCaseBase::init();
82 
83 	const char* vs("${VERSION}\n"
84 				   "void main (void)\n"
85 				   "{\n"
86 				   "}\n");
87 	const char* tcs("${VERSION}\n"
88 					"${TESSELLATION_SHADER_REQUIRE}\n"
89 					"layout (vertices = 1) out;\n"
90 					"void main (void)\n"
91 					"{\n"
92 					"	gl_TessLevelInner[0] = 5.0;\n"
93 					"	gl_TessLevelInner[1] = 5.0;\n"
94 					"\n"
95 					"	gl_TessLevelOuter[0] = 5.0;\n"
96 					"	gl_TessLevelOuter[1] = 5.0;\n"
97 					"	gl_TessLevelOuter[2] = 5.0;\n"
98 					"	gl_TessLevelOuter[3] = 5.0;\n"
99 					"}\n");
100 	const char* tes("${VERSION}\n"
101 					"${TESSELLATION_SHADER_REQUIRE}\n"
102 					"layout (${PRIMITIVE_TYPE}, ${WINDING}) in;\n"
103 					"void main (void)\n"
104 					"{\n"
105 					"	gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
106 					"}\n");
107 	const char* fs("${VERSION}\n"
108 				   "layout (location = 0) out mediump vec4 o_color;\n"
109 				   "void main (void)\n"
110 				   "{\n"
111 				   "	o_color = vec4(1.0);\n"
112 				   "}\n");
113 
114 	m_program = de::SharedPtr<const glu::ShaderProgram>(
115 		new glu::ShaderProgram(m_context.getRenderContext(),
116 							   glu::ProgramSources() << glu::VertexSource(specializeShader(1, &vs))
117 													 << glu::TessellationControlSource(specializeShader(1, &tcs))
118 													 << glu::TessellationEvaluationSource(specializeShader(1, &tes))
119 													 << glu::FragmentSource(specializeShader(1, &fs))));
120 
121 	m_testCtx.getLog() << *m_program;
122 	if (!m_program->isOk())
123 		TCU_FAIL("Program compilation failed");
124 }
125 
deinit(void)126 void WindingCase::deinit(void)
127 {
128 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
129 
130 	if (m_fbo)
131 	{
132 		gl.deleteFramebuffers(1, &m_fbo);
133 		m_fbo = 0;
134 	}
135 
136 	if (m_rbo)
137 	{
138 		gl.deleteRenderbuffers(1, &m_rbo);
139 		m_rbo = 0;
140 	}
141 
142 	m_program.clear();
143 }
144 
145 /** @brief Bind default framebuffer object.
146  *
147  *  @note The function may throw if unexpected error has occured.
148  */
prepareFramebuffer()149 void WindingCase::prepareFramebuffer()
150 {
151 	/* Shortcut for GL functionality */
152 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
153 
154 	gl.genRenderbuffers(1, &m_rbo);
155 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed.");
156 
157 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo);
158 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed.");
159 
160 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE);
161 	GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed.");
162 
163 	gl.genFramebuffers(1, &m_fbo);
164 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed.");
165 
166 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
167 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed.");
168 
169 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);
170 	GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed.");
171 }
172 
iterate(void)173 WindingCase::IterateResult WindingCase::iterate(void)
174 {
175 	const glu::RenderContext& renderCtx = m_context.getRenderContext();
176 	const deUint32			  programGL = m_program->getProgram();
177 	const glw::Functions&	 gl		= renderCtx.getFunctions();
178 
179 	const unsigned int windingTaken[2]	 = { GL_CW, GL_CCW };
180 	const char*		   windingTakenName[2] = { "GL_CW", "GL_CCW" };
181 
182 	const bool testPrimitiveTypeIsTriangles = (m_specializationMap["PRIMITIVE_TYPE"].compare("triangles") == 0);
183 	const bool testWindingIsCW				= (m_specializationMap["WINDING"].compare("cw") == 0);
184 	bool	   success						= true;
185 
186 	prepareFramebuffer();
187 	gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
188 	gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
189 	gl.useProgram(programGL);
190 
191 	gl.patchParameteri(GL_PATCH_VERTICES, 1);
192 
193 	gl.enable(GL_CULL_FACE);
194 
195 	deUint32 vaoGL;
196 	gl.genVertexArrays(1, &vaoGL);
197 	gl.bindVertexArray(vaoGL);
198 
199 	m_testCtx.getLog() << tcu::TestLog::Message << "Face culling enabled" << tcu::TestLog::EndMessage;
200 
201 	for (int windingIndex = 0; windingIndex < 2; windingIndex++)
202 	{
203 		m_testCtx.getLog() << tcu::TestLog::Message << "Setting glFrontFace(" << windingTakenName[windingIndex] << ")"
204 						   << tcu::TestLog::EndMessage;
205 
206 		gl.frontFace(windingTaken[windingIndex]);
207 
208 		gl.clear(GL_COLOR_BUFFER_BIT);
209 		gl.drawArrays(GL_PATCHES, 0, 1);
210 		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
211 
212 		{
213 			tcu::Surface rendered(RENDER_SIZE, RENDER_SIZE);
214 			glu::readPixels(renderCtx, 0, 0, rendered.getAccess());
215 			m_testCtx.getLog() << tcu::TestLog::Image("RenderedImage", "Rendered Image", rendered);
216 
217 			{
218 				const int badPixelTolerance =
219 					testPrimitiveTypeIsTriangles ? 5 * de::max(rendered.getWidth(), rendered.getHeight()) : 0;
220 				const int totalNumPixels = rendered.getWidth() * rendered.getHeight();
221 
222 				int numWhitePixels = 0;
223 				int numRedPixels   = 0;
224 				for (int y = 0; y < rendered.getHeight(); y++)
225 					for (int x = 0; x < rendered.getWidth(); x++)
226 					{
227 						numWhitePixels += rendered.getPixel(x, y) == tcu::RGBA::white() ? 1 : 0;
228 						numRedPixels += rendered.getPixel(x, y) == tcu::RGBA::red() ? 1 : 0;
229 					}
230 
231 				DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
232 
233 				m_testCtx.getLog() << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and "
234 								   << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
235 
236 				if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance)
237 				{
238 					m_testCtx.getLog() << tcu::TestLog::Message << "Failure: Got "
239 									   << totalNumPixels - numWhitePixels - numRedPixels
240 									   << " other than white or red pixels (maximum tolerance " << badPixelTolerance
241 									   << ")" << tcu::TestLog::EndMessage;
242 					success = false;
243 					break;
244 				}
245 
246 				bool frontFaceWindingIsCW = (windingIndex == 0);
247 				if (frontFaceWindingIsCW == testWindingIsCW)
248 				{
249 					if (testPrimitiveTypeIsTriangles)
250 					{
251 						if (de::abs(numWhitePixels - totalNumPixels / 2) > badPixelTolerance)
252 						{
253 							m_testCtx.getLog() << tcu::TestLog::Message
254 											   << "Failure: wrong number of white pixels; expected approximately "
255 											   << totalNumPixels / 2 << tcu::TestLog::EndMessage;
256 							success = false;
257 							break;
258 						}
259 					}
260 					else // test primitive type is quads
261 					{
262 						if (numWhitePixels != totalNumPixels)
263 						{
264 							m_testCtx.getLog()
265 								<< tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)"
266 								<< tcu::TestLog::EndMessage;
267 							success = false;
268 							break;
269 						}
270 					}
271 				}
272 				else
273 				{
274 					if (numWhitePixels != 0)
275 					{
276 						m_testCtx.getLog()
277 							<< tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)"
278 							<< tcu::TestLog::EndMessage;
279 						success = false;
280 						break;
281 					}
282 				}
283 			}
284 		}
285 	}
286 
287 	gl.bindVertexArray(0);
288 	gl.deleteVertexArrays(1, &vaoGL);
289 
290 	m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
291 							success ? "Pass" : "Image verification failed");
292 	return STOP;
293 }
294 
295 /** Constructor
296  *
297  * @param context Test context
298  **/
TesselationShaderWindingTests(glcts::Context & context,const ExtParameters & extParams)299 TesselationShaderWindingTests::TesselationShaderWindingTests(glcts::Context& context, const ExtParameters& extParams)
300 	: TestCaseGroupBase(context, extParams, "winding", "Verifies winding order with tessellation shaders")
301 {
302 }
303 
304 /**
305  * Initializes test groups for winding tests
306  **/
init(void)307 void TesselationShaderWindingTests::init(void)
308 {
309 	addChild(new WindingCase(m_context, m_extParams, "triangles_ccw", "triangles", "ccw"));
310 	addChild(new WindingCase(m_context, m_extParams, "triangles_cw", "triangles", "cw"));
311 	addChild(new WindingCase(m_context, m_extParams, "quads_ccw", "quads", "ccw"));
312 	addChild(new WindingCase(m_context, m_extParams, "quads_cw", "quads", "cw"));
313 }
314 
315 } /* namespace glcts */
316