1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
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
21 * \brief Default vertex attribute test
22 *//*--------------------------------------------------------------------*/
23
24 #include "es2fDefaultVertexAttributeTests.hpp"
25 #include "tcuVector.hpp"
26 #include "tcuRenderTarget.hpp"
27 #include "tcuSurface.hpp"
28 #include "tcuTextureUtil.hpp"
29 #include "gluRenderContext.hpp"
30 #include "gluCallLogWrapper.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "gluObjectWrapper.hpp"
33 #include "gluPixelTransfer.hpp"
34 #include "glwEnums.hpp"
35 #include "glwFunctions.hpp"
36 #include "deMath.h"
37 #include "deStringUtil.hpp"
38 #include "deString.h"
39
40 #include <limits>
41
42 namespace deqp
43 {
44 namespace gles2
45 {
46 namespace Functional
47 {
48 namespace
49 {
50
51 static const int s_valueRange = 10;
52
53 static const char* const s_passThroughFragmentShaderSource = "varying mediump vec4 v_color;\n"
54 "void main (void)\n"
55 "{\n"
56 " gl_FragColor = v_color;\n"
57 "}\n";
58
59 template <typename T1, int S1, typename T2, int S2>
convertToTypeVec(const tcu::Vector<T2,S2> & v)60 tcu::Vector<T1, S1> convertToTypeVec (const tcu::Vector<T2, S2>& v)
61 {
62 tcu::Vector<T1, S1> retVal;
63
64 for (int ndx = 0; ndx < S1; ++ndx)
65 retVal[ndx] = T1(0);
66
67 if (S1 == 4)
68 retVal[3] = T1(1);
69
70 for (int ndx = 0; ndx < de::min(S1, S2); ++ndx)
71 retVal[ndx] = T1(v[ndx]);
72
73 return retVal;
74 }
75
76 class FloatLoader
77 {
78 public:
~FloatLoader(void)79 virtual ~FloatLoader (void) {};
80
81 // returns the value loaded
82 virtual tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const = 0;
83 };
84
85 #define GEN_DIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME, VALUES) \
86 class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader \
87 { \
88 public: \
89 enum \
90 { \
91 NORMALIZING = 0, \
92 }; \
93 enum \
94 { \
95 COMPONENTS = (COMPS) \
96 }; \
97 typedef TYPE Type; \
98 \
99 tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const \
100 { \
101 tcu::Vector<TYPE, COMPONENTS> value; \
102 value = convertToTypeVec<Type, COMPONENTS>(v); \
103 \
104 gl.glVertexAttrib ##COMPS ##TYPECODE VALUES; \
105 return convertToTypeVec<float, 4>(value); \
106 } \
107 \
108 static const char* getCaseName (void) \
109 { \
110 return CASENAME; \
111 } \
112 \
113 static const char* getName (void) \
114 { \
115 return "VertexAttrib" #COMPS #TYPECODE; \
116 } \
117 }
118
119 #define GEN_INDIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME) \
120 class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader \
121 { \
122 public: \
123 enum \
124 { \
125 NORMALIZING = 0, \
126 }; \
127 enum \
128 { \
129 COMPONENTS = (COMPS) \
130 }; \
131 typedef TYPE Type; \
132 \
133 tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const \
134 { \
135 tcu::Vector<TYPE, COMPONENTS> value; \
136 value = convertToTypeVec<Type, COMPONENTS>(v); \
137 \
138 gl.glVertexAttrib ##COMPS ##TYPECODE (index, value.getPtr()); \
139 return convertToTypeVec<float, 4>(value); \
140 } \
141 \
142 static const char* getCaseName (void) \
143 { \
144 return CASENAME; \
145 } \
146 \
147 static const char* getName (void) \
148 { \
149 return "VertexAttrib" #COMPS #TYPECODE; \
150 } \
151 }
152
153 GEN_DIRECT_FLOAT_LOADER(float, 1, f, "vertex_attrib_1f", (index, value.x()));
154 GEN_DIRECT_FLOAT_LOADER(float, 2, f, "vertex_attrib_2f", (index, value.x(), value.y()));
155 GEN_DIRECT_FLOAT_LOADER(float, 3, f, "vertex_attrib_3f", (index, value.x(), value.y(), value.z()));
156 GEN_DIRECT_FLOAT_LOADER(float, 4, f, "vertex_attrib_4f", (index, value.x(), value.y(), value.z(), value.w()));
157
158 GEN_INDIRECT_FLOAT_LOADER(float, 1, fv, "vertex_attrib_1fv");
159 GEN_INDIRECT_FLOAT_LOADER(float, 2, fv, "vertex_attrib_2fv");
160 GEN_INDIRECT_FLOAT_LOADER(float, 3, fv, "vertex_attrib_3fv");
161 GEN_INDIRECT_FLOAT_LOADER(float, 4, fv, "vertex_attrib_4fv");
162
163 class AttributeCase : public TestCase
164 {
165 AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType);
166 public:
167 template<typename LoaderType>
168 static AttributeCase* create (Context& ctx, glu::DataType dataType);
169 ~AttributeCase (void);
170
171 private:
172 void init (void);
173 void deinit (void);
174 IterateResult iterate (void);
175
176 glu::DataType getTargetType (void) const;
177 std::string genVertexSource (void) const;
178 bool renderWithValue (const tcu::Vec4& v);
179 tcu::Vec4 computeColor (const tcu::Vec4& value);
180 bool verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue);
181
182 const bool m_normalizing;
183 const bool m_useNegativeValues;
184 const char* const m_funcName;
185 const glu::DataType m_dataType;
186 const FloatLoader* m_loader;
187 glu::ShaderProgram* m_program;
188 deUint32 m_bufID;
189 bool m_allIterationsPassed;
190 int m_iteration;
191
192 enum
193 {
194 RENDER_SIZE = 32
195 };
196 };
197
AttributeCase(Context & ctx,const char * name,const char * desc,const char * funcName,bool normalizing,bool useNegative,glu::DataType dataType)198 AttributeCase::AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType)
199 : TestCase (ctx, name, desc)
200 , m_normalizing (normalizing)
201 , m_useNegativeValues (useNegative)
202 , m_funcName (funcName)
203 , m_dataType (dataType)
204 , m_loader (DE_NULL)
205 , m_program (DE_NULL)
206 , m_bufID (0)
207 , m_allIterationsPassed (true)
208 , m_iteration (0)
209 {
210 }
211
212 template<typename LoaderType>
create(Context & ctx,glu::DataType dataType)213 AttributeCase* AttributeCase::create (Context& ctx, glu::DataType dataType)
214 {
215 AttributeCase* retVal = new AttributeCase(ctx,
216 LoaderType::getCaseName(),
217 (std::string("Test ") + LoaderType::getName()).c_str(),
218 LoaderType::getName(),
219 LoaderType::NORMALIZING != 0,
220 std::numeric_limits<typename LoaderType::Type>::is_signed,
221 dataType);
222 retVal->m_loader = new LoaderType();
223 return retVal;
224 }
225
~AttributeCase(void)226 AttributeCase::~AttributeCase (void)
227 {
228 deinit();
229 }
230
init(void)231 void AttributeCase::init (void)
232 {
233 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
234 throw tcu::NotSupportedError("Render target must be at least " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE));
235
236 // log test info
237
238 {
239 const float maxRange = (m_normalizing) ? (1.0f) : (s_valueRange);
240 const float minRange = (m_useNegativeValues) ? (-maxRange) : (0.0f);
241
242 m_testCtx.getLog()
243 << tcu::TestLog::Message
244 << "Loading attribute values using " << m_funcName << "\n"
245 << "Attribute type: " << glu::getDataTypeName(m_dataType) << "\n"
246 << "Attribute value range: [" << minRange << ", " << maxRange << "]"
247 << tcu::TestLog::EndMessage;
248 }
249
250 // gen shader and base quad
251
252 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_passThroughFragmentShaderSource));
253 m_testCtx.getLog() << *m_program;
254 if (!m_program->isOk())
255 throw tcu::TestError("could not build program");
256
257 {
258 const tcu::Vec4 fullscreenQuad[] =
259 {
260 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
261 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
262 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
263 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
264 };
265
266 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
267
268 gl.genBuffers(1, &m_bufID);
269 gl.bindBuffer(GL_ARRAY_BUFFER, m_bufID);
270 gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW);
271 GLU_EXPECT_NO_ERROR(gl.getError(), "fill buffer");
272 }
273 }
274
deinit(void)275 void AttributeCase::deinit (void)
276 {
277 delete m_loader;
278 m_loader = DE_NULL;
279
280 delete m_program;
281 m_program = DE_NULL;
282
283 if (m_bufID)
284 {
285 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufID);
286 m_bufID = 0;
287 }
288 }
289
iterate(void)290 AttributeCase::IterateResult AttributeCase::iterate (void)
291 {
292 static const tcu::Vec4 testValues[] =
293 {
294 tcu::Vec4(0.0f, 0.5f, 0.2f, 1.0f),
295 tcu::Vec4(0.1f, 0.7f, 1.0f, 0.6f),
296 tcu::Vec4(0.4f, 0.2f, 0.0f, 0.5f),
297 tcu::Vec4(0.5f, 0.0f, 0.9f, 0.1f),
298 tcu::Vec4(0.6f, 0.2f, 0.2f, 0.9f),
299 tcu::Vec4(0.9f, 1.0f, 0.0f, 0.0f),
300 tcu::Vec4(1.0f, 0.5f, 0.3f, 0.8f),
301 };
302
303 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(m_iteration+1) + "/" + de::toString(DE_LENGTH_OF_ARRAY(testValues)));
304
305 // Test normalizing transfers with whole range, non-normalizing with up to s_valueRange
306 const tcu::Vec4 testValue = ((m_useNegativeValues) ? (testValues[m_iteration] * 2.0f - tcu::Vec4(1.0f)) : (testValues[m_iteration])) * ((m_normalizing) ? (1.0f) : ((float)s_valueRange));
307
308 if (!renderWithValue(testValue))
309 m_allIterationsPassed = false;
310
311 // continue
312
313 if (++m_iteration < DE_LENGTH_OF_ARRAY(testValues))
314 return CONTINUE;
315
316 if (m_allIterationsPassed)
317 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
318 else
319 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected values");
320
321 return STOP;
322 }
323
genVertexSource(void) const324 std::string AttributeCase::genVertexSource (void) const
325 {
326 const int vectorSize = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeScalarSize(m_dataType)) : (-1);
327 const char* const vectorType = glu::getDataTypeName((glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::TYPE_FLOAT));
328 const int components = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
329 std::ostringstream buf;
330
331 buf << "attribute highp vec4 a_position;\n"
332 "attribute highp " << glu::getDataTypeName(m_dataType) << " a_value;\n"
333 "varying highp vec4 v_color;\n"
334 "void main (void)\n"
335 "{\n"
336 " gl_Position = a_position;\n"
337 "\n";
338
339 if (m_normalizing)
340 buf << " highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ");\n";
341 else
342 buf << " highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ") / float(" << s_valueRange << ");\n";
343
344 if (m_useNegativeValues)
345 buf << " highp " << vectorType << " positiveNormalizedValue = (normalizedValue + " << vectorType << "(1.0)) / 2.0;\n";
346 else
347 buf << " highp " << vectorType << " positiveNormalizedValue = normalizedValue;\n";
348
349 if (components == 1)
350 buf << " v_color = vec4(positiveNormalizedValue, 0.0, 0.0, 1.0);\n";
351 else if (components == 2)
352 buf << " v_color = vec4(positiveNormalizedValue.xy, 0.0, 1.0);\n";
353 else if (components == 3)
354 buf << " v_color = vec4(positiveNormalizedValue.xyz, 1.0);\n";
355 else if (components == 4)
356 buf << " v_color = vec4((positiveNormalizedValue.xy + positiveNormalizedValue.zz) / 2.0, positiveNormalizedValue.w, 1.0);\n";
357 else
358 DE_ASSERT(DE_FALSE);
359
360 buf << "}\n";
361
362 return buf.str();
363 }
364
renderWithValue(const tcu::Vec4 & v)365 bool AttributeCase::renderWithValue (const tcu::Vec4& v)
366 {
367 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
368
369 gl.enableLogging(true);
370
371 const int positionIndex = gl.glGetAttribLocation(m_program->getProgram(), "a_position");
372 const int valueIndex = gl.glGetAttribLocation(m_program->getProgram(), "a_value");
373 tcu::Surface dest (RENDER_SIZE, RENDER_SIZE);
374 tcu::Vec4 loadedValue;
375
376 gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
377 gl.glClear(GL_COLOR_BUFFER_BIT);
378 gl.glViewport(0, 0, RENDER_SIZE, RENDER_SIZE);
379 GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup");
380
381 gl.glBindBuffer(GL_ARRAY_BUFFER, m_bufID);
382 gl.glVertexAttribPointer(positionIndex, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
383 gl.glEnableVertexAttribArray(positionIndex);
384 GLU_EXPECT_NO_ERROR(gl.glGetError(), "position va");
385
386 // transfer test value. Load to the second column in the matrix case
387 loadedValue = m_loader->load(gl, (glu::isDataTypeMatrix(m_dataType)) ? (valueIndex + 1) : (valueIndex), v);
388 GLU_EXPECT_NO_ERROR(gl.glGetError(), "default va");
389
390 gl.glUseProgram(m_program->getProgram());
391 gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
392 gl.glUseProgram(0);
393 GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw");
394
395 glu::readPixels(m_context.getRenderContext(), 0, 0, dest.getAccess());
396
397 // check whole result is colored correctly
398 return verifyUnicoloredBuffer(dest, computeColor(loadedValue));
399 }
400
computeColor(const tcu::Vec4 & value)401 tcu::Vec4 AttributeCase::computeColor (const tcu::Vec4& value)
402 {
403 const tcu::Vec4 normalizedValue = value / ((m_normalizing) ? (1.0f) : ((float)s_valueRange));
404 const tcu::Vec4 positiveNormalizedValue = ((m_useNegativeValues) ? ((normalizedValue + tcu::Vec4(1.0f)) / 2.0f) : (normalizedValue));
405 const int components = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
406
407 if (components == 1)
408 return tcu::Vec4(positiveNormalizedValue.x(), 0.0f, 0.0f, 1.0f);
409 else if (components == 2)
410 return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), 0.0f, 1.0f);
411 else if (components == 3)
412 return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), positiveNormalizedValue.z(), 1.0f);
413 else if (components == 4)
414 return tcu::Vec4((positiveNormalizedValue.x() + positiveNormalizedValue.z()) / 2.0f, (positiveNormalizedValue.y() + positiveNormalizedValue.z()) / 2.0f, positiveNormalizedValue.w(), 1.0f);
415 else
416 DE_ASSERT(DE_FALSE);
417
418 return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
419 }
420
verifyUnicoloredBuffer(const tcu::Surface & scene,const tcu::Vec4 & refValue)421 bool AttributeCase::verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue)
422 {
423 tcu::Surface errorMask (RENDER_SIZE, RENDER_SIZE);
424 const tcu::RGBA refColor (refValue);
425 const int resultThreshold = 2;
426 const tcu::RGBA colorThreshold = m_context.getRenderTarget().getPixelFormat().getColorThreshold() * resultThreshold;
427 bool error = false;
428
429 tcu::RGBA exampleColor;
430 tcu::IVec2 examplePos;
431
432 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
433
434 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered image. Expecting color " << refColor << ", threshold " << colorThreshold << tcu::TestLog::EndMessage;
435
436 for (int y = 0; y < RENDER_SIZE; ++y)
437 for (int x = 0; x < RENDER_SIZE; ++x)
438 {
439 const tcu::RGBA color = scene.getPixel(x, y);
440
441 if (de::abs(color.getRed() - refColor.getRed()) > colorThreshold.getRed() ||
442 de::abs(color.getGreen() - refColor.getGreen()) > colorThreshold.getGreen() ||
443 de::abs(color.getBlue() - refColor.getBlue()) > colorThreshold.getBlue())
444 {
445 // first error
446 if (!error)
447 {
448 exampleColor = color;
449 examplePos = tcu::IVec2(x, y);
450 }
451
452 error = true;
453 errorMask.setPixel(x, y, tcu::RGBA::red());
454 }
455 }
456
457 if (!error)
458 m_testCtx.getLog() << tcu::TestLog::Message << "Rendered image is valid." << tcu::TestLog::EndMessage;
459 else
460 {
461 m_testCtx.getLog() << tcu::TestLog::Message
462 << "Found invalid pixel(s).\n"
463 << "Pixel at (" << examplePos.x() << ", " << examplePos.y() << ") color: " << exampleColor
464 << tcu::TestLog::EndMessage
465 << tcu::TestLog::ImageSet("Result", "Render result")
466 << tcu::TestLog::Image("Result", "Result", scene)
467 << tcu::TestLog::Image("ErrorMask", "Error Mask", errorMask)
468 << tcu::TestLog::EndImageSet;
469 }
470
471 return !error;
472 }
473
474 } // anonymous
475
DefaultVertexAttributeTests(Context & context)476 DefaultVertexAttributeTests::DefaultVertexAttributeTests (Context& context)
477 : TestCaseGroup(context, "default_vertex_attrib", "Test default vertex attributes")
478 {
479 }
480
~DefaultVertexAttributeTests(void)481 DefaultVertexAttributeTests::~DefaultVertexAttributeTests (void)
482 {
483 }
484
init(void)485 void DefaultVertexAttributeTests::init (void)
486 {
487 struct Target
488 {
489 const char* name;
490 glu::DataType dataType;
491 bool reducedTestSets; // !< use reduced coverage
492 };
493
494 static const Target floatTargets[] =
495 {
496 { "float", glu::TYPE_FLOAT, false },
497 { "vec2", glu::TYPE_FLOAT_VEC2, true },
498 { "vec3", glu::TYPE_FLOAT_VEC3, true },
499 { "vec4", glu::TYPE_FLOAT_VEC4, false },
500 { "mat2", glu::TYPE_FLOAT_MAT2, true },
501 { "mat3", glu::TYPE_FLOAT_MAT3, true },
502 { "mat4", glu::TYPE_FLOAT_MAT4, false },
503 };
504
505 // float targets
506
507 for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(floatTargets); ++targetNdx)
508 {
509 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, floatTargets[targetNdx].name, (std::string("test with ") + floatTargets[targetNdx].name).c_str());
510 const bool fullSet = !floatTargets[targetNdx].reducedTestSets;
511
512 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, floatTargets[targetNdx].dataType))
513 #define ADD_REDUCED_CASE(X) if (fullSet) ADD_CASE(X)
514
515 ADD_CASE (LoaderVertexAttrib1f);
516 ADD_REDUCED_CASE(LoaderVertexAttrib2f);
517 ADD_REDUCED_CASE(LoaderVertexAttrib3f);
518 ADD_CASE (LoaderVertexAttrib4f);
519
520 ADD_CASE (LoaderVertexAttrib1fv);
521 ADD_REDUCED_CASE(LoaderVertexAttrib2fv);
522 ADD_REDUCED_CASE(LoaderVertexAttrib3fv);
523 ADD_CASE (LoaderVertexAttrib4fv);
524
525 #undef ADD_CASE
526 #undef ADD_REDUCED_CASE
527
528 addChild(group);
529 }
530 }
531
532 } // Functional
533 } // gles2
534 } // deqp
535