| /*------------------------------------------------------------------------- |
| * 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 FboTestUtil::getFormatName; |
| using FboTestUtil::getFramebufferReadFormat; |
| using std::string; |
| using std::vector; |
| using tcu::BVec4; |
| using tcu::IVec2; |
| using tcu::IVec4; |
| using tcu::TestLog; |
| using tcu::UVec2; |
| using tcu::UVec4; |
| using tcu::Vec2; |
| using tcu::Vec3; |
| using tcu::Vec4; |
| |
| struct BufferSpec |
| { |
| BufferSpec(void) : format(GL_NONE), width(0), height(0), samples(0) |
| { |
| } |
| |
| BufferSpec(uint32_t format_, int width_, int height_, int samples_) |
| : format(format_) |
| , width(width_) |
| , height(height_) |
| , samples(samples_) |
| { |
| } |
| |
| uint32_t 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; |
| uint32_t m_framebuffer; |
| vector<uint32_t> 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::getTextureFormatStr(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++) |
| { |
| uint32_t rbo = m_renderbuffers[bufNdx]; |
| const BufferSpec &bufSpec = m_fboSpec[bufNdx]; |
| uint32_t 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"); |
| |
| uint32_t 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<uint8_t> renderedData; |
| vector<uint8_t> 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, (int)(((int64_t)0x1u << bitDepth.w()) - 1)); |
| |
| for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++) |
| dstPixel[channelNdx] = srcPixel[channelNdx]; |
| |
| access.setPixel(dstPixel, x, y); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| 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<uint32_t>> inputs(numInputVecs); |
| vector<float> positions(numVertices * 4); |
| vector<uint16_t> indices(numIndices); |
| |
| const int readAlignment = 4; |
| const int viewportW = minBufSize.x(); |
| const int viewportH = minBufSize.y(); |
| const int numAttachments = (int)m_fboSpec.size(); |
| |
| vector<uint32_t> 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] = uint16_t(quadX + quadY * gridWidth); |
| indices[quadNdx * 6 + 1] = uint16_t(quadX + (quadY + 1) * gridWidth); |
| indices[quadNdx * 6 + 2] = uint16_t(quadX + quadY * gridWidth + 1); |
| indices[quadNdx * 6 + 3] = indices[quadNdx * 6 + 1]; |
| indices[quadNdx * 6 + 4] = uint16_t(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 shift = tcu::clamp(fmtBits - 1, tcu::IVec4(0), tcu::IVec4(256)); |
| const IVec4 fmtMinVal = (-(tcu::Vector<int64_t, 4>(1) << shift.cast<int64_t>())).asInt(); |
| const IVec4 fmtMaxVal = |
| ((tcu::Vector<int64_t, 4>(1) << shift.cast<int64_t>()) - int64_t(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<int64_t>() - minVal.cast<int64_t>()) / (rangeDiv.cast<int64_t>())).asInt(); |
| int32_t *dst = (int32_t *)&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); |
| int32_t *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<uint64_t, 4>(1) << fmtBits.cast<uint64_t>()) - uint64_t(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(); |
| uint32_t *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); |
| uint32_t *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); |
| uint32_t 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 uint32_t interpThreshold = 4; //!< 4 ULP interpolation threshold (interpolation always in highp) |
| uint32_t 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"); |
| } |
| |
| 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, uint32_t 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 uint32_t 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 uint32_t intFormats[] = {GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I, |
| GL_RG8I, GL_R32I, GL_R16I, GL_R8I}; |
| static const uint32_t 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); |
| uint32_t format = 0; |
| |
| if (isFloat) |
| format = rnd.choose<uint32_t>(&floatFormats[0], &floatFormats[0] + DE_LENGTH_OF_ARRAY(floatFormats)); |
| else if (isInt) |
| format = rnd.choose<uint32_t>(&intFormats[0], &intFormats[0] + DE_LENGTH_OF_ARRAY(intFormats)); |
| else if (isUint) |
| format = rnd.choose<uint32_t>(&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 uint32_t requiredFloatFormats[] = {GL_RGBA32F, GL_RGBA16F, GL_R11F_G11F_B10F, GL_RG32F, GL_RG16F, |
| GL_R32F, GL_R16F}; |
| static const uint32_t requiredFixedFormats[] = {GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGBA4, GL_RGB5_A1, |
| GL_RGB8, GL_RGB565, GL_RG8, GL_R8}; |
| static const uint32_t requiredIntFormats[] = {GL_RGBA32I, GL_RGBA16I, GL_RGBA8I, GL_RG32I, GL_RG16I, |
| GL_RG8I, GL_R32I, GL_R16I, GL_R8I}; |
| static const uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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++) |
| { |
| uint32_t 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 (uint32_t seed = 0; seed < 100; seed++) |
| randomGroup->addChild(createRandomCase(m_context, 2, 4, seed)); |
| } |
| } |
| |
| } // namespace Functional |
| } // namespace gles3 |
| } // namespace deqp |