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 {
BufferSpecdeqp::gles3::Functional::BufferSpec71 BufferSpec (void)
72 : format (GL_NONE)
73 , width (0)
74 , height (0)
75 , samples (0)
76 {
77 }
78
BufferSpecdeqp::gles3::Functional::BufferSpec79 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 {
FragmentOutputdeqp::gles3::Functional::FragmentOutput95 FragmentOutput (void)
96 : type (glu::TYPE_LAST)
97 , precision (glu::PRECISION_LAST)
98 , location (0)
99 , arrayLength (0)
100 {
101 }
102
FragmentOutputdeqp::gles3::Functional::FragmentOutput103 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
operator <<deqp::gles3::Functional::OutputVec121 OutputVec& operator<< (const FragmentOutput& output)
122 {
123 outputs.push_back(output);
124 return *this;
125 }
126
toVecdeqp::gles3::Functional::OutputVec127 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
FragmentOutputCase(Context & context,const char * name,const char * desc,const vector<BufferSpec> & fboSpec,const vector<FragmentOutput> & outputs)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
~FragmentOutputCase(void)164 FragmentOutputCase::~FragmentOutputCase (void)
165 {
166 deinit();
167 }
168
createProgram(const glu::RenderContext & context,const vector<FragmentOutput> & outputs)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
init(void)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
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[] =
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
getIntRange(glu::Precision precision)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
getUintRange(glu::Precision precision)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
readVec4(const float * ptr,int numComponents)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
readIVec4(const int * ptr,int numComponents)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
renderFloatReference(const tcu::PixelBufferAccess & dst,int gridWidth,int gridHeight,int numComponents,const float * vertices)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
renderIntReference(const tcu::PixelBufferAccess & dst,int gridWidth,int gridHeight,int numComponents,const int * vertices)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>
swizzleVec(const tcu::Vector<T,4> & vec,int swzNdx)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>
valueRangeToString(int numValidChannels,const tcu::Vector<Type,4> & minValue,const tcu::Vector<Type,4> & maxValue)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
clearUndefined(const tcu::PixelBufferAccess & access,int numValidChannels)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
iterate(void)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
FragmentOutputTests(Context & context)961 FragmentOutputTests::FragmentOutputTests (Context& context)
962 : TestCaseGroup(context, "fragment_out", "Fragment output tests")
963 {
964 }
965
~FragmentOutputTests(void)966 FragmentOutputTests::~FragmentOutputTests (void)
967 {
968 }
969
createRandomCase(Context & context,int minRenderTargets,int maxRenderTargets,deUint32 seed)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
init(void)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
1372