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