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