1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2020 Google Inc.
6  * Copyright (c) 2020 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */ /*!
21  * \file  es3cNumberParsingTests.cpp
22  * \brief Tests for numeric value parsing in GLSL ES 3.0
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "es3cNumberParsingTests.hpp"
26 
27 #include "gluDefs.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluDrawUtil.hpp"
30 #include "gluShaderProgram.hpp"
31 
32 #include "glwDefs.hpp"
33 #include "glwFunctions.hpp"
34 #include "glwEnums.hpp"
35 
36 #include "tcuTestLog.hpp"
37 #include "tcuRenderTarget.hpp"
38 #include "tcuStringTemplate.hpp"
39 
40 #include <string>
41 #include <vector>
42 #include <map>
43 
44 #include <functional>
45 
46 namespace es3cts
47 {
48 
49 namespace
50 {
51 using std::string;
52 using std::vector;
53 using std::map;
54 
55 using std::function;
56 using std::bind;
57 using namespace std::placeholders;
58 
59 static const string					defaultVertexShader					=
60 	"#version 300 es\n"
61 	"in vec4 vPosition;\n"
62 	"void main()\n"
63 	"{\n"
64 	"    gl_Position = vPosition;\n"
65 	"}\n";
66 
67 static const string					fragmentShaderTemplate				=
68 	"#version 300 es\n"
69 	"precision highp float;\n"
70 	"precision highp int;\n"
71 	"out vec4 my_FragColor;\n"
72 	"${TEST_GLOBALS}"
73 	"void main()\n"
74 	"{\n"
75     "${TEST_CODE}"
76     "    my_FragColor = vec4(0.0, correct, 0.0, 1.0);\n"
77 	"}\n";
78 
79 typedef function<void (const glu::ShaderProgram&, const glw::Functions&)> SetupUniformsFn;
80 
81 enum struct TestType
82 {
83 	NORMAL				= 0,
84 	EXPECT_SHADER_FAIL
85 };
86 
87 struct TestParams
88 {
89 	TestType			testType;
90 	string				name;
91 	string				description;
92 	string				testGlobals;
93 	string				testCode;
94 	SetupUniformsFn	setupUniformsFn;
95 };
96 
97 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value);
98 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl);
99 
100 static const TestParams			tests[]									=
101 {
102 	{
103 		TestType::NORMAL,																								// TestType			testType
104 		"unsigned_integer_above_signed_range_decimal",																	// string			name
105 		"Test that uint value higher than INT_MAX is parsed correctly",													// string			description
106 		"uniform uint expected;\n",																						// string			testGlobals
107 		"    uint i        = 3221225472u;\n"
108 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
109 		bind(initializeExpectedValue, _1, _2, 3221225472u)																// SetupUniformsFn	setupUniformsFn
110 	},
111 	{
112 		TestType::NORMAL,																								// TestType			testType
113 		"unsigned_integer_above_signed_range_base8",																	// string			name
114 		"Test that uint value higher than INT_MAX is parsed correctly in base 8 (octal)",								// string			description
115 		"uniform uint expected;\n",																						// string			testGlobals
116 		"    uint i        = 030000000000u;\n"
117 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
118 		bind(initializeExpectedValue, _1, _2, 3221225472u)																// SetupUniformsFn	setupUniformsFn
119 	},
120 	{
121 		TestType::NORMAL,																								// TestType			testType
122 		"unsigned_integer_above_signed_range_base16",																	// string			name
123 		"Test that uint value higher than INT_MAX is parsed correctly in base 16 (hex)",								// string			description
124 		"uniform uint expected;\n",																						// string			testGlobals
125 		"    uint i        = 0xc0000000u;\n"
126 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
127 		bind(initializeExpectedValue, _1, _2, 3221225472u)																// SetupUniformsFn	setupUniformsFn
128 	},
129 	{
130 		TestType::NORMAL,																								// TestType			testType
131 		"unsigned_integer_smallest_value_above_signed_range_decimal",													// string			name
132 		"Test that uint value equal to INT_MAX+1 is parsed correctly",													// string			description
133 		"uniform uint expected;\n",																						// string			testGlobals
134 		"    uint i        = 2147483648u;\n"
135 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
136 		bind(initializeExpectedValue, _1, _2, 2147483648u)																// SetupUniformsFn	setupUniformsFn
137 	},
138 	{
139 		TestType::NORMAL,																								// TestType			testType
140 		"unsigned_integer_smallest_value_above_signed_range_base8",														// string			name
141 		"Test that uint value equal to INT_MAX+1 is parsed correctly in base 8 (octal)",								// string			description
142 		"uniform uint expected;\n",																						// string			testGlobals
143 		"    uint i        = 020000000000u;\n"
144 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
145 		bind(initializeExpectedValue, _1, _2, 2147483648u)																// SetupUniformsFn	setupUniformsFn
146 	},
147 	{
148 		TestType::NORMAL,																								// TestType			testType
149 		"unsigned_integer_smallest_value_above_signed_range_base16",													// string			name
150 		"Test that uint value equal to INT_MAX+1 is parsed correctly in base 16 (hex)",									// string			description
151 		"uniform uint expected;\n",																						// string			testGlobals
152 		"    uint i        = 0x80000000u;\n"
153 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
154 		bind(initializeExpectedValue, _1, _2, 2147483648u)																// SetupUniformsFn	setupUniformsFn
155 	},
156 	{
157 		TestType::NORMAL,																								// TestType			testType
158 		"unsigned_integer_max_value_decimal",																			// string			name
159 		"Test that uint value equal to UINT_MAX is parsed correctly",													// string			description
160 		"uniform uint expected;\n",																						// string			testGlobals
161 		"    uint i        = 4294967295u;\n"
162 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
163 		bind(initializeExpectedValue, _1, _2, 4294967295u)																// SetupUniformsFn	setupUniformsFn
164 	},
165 	{
166 		TestType::NORMAL,																								// TestType			testType
167 		"unsigned_integer_max_value_base8",																				// string			name
168 		"Test that uint value equal to UINT_MAX is parsed correctly in base 8 (octal)",									// string			description
169 		"uniform uint expected;\n",																						// string			testGlobals
170 		"    uint i        = 037777777777u;\n"
171 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
172 		bind(initializeExpectedValue, _1, _2, 4294967295u)																// SetupUniformsFn	setupUniformsFn
173 	},
174 	{
175 		TestType::NORMAL,																								// TestType			testType
176 		"unsigned_integer_max_value_base16",																			// string			name
177 		"Test that uint value equal to UINT_MAX is parsed correctly in base 16 (hex)",									// string			description
178 		"uniform uint expected;\n",																						// string			testGlobals
179 		"    uint i        = 0xffffffffu;\n"
180 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
181 		bind(initializeExpectedValue, _1, _2, 4294967295u)																// SetupUniformsFn	setupUniformsFn
182 	},
183 	{
184 		TestType::EXPECT_SHADER_FAIL,																					// TestType			testType
185 		"unsigned_integer_too_large_value_invalid",																		// string			name
186 		"Test that uint value outside uint range fails to compile",														// string			description
187 		"",																												// string			testGlobals
188 		"    uint i        = 0xfffffffffu;"
189 		"    float correct = 0.0;",
190 		nullptr																											// SetupUniformsFn	setupUniformsFn
191 	},
192 	{
193 		TestType::NORMAL,																								// TestType			testType
194 		"unsigned_integer_negative_value_as_uint",																		// string			name
195 		"Test that -1u is parsed correctly",																			// string			description
196 		"uniform uint expected;\n",																						// string			testGlobals
197 		"    uint i        = -1u;"
198 		"    float correct = (i == expected) ? 1.0 : 0.0;\n",
199 		bind(initializeExpectedValue, _1, _2, 0xffffffffu)																// SetupUniformsFn	setupUniformsFn
200 	},
201 	/* The following floating point parsing tests are taken from the Khronos WebGL conformance tests at:
202 	 *     https://www.khronos.org/registry/webgl/sdk/tests/conformance2/glsl3/float-parsing.html */
203 	{
204 		TestType::NORMAL,																								// TestType			testType
205 		"float_out_of_range_as_infinity",																				// string			name
206 		"Floats of too large magnitude should be converted infinity",													// string			description
207 		"",																												// string			testGlobals
208 		"    // Out-of-range floats should overflow to infinity\n"														// string			testCode
209 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
210 		"    // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
211 		"    float correct = isinf(1.0e40) ? 1.0 : 0.0;\n",
212 		nullptr																											// SetupUniformsFn	setupUniformsFn
213 	},
214 	{
215 		TestType::NORMAL,																								// TestType			testType
216 		"float_out_of_range_as_zero",																					// string			name
217 		"Floats of too small magnitude should be converted to zero",													// string			description
218 		"",																												// string			testGlobals
219 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"																	// string			testCode
220 		"    // \"A value with a magnitude too small to be represented as a mantissa and exponent is converted to zero.\"\n"
221 		"    // 1.0e-50 is small enough that it can't even be stored as subnormal.\n"
222 		"    float correct = (1.0e-50 == 0.0) ? 1.0 : 0.0;\n",
223 		nullptr																											// SetupUniformsFn	setupUniformsFn
224 	},
225 	{
226 		TestType::NORMAL,																								// TestType			testType
227 		"float_no_limit_on_number_of_digits_positive_exponent",															// string			name
228 		"Number of digits in any digit-sequence is not limited - test with a small mantissa and large exponent",		// string			description
229 		"",																												// string			testGlobals
230 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"																	// string			testCode
231 		"    // \"There is no limit on the number of digits in any digit-sequence.\"\n"
232 		"    // The below float string has 100 zeros after the decimal point, but represents 1.0.\n"
233 		"    float x = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e101;\n"
234 		"    float correct = (x == 1.0) ? 1.0 : 0.0;\n",
235 		nullptr																											// SetupUniformsFn	setupUniformsFn
236 	},
237 	{
238 		TestType::NORMAL,																								// TestType			testType
239 		"float_no_limit_on_number_of_digits_negative_exponent",															// string			name
240 		"Number of digits in any digit-sequence is not limited - test with a large mantissa and negative exponent",		// string			description
241 		"",																												// string			testGlobals
242 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"																	// string			testCode
243 		"    // \"There is no limit on the number of digits in any digit-sequence.\"\n"
244 		"    // The below float string has 100 zeros, but represents 1.0.\n"
245 		"    float x = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0e-100;\n"
246 		"    float correct = (x == 1.0) ? 1.0 : 0.0;\n",
247 		nullptr																											// SetupUniformsFn	setupUniformsFn
248 	},
249 	{
250 		TestType::NORMAL,																								// TestType			testType
251 		"float_slightly_out_of_range_exponent_as_positive_infinity",													// string			name
252 		"Test that an exponent that slightly overflows signed 32-bit int range works",									// string			description
253 		"",																												// string			testGlobals
254 		"    // Out-of-range floats should overflow to infinity\n"														// string			testCode
255 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
256 		"    // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
257 		"    float correct = isinf(1.0e2147483649) ? 1.0 : 0.0;\n",
258 		nullptr																											// SetupUniformsFn	setupUniformsFn
259 	},
260 	{
261 		TestType::NORMAL,																								// TestType			testType
262 		"float_overflow_to_positive_infinity",																			// string			name
263 		"Out-of-range floats greater than zero should overflow to positive infinity",									// string			description
264 		"uniform float zero;\n",																						// string			testGlobals
265 		"    // Out-of-range floats should overflow to infinity\n"														// string			testCode
266 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
267 		"    // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
268 		"    float f = 1.0e2048 - zero;\n"
269 		"    float correct = (isinf(f) && f > 0.0) ? 1.0 : 0.0;\n",
270 		initializeZeroValue																								// SetupUniformsFn	setupUniformsFn
271 	},
272 	{
273 		TestType::NORMAL,																								// TestType			testType
274 		"float_overflow_to_negative_infinity",																			// string			name
275 		"Out-of-range floats less than zero should overflow to negative infinity",										// string			description
276 		"uniform float zero;\n",																						// string			testGlobals
277 		"    // Out-of-range floats should overflow to infinity\n"														// string			testCode
278 		"    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
279 		"    // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
280 		"    float f = -1.0e2048 + zero;\n"
281 		"    float correct = (isinf(f) && f < 0.0) ? 1.0 : 0.0;\n",
282 		initializeZeroValue																								// SetupUniformsFn	setupUniformsFn
283 	}
284 };
285 
initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value)286 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value)
287 {
288 	const auto location = gl.getUniformLocation(program.getProgram(), "expected");
289 	GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
290 
291 	gl.uniform1ui(location, value);
292 	GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
293 }
294 
initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl)295 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl)
296 {
297 	const auto location = gl.getUniformLocation(program.getProgram(), "zero");
298 	GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
299 
300 	gl.uniform1f(location, 0.0f);
301 	GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
302 }
303 
replacePlaceholders(const string& shaderTemplate, const TestParams& params)304 static string replacePlaceholders(const string& shaderTemplate, const TestParams& params)
305 {
306 	map<string,string> fields;
307 	fields["TEST_GLOBALS"]	= params.testGlobals;
308 	fields["TEST_CODE"]		= params.testCode;
309 
310 	tcu::StringTemplate output(shaderTemplate);
311 	return output.specialize(fields);
312 }
313 
314 static const std::vector<float>		positions				=
315 {
316 	-1.0f, -1.0f,
317 	 1.0f, -1.0f,
318 	-1.0f,  1.0f,
319 	 1.0f,  1.0f
320 };
321 
322 static const std::vector<deUint32>	indices					= { 0, 1, 2, 3 };
323 
324 const deInt32						RENDERTARGET_WIDTH		= 16;
325 const deInt32						RENDERTARGET_HEIGHT		= 16;
326 
327 class NumberParsingCase : public deqp::TestCase
328 {
329 public:
330 	NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, const string& fragmentShader);
331 
332 	IterateResult iterate();
333 
334 private:
335 	void setupRenderTarget();
336 	void releaseRenderTarget();
337 
338 	glw::GLuint			m_fboId;
339 	glw::GLuint			m_rboId;
340 
341 	const TestParams&	m_params;
342 	string				m_vertexShader;
343 	string				m_fragmentShader;
344 };
345 
NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, const string& fragmentShader)346 NumberParsingCase::NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, const string& fragmentShader)
347 	: TestCase(context, name.c_str(), params.description.c_str())
348 	, m_fboId(0)
349 	, m_rboId(0)
350 	, m_params(params)
351 	, m_vertexShader(vertexShader)
352 	, m_fragmentShader(fragmentShader)
353 {
354 }
355 
iterate(void)356 NumberParsingCase::IterateResult NumberParsingCase::iterate(void)
357 {
358 	const auto&	renderContext	= m_context.getRenderContext();
359 	const auto&	gl				= renderContext.getFunctions();
360 	const auto	textureFormat	= tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNORM_INT8);
361 	const auto	transferFormat	= glu::getTransferFormat(textureFormat);
362 
363 	setupRenderTarget();
364 
365 	glu::ShaderProgram program(renderContext, glu::makeVtxFragSources(m_vertexShader, m_fragmentShader));
366 	if (!program.isOk())
367 		switch(m_params.testType)
368 		{
369 		case TestType::EXPECT_SHADER_FAIL:
370 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
371 			return STOP;
372 		default:
373 			TCU_FAIL("Shader compilation failed:\nVertex shader:\n" + m_vertexShader + "\nFragment shader:\n" + m_fragmentShader);
374 		}
375 
376 	const std::vector<glu::VertexArrayBinding> vertexArrays =
377 	{
378 		glu::va::Float("vPosition", 2, positions.size(), 0, positions.data()),
379 	};
380 
381 	gl.useProgram(program.getProgram());
382 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram failed");
383 
384 	if (m_params.setupUniformsFn != DE_NULL)
385 		m_params.setupUniformsFn(program, gl);
386 
387 	gl.clear(GL_COLOR_BUFFER_BIT);
388 
389 
390 	glu::draw(renderContext, program.getProgram(),
391 			  static_cast<int>(vertexArrays.size()), vertexArrays.data(),
392 			  glu::pr::TriangleStrip(static_cast<int>(indices.size()), indices.data()));
393 
394 	const auto						pixelSize				= tcu::getPixelSize(textureFormat);
395 	std::vector<deUint8>			fbData					(RENDERTARGET_WIDTH * RENDERTARGET_HEIGHT * pixelSize);
396 
397 	if (pixelSize < 4)
398 		gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
399 
400 	gl.readPixels(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, transferFormat.format, transferFormat.dataType, fbData.data());
401 	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
402 
403 	tcu::ConstPixelBufferAccess		fbAccess				{ textureFormat, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, 1, fbData.data() };
404 	const auto						expectedColor			= tcu::RGBA::green().toVec();
405 	bool pass = true;
406 	for(int y = 0; pass && y < RENDERTARGET_HEIGHT; ++y)
407 		for(int x = 0; x < RENDERTARGET_WIDTH; ++x)
408 			if (fbAccess.getPixel(x,y) != expectedColor)
409 			{
410 				pass = false;
411 				break;
412 			}
413 
414 	releaseRenderTarget();
415 
416 	const qpTestResult				result					= (pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL);
417 	const char*						desc					= (pass ? "Pass" : "Pixel mismatch; numeric value parsed incorrectly");
418 
419 	m_testCtx.setTestResult(result, desc);
420 
421 	return STOP;
422 }
423 
setupRenderTarget()424 void NumberParsingCase::setupRenderTarget()
425 {
426 	const auto&	renderContext	= m_context.getRenderContext();
427 	const auto&	gl				= renderContext.getFunctions();
428 
429 	gl.genFramebuffers(1, &m_fboId);
430 	GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
431 
432 	gl.genRenderbuffers(1, &m_rboId);
433 	GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderBuffers");
434 
435 	gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboId);
436 	GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderBuffer");
437 
438 	gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
439 	GLU_EXPECT_NO_ERROR(gl.getError(), "RenderBufferStorage");
440 
441 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboId);
442 	GLU_EXPECT_NO_ERROR(gl.getError(), "BindFrameBuffer");
443 
444 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboId);
445 	GLU_EXPECT_NO_ERROR(gl.getError(), "FrameBufferRenderBuffer");
446 
447 	glw::GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
448 	gl.drawBuffers(1, &drawBuffer);
449 	GLU_EXPECT_NO_ERROR(gl.getError(), "DrawBuffers");
450 
451 	glw::GLfloat clearColor[4] = { 0, 0, 0, 0 };
452 	gl.clearBufferfv(GL_COLOR, 0, clearColor);
453 	GLU_EXPECT_NO_ERROR(gl.getError(), "ClearBuffers");
454 
455 	gl.viewport(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
456 	GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
457 }
458 
releaseRenderTarget()459 void NumberParsingCase::releaseRenderTarget()
460 {
461 	const auto&	renderContext	= m_context.getRenderContext();
462 	const auto&	gl				= renderContext.getFunctions();
463 	if (m_fboId != 0)
464 	{
465 		gl.deleteFramebuffers(1, &m_fboId);
466 		m_fboId = 0;
467 	}
468 	if (m_rboId != 0)
469 	{
470 		gl.deleteRenderbuffers(1, &m_rboId);
471 		m_rboId = 0;
472 	}
473 }
474 
475 }
476 
NumberParsingTests(deqp::Context& context)477 NumberParsingTests::NumberParsingTests(deqp::Context& context)
478 	: deqp::TestCaseGroup(context, "number_parsing", "GLSL number parsing tests")
479 {
480 }
481 
~NumberParsingTests(void)482 NumberParsingTests::~NumberParsingTests(void)
483 {
484 }
485 
init(void)486 void NumberParsingTests::init(void)
487 {
488 	for(const auto& params : tests)
489 	{
490 		addChild(new NumberParsingCase(m_context, params.name, params, defaultVertexShader, replacePlaceholders(fragmentShaderTemplate, params)));
491 	}
492 }
493 
494 }
495