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