/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Fragment shader output tests. * * \todo [2012-04-10 pyry] Missing: * + non-contiguous attachments in framebuffer *//*--------------------------------------------------------------------*/ #include "es3fFragmentOutputTests.hpp" #include "gluShaderUtil.hpp" #include "gluShaderProgram.hpp" #include "gluTextureUtil.hpp" #include "gluStrUtil.hpp" #include "tcuTestLog.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuVector.hpp" #include "tcuVectorUtil.hpp" #include "tcuImageCompare.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deMath.h" // For getFormatName() \todo [pyry] Move to glu? #include "es3fFboTestUtil.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" namespace deqp { namespace gles3 { namespace Functional { using std::vector; using std::string; using tcu::IVec2; using tcu::IVec4; using tcu::UVec2; using tcu::UVec4; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using tcu::BVec4; using tcu::TestLog; using FboTestUtil::getFormatName; using FboTestUtil::getFramebufferReadFormat; struct BufferSpec { BufferSpec (void) : format (GL_NONE) , width (0) , height (0) , samples (0) { } BufferSpec (deUint32 format_, int width_, int height_, int samples_) : format (format_) , width (width_) , height (height_) , samples (samples_) { } deUint32 format; int width; int height; int samples; }; struct FragmentOutput { FragmentOutput (void) : type (glu::TYPE_LAST) , precision (glu::PRECISION_LAST) , location (0) , arrayLength (0) { } FragmentOutput (glu::DataType type_, glu::Precision precision_, int location_, int arrayLength_ = 0) : type (type_) , precision (precision_) , location (location_) , arrayLength (arrayLength_) { } glu::DataType type; glu::Precision precision; int location; int arrayLength; //!< 0 if not an array. }; struct OutputVec { vector<FragmentOutput> outputs; OutputVec& operator<< (const FragmentOutput& output) { outputs.push_back(output); return *this; } vector<FragmentOutput> toVec (void) const { return outputs; } }; class FragmentOutputCase : public TestCase { public: FragmentOutputCase (Context& context, const char* name, const char* desc, const vector<BufferSpec>& fboSpec, const vector<FragmentOutput>& outputs); ~FragmentOutputCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: FragmentOutputCase (const FragmentOutputCase& other); FragmentOutputCase& operator= (const FragmentOutputCase& other); vector<BufferSpec> m_fboSpec; vector<FragmentOutput> m_outputs; glu::ShaderProgram* m_program; deUint32 m_framebuffer; vector<deUint32> m_renderbuffers; }; FragmentOutputCase::FragmentOutputCase (Context& context, const char* name, const char* desc, const vector<BufferSpec>& fboSpec, const vector<FragmentOutput>& outputs) : TestCase (context, name, desc) , m_fboSpec (fboSpec) , m_outputs (outputs) , m_program (DE_NULL) , m_framebuffer (0) { } FragmentOutputCase::~FragmentOutputCase (void) { deinit(); } static glu::ShaderProgram* createProgram (const glu::RenderContext& context, const vector<FragmentOutput>& outputs) { std::ostringstream vtx; std::ostringstream frag; vtx << "#version 300 es\n" << "in highp vec4 a_position;\n"; frag << "#version 300 es\n"; // Input-output declarations. for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++) { const FragmentOutput& output = outputs[outNdx]; bool isArray = output.arrayLength > 0; const char* typeName = glu::getDataTypeName(output.type); const char* outputPrec = glu::getPrecisionName(output.precision); bool isFloat = glu::isDataTypeFloatOrVec(output.type); const char* interp = isFloat ? "smooth" : "flat"; const char* interpPrec = isFloat ? "highp" : outputPrec; if (isArray) { for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++) { vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << "_" << elemNdx << ";\n" << interp << " out " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx << ";\n"; frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx << ";\n"; } frag << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out" << outNdx << "[" << output.arrayLength << "];\n"; } else { vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << ";\n" << interp << " out " << interpPrec << " " << typeName << " var" << outNdx << ";\n"; frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << ";\n" << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out" << outNdx << ";\n"; } } vtx << "\nvoid main()\n{\n"; frag << "\nvoid main()\n{\n"; vtx << " gl_Position = a_position;\n"; // Copy body for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++) { const FragmentOutput& output = outputs[outNdx]; bool isArray = output.arrayLength > 0; if (isArray) { for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++) { vtx << "\tvar" << outNdx << "_" << elemNdx << " = in" << outNdx << "_" << elemNdx << ";\n"; frag << "\tout" << outNdx << "[" << elemNdx << "] = var" << outNdx << "_" << elemNdx << ";\n"; } } else { vtx << "\tvar" << outNdx << " = in" << outNdx << ";\n"; frag << "\tout" << outNdx << " = var" << outNdx << ";\n"; } } vtx << "}\n"; frag << "}\n"; return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str())); } void FragmentOutputCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); TestLog& log = m_testCtx.getLog(); // Check that all attachments are supported for (std::vector<BufferSpec>::const_iterator bufIter = m_fboSpec.begin(); bufIter != m_fboSpec.end(); ++bufIter) { if (!glu::isSizedFormatColorRenderable(m_context.getRenderContext(), m_context.getContextInfo(), bufIter->format)) throw tcu::NotSupportedError("Unsupported attachment format"); } DE_ASSERT(!m_program); m_program = createProgram(m_context.getRenderContext(), m_outputs); log << *m_program; if (!m_program->isOk()) TCU_FAIL("Compile failed"); // Print render target info to log. log << TestLog::Section("Framebuffer", "Framebuffer configuration"); for (int ndx = 0; ndx < (int)m_fboSpec.size(); ndx++) log << TestLog::Message << "COLOR_ATTACHMENT" << ndx << ": " << glu::getPixelFormatStr(m_fboSpec[ndx].format) << ", " << m_fboSpec[ndx].width << "x" << m_fboSpec[ndx].height << ", " << m_fboSpec[ndx].samples << " samples" << TestLog::EndMessage; log << TestLog::EndSection; // Create framebuffer. m_renderbuffers.resize(m_fboSpec.size(), 0); gl.genFramebuffers(1, &m_framebuffer); gl.genRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]); gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); for (int bufNdx = 0; bufNdx < (int)m_renderbuffers.size(); bufNdx++) { deUint32 rbo = m_renderbuffers[bufNdx]; const BufferSpec& bufSpec = m_fboSpec[bufNdx]; deUint32 attachment = GL_COLOR_ATTACHMENT0+bufNdx; gl.bindRenderbuffer(GL_RENDERBUFFER, rbo); gl.renderbufferStorageMultisample(GL_RENDERBUFFER, bufSpec.samples, bufSpec.format, bufSpec.width, bufSpec.height); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo); } GLU_EXPECT_NO_ERROR(gl.getError(), "After framebuffer setup"); deUint32 fboStatus = gl.checkFramebufferStatus(GL_FRAMEBUFFER); if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED) throw tcu::NotSupportedError("Framebuffer not supported", "", __FILE__, __LINE__); else if (fboStatus != GL_FRAMEBUFFER_COMPLETE) throw tcu::TestError((string("Incomplete framebuffer: ") + glu::getFramebufferStatusStr(fboStatus).toString()).c_str(), "", __FILE__, __LINE__); gl.bindFramebuffer(GL_FRAMEBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "After init"); } void FragmentOutputCase::deinit (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_framebuffer) { gl.deleteFramebuffers(1, &m_framebuffer); m_framebuffer = 0; } if (!m_renderbuffers.empty()) { gl.deleteRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]); m_renderbuffers.clear(); } delete m_program; m_program = DE_NULL; } static IVec2 getMinSize (const vector<BufferSpec>& fboSpec) { IVec2 minSize(0x7fffffff, 0x7fffffff); for (vector<BufferSpec>::const_iterator i = fboSpec.begin(); i != fboSpec.end(); i++) { minSize.x() = de::min(minSize.x(), i->width); minSize.y() = de::min(minSize.y(), i->height); } return minSize; } static int getNumInputVectors (const vector<FragmentOutput>& outputs) { int numVecs = 0; for (vector<FragmentOutput>::const_iterator i = outputs.begin(); i != outputs.end(); i++) numVecs += (i->arrayLength > 0 ? i->arrayLength : 1); return numVecs; } static Vec2 getFloatRange (glu::Precision precision) { // \todo [2012-04-09 pyry] Not quite the full ranges. static const Vec2 ranges[] = { Vec2(-2.0f, 2.0f), Vec2(-16000.0f, 16000.0f), Vec2(-1e35f, 1e35f) }; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST); DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges))); return ranges[precision]; } static IVec2 getIntRange (glu::Precision precision) { static const IVec2 ranges[] = { IVec2(-(1<< 7), (1<< 7)-1), IVec2(-(1<<15), (1<<15)-1), IVec2(0x80000000, 0x7fffffff) }; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST); DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges))); return ranges[precision]; } static UVec2 getUintRange (glu::Precision precision) { static const UVec2 ranges[] = { UVec2(0, (1<< 8)-1), UVec2(0, (1<<16)-1), UVec2(0, 0xffffffffu) }; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST); DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges))); return ranges[precision]; } static inline Vec4 readVec4 (const float* ptr, int numComponents) { DE_ASSERT(numComponents >= 1); return Vec4(ptr[0], numComponents >= 2 ? ptr[1] : 0.0f, numComponents >= 3 ? ptr[2] : 0.0f, numComponents >= 4 ? ptr[3] : 0.0f); } static inline IVec4 readIVec4 (const int* ptr, int numComponents) { DE_ASSERT(numComponents >= 1); return IVec4(ptr[0], numComponents >= 2 ? ptr[1] : 0, numComponents >= 3 ? ptr[2] : 0, numComponents >= 4 ? ptr[3] : 0); } static void renderFloatReference (const tcu::PixelBufferAccess& dst, int gridWidth, int gridHeight, int numComponents, const float* vertices) { const bool isSRGB = tcu::isSRGB(dst.getFormat()); const float cellW = (float)dst.getWidth() / (float)(gridWidth-1); const float cellH = (float)dst.getHeight() / (float)(gridHeight-1); for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { const int cellX = de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth-2); const int cellY = de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight-2); const float xf = ((float)x - (float)cellX*cellW + 0.5f) / cellW; const float yf = ((float)y - (float)cellY*cellH + 0.5f) / cellH; const Vec4 v00 = readVec4(vertices + ((cellY+0)*gridWidth + cellX+0)*numComponents, numComponents); const Vec4 v01 = readVec4(vertices + ((cellY+1)*gridWidth + cellX+0)*numComponents, numComponents); const Vec4 v10 = readVec4(vertices + ((cellY+0)*gridWidth + cellX+1)*numComponents, numComponents); const Vec4 v11 = readVec4(vertices + ((cellY+1)*gridWidth + cellX+1)*numComponents, numComponents); const bool tri = xf + yf >= 1.0f; const Vec4& v0 = tri ? v11 : v00; const Vec4& v1 = tri ? v01 : v10; const Vec4& v2 = tri ? v10 : v01; const float s = tri ? 1.0f-xf : xf; const float t = tri ? 1.0f-yf : yf; const Vec4 color = v0 + (v1-v0)*s + (v2-v0)*t; dst.setPixel(isSRGB ? tcu::linearToSRGB(color) : color, x, y); } } } static void renderIntReference (const tcu::PixelBufferAccess& dst, int gridWidth, int gridHeight, int numComponents, const int* vertices) { float cellW = (float)dst.getWidth() / (float)(gridWidth-1); float cellH = (float)dst.getHeight() / (float)(gridHeight-1); for (int y = 0; y < dst.getHeight(); y++) { for (int x = 0; x < dst.getWidth(); x++) { int cellX = de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth-2); int cellY = de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight-2); IVec4 c = readIVec4(vertices + (cellY*gridWidth + cellX+1)*numComponents, numComponents); dst.setPixel(c, x, y); } } } static const IVec4 s_swizzles[] = { IVec4(0,1,2,3), IVec4(1,2,3,0), IVec4(2,3,0,1), IVec4(3,0,1,2), IVec4(3,2,1,0), IVec4(2,1,0,3), IVec4(1,0,3,2), IVec4(0,3,2,1) }; template <typename T> inline tcu::Vector<T, 4> swizzleVec (const tcu::Vector<T, 4>& vec, int swzNdx) { const IVec4& swz = s_swizzles[swzNdx % DE_LENGTH_OF_ARRAY(s_swizzles)]; return vec.swizzle(swz[0], swz[1], swz[2], swz[3]); } namespace { struct AttachmentData { tcu::TextureFormat format; //!< Actual format of attachment. tcu::TextureFormat referenceFormat; //!< Used for reference rendering. tcu::TextureFormat readFormat; int numWrittenChannels; glu::Precision outPrecision; vector<deUint8> renderedData; vector<deUint8> referenceData; }; template<typename Type> string valueRangeToString (int numValidChannels, const tcu::Vector<Type, 4>& minValue, const tcu::Vector<Type, 4>& maxValue) { std::ostringstream stream; stream << "("; for (int i = 0; i < 4; i++) { if (i != 0) stream << ", "; if (i < numValidChannels) stream << minValue[i] << " -> " << maxValue[i]; else stream << "Undef"; } stream << ")"; return stream.str(); } void clearUndefined (const tcu::PixelBufferAccess& access, int numValidChannels) { for (int y = 0; y < access.getHeight(); y++) for (int x = 0; x < access.getWidth(); x++) { switch (tcu::getTextureChannelClass(access.getFormat().type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: { const Vec4 srcPixel = access.getPixel(x, y); Vec4 dstPixel (0.0f, 0.0f, 0.0f, 1.0f); for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++) dstPixel[channelNdx] = srcPixel[channelNdx]; access.setPixel(dstPixel, x, y); break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { const IVec4 bitDepth = tcu::getTextureFormatBitDepth(access.getFormat()); const IVec4 srcPixel = access.getPixelInt(x, y); IVec4 dstPixel (0, 0, 0, (0x1u << (deUint64)bitDepth.w()) - 1); for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++) dstPixel[channelNdx] = srcPixel[channelNdx]; access.setPixel(dstPixel, x, y); break; } default: DE_ASSERT(false); } } } } // anonymous FragmentOutputCase::IterateResult FragmentOutputCase::iterate (void) { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // Compute grid size & index list. const int minCellSize = 8; const IVec2 minBufSize = getMinSize(m_fboSpec); const int gridWidth = de::clamp(minBufSize.x()/minCellSize, 1, 255)+1; const int gridHeight = de::clamp(minBufSize.y()/minCellSize, 1, 255)+1; const int numVertices = gridWidth*gridHeight; const int numQuads = (gridWidth-1)*(gridHeight-1); const int numIndices = numQuads*6; const int numInputVecs = getNumInputVectors(m_outputs); vector<vector<deUint32> > inputs (numInputVecs); vector<float> positions (numVertices*4); vector<deUint16> indices (numIndices); const int readAlignment = 4; const int viewportW = minBufSize.x(); const int viewportH = minBufSize.y(); const int numAttachments = (int)m_fboSpec.size(); vector<deUint32> drawBuffers (numAttachments); vector<AttachmentData> attachments (numAttachments); // Initialize attachment data. for (int ndx = 0; ndx < numAttachments; ndx++) { const tcu::TextureFormat texFmt = glu::mapGLInternalFormat(m_fboSpec[ndx].format); const tcu::TextureChannelClass chnClass = tcu::getTextureChannelClass(texFmt.type); const bool isFixedPoint = chnClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || chnClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT; // \note Fixed-point formats use float reference to enable more accurate result verification. const tcu::TextureFormat refFmt = isFixedPoint ? tcu::TextureFormat(texFmt.order, tcu::TextureFormat::FLOAT) : texFmt; const tcu::TextureFormat readFmt = getFramebufferReadFormat(texFmt); const int attachmentW = m_fboSpec[ndx].width; const int attachmentH = m_fboSpec[ndx].height; drawBuffers[ndx] = GL_COLOR_ATTACHMENT0+ndx; attachments[ndx].format = texFmt; attachments[ndx].readFormat = readFmt; attachments[ndx].referenceFormat = refFmt; attachments[ndx].renderedData.resize(readFmt.getPixelSize()*attachmentW*attachmentH); attachments[ndx].referenceData.resize(refFmt.getPixelSize()*attachmentW*attachmentH); } // Initialize indices. for (int quadNdx = 0; quadNdx < numQuads; quadNdx++) { int quadY = quadNdx / (gridWidth-1); int quadX = quadNdx - quadY*(gridWidth-1); indices[quadNdx*6+0] = quadX + quadY*gridWidth; indices[quadNdx*6+1] = quadX + (quadY+1)*gridWidth; indices[quadNdx*6+2] = quadX + quadY*gridWidth + 1; indices[quadNdx*6+3] = indices[quadNdx*6+1]; indices[quadNdx*6+4] = quadX + (quadY+1)*gridWidth + 1; indices[quadNdx*6+5] = indices[quadNdx*6+2]; } for (int y = 0; y < gridHeight; y++) { for (int x = 0; x < gridWidth; x++) { float xf = (float)x / (float)(gridWidth-1); float yf = (float)y / (float)(gridHeight-1); positions[(y*gridWidth + x)*4 + 0] = 2.0f*xf - 1.0f; positions[(y*gridWidth + x)*4 + 1] = 2.0f*yf - 1.0f; positions[(y*gridWidth + x)*4 + 2] = 0.0f; positions[(y*gridWidth + x)*4 + 3] = 1.0f; } } // Initialize input vectors. { int curInVec = 0; for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++) { const FragmentOutput& output = m_outputs[outputNdx]; bool isFloat = glu::isDataTypeFloatOrVec(output.type); bool isInt = glu::isDataTypeIntOrIVec(output.type); bool isUint = glu::isDataTypeUintOrUVec(output.type); int numVecs = output.arrayLength > 0 ? output.arrayLength : 1; int numScalars = glu::getDataTypeScalarSize(output.type); for (int vecNdx = 0; vecNdx < numVecs; vecNdx++) { inputs[curInVec].resize(numVertices*numScalars); // Record how many outputs are written in attachment. DE_ASSERT(output.location+vecNdx < (int)attachments.size()); attachments[output.location+vecNdx].numWrittenChannels = numScalars; attachments[output.location+vecNdx].outPrecision = output.precision; if (isFloat) { Vec2 range = getFloatRange(output.precision); Vec4 minVal (range.x()); Vec4 maxVal (range.y()); float* dst = (float*)&inputs[curInVec][0]; if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size())) { // \note Floating-point precision conversion is not well-defined. For that reason we must // limit value range to intersection of both data type and render target value ranges. const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(attachments[output.location+vecNdx].format); minVal = tcu::max(minVal, fmtInfo.valueMin); maxVal = tcu::min(maxVal, fmtInfo.valueMax); } m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: " << valueRangeToString(numScalars, minVal, maxVal) << TestLog::EndMessage; for (int y = 0; y < gridHeight; y++) { for (int x = 0; x < gridWidth; x++) { float xf = (float)x / (float)(gridWidth-1); float yf = (float)y / (float)(gridHeight-1); float f0 = (xf + yf) * 0.5f; float f1 = 0.5f + (xf - yf) * 0.5f; Vec4 f = swizzleVec(Vec4(f0, f1, 1.0f-f0, 1.0f-f1), curInVec); Vec4 c = minVal + (maxVal-minVal)*f; float* v = dst + (y*gridWidth + x)*numScalars; for (int ndx = 0; ndx < numScalars; ndx++) v[ndx] = c[ndx]; } } } else if (isInt) { const IVec2 range = getIntRange(output.precision); IVec4 minVal (range.x()); IVec4 maxVal (range.y()); if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size())) { // Limit to range of output format as conversion mode is not specified. const IVec4 fmtBits = tcu::getTextureFormatBitDepth(attachments[output.location+vecNdx].format); const BVec4 isZero = lessThanEqual(fmtBits, IVec4(0)); const IVec4 fmtMinVal = (-(tcu::Vector<deInt64, 4>(1) << (fmtBits-1).cast<deInt64>())).asInt(); const IVec4 fmtMaxVal = ((tcu::Vector<deInt64, 4>(1) << (fmtBits-1).cast<deInt64>())-deInt64(1)).asInt(); minVal = select(minVal, tcu::max(minVal, fmtMinVal), isZero); maxVal = select(maxVal, tcu::min(maxVal, fmtMaxVal), isZero); } m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: " << valueRangeToString(numScalars, minVal, maxVal) << TestLog::EndMessage; const IVec4 rangeDiv = swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight)-1), curInVec); const IVec4 step = ((maxVal.cast<deInt64>() - minVal.cast<deInt64>()) / (rangeDiv.cast<deInt64>())).asInt(); deInt32* dst = (deInt32*)&inputs[curInVec][0]; for (int y = 0; y < gridHeight; y++) { for (int x = 0; x < gridWidth; x++) { int ix = gridWidth - x - 1; int iy = gridHeight - y - 1; IVec4 c = minVal + step*swizzleVec(IVec4(x, y, ix, iy), curInVec); deInt32* v = dst + (y*gridWidth + x)*numScalars; DE_ASSERT(boolAll(logicalAnd(greaterThanEqual(c, minVal), lessThanEqual(c, maxVal)))); for (int ndx = 0; ndx < numScalars; ndx++) v[ndx] = c[ndx]; } } } else if (isUint) { const UVec2 range = getUintRange(output.precision); UVec4 maxVal (range.y()); if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size())) { // Limit to range of output format as conversion mode is not specified. const IVec4 fmtBits = tcu::getTextureFormatBitDepth(attachments[output.location+vecNdx].format); const UVec4 fmtMaxVal = ((tcu::Vector<deUint64, 4>(1) << fmtBits.cast<deUint64>())-deUint64(1)).asUint(); maxVal = tcu::min(maxVal, fmtMaxVal); } m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: " << valueRangeToString(numScalars, UVec4(0), maxVal) << TestLog::EndMessage; const IVec4 rangeDiv = swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight)-1), curInVec); const UVec4 step = maxVal / rangeDiv.asUint(); deUint32* dst = &inputs[curInVec][0]; DE_ASSERT(range.x() == 0); for (int y = 0; y < gridHeight; y++) { for (int x = 0; x < gridWidth; x++) { int ix = gridWidth - x - 1; int iy = gridHeight - y - 1; UVec4 c = step*swizzleVec(IVec4(x, y, ix, iy).asUint(), curInVec); deUint32* v = dst + (y*gridWidth + x)*numScalars; DE_ASSERT(boolAll(lessThanEqual(c, maxVal))); for (int ndx = 0; ndx < numScalars; ndx++) v[ndx] = c[ndx]; } } } else DE_ASSERT(false); curInVec += 1; } } } // Render using gl. gl.useProgram(m_program->getProgram()); gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); gl.viewport(0, 0, viewportW, viewportH); gl.drawBuffers((int)drawBuffers.size(), &drawBuffers[0]); gl.disable(GL_DITHER); // Dithering causes issues with unorm formats. Those issues could be worked around in threshold, but it makes validation less accurate. GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup"); { int curInVec = 0; for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++) { const FragmentOutput& output = m_outputs[outputNdx]; bool isArray = output.arrayLength > 0; bool isFloat = glu::isDataTypeFloatOrVec(output.type); bool isInt = glu::isDataTypeIntOrIVec(output.type); bool isUint = glu::isDataTypeUintOrUVec(output.type); int scalarSize = glu::getDataTypeScalarSize(output.type); deUint32 glScalarType = isFloat ? GL_FLOAT : isInt ? GL_INT : isUint ? GL_UNSIGNED_INT : GL_NONE; int numVecs = isArray ? output.arrayLength : 1; for (int vecNdx = 0; vecNdx < numVecs; vecNdx++) { string name = string("in") + de::toString(outputNdx) + (isArray ? string("_") + de::toString(vecNdx) : string()); int loc = gl.getAttribLocation(m_program->getProgram(), name.c_str()); if (loc >= 0) { gl.enableVertexAttribArray(loc); if (isFloat) gl.vertexAttribPointer(loc, scalarSize, glScalarType, GL_FALSE, 0, &inputs[curInVec][0]); else gl.vertexAttribIPointer(loc, scalarSize, glScalarType, 0, &inputs[curInVec][0]); } else log << TestLog::Message << "Warning: No location for attribute '" << name << "' found." << TestLog::EndMessage; curInVec += 1; } } } { int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position"); TCU_CHECK(posLoc >= 0); gl.enableVertexAttribArray(posLoc); gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &positions[0]); } GLU_EXPECT_NO_ERROR(gl.getError(), "After attribute setup"); gl.drawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, &indices[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements"); // Read all attachment points. for (int ndx = 0; ndx < numAttachments; ndx++) { const glu::TransferFormat transferFmt = glu::getTransferFormat(attachments[ndx].readFormat); void* dst = &attachments[ndx].renderedData[0]; const int attachmentW = m_fboSpec[ndx].width; const int attachmentH = m_fboSpec[ndx].height; const int numValidChannels = attachments[ndx].numWrittenChannels; const tcu::PixelBufferAccess rendered (attachments[ndx].readFormat, attachmentW, attachmentH, 1, deAlign32(attachments[ndx].readFormat.getPixelSize()*attachmentW, readAlignment), 0, &attachments[ndx].renderedData[0]); gl.readBuffer(GL_COLOR_ATTACHMENT0+ndx); gl.readPixels(0, 0, minBufSize.x(), minBufSize.y(), transferFmt.format, transferFmt.dataType, dst); clearUndefined(rendered, numValidChannels); } // Render reference images. { int curInNdx = 0; for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++) { const FragmentOutput& output = m_outputs[outputNdx]; const bool isArray = output.arrayLength > 0; const bool isFloat = glu::isDataTypeFloatOrVec(output.type); const bool isInt = glu::isDataTypeIntOrIVec(output.type); const bool isUint = glu::isDataTypeUintOrUVec(output.type); const int scalarSize = glu::getDataTypeScalarSize(output.type); const int numVecs = isArray ? output.arrayLength : 1; for (int vecNdx = 0; vecNdx < numVecs; vecNdx++) { const int location = output.location+vecNdx; const void* inputData = &inputs[curInNdx][0]; DE_ASSERT(de::inBounds(location, 0, (int)m_fboSpec.size())); const int bufW = m_fboSpec[location].width; const int bufH = m_fboSpec[location].height; const tcu::PixelBufferAccess buf (attachments[location].referenceFormat, bufW, bufH, 1, &attachments[location].referenceData[0]); const tcu::PixelBufferAccess viewportBuf = getSubregion(buf, 0, 0, 0, viewportW, viewportH, 1); if (isInt || isUint) renderIntReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const int*)inputData); else if (isFloat) renderFloatReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const float*)inputData); else DE_ASSERT(false); curInNdx += 1; } } } // Compare all images. bool allLevelsOk = true; for (int attachNdx = 0; attachNdx < numAttachments; attachNdx++) { const int attachmentW = m_fboSpec[attachNdx].width; const int attachmentH = m_fboSpec[attachNdx].height; const int numValidChannels = attachments[attachNdx].numWrittenChannels; const tcu::BVec4 cmpMask (numValidChannels >= 1, numValidChannels >= 2, numValidChannels >= 3, numValidChannels >= 4); const glu::Precision outPrecision = attachments[attachNdx].outPrecision; const tcu::TextureFormat& format = attachments[attachNdx].format; tcu::ConstPixelBufferAccess rendered (attachments[attachNdx].readFormat, attachmentW, attachmentH, 1, deAlign32(attachments[attachNdx].readFormat.getPixelSize()*attachmentW, readAlignment), 0, &attachments[attachNdx].renderedData[0]); tcu::ConstPixelBufferAccess reference (attachments[attachNdx].referenceFormat, attachmentW, attachmentH, 1, &attachments[attachNdx].referenceData[0]); tcu::TextureChannelClass texClass = tcu::getTextureChannelClass(format.type); bool isOk = true; const string name = string("Attachment") + de::toString(attachNdx); const string desc = string("Color attachment ") + de::toString(attachNdx); log << TestLog::Message << "Attachment " << attachNdx << ": " << numValidChannels << " channels have defined values and used for comparison" << TestLog::EndMessage; switch (texClass) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: { const deUint32 interpThreshold = 4; //!< 4 ULP interpolation threshold (interpolation always in highp) deUint32 outTypeThreshold = 0; //!< Threshold based on output type UVec4 formatThreshold; //!< Threshold computed based on format. UVec4 finalThreshold; // 1 ULP rounding error is allowed for smaller floating-point formats switch (format.type) { case tcu::TextureFormat::FLOAT: formatThreshold = UVec4(0); break; case tcu::TextureFormat::HALF_FLOAT: formatThreshold = UVec4((1<<(23-10))); break; case tcu::TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: formatThreshold = UVec4((1<<(23-6)), (1<<(23-6)), (1<<(23-5)), 0); break; default: DE_ASSERT(false); break; } // 1 ULP rounding error for highp -> output precision cast switch (outPrecision) { case glu::PRECISION_LOWP: outTypeThreshold = (1<<(23-8)); break; case glu::PRECISION_MEDIUMP: outTypeThreshold = (1<<(23-10)); break; case glu::PRECISION_HIGHP: outTypeThreshold = 0; break; default: DE_ASSERT(false); } finalThreshold = select(max(formatThreshold, UVec4(deMax32(interpThreshold, outTypeThreshold))), UVec4(~0u), cmpMask); isOk = tcu::floatUlpThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, finalThreshold, tcu::COMPARE_LOG_RESULT); break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { // \note glReadPixels() allows only 8 bits to be read. This means that RGB10_A2 will loose some // bits in the process and it must be taken into account when computing threshold. const IVec4 bits = min(IVec4(8), tcu::getTextureFormatBitDepth(format)); const Vec4 baseThreshold = 1.0f / ((IVec4(1) << bits)-1).asFloat(); const Vec4 threshold = select(baseThreshold, Vec4(2.0f), cmpMask); isOk = tcu::floatThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const tcu::UVec4 threshold = select(UVec4(0u), UVec4(~0u), cmpMask); isOk = tcu::intThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); break; } default: TCU_FAIL("Unsupported comparison"); break; } if (!isOk) allLevelsOk = false; } m_testCtx.setTestResult(allLevelsOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, allLevelsOk ? "Pass" : "Image comparison failed"); return STOP; } FragmentOutputTests::FragmentOutputTests (Context& context) : TestCaseGroup(context, "fragment_out", "Fragment output tests") { } FragmentOutputTests::~FragmentOutputTests (void) { } static FragmentOutputCase* createRandomCase (Context& context, int minRenderTargets, int maxRenderTargets, deUint32 seed) { static const glu::DataType outputTypes[] = { glu::TYPE_FLOAT, glu::TYPE_FLOAT_VEC2, glu::TYPE_FLOAT_VEC3, glu::TYPE_FLOAT_VEC4, glu::TYPE_INT, glu::TYPE_INT_VEC2, glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, glu::TYPE_UINT, glu::TYPE_UINT_VEC2, glu::TYPE_UINT_VEC3, glu::TYPE_UINT_VEC4 }; static const glu::Precision precisions[] = { glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP }; static const deUint32 floatFormats[] = { GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F, GL_R32F, GL_R16F, GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGBA4, GL_RGB5_A1, GL_RGB8, GL_RGB565, GL_RG8, GL_R8 }; static const deUint32 intFormats[] = { GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I, GL_RG8I, GL_R32I, GL_R16I, GL_R8I }; static const deUint32 uintFormats[] = { GL_RGBA32UI, GL_RGBA16UI, GL_RGBA8UI, GL_RGB10_A2UI, GL_RG32UI, GL_RG16UI, GL_RG8UI, GL_R32UI, GL_R16UI, GL_R8UI }; de::Random rnd (seed); vector<FragmentOutput> outputs; vector<BufferSpec> targets; vector<glu::DataType> outTypes; int numTargets = rnd.getInt(minRenderTargets, maxRenderTargets); const int width = 128; // \todo [2012-04-10 pyry] Separate randomized sizes per target? const int height = 64; const int samples = 0; // Compute outputs. int curLoc = 0; while (curLoc < numTargets) { bool useArray = rnd.getFloat() < 0.3f; int maxArrayLen = numTargets-curLoc; int arrayLen = useArray ? rnd.getInt(1, maxArrayLen) : 0; glu::DataType basicType = rnd.choose<glu::DataType>(&outputTypes[0], &outputTypes[0] + DE_LENGTH_OF_ARRAY(outputTypes)); glu::Precision precision = rnd.choose<glu::Precision>(&precisions[0], &precisions[0] + DE_LENGTH_OF_ARRAY(precisions)); int numLocations = useArray ? arrayLen : 1; outputs.push_back(FragmentOutput(basicType, precision, curLoc, arrayLen)); for (int ndx = 0; ndx < numLocations; ndx++) outTypes.push_back(basicType); curLoc += numLocations; } DE_ASSERT(curLoc == numTargets); DE_ASSERT((int)outTypes.size() == numTargets); // Compute buffers. while ((int)targets.size() < numTargets) { glu::DataType outType = outTypes[targets.size()]; bool isFloat = glu::isDataTypeFloatOrVec(outType); bool isInt = glu::isDataTypeIntOrIVec(outType); bool isUint = glu::isDataTypeUintOrUVec(outType); deUint32 format = 0; if (isFloat) format = rnd.choose<deUint32>(&floatFormats[0], &floatFormats[0] + DE_LENGTH_OF_ARRAY(floatFormats)); else if (isInt) format = rnd.choose<deUint32>(&intFormats[0], &intFormats[0] + DE_LENGTH_OF_ARRAY(intFormats)); else if (isUint) format = rnd.choose<deUint32>(&uintFormats[0], &uintFormats[0] + DE_LENGTH_OF_ARRAY(uintFormats)); else DE_ASSERT(false); targets.push_back(BufferSpec(format, width, height, samples)); } return new FragmentOutputCase(context, de::toString(seed).c_str(), "", targets, outputs); } void FragmentOutputTests::init (void) { static const deUint32 requiredFloatFormats[] = { GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F, GL_R32F, GL_R16F }; static const deUint32 requiredFixedFormats[] = { GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGBA4, GL_RGB5_A1, GL_RGB8, GL_RGB565, GL_RG8, GL_R8 }; static const deUint32 requiredIntFormats[] = { GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I, GL_RG8I, GL_R32I, GL_R16I, GL_R8I }; static const deUint32 requiredUintFormats[] = { GL_RGBA32UI, GL_RGBA16UI, GL_RGBA8UI, GL_RGB10_A2UI, GL_RG32UI, GL_RG16UI, GL_RG8UI, GL_R32UI, GL_R16UI, GL_R8UI }; static const glu::Precision precisions[] = { glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP }; // .basic. { tcu::TestCaseGroup* basicGroup = new tcu::TestCaseGroup(m_testCtx, "basic", "Basic fragment output tests"); addChild(basicGroup); const int width = 64; const int height = 64; const int samples = 0; // .float tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests"); basicGroup->addChild(floatGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++) { deUint32 format = requiredFloatFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0)).toVec())); } } // .fixed tcu::TestCaseGroup* fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests"); basicGroup->addChild(fixedGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++) { deUint32 format = requiredFixedFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0)).toVec())); } } // .int tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests"); basicGroup->addChild(intGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++) { deUint32 format = requiredIntFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_int").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT, prec, 0)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2, prec, 0)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3, prec, 0)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4, prec, 0)).toVec())); } } // .uint tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests"); basicGroup->addChild(uintGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++) { deUint32 format = requiredUintFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uint").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT, prec, 0)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2, prec, 0)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3, prec, 0)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4, prec, 0)).toVec())); } } } // .array { tcu::TestCaseGroup* arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Array outputs"); addChild(arrayGroup); const int width = 64; const int height = 64; const int samples = 0; const int numTargets = 3; // .float tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests"); arrayGroup->addChild(floatGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++) { deUint32 format = requiredFloatFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; for (int ndx = 0; ndx < numTargets; ndx++) fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0, numTargets)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0, numTargets)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0, numTargets)).toVec())); floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0, numTargets)).toVec())); } } // .fixed tcu::TestCaseGroup* fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests"); arrayGroup->addChild(fixedGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++) { deUint32 format = requiredFixedFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; for (int ndx = 0; ndx < numTargets; ndx++) fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT, prec, 0, numTargets)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2, prec, 0, numTargets)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3, prec, 0, numTargets)).toVec())); fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4, prec, 0, numTargets)).toVec())); } } // .int tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests"); arrayGroup->addChild(intGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++) { deUint32 format = requiredIntFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; for (int ndx = 0; ndx < numTargets; ndx++) fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_int").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT, prec, 0, numTargets)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2, prec, 0, numTargets)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3, prec, 0, numTargets)).toVec())); intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4, prec, 0, numTargets)).toVec())); } } // .uint tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests"); arrayGroup->addChild(uintGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++) { deUint32 format = requiredUintFormats[fmtNdx]; string fmtName = getFormatName(format); vector<BufferSpec> fboSpec; for (int ndx = 0; ndx < numTargets; ndx++) fboSpec.push_back(BufferSpec(format, width, height, samples)); for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) { glu::Precision prec = precisions[precNdx]; string precName = glu::getPrecisionName(prec); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uint").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT, prec, 0, numTargets)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec2").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2, prec, 0, numTargets)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec3").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3, prec, 0, numTargets)).toVec())); uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec4").c_str(), "", fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4, prec, 0, numTargets)).toVec())); } } } // .random { tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Random fragment output cases"); addChild(randomGroup); for (deUint32 seed = 0; seed < 100; seed++) randomGroup->addChild(createRandomCase(m_context, 2, 4, seed)); } } } // Functional } // gles3 } // deqp