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 uint32_t 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     uint32_t 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