xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fFragmentOutputTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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