1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.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 Fragment shader output tests.
22 *
23 * \todo [2012-04-10 pyry] Missing:
24 * + non-contiguous attachments in framebuffer
25 *//*--------------------------------------------------------------------*/
26
27 #include "es3fFragmentOutputTests.hpp"
28 #include "gluShaderUtil.hpp"
29 #include "gluShaderProgram.hpp"
30 #include "gluTextureUtil.hpp"
31 #include "gluStrUtil.hpp"
32 #include "tcuTestLog.hpp"
33 #include "tcuTexture.hpp"
34 #include "tcuTextureUtil.hpp"
35 #include "tcuVector.hpp"
36 #include "tcuVectorUtil.hpp"
37 #include "tcuImageCompare.hpp"
38 #include "deRandom.hpp"
39 #include "deStringUtil.hpp"
40 #include "deMath.h"
41
42 // For getFormatName() \todo [pyry] Move to glu?
43 #include "es3fFboTestUtil.hpp"
44
45 #include "glwEnums.hpp"
46 #include "glwFunctions.hpp"
47
48 namespace deqp
49 {
50 namespace gles3
51 {
52 namespace Functional
53 {
54
55 using FboTestUtil::getFormatName;
56 using FboTestUtil::getFramebufferReadFormat;
57 using std::string;
58 using std::vector;
59 using tcu::BVec4;
60 using tcu::IVec2;
61 using tcu::IVec4;
62 using tcu::TestLog;
63 using tcu::UVec2;
64 using tcu::UVec4;
65 using tcu::Vec2;
66 using tcu::Vec3;
67 using tcu::Vec4;
68
69 struct BufferSpec
70 {
BufferSpecdeqp::gles3::Functional::BufferSpec71 BufferSpec(void) : format(GL_NONE), width(0), height(0), samples(0)
72 {
73 }
74
BufferSpecdeqp::gles3::Functional::BufferSpec75 BufferSpec(uint32_t format_, int width_, int height_, int samples_)
76 : format(format_)
77 , width(width_)
78 , height(height_)
79 , samples(samples_)
80 {
81 }
82
83 uint32_t format;
84 int width;
85 int height;
86 int samples;
87 };
88
89 struct FragmentOutput
90 {
FragmentOutputdeqp::gles3::Functional::FragmentOutput91 FragmentOutput(void) : type(glu::TYPE_LAST), precision(glu::PRECISION_LAST), location(0), arrayLength(0)
92 {
93 }
94
FragmentOutputdeqp::gles3::Functional::FragmentOutput95 FragmentOutput(glu::DataType type_, glu::Precision precision_, int location_, int arrayLength_ = 0)
96 : type(type_)
97 , precision(precision_)
98 , location(location_)
99 , arrayLength(arrayLength_)
100 {
101 }
102
103 glu::DataType type;
104 glu::Precision precision;
105 int location;
106 int arrayLength; //!< 0 if not an array.
107 };
108
109 struct OutputVec
110 {
111 vector<FragmentOutput> outputs;
112
operator <<deqp::gles3::Functional::OutputVec113 OutputVec &operator<<(const FragmentOutput &output)
114 {
115 outputs.push_back(output);
116 return *this;
117 }
118
toVecdeqp::gles3::Functional::OutputVec119 vector<FragmentOutput> toVec(void) const
120 {
121 return outputs;
122 }
123 };
124
125 class FragmentOutputCase : public TestCase
126 {
127 public:
128 FragmentOutputCase(Context &context, const char *name, const char *desc, const vector<BufferSpec> &fboSpec,
129 const vector<FragmentOutput> &outputs);
130 ~FragmentOutputCase(void);
131
132 void init(void);
133 void deinit(void);
134 IterateResult iterate(void);
135
136 private:
137 FragmentOutputCase(const FragmentOutputCase &other);
138 FragmentOutputCase &operator=(const FragmentOutputCase &other);
139
140 vector<BufferSpec> m_fboSpec;
141 vector<FragmentOutput> m_outputs;
142
143 glu::ShaderProgram *m_program;
144 uint32_t m_framebuffer;
145 vector<uint32_t> m_renderbuffers;
146 };
147
FragmentOutputCase(Context & context,const char * name,const char * desc,const vector<BufferSpec> & fboSpec,const vector<FragmentOutput> & outputs)148 FragmentOutputCase::FragmentOutputCase(Context &context, const char *name, const char *desc,
149 const vector<BufferSpec> &fboSpec, const vector<FragmentOutput> &outputs)
150 : TestCase(context, name, desc)
151 , m_fboSpec(fboSpec)
152 , m_outputs(outputs)
153 , m_program(DE_NULL)
154 , m_framebuffer(0)
155 {
156 }
157
~FragmentOutputCase(void)158 FragmentOutputCase::~FragmentOutputCase(void)
159 {
160 deinit();
161 }
162
createProgram(const glu::RenderContext & context,const vector<FragmentOutput> & outputs)163 static glu::ShaderProgram *createProgram(const glu::RenderContext &context, const vector<FragmentOutput> &outputs)
164 {
165 std::ostringstream vtx;
166 std::ostringstream frag;
167
168 vtx << "#version 300 es\n"
169 << "in highp vec4 a_position;\n";
170 frag << "#version 300 es\n";
171
172 // Input-output declarations.
173 for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++)
174 {
175 const FragmentOutput &output = outputs[outNdx];
176 bool isArray = output.arrayLength > 0;
177 const char *typeName = glu::getDataTypeName(output.type);
178 const char *outputPrec = glu::getPrecisionName(output.precision);
179 bool isFloat = glu::isDataTypeFloatOrVec(output.type);
180 const char *interp = isFloat ? "smooth" : "flat";
181 const char *interpPrec = isFloat ? "highp" : outputPrec;
182
183 if (isArray)
184 {
185 for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++)
186 {
187 vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << "_" << elemNdx << ";\n"
188 << interp << " out " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx
189 << ";\n";
190 frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx
191 << ";\n";
192 }
193 frag << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out"
194 << outNdx << "[" << output.arrayLength << "];\n";
195 }
196 else
197 {
198 vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << ";\n"
199 << interp << " out " << interpPrec << " " << typeName << " var" << outNdx << ";\n";
200 frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << ";\n"
201 << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out"
202 << outNdx << ";\n";
203 }
204 }
205
206 vtx << "\nvoid main()\n{\n";
207 frag << "\nvoid main()\n{\n";
208
209 vtx << " gl_Position = a_position;\n";
210
211 // Copy body
212 for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++)
213 {
214 const FragmentOutput &output = outputs[outNdx];
215 bool isArray = output.arrayLength > 0;
216
217 if (isArray)
218 {
219 for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++)
220 {
221 vtx << "\tvar" << outNdx << "_" << elemNdx << " = in" << outNdx << "_" << elemNdx << ";\n";
222 frag << "\tout" << outNdx << "[" << elemNdx << "] = var" << outNdx << "_" << elemNdx << ";\n";
223 }
224 }
225 else
226 {
227 vtx << "\tvar" << outNdx << " = in" << outNdx << ";\n";
228 frag << "\tout" << outNdx << " = var" << outNdx << ";\n";
229 }
230 }
231
232 vtx << "}\n";
233 frag << "}\n";
234
235 return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
236 }
237
init(void)238 void FragmentOutputCase::init(void)
239 {
240 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
241 TestLog &log = m_testCtx.getLog();
242
243 // Check that all attachments are supported
244 for (std::vector<BufferSpec>::const_iterator bufIter = m_fboSpec.begin(); bufIter != m_fboSpec.end(); ++bufIter)
245 {
246 if (!glu::isSizedFormatColorRenderable(m_context.getRenderContext(), m_context.getContextInfo(),
247 bufIter->format))
248 throw tcu::NotSupportedError("Unsupported attachment format");
249 }
250
251 DE_ASSERT(!m_program);
252 m_program = createProgram(m_context.getRenderContext(), m_outputs);
253
254 log << *m_program;
255 if (!m_program->isOk())
256 TCU_FAIL("Compile failed");
257
258 // Print render target info to log.
259 log << TestLog::Section("Framebuffer", "Framebuffer configuration");
260
261 for (int ndx = 0; ndx < (int)m_fboSpec.size(); ndx++)
262 log << TestLog::Message << "COLOR_ATTACHMENT" << ndx << ": " << glu::getTextureFormatStr(m_fboSpec[ndx].format)
263 << ", " << m_fboSpec[ndx].width << "x" << m_fboSpec[ndx].height << ", " << m_fboSpec[ndx].samples
264 << " samples" << TestLog::EndMessage;
265
266 log << TestLog::EndSection;
267
268 // Create framebuffer.
269 m_renderbuffers.resize(m_fboSpec.size(), 0);
270 gl.genFramebuffers(1, &m_framebuffer);
271 gl.genRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]);
272
273 gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
274
275 for (int bufNdx = 0; bufNdx < (int)m_renderbuffers.size(); bufNdx++)
276 {
277 uint32_t rbo = m_renderbuffers[bufNdx];
278 const BufferSpec &bufSpec = m_fboSpec[bufNdx];
279 uint32_t attachment = GL_COLOR_ATTACHMENT0 + bufNdx;
280
281 gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
282 gl.renderbufferStorageMultisample(GL_RENDERBUFFER, bufSpec.samples, bufSpec.format, bufSpec.width,
283 bufSpec.height);
284 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo);
285 }
286 GLU_EXPECT_NO_ERROR(gl.getError(), "After framebuffer setup");
287
288 uint32_t fboStatus = gl.checkFramebufferStatus(GL_FRAMEBUFFER);
289 if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED)
290 throw tcu::NotSupportedError("Framebuffer not supported", "", __FILE__, __LINE__);
291 else if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
292 throw tcu::TestError(
293 (string("Incomplete framebuffer: ") + glu::getFramebufferStatusStr(fboStatus).toString()).c_str(), "",
294 __FILE__, __LINE__);
295
296 gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
297 GLU_EXPECT_NO_ERROR(gl.getError(), "After init");
298 }
299
deinit(void)300 void FragmentOutputCase::deinit(void)
301 {
302 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
303
304 if (m_framebuffer)
305 {
306 gl.deleteFramebuffers(1, &m_framebuffer);
307 m_framebuffer = 0;
308 }
309
310 if (!m_renderbuffers.empty())
311 {
312 gl.deleteRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]);
313 m_renderbuffers.clear();
314 }
315
316 delete m_program;
317 m_program = DE_NULL;
318 }
319
getMinSize(const vector<BufferSpec> & fboSpec)320 static IVec2 getMinSize(const vector<BufferSpec> &fboSpec)
321 {
322 IVec2 minSize(0x7fffffff, 0x7fffffff);
323 for (vector<BufferSpec>::const_iterator i = fboSpec.begin(); i != fboSpec.end(); i++)
324 {
325 minSize.x() = de::min(minSize.x(), i->width);
326 minSize.y() = de::min(minSize.y(), i->height);
327 }
328 return minSize;
329 }
330
getNumInputVectors(const vector<FragmentOutput> & outputs)331 static int getNumInputVectors(const vector<FragmentOutput> &outputs)
332 {
333 int numVecs = 0;
334 for (vector<FragmentOutput>::const_iterator i = outputs.begin(); i != outputs.end(); i++)
335 numVecs += (i->arrayLength > 0 ? i->arrayLength : 1);
336 return numVecs;
337 }
338
getFloatRange(glu::Precision precision)339 static Vec2 getFloatRange(glu::Precision precision)
340 {
341 // \todo [2012-04-09 pyry] Not quite the full ranges.
342 static const Vec2 ranges[] = {Vec2(-2.0f, 2.0f), Vec2(-16000.0f, 16000.0f), Vec2(-1e35f, 1e35f)};
343 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
344 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
345 return ranges[precision];
346 }
347
getIntRange(glu::Precision precision)348 static IVec2 getIntRange(glu::Precision precision)
349 {
350 static const IVec2 ranges[] = {IVec2(-(1 << 7), (1 << 7) - 1), IVec2(-(1 << 15), (1 << 15) - 1),
351 IVec2(0x80000000, 0x7fffffff)};
352 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
353 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
354 return ranges[precision];
355 }
356
getUintRange(glu::Precision precision)357 static UVec2 getUintRange(glu::Precision precision)
358 {
359 static const UVec2 ranges[] = {UVec2(0, (1 << 8) - 1), UVec2(0, (1 << 16) - 1), UVec2(0, 0xffffffffu)};
360 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
361 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
362 return ranges[precision];
363 }
364
readVec4(const float * ptr,int numComponents)365 static inline Vec4 readVec4(const float *ptr, int numComponents)
366 {
367 DE_ASSERT(numComponents >= 1);
368 return Vec4(ptr[0], numComponents >= 2 ? ptr[1] : 0.0f, numComponents >= 3 ? ptr[2] : 0.0f,
369 numComponents >= 4 ? ptr[3] : 0.0f);
370 }
371
readIVec4(const int * ptr,int numComponents)372 static inline IVec4 readIVec4(const int *ptr, int numComponents)
373 {
374 DE_ASSERT(numComponents >= 1);
375 return IVec4(ptr[0], numComponents >= 2 ? ptr[1] : 0, numComponents >= 3 ? ptr[2] : 0,
376 numComponents >= 4 ? ptr[3] : 0);
377 }
378
renderFloatReference(const tcu::PixelBufferAccess & dst,int gridWidth,int gridHeight,int numComponents,const float * vertices)379 static void renderFloatReference(const tcu::PixelBufferAccess &dst, int gridWidth, int gridHeight, int numComponents,
380 const float *vertices)
381 {
382 const bool isSRGB = tcu::isSRGB(dst.getFormat());
383 const float cellW = (float)dst.getWidth() / (float)(gridWidth - 1);
384 const float cellH = (float)dst.getHeight() / (float)(gridHeight - 1);
385
386 for (int y = 0; y < dst.getHeight(); y++)
387 {
388 for (int x = 0; x < dst.getWidth(); x++)
389 {
390 const int cellX = de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth - 2);
391 const int cellY = de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight - 2);
392 const float xf = ((float)x - (float)cellX * cellW + 0.5f) / cellW;
393 const float yf = ((float)y - (float)cellY * cellH + 0.5f) / cellH;
394 const Vec4 v00 = readVec4(vertices + ((cellY + 0) * gridWidth + cellX + 0) * numComponents, numComponents);
395 const Vec4 v01 = readVec4(vertices + ((cellY + 1) * gridWidth + cellX + 0) * numComponents, numComponents);
396 const Vec4 v10 = readVec4(vertices + ((cellY + 0) * gridWidth + cellX + 1) * numComponents, numComponents);
397 const Vec4 v11 = readVec4(vertices + ((cellY + 1) * gridWidth + cellX + 1) * numComponents, numComponents);
398 const bool tri = xf + yf >= 1.0f;
399 const Vec4 &v0 = tri ? v11 : v00;
400 const Vec4 &v1 = tri ? v01 : v10;
401 const Vec4 &v2 = tri ? v10 : v01;
402 const float s = tri ? 1.0f - xf : xf;
403 const float t = tri ? 1.0f - yf : yf;
404 const Vec4 color = v0 + (v1 - v0) * s + (v2 - v0) * t;
405
406 dst.setPixel(isSRGB ? tcu::linearToSRGB(color) : color, x, y);
407 }
408 }
409 }
410
renderIntReference(const tcu::PixelBufferAccess & dst,int gridWidth,int gridHeight,int numComponents,const int * vertices)411 static void renderIntReference(const tcu::PixelBufferAccess &dst, int gridWidth, int gridHeight, int numComponents,
412 const int *vertices)
413 {
414 float cellW = (float)dst.getWidth() / (float)(gridWidth - 1);
415 float cellH = (float)dst.getHeight() / (float)(gridHeight - 1);
416
417 for (int y = 0; y < dst.getHeight(); y++)
418 {
419 for (int x = 0; x < dst.getWidth(); x++)
420 {
421 int cellX = de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth - 2);
422 int cellY = de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight - 2);
423 IVec4 c = readIVec4(vertices + (cellY * gridWidth + cellX + 1) * numComponents, numComponents);
424
425 dst.setPixel(c, x, y);
426 }
427 }
428 }
429
430 static const IVec4 s_swizzles[] = {IVec4(0, 1, 2, 3), IVec4(1, 2, 3, 0), IVec4(2, 3, 0, 1), IVec4(3, 0, 1, 2),
431 IVec4(3, 2, 1, 0), IVec4(2, 1, 0, 3), IVec4(1, 0, 3, 2), IVec4(0, 3, 2, 1)};
432
433 template <typename T>
swizzleVec(const tcu::Vector<T,4> & vec,int swzNdx)434 inline tcu::Vector<T, 4> swizzleVec(const tcu::Vector<T, 4> &vec, int swzNdx)
435 {
436 const IVec4 &swz = s_swizzles[swzNdx % DE_LENGTH_OF_ARRAY(s_swizzles)];
437 return vec.swizzle(swz[0], swz[1], swz[2], swz[3]);
438 }
439
440 namespace
441 {
442
443 struct AttachmentData
444 {
445 tcu::TextureFormat format; //!< Actual format of attachment.
446 tcu::TextureFormat referenceFormat; //!< Used for reference rendering.
447 tcu::TextureFormat readFormat;
448 int numWrittenChannels;
449 glu::Precision outPrecision;
450 vector<uint8_t> renderedData;
451 vector<uint8_t> referenceData;
452 };
453
454 template <typename Type>
valueRangeToString(int numValidChannels,const tcu::Vector<Type,4> & minValue,const tcu::Vector<Type,4> & maxValue)455 string valueRangeToString(int numValidChannels, const tcu::Vector<Type, 4> &minValue,
456 const tcu::Vector<Type, 4> &maxValue)
457 {
458 std::ostringstream stream;
459
460 stream << "(";
461
462 for (int i = 0; i < 4; i++)
463 {
464 if (i != 0)
465 stream << ", ";
466
467 if (i < numValidChannels)
468 stream << minValue[i] << " -> " << maxValue[i];
469 else
470 stream << "Undef";
471 }
472
473 stream << ")";
474
475 return stream.str();
476 }
477
clearUndefined(const tcu::PixelBufferAccess & access,int numValidChannels)478 void clearUndefined(const tcu::PixelBufferAccess &access, int numValidChannels)
479 {
480 for (int y = 0; y < access.getHeight(); y++)
481 for (int x = 0; x < access.getWidth(); x++)
482 {
483 switch (tcu::getTextureChannelClass(access.getFormat().type))
484 {
485 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
486 {
487 const Vec4 srcPixel = access.getPixel(x, y);
488 Vec4 dstPixel(0.0f, 0.0f, 0.0f, 1.0f);
489
490 for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++)
491 dstPixel[channelNdx] = srcPixel[channelNdx];
492
493 access.setPixel(dstPixel, x, y);
494 break;
495 }
496
497 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
498 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
499 case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
500 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
501 {
502 const IVec4 bitDepth = tcu::getTextureFormatBitDepth(access.getFormat());
503 const IVec4 srcPixel = access.getPixelInt(x, y);
504 IVec4 dstPixel(0, 0, 0, (int)(((int64_t)0x1u << bitDepth.w()) - 1));
505
506 for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++)
507 dstPixel[channelNdx] = srcPixel[channelNdx];
508
509 access.setPixel(dstPixel, x, y);
510 break;
511 }
512
513 default:
514 DE_ASSERT(false);
515 }
516 }
517 }
518
519 } // namespace
520
iterate(void)521 FragmentOutputCase::IterateResult FragmentOutputCase::iterate(void)
522 {
523 TestLog &log = m_testCtx.getLog();
524 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
525
526 // Compute grid size & index list.
527 const int minCellSize = 8;
528 const IVec2 minBufSize = getMinSize(m_fboSpec);
529 const int gridWidth = de::clamp(minBufSize.x() / minCellSize, 1, 255) + 1;
530 const int gridHeight = de::clamp(minBufSize.y() / minCellSize, 1, 255) + 1;
531 const int numVertices = gridWidth * gridHeight;
532 const int numQuads = (gridWidth - 1) * (gridHeight - 1);
533 const int numIndices = numQuads * 6;
534
535 const int numInputVecs = getNumInputVectors(m_outputs);
536 vector<vector<uint32_t>> inputs(numInputVecs);
537 vector<float> positions(numVertices * 4);
538 vector<uint16_t> indices(numIndices);
539
540 const int readAlignment = 4;
541 const int viewportW = minBufSize.x();
542 const int viewportH = minBufSize.y();
543 const int numAttachments = (int)m_fboSpec.size();
544
545 vector<uint32_t> drawBuffers(numAttachments);
546 vector<AttachmentData> attachments(numAttachments);
547
548 // Initialize attachment data.
549 for (int ndx = 0; ndx < numAttachments; ndx++)
550 {
551 const tcu::TextureFormat texFmt = glu::mapGLInternalFormat(m_fboSpec[ndx].format);
552 const tcu::TextureChannelClass chnClass = tcu::getTextureChannelClass(texFmt.type);
553 const bool isFixedPoint = chnClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ||
554 chnClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
555
556 // \note Fixed-point formats use float reference to enable more accurate result verification.
557 const tcu::TextureFormat refFmt =
558 isFixedPoint ? tcu::TextureFormat(texFmt.order, tcu::TextureFormat::FLOAT) : texFmt;
559 const tcu::TextureFormat readFmt = getFramebufferReadFormat(texFmt);
560 const int attachmentW = m_fboSpec[ndx].width;
561 const int attachmentH = m_fboSpec[ndx].height;
562
563 drawBuffers[ndx] = GL_COLOR_ATTACHMENT0 + ndx;
564 attachments[ndx].format = texFmt;
565 attachments[ndx].readFormat = readFmt;
566 attachments[ndx].referenceFormat = refFmt;
567 attachments[ndx].renderedData.resize(readFmt.getPixelSize() * attachmentW * attachmentH);
568 attachments[ndx].referenceData.resize(refFmt.getPixelSize() * attachmentW * attachmentH);
569 }
570
571 // Initialize indices.
572 for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
573 {
574 int quadY = quadNdx / (gridWidth - 1);
575 int quadX = quadNdx - quadY * (gridWidth - 1);
576
577 indices[quadNdx * 6 + 0] = uint16_t(quadX + quadY * gridWidth);
578 indices[quadNdx * 6 + 1] = uint16_t(quadX + (quadY + 1) * gridWidth);
579 indices[quadNdx * 6 + 2] = uint16_t(quadX + quadY * gridWidth + 1);
580 indices[quadNdx * 6 + 3] = indices[quadNdx * 6 + 1];
581 indices[quadNdx * 6 + 4] = uint16_t(quadX + (quadY + 1) * gridWidth + 1);
582 indices[quadNdx * 6 + 5] = indices[quadNdx * 6 + 2];
583 }
584
585 for (int y = 0; y < gridHeight; y++)
586 {
587 for (int x = 0; x < gridWidth; x++)
588 {
589 float xf = (float)x / (float)(gridWidth - 1);
590 float yf = (float)y / (float)(gridHeight - 1);
591
592 positions[(y * gridWidth + x) * 4 + 0] = 2.0f * xf - 1.0f;
593 positions[(y * gridWidth + x) * 4 + 1] = 2.0f * yf - 1.0f;
594 positions[(y * gridWidth + x) * 4 + 2] = 0.0f;
595 positions[(y * gridWidth + x) * 4 + 3] = 1.0f;
596 }
597 }
598
599 // Initialize input vectors.
600 {
601 int curInVec = 0;
602 for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
603 {
604 const FragmentOutput &output = m_outputs[outputNdx];
605 bool isFloat = glu::isDataTypeFloatOrVec(output.type);
606 bool isInt = glu::isDataTypeIntOrIVec(output.type);
607 bool isUint = glu::isDataTypeUintOrUVec(output.type);
608 int numVecs = output.arrayLength > 0 ? output.arrayLength : 1;
609 int numScalars = glu::getDataTypeScalarSize(output.type);
610
611 for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
612 {
613 inputs[curInVec].resize(numVertices * numScalars);
614
615 // Record how many outputs are written in attachment.
616 DE_ASSERT(output.location + vecNdx < (int)attachments.size());
617 attachments[output.location + vecNdx].numWrittenChannels = numScalars;
618 attachments[output.location + vecNdx].outPrecision = output.precision;
619
620 if (isFloat)
621 {
622 Vec2 range = getFloatRange(output.precision);
623 Vec4 minVal(range.x());
624 Vec4 maxVal(range.y());
625 float *dst = (float *)&inputs[curInVec][0];
626
627 if (de::inBounds(output.location + vecNdx, 0, (int)attachments.size()))
628 {
629 // \note Floating-point precision conversion is not well-defined. For that reason we must
630 // limit value range to intersection of both data type and render target value ranges.
631 const tcu::TextureFormatInfo fmtInfo =
632 tcu::getTextureFormatInfo(attachments[output.location + vecNdx].format);
633 minVal = tcu::max(minVal, fmtInfo.valueMin);
634 maxVal = tcu::min(maxVal, fmtInfo.valueMax);
635 }
636
637 m_testCtx.getLog() << TestLog::Message << "out" << curInVec
638 << " value range: " << valueRangeToString(numScalars, minVal, maxVal)
639 << TestLog::EndMessage;
640
641 for (int y = 0; y < gridHeight; y++)
642 {
643 for (int x = 0; x < gridWidth; x++)
644 {
645 float xf = (float)x / (float)(gridWidth - 1);
646 float yf = (float)y / (float)(gridHeight - 1);
647
648 float f0 = (xf + yf) * 0.5f;
649 float f1 = 0.5f + (xf - yf) * 0.5f;
650 Vec4 f = swizzleVec(Vec4(f0, f1, 1.0f - f0, 1.0f - f1), curInVec);
651 Vec4 c = minVal + (maxVal - minVal) * f;
652 float *v = dst + (y * gridWidth + x) * numScalars;
653
654 for (int ndx = 0; ndx < numScalars; ndx++)
655 v[ndx] = c[ndx];
656 }
657 }
658 }
659 else if (isInt)
660 {
661 const IVec2 range = getIntRange(output.precision);
662 IVec4 minVal(range.x());
663 IVec4 maxVal(range.y());
664
665 if (de::inBounds(output.location + vecNdx, 0, (int)attachments.size()))
666 {
667 // Limit to range of output format as conversion mode is not specified.
668 const IVec4 fmtBits =
669 tcu::getTextureFormatBitDepth(attachments[output.location + vecNdx].format);
670 const BVec4 isZero = lessThanEqual(fmtBits, IVec4(0));
671 const IVec4 shift = tcu::clamp(fmtBits - 1, tcu::IVec4(0), tcu::IVec4(256));
672 const IVec4 fmtMinVal = (-(tcu::Vector<int64_t, 4>(1) << shift.cast<int64_t>())).asInt();
673 const IVec4 fmtMaxVal =
674 ((tcu::Vector<int64_t, 4>(1) << shift.cast<int64_t>()) - int64_t(1)).asInt();
675
676 minVal = select(minVal, tcu::max(minVal, fmtMinVal), isZero);
677 maxVal = select(maxVal, tcu::min(maxVal, fmtMaxVal), isZero);
678 }
679
680 m_testCtx.getLog() << TestLog::Message << "out" << curInVec
681 << " value range: " << valueRangeToString(numScalars, minVal, maxVal)
682 << TestLog::EndMessage;
683
684 const IVec4 rangeDiv =
685 swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight) - 1), curInVec);
686 const IVec4 step =
687 ((maxVal.cast<int64_t>() - minVal.cast<int64_t>()) / (rangeDiv.cast<int64_t>())).asInt();
688 int32_t *dst = (int32_t *)&inputs[curInVec][0];
689
690 for (int y = 0; y < gridHeight; y++)
691 {
692 for (int x = 0; x < gridWidth; x++)
693 {
694 int ix = gridWidth - x - 1;
695 int iy = gridHeight - y - 1;
696 IVec4 c = minVal + step * swizzleVec(IVec4(x, y, ix, iy), curInVec);
697 int32_t *v = dst + (y * gridWidth + x) * numScalars;
698
699 DE_ASSERT(boolAll(logicalAnd(greaterThanEqual(c, minVal), lessThanEqual(c, maxVal))));
700
701 for (int ndx = 0; ndx < numScalars; ndx++)
702 v[ndx] = c[ndx];
703 }
704 }
705 }
706 else if (isUint)
707 {
708 const UVec2 range = getUintRange(output.precision);
709 UVec4 maxVal(range.y());
710
711 if (de::inBounds(output.location + vecNdx, 0, (int)attachments.size()))
712 {
713 // Limit to range of output format as conversion mode is not specified.
714 const IVec4 fmtBits =
715 tcu::getTextureFormatBitDepth(attachments[output.location + vecNdx].format);
716 const UVec4 fmtMaxVal =
717 ((tcu::Vector<uint64_t, 4>(1) << fmtBits.cast<uint64_t>()) - uint64_t(1)).asUint();
718
719 maxVal = tcu::min(maxVal, fmtMaxVal);
720 }
721
722 m_testCtx.getLog() << TestLog::Message << "out" << curInVec
723 << " value range: " << valueRangeToString(numScalars, UVec4(0), maxVal)
724 << TestLog::EndMessage;
725
726 const IVec4 rangeDiv =
727 swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight) - 1), curInVec);
728 const UVec4 step = maxVal / rangeDiv.asUint();
729 uint32_t *dst = &inputs[curInVec][0];
730
731 DE_ASSERT(range.x() == 0);
732
733 for (int y = 0; y < gridHeight; y++)
734 {
735 for (int x = 0; x < gridWidth; x++)
736 {
737 int ix = gridWidth - x - 1;
738 int iy = gridHeight - y - 1;
739 UVec4 c = step * swizzleVec(IVec4(x, y, ix, iy).asUint(), curInVec);
740 uint32_t *v = dst + (y * gridWidth + x) * numScalars;
741
742 DE_ASSERT(boolAll(lessThanEqual(c, maxVal)));
743
744 for (int ndx = 0; ndx < numScalars; ndx++)
745 v[ndx] = c[ndx];
746 }
747 }
748 }
749 else
750 DE_ASSERT(false);
751
752 curInVec += 1;
753 }
754 }
755 }
756
757 // Render using gl.
758 gl.useProgram(m_program->getProgram());
759 gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
760 gl.viewport(0, 0, viewportW, viewportH);
761 gl.drawBuffers((int)drawBuffers.size(), &drawBuffers[0]);
762 gl.disable(
763 GL_DITHER); // Dithering causes issues with unorm formats. Those issues could be worked around in threshold, but it makes validation less accurate.
764 GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
765
766 {
767 int curInVec = 0;
768 for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
769 {
770 const FragmentOutput &output = m_outputs[outputNdx];
771 bool isArray = output.arrayLength > 0;
772 bool isFloat = glu::isDataTypeFloatOrVec(output.type);
773 bool isInt = glu::isDataTypeIntOrIVec(output.type);
774 bool isUint = glu::isDataTypeUintOrUVec(output.type);
775 int scalarSize = glu::getDataTypeScalarSize(output.type);
776 uint32_t glScalarType = isFloat ? GL_FLOAT : isInt ? GL_INT : isUint ? GL_UNSIGNED_INT : GL_NONE;
777 int numVecs = isArray ? output.arrayLength : 1;
778
779 for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
780 {
781 string name =
782 string("in") + de::toString(outputNdx) + (isArray ? string("_") + de::toString(vecNdx) : string());
783 int loc = gl.getAttribLocation(m_program->getProgram(), name.c_str());
784
785 if (loc >= 0)
786 {
787 gl.enableVertexAttribArray(loc);
788 if (isFloat)
789 gl.vertexAttribPointer(loc, scalarSize, glScalarType, GL_FALSE, 0, &inputs[curInVec][0]);
790 else
791 gl.vertexAttribIPointer(loc, scalarSize, glScalarType, 0, &inputs[curInVec][0]);
792 }
793 else
794 log << TestLog::Message << "Warning: No location for attribute '" << name << "' found."
795 << TestLog::EndMessage;
796
797 curInVec += 1;
798 }
799 }
800 }
801 {
802 int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position");
803 TCU_CHECK(posLoc >= 0);
804 gl.enableVertexAttribArray(posLoc);
805 gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &positions[0]);
806 }
807 GLU_EXPECT_NO_ERROR(gl.getError(), "After attribute setup");
808
809 gl.drawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, &indices[0]);
810 GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements");
811
812 // Read all attachment points.
813 for (int ndx = 0; ndx < numAttachments; ndx++)
814 {
815 const glu::TransferFormat transferFmt = glu::getTransferFormat(attachments[ndx].readFormat);
816 void *dst = &attachments[ndx].renderedData[0];
817 const int attachmentW = m_fboSpec[ndx].width;
818 const int attachmentH = m_fboSpec[ndx].height;
819 const int numValidChannels = attachments[ndx].numWrittenChannels;
820 const tcu::PixelBufferAccess rendered(
821 attachments[ndx].readFormat, attachmentW, attachmentH, 1,
822 deAlign32(attachments[ndx].readFormat.getPixelSize() * attachmentW, readAlignment), 0,
823 &attachments[ndx].renderedData[0]);
824
825 gl.readBuffer(GL_COLOR_ATTACHMENT0 + ndx);
826 gl.readPixels(0, 0, minBufSize.x(), minBufSize.y(), transferFmt.format, transferFmt.dataType, dst);
827
828 clearUndefined(rendered, numValidChannels);
829 }
830
831 // Render reference images.
832 {
833 int curInNdx = 0;
834 for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
835 {
836 const FragmentOutput &output = m_outputs[outputNdx];
837 const bool isArray = output.arrayLength > 0;
838 const bool isFloat = glu::isDataTypeFloatOrVec(output.type);
839 const bool isInt = glu::isDataTypeIntOrIVec(output.type);
840 const bool isUint = glu::isDataTypeUintOrUVec(output.type);
841 const int scalarSize = glu::getDataTypeScalarSize(output.type);
842 const int numVecs = isArray ? output.arrayLength : 1;
843
844 for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
845 {
846 const int location = output.location + vecNdx;
847 const void *inputData = &inputs[curInNdx][0];
848
849 DE_ASSERT(de::inBounds(location, 0, (int)m_fboSpec.size()));
850
851 const int bufW = m_fboSpec[location].width;
852 const int bufH = m_fboSpec[location].height;
853 const tcu::PixelBufferAccess buf(attachments[location].referenceFormat, bufW, bufH, 1,
854 &attachments[location].referenceData[0]);
855 const tcu::PixelBufferAccess viewportBuf = getSubregion(buf, 0, 0, 0, viewportW, viewportH, 1);
856
857 if (isInt || isUint)
858 renderIntReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const int *)inputData);
859 else if (isFloat)
860 renderFloatReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const float *)inputData);
861 else
862 DE_ASSERT(false);
863
864 curInNdx += 1;
865 }
866 }
867 }
868
869 // Compare all images.
870 bool allLevelsOk = true;
871 for (int attachNdx = 0; attachNdx < numAttachments; attachNdx++)
872 {
873 const int attachmentW = m_fboSpec[attachNdx].width;
874 const int attachmentH = m_fboSpec[attachNdx].height;
875 const int numValidChannels = attachments[attachNdx].numWrittenChannels;
876 const tcu::BVec4 cmpMask(numValidChannels >= 1, numValidChannels >= 2, numValidChannels >= 3,
877 numValidChannels >= 4);
878 const glu::Precision outPrecision = attachments[attachNdx].outPrecision;
879 const tcu::TextureFormat &format = attachments[attachNdx].format;
880 tcu::ConstPixelBufferAccess rendered(
881 attachments[attachNdx].readFormat, attachmentW, attachmentH, 1,
882 deAlign32(attachments[attachNdx].readFormat.getPixelSize() * attachmentW, readAlignment), 0,
883 &attachments[attachNdx].renderedData[0]);
884 tcu::ConstPixelBufferAccess reference(attachments[attachNdx].referenceFormat, attachmentW, attachmentH, 1,
885 &attachments[attachNdx].referenceData[0]);
886 tcu::TextureChannelClass texClass = tcu::getTextureChannelClass(format.type);
887 bool isOk = true;
888 const string name = string("Attachment") + de::toString(attachNdx);
889 const string desc = string("Color attachment ") + de::toString(attachNdx);
890
891 log << TestLog::Message << "Attachment " << attachNdx << ": " << numValidChannels
892 << " channels have defined values and used for comparison" << TestLog::EndMessage;
893
894 switch (texClass)
895 {
896 case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
897 {
898 const uint32_t interpThreshold = 4; //!< 4 ULP interpolation threshold (interpolation always in highp)
899 uint32_t outTypeThreshold = 0; //!< Threshold based on output type
900 UVec4 formatThreshold; //!< Threshold computed based on format.
901 UVec4 finalThreshold;
902
903 // 1 ULP rounding error is allowed for smaller floating-point formats
904 switch (format.type)
905 {
906 case tcu::TextureFormat::FLOAT:
907 formatThreshold = UVec4(0);
908 break;
909 case tcu::TextureFormat::HALF_FLOAT:
910 formatThreshold = UVec4((1 << (23 - 10)));
911 break;
912 case tcu::TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:
913 formatThreshold = UVec4((1 << (23 - 6)), (1 << (23 - 6)), (1 << (23 - 5)), 0);
914 break;
915 default:
916 DE_ASSERT(false);
917 break;
918 }
919
920 // 1 ULP rounding error for highp -> output precision cast
921 switch (outPrecision)
922 {
923 case glu::PRECISION_LOWP:
924 outTypeThreshold = (1 << (23 - 8));
925 break;
926 case glu::PRECISION_MEDIUMP:
927 outTypeThreshold = (1 << (23 - 10));
928 break;
929 case glu::PRECISION_HIGHP:
930 outTypeThreshold = 0;
931 break;
932 default:
933 DE_ASSERT(false);
934 }
935
936 finalThreshold =
937 select(max(formatThreshold, UVec4(deMax32(interpThreshold, outTypeThreshold))), UVec4(~0u), cmpMask);
938
939 isOk = tcu::floatUlpThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, finalThreshold,
940 tcu::COMPARE_LOG_RESULT);
941 break;
942 }
943
944 case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
945 {
946 // \note glReadPixels() allows only 8 bits to be read. This means that RGB10_A2 will loose some
947 // bits in the process and it must be taken into account when computing threshold.
948 const IVec4 bits = min(IVec4(8), tcu::getTextureFormatBitDepth(format));
949 const Vec4 baseThreshold = 1.0f / ((IVec4(1) << bits) - 1).asFloat();
950 const Vec4 threshold = select(baseThreshold, Vec4(2.0f), cmpMask);
951
952 isOk = tcu::floatThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold,
953 tcu::COMPARE_LOG_RESULT);
954 break;
955 }
956
957 case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
958 case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
959 {
960 const tcu::UVec4 threshold = select(UVec4(0u), UVec4(~0u), cmpMask);
961 isOk = tcu::intThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold,
962 tcu::COMPARE_LOG_RESULT);
963 break;
964 }
965
966 default:
967 TCU_FAIL("Unsupported comparison");
968 }
969
970 if (!isOk)
971 allLevelsOk = false;
972 }
973
974 m_testCtx.setTestResult(allLevelsOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
975 allLevelsOk ? "Pass" : "Image comparison failed");
976 return STOP;
977 }
978
FragmentOutputTests(Context & context)979 FragmentOutputTests::FragmentOutputTests(Context &context)
980 : TestCaseGroup(context, "fragment_out", "Fragment output tests")
981 {
982 }
983
~FragmentOutputTests(void)984 FragmentOutputTests::~FragmentOutputTests(void)
985 {
986 }
987
createRandomCase(Context & context,int minRenderTargets,int maxRenderTargets,uint32_t seed)988 static FragmentOutputCase *createRandomCase(Context &context, int minRenderTargets, int maxRenderTargets, uint32_t seed)
989 {
990 static const glu::DataType outputTypes[] = {glu::TYPE_FLOAT, glu::TYPE_FLOAT_VEC2, glu::TYPE_FLOAT_VEC3,
991 glu::TYPE_FLOAT_VEC4, glu::TYPE_INT, glu::TYPE_INT_VEC2,
992 glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, glu::TYPE_UINT,
993 glu::TYPE_UINT_VEC2, glu::TYPE_UINT_VEC3, glu::TYPE_UINT_VEC4};
994 static const glu::Precision precisions[] = {glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP};
995 static const uint32_t floatFormats[] = {
996 GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F, GL_R32F, GL_R16F, GL_RGBA8,
997 GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGBA4, GL_RGB5_A1, GL_RGB8, GL_RGB565, GL_RG8, GL_R8};
998 static const uint32_t intFormats[] = {GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I,
999 GL_RG8I, GL_R32I, GL_R16I, GL_R8I};
1000 static const uint32_t uintFormats[] = {GL_RGBA32UI, GL_RGBA16UI, GL_RGBA8UI, GL_RGB10_A2UI, GL_RG32UI,
1001 GL_RG16UI, GL_RG8UI, GL_R32UI, GL_R16UI, GL_R8UI};
1002
1003 de::Random rnd(seed);
1004 vector<FragmentOutput> outputs;
1005 vector<BufferSpec> targets;
1006 vector<glu::DataType> outTypes;
1007
1008 int numTargets = rnd.getInt(minRenderTargets, maxRenderTargets);
1009 const int width = 128; // \todo [2012-04-10 pyry] Separate randomized sizes per target?
1010 const int height = 64;
1011 const int samples = 0;
1012
1013 // Compute outputs.
1014 int curLoc = 0;
1015 while (curLoc < numTargets)
1016 {
1017 bool useArray = rnd.getFloat() < 0.3f;
1018 int maxArrayLen = numTargets - curLoc;
1019 int arrayLen = useArray ? rnd.getInt(1, maxArrayLen) : 0;
1020 glu::DataType basicType =
1021 rnd.choose<glu::DataType>(&outputTypes[0], &outputTypes[0] + DE_LENGTH_OF_ARRAY(outputTypes));
1022 glu::Precision precision =
1023 rnd.choose<glu::Precision>(&precisions[0], &precisions[0] + DE_LENGTH_OF_ARRAY(precisions));
1024 int numLocations = useArray ? arrayLen : 1;
1025
1026 outputs.push_back(FragmentOutput(basicType, precision, curLoc, arrayLen));
1027
1028 for (int ndx = 0; ndx < numLocations; ndx++)
1029 outTypes.push_back(basicType);
1030
1031 curLoc += numLocations;
1032 }
1033 DE_ASSERT(curLoc == numTargets);
1034 DE_ASSERT((int)outTypes.size() == numTargets);
1035
1036 // Compute buffers.
1037 while ((int)targets.size() < numTargets)
1038 {
1039 glu::DataType outType = outTypes[targets.size()];
1040 bool isFloat = glu::isDataTypeFloatOrVec(outType);
1041 bool isInt = glu::isDataTypeIntOrIVec(outType);
1042 bool isUint = glu::isDataTypeUintOrUVec(outType);
1043 uint32_t format = 0;
1044
1045 if (isFloat)
1046 format = rnd.choose<uint32_t>(&floatFormats[0], &floatFormats[0] + DE_LENGTH_OF_ARRAY(floatFormats));
1047 else if (isInt)
1048 format = rnd.choose<uint32_t>(&intFormats[0], &intFormats[0] + DE_LENGTH_OF_ARRAY(intFormats));
1049 else if (isUint)
1050 format = rnd.choose<uint32_t>(&uintFormats[0], &uintFormats[0] + DE_LENGTH_OF_ARRAY(uintFormats));
1051 else
1052 DE_ASSERT(false);
1053
1054 targets.push_back(BufferSpec(format, width, height, samples));
1055 }
1056
1057 return new FragmentOutputCase(context, de::toString(seed).c_str(), "", targets, outputs);
1058 }
1059
init(void)1060 void FragmentOutputTests::init(void)
1061 {
1062 static const uint32_t requiredFloatFormats[] = {GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F,
1063 GL_R32F, GL_R16F};
1064 static const uint32_t requiredFixedFormats[] = {GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGBA4, GL_RGB5_A1,
1065 GL_RGB8, GL_RGB565, GL_RG8, GL_R8};
1066 static const uint32_t requiredIntFormats[] = {GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I,
1067 GL_RG8I, GL_R32I, GL_R16I, GL_R8I};
1068 static const uint32_t requiredUintFormats[] = {GL_RGBA32UI, GL_RGBA16UI, GL_RGBA8UI, GL_RGB10_A2UI, GL_RG32UI,
1069 GL_RG16UI, GL_RG8UI, GL_R32UI, GL_R16UI, GL_R8UI};
1070
1071 static const glu::Precision precisions[] = {glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP};
1072
1073 // .basic.
1074 {
1075 tcu::TestCaseGroup *basicGroup = new tcu::TestCaseGroup(m_testCtx, "basic", "Basic fragment output tests");
1076 addChild(basicGroup);
1077
1078 const int width = 64;
1079 const int height = 64;
1080 const int samples = 0;
1081
1082 // .float
1083 tcu::TestCaseGroup *floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests");
1084 basicGroup->addChild(floatGroup);
1085 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++)
1086 {
1087 uint32_t format = requiredFloatFormats[fmtNdx];
1088 string fmtName = getFormatName(format);
1089 vector<BufferSpec> fboSpec;
1090
1091 fboSpec.push_back(BufferSpec(format, width, height, samples));
1092
1093 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1094 {
1095 glu::Precision prec = precisions[precNdx];
1096 string precName = glu::getPrecisionName(prec);
1097
1098 floatGroup->addChild(
1099 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec,
1100 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0)).toVec()));
1101 floatGroup->addChild(
1102 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec,
1103 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0)).toVec()));
1104 floatGroup->addChild(
1105 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec,
1106 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0)).toVec()));
1107 floatGroup->addChild(
1108 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec,
1109 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0)).toVec()));
1110 }
1111 }
1112
1113 // .fixed
1114 tcu::TestCaseGroup *fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests");
1115 basicGroup->addChild(fixedGroup);
1116 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++)
1117 {
1118 uint32_t format = requiredFixedFormats[fmtNdx];
1119 string fmtName = getFormatName(format);
1120 vector<BufferSpec> fboSpec;
1121
1122 fboSpec.push_back(BufferSpec(format, width, height, samples));
1123
1124 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1125 {
1126 glu::Precision prec = precisions[precNdx];
1127 string precName = glu::getPrecisionName(prec);
1128
1129 fixedGroup->addChild(
1130 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec,
1131 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0)).toVec()));
1132 fixedGroup->addChild(
1133 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec,
1134 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0)).toVec()));
1135 fixedGroup->addChild(
1136 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec,
1137 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0)).toVec()));
1138 fixedGroup->addChild(
1139 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec,
1140 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0)).toVec()));
1141 }
1142 }
1143
1144 // .int
1145 tcu::TestCaseGroup *intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests");
1146 basicGroup->addChild(intGroup);
1147 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++)
1148 {
1149 uint32_t format = requiredIntFormats[fmtNdx];
1150 string fmtName = getFormatName(format);
1151 vector<BufferSpec> fboSpec;
1152
1153 fboSpec.push_back(BufferSpec(format, width, height, samples));
1154
1155 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1156 {
1157 glu::Precision prec = precisions[precNdx];
1158 string precName = glu::getPrecisionName(prec);
1159
1160 intGroup->addChild(
1161 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_int").c_str(), "", fboSpec,
1162 (OutputVec() << FragmentOutput(glu::TYPE_INT, prec, 0)).toVec()));
1163 intGroup->addChild(
1164 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec2").c_str(), "", fboSpec,
1165 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2, prec, 0)).toVec()));
1166 intGroup->addChild(
1167 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec3").c_str(), "", fboSpec,
1168 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3, prec, 0)).toVec()));
1169 intGroup->addChild(
1170 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec4").c_str(), "", fboSpec,
1171 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4, prec, 0)).toVec()));
1172 }
1173 }
1174
1175 // .uint
1176 tcu::TestCaseGroup *uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests");
1177 basicGroup->addChild(uintGroup);
1178 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++)
1179 {
1180 uint32_t format = requiredUintFormats[fmtNdx];
1181 string fmtName = getFormatName(format);
1182 vector<BufferSpec> fboSpec;
1183
1184 fboSpec.push_back(BufferSpec(format, width, height, samples));
1185
1186 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1187 {
1188 glu::Precision prec = precisions[precNdx];
1189 string precName = glu::getPrecisionName(prec);
1190
1191 uintGroup->addChild(
1192 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uint").c_str(), "", fboSpec,
1193 (OutputVec() << FragmentOutput(glu::TYPE_UINT, prec, 0)).toVec()));
1194 uintGroup->addChild(
1195 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec2").c_str(), "", fboSpec,
1196 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2, prec, 0)).toVec()));
1197 uintGroup->addChild(
1198 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec3").c_str(), "", fboSpec,
1199 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3, prec, 0)).toVec()));
1200 uintGroup->addChild(
1201 new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec4").c_str(), "", fboSpec,
1202 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4, prec, 0)).toVec()));
1203 }
1204 }
1205 }
1206
1207 // .array
1208 {
1209 tcu::TestCaseGroup *arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Array outputs");
1210 addChild(arrayGroup);
1211
1212 const int width = 64;
1213 const int height = 64;
1214 const int samples = 0;
1215 const int numTargets = 3;
1216
1217 // .float
1218 tcu::TestCaseGroup *floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests");
1219 arrayGroup->addChild(floatGroup);
1220 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++)
1221 {
1222 uint32_t format = requiredFloatFormats[fmtNdx];
1223 string fmtName = getFormatName(format);
1224 vector<BufferSpec> fboSpec;
1225
1226 for (int ndx = 0; ndx < numTargets; ndx++)
1227 fboSpec.push_back(BufferSpec(format, width, height, samples));
1228
1229 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1230 {
1231 glu::Precision prec = precisions[precNdx];
1232 string precName = glu::getPrecisionName(prec);
1233
1234 floatGroup->addChild(new FragmentOutputCase(
1235 m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec,
1236 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0, numTargets)).toVec()));
1237 floatGroup->addChild(new FragmentOutputCase(
1238 m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec,
1239 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0, numTargets)).toVec()));
1240 floatGroup->addChild(new FragmentOutputCase(
1241 m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec,
1242 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0, numTargets)).toVec()));
1243 floatGroup->addChild(new FragmentOutputCase(
1244 m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec,
1245 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0, numTargets)).toVec()));
1246 }
1247 }
1248
1249 // .fixed
1250 tcu::TestCaseGroup *fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests");
1251 arrayGroup->addChild(fixedGroup);
1252 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++)
1253 {
1254 uint32_t format = requiredFixedFormats[fmtNdx];
1255 string fmtName = getFormatName(format);
1256 vector<BufferSpec> fboSpec;
1257
1258 for (int ndx = 0; ndx < numTargets; ndx++)
1259 fboSpec.push_back(BufferSpec(format, width, height, samples));
1260
1261 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1262 {
1263 glu::Precision prec = precisions[precNdx];
1264 string precName = glu::getPrecisionName(prec);
1265
1266 fixedGroup->addChild(new FragmentOutputCase(
1267 m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec,
1268 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0, numTargets)).toVec()));
1269 fixedGroup->addChild(new FragmentOutputCase(
1270 m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec,
1271 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0, numTargets)).toVec()));
1272 fixedGroup->addChild(new FragmentOutputCase(
1273 m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec,
1274 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0, numTargets)).toVec()));
1275 fixedGroup->addChild(new FragmentOutputCase(
1276 m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec,
1277 (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0, numTargets)).toVec()));
1278 }
1279 }
1280
1281 // .int
1282 tcu::TestCaseGroup *intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests");
1283 arrayGroup->addChild(intGroup);
1284 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++)
1285 {
1286 uint32_t format = requiredIntFormats[fmtNdx];
1287 string fmtName = getFormatName(format);
1288 vector<BufferSpec> fboSpec;
1289
1290 for (int ndx = 0; ndx < numTargets; ndx++)
1291 fboSpec.push_back(BufferSpec(format, width, height, samples));
1292
1293 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1294 {
1295 glu::Precision prec = precisions[precNdx];
1296 string precName = glu::getPrecisionName(prec);
1297
1298 intGroup->addChild(new FragmentOutputCase(
1299 m_context, (fmtName + "_" + precName + "_int").c_str(), "", fboSpec,
1300 (OutputVec() << FragmentOutput(glu::TYPE_INT, prec, 0, numTargets)).toVec()));
1301 intGroup->addChild(new FragmentOutputCase(
1302 m_context, (fmtName + "_" + precName + "_ivec2").c_str(), "", fboSpec,
1303 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2, prec, 0, numTargets)).toVec()));
1304 intGroup->addChild(new FragmentOutputCase(
1305 m_context, (fmtName + "_" + precName + "_ivec3").c_str(), "", fboSpec,
1306 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3, prec, 0, numTargets)).toVec()));
1307 intGroup->addChild(new FragmentOutputCase(
1308 m_context, (fmtName + "_" + precName + "_ivec4").c_str(), "", fboSpec,
1309 (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4, prec, 0, numTargets)).toVec()));
1310 }
1311 }
1312
1313 // .uint
1314 tcu::TestCaseGroup *uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests");
1315 arrayGroup->addChild(uintGroup);
1316 for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++)
1317 {
1318 uint32_t format = requiredUintFormats[fmtNdx];
1319 string fmtName = getFormatName(format);
1320 vector<BufferSpec> fboSpec;
1321
1322 for (int ndx = 0; ndx < numTargets; ndx++)
1323 fboSpec.push_back(BufferSpec(format, width, height, samples));
1324
1325 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1326 {
1327 glu::Precision prec = precisions[precNdx];
1328 string precName = glu::getPrecisionName(prec);
1329
1330 uintGroup->addChild(new FragmentOutputCase(
1331 m_context, (fmtName + "_" + precName + "_uint").c_str(), "", fboSpec,
1332 (OutputVec() << FragmentOutput(glu::TYPE_UINT, prec, 0, numTargets)).toVec()));
1333 uintGroup->addChild(new FragmentOutputCase(
1334 m_context, (fmtName + "_" + precName + "_uvec2").c_str(), "", fboSpec,
1335 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2, prec, 0, numTargets)).toVec()));
1336 uintGroup->addChild(new FragmentOutputCase(
1337 m_context, (fmtName + "_" + precName + "_uvec3").c_str(), "", fboSpec,
1338 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3, prec, 0, numTargets)).toVec()));
1339 uintGroup->addChild(new FragmentOutputCase(
1340 m_context, (fmtName + "_" + precName + "_uvec4").c_str(), "", fboSpec,
1341 (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4, prec, 0, numTargets)).toVec()));
1342 }
1343 }
1344 }
1345
1346 // .random
1347 {
1348 tcu::TestCaseGroup *randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Random fragment output cases");
1349 addChild(randomGroup);
1350
1351 for (uint32_t seed = 0; seed < 100; seed++)
1352 randomGroup->addChild(createRandomCase(m_context, 2, 4, seed));
1353 }
1354 }
1355
1356 } // namespace Functional
1357 } // namespace gles3
1358 } // namespace deqp
1359