/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2017 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 FBO sRGB tests. *//*--------------------------------------------------------------------*/ #include "es31fFboSRGBWriteControlTests.hpp" #include "es31fFboTestUtil.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "tcuTestLog.hpp" #include "glwEnums.hpp" #include "sglrContextUtil.hpp" #include "glwFunctions.hpp" #include "deUniquePtr.hpp" #include "deSharedPtr.hpp" #include "gluObjectWrapper.hpp" #include "gluPixelTransfer.hpp" #include "glsTextureTestUtil.hpp" #include "tcuVectorUtil.hpp" #include "gluStrUtil.hpp" namespace deqp { namespace gles31 { namespace Functional { namespace { tcu::Vec4 getTestColorLinear (void) { return tcu::Vec4(0.2f, 0.3f, 0.4f, 1.0f); } tcu::Vec4 getTestColorSRGB (void) { return linearToSRGB(tcu::Vec4(0.2f, 0.3f, 0.4f, 1.0f)); } tcu::Vec4 getTestColorBlank (void) { return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); } tcu::Vec4 getEpsilonError (void) { return tcu::Vec4(0.005f); } enum QueryType { QUERYTYPE_ISENABLED = 0, QUERYTYPE_BOOLEAN, QUERYTYPE_FLOAT, QUERYTYPE_INT, QUERYTYPE_INT64, QUERYTYPE_LAST }; enum DataType { DATATYPE_BOOLEAN = 0, DATATYPE_FLOAT, DATATYPE_INT, DATATYPE_INT64, }; enum FramebufferSRGB { FRAMEBUFFERSRGB_ENABLED = 0, FRAMEBUFFERSRGB_DISABLED }; enum FramebufferBlend { FRAMEBUFFERBLEND_ENABLED = 0, FRAMEBUFFERBLEND_DISABLED }; enum TextureSourcesType { TEXTURESOURCESTYPE_RGBA = 0, TEXTURESOURCESTYPE_SRGBA, TEXTURESOURCESTYPE_BOTH, TEXTURESOURCESTYPE_NONE }; enum FboType { FBOTYPE_SOURCE = 0, FBOTYPE_DESTINATION }; enum RendererTask { RENDERERTASK_DRAW = 0, RENDERERTASK_COPY }; enum SamplingType { SAMPLINGTYPE_TEXTURE = 0, SAMPLINGTYPE_TEXTURE_LOD, SAMPLINGTYPE_TEXTURE_GRAD, SAMPLINGTYPE_TEXTURE_OFFSET, SAMPLINGTYPE_TEXTURE_PROJ, }; namespace TestTextureSizes { const int WIDTH = 128; const int HEIGHT = 128; } // global test texture sizes namespace SampligTypeCount { const int MAX = 5; } // global max number of sampling types std::string buildSamplingPassType (const int samplerTotal) { std::ostringstream shaderFragment; const SamplingType samplingTypeList [] = { SAMPLINGTYPE_TEXTURE, SAMPLINGTYPE_TEXTURE_LOD, SAMPLINGTYPE_TEXTURE_GRAD, SAMPLINGTYPE_TEXTURE_OFFSET, SAMPLINGTYPE_TEXTURE_PROJ } ; for (int samplerTypeIdx = 0; samplerTypeIdx < DE_LENGTH_OF_ARRAY(samplingTypeList); samplerTypeIdx++) { shaderFragment << " if (uFunctionType == " << samplerTypeIdx << ") \n" << " { \n"; for (int samplerIdx = 0; samplerIdx < samplerTotal; samplerIdx++) { switch (static_cast<SamplingType>(samplerTypeIdx)) { case SAMPLINGTYPE_TEXTURE: { shaderFragment << " texelColor" << samplerIdx << " = texture(uTexture" << samplerIdx << ", vs_aTexCoord); \n"; break; } case SAMPLINGTYPE_TEXTURE_LOD: { shaderFragment << " texelColor" << samplerIdx << " = textureLod(uTexture" << samplerIdx << ", vs_aTexCoord, 0.0f); \n"; break; } case SAMPLINGTYPE_TEXTURE_GRAD: { shaderFragment << " texelColor" << samplerIdx << " = textureGrad(uTexture" << samplerIdx << ", vs_aTexCoord, vec2(0.0f, 0.0f), vec2(0.0f, 0.0f)); \n"; break; } case SAMPLINGTYPE_TEXTURE_OFFSET: { shaderFragment << " texelColor" << samplerIdx << " = textureOffset(uTexture" << samplerIdx << ", vs_aTexCoord, ivec2(0.0f, 0.0f)); \n"; break; } case SAMPLINGTYPE_TEXTURE_PROJ: { shaderFragment << " texelColor" << samplerIdx << " = textureProj(uTexture" << samplerIdx << ", vec3(vs_aTexCoord, 1.0f)); \n"; break; } default: DE_FATAL("Error: sampling type unrecognised"); } } shaderFragment << " } \n"; } return shaderFragment.str(); } void logColor (Context& context, const std::string& colorLogMessage, const tcu::Vec4 resultColor) { tcu::TestLog& log = context.getTestContext().getLog(); std::ostringstream message; message << colorLogMessage << " = (" << resultColor.x() << ", " << resultColor.y() << ", " << resultColor.z() << ", " << resultColor.w() << ")"; log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; } struct TestFunction { explicit TestFunction (const bool hasFunctionValue) : hasFunction (hasFunctionValue) {} TestFunction (const char* const functionNameValue, const char* const functionDefinition) : hasFunction (true) , functionName (functionNameValue) , functionDefintion (functionDefinition) {} ~TestFunction (void) {} bool hasFunction; const char* functionName; const char* functionDefintion; }; TestFunction getFunctionBlendLinearToSRGBCheck (void) { const char* const functionName = "blendPlusLinearToSRGB"; const char* const functionDefinition = "mediump vec4 blendPlusLinearToSRGB(in mediump vec4 colorSrc, in mediump vec4 colorDst) \n" "{ \n" " const int MAX_VECTOR_SIZE = 4; \n" " mediump vec4 colorConverted; \n" " mediump vec4 colorBlended; \n" " for (int idx = 0; idx < MAX_VECTOR_SIZE; idx++) \n" " { \n" " if (uBlendFunctionType == 0) \n" " { \n" " colorBlended[idx] = (colorSrc[idx] * uFactorSrc) + colorDst[idx] * uFactorDst; \n" " } \n" " if (uBlendFunctionType == 1) \n" " { \n" " colorBlended[idx] = (colorSrc[idx] * uFactorSrc) - (colorDst[idx] * uFactorDst); \n" " } \n" "if (uBlendFunctionType == 2) \n" " { \n" " colorBlended[idx] = (colorDst[idx] * uFactorDst) - (colorSrc[idx] * uFactorSrc); \n" " } \n" " if (colorBlended[idx] < 0.0031308f) \n" " { \n" " colorConverted[idx] = 12.92f * colorBlended[idx]; \n" " } \n" " else \n" " { \n" " colorConverted[idx] = 1.055f * pow(colorBlended[idx], 0.41666f) - 0.055f; \n" " } \n" " } \n" " return colorConverted; \n" "} \n"; TestFunction testFunction(functionName, functionDefinition); return testFunction; } struct FBOConfig { FBOConfig (const deUint32 textureInternalFormatValue, const tcu::Vec4 textureColorValue, const deUint32 fboTargetTypeValue, const deUint32 fboColorAttachmentValue, const FboType fboTypeValue) : textureInternalFormat (textureInternalFormatValue) , textureColor (textureColorValue) , fboTargetType (fboTargetTypeValue) , fboColorAttachment (fboColorAttachmentValue) , fboType (fboTypeValue) {} ~FBOConfig (void) {} deUint32 textureInternalFormat; tcu::Vec4 textureColor; deUint32 fboTargetType; deUint32 fboColorAttachment; FboType fboType; }; struct BlendConfig { deUint32 equation; deUint32 funcSrc; deUint32 funcDst; }; std::vector<BlendConfig> getBlendingConfigList (void) { BlendConfig blendConfigs[12]; // add function permutations blendConfigs[0].equation = GL_FUNC_ADD; blendConfigs[1].equation = GL_FUNC_ADD; blendConfigs[2].equation = GL_FUNC_ADD; blendConfigs[3].equation = GL_FUNC_ADD; blendConfigs[0].funcSrc = GL_ONE; blendConfigs[0].funcDst = GL_ONE; blendConfigs[1].funcSrc = GL_ONE; blendConfigs[1].funcDst = GL_ZERO; blendConfigs[2].funcSrc = GL_ZERO; blendConfigs[2].funcDst = GL_ONE; blendConfigs[3].funcSrc = GL_ZERO; blendConfigs[3].funcDst = GL_ZERO; // subtract function permutations blendConfigs[4].equation = GL_FUNC_SUBTRACT; blendConfigs[5].equation = GL_FUNC_SUBTRACT; blendConfigs[6].equation = GL_FUNC_SUBTRACT; blendConfigs[7].equation = GL_FUNC_SUBTRACT; blendConfigs[4].funcSrc = GL_ONE; blendConfigs[4].funcDst = GL_ONE; blendConfigs[5].funcSrc = GL_ONE; blendConfigs[5].funcDst = GL_ZERO; blendConfigs[6].funcSrc = GL_ZERO; blendConfigs[6].funcDst = GL_ONE; blendConfigs[7].funcSrc = GL_ZERO; blendConfigs[7].funcDst = GL_ZERO; // reverse subtract function permutations blendConfigs[8].equation = GL_FUNC_REVERSE_SUBTRACT; blendConfigs[9].equation = GL_FUNC_REVERSE_SUBTRACT; blendConfigs[10].equation = GL_FUNC_REVERSE_SUBTRACT; blendConfigs[11].equation = GL_FUNC_REVERSE_SUBTRACT; blendConfigs[8].funcSrc = GL_ONE; blendConfigs[8].funcDst = GL_ONE; blendConfigs[9].funcSrc = GL_ONE; blendConfigs[9].funcDst = GL_ZERO; blendConfigs[10].funcSrc = GL_ZERO; blendConfigs[10].funcDst = GL_ONE; blendConfigs[11].funcSrc = GL_ZERO; blendConfigs[11].funcDst = GL_ZERO; std::vector<BlendConfig> configList(blendConfigs, blendConfigs + DE_LENGTH_OF_ARRAY(blendConfigs)); return configList; } struct TestRenderPassConfig { TestRenderPassConfig (void) : testFunction (false) {} TestRenderPassConfig (const TextureSourcesType textureSourcesTypeValue, FBOConfig fboConfigListValue, const FramebufferSRGB framebufferSRGBValue, const FramebufferBlend framebufferBendValue, const RendererTask rendererTaskValue) : textureSourcesType (textureSourcesTypeValue) , framebufferSRGB (framebufferSRGBValue) , frameBufferBlend (framebufferBendValue) , testFunction (false) , rendererTask (rendererTaskValue) {fboConfigList.push_back(fboConfigListValue);} TestRenderPassConfig (const TextureSourcesType textureSourcesTypeValue, FBOConfig fboConfigListValue, const FramebufferSRGB framebufferSRGBValue, const FramebufferBlend framebufferBendValue, TestFunction testFunctionValue, const RendererTask rendererTaskValue) : textureSourcesType (textureSourcesTypeValue) , framebufferSRGB (framebufferSRGBValue) , frameBufferBlend (framebufferBendValue) , testFunction (testFunctionValue) , rendererTask (rendererTaskValue) {fboConfigList.push_back(fboConfigListValue);} TestRenderPassConfig (const TextureSourcesType textureSourcesTypeValue, std::vector<FBOConfig> fboConfigListValue, const FramebufferSRGB framebufferSRGBValue, const FramebufferBlend framebufferBendValue, TestFunction testFunctionValue, const RendererTask rendererTaskValue) : textureSourcesType (textureSourcesTypeValue) , fboConfigList (fboConfigListValue) , framebufferSRGB (framebufferSRGBValue) , frameBufferBlend (framebufferBendValue) , testFunction (testFunctionValue) , rendererTask (rendererTaskValue) {} ~TestRenderPassConfig (void) {} TextureSourcesType textureSourcesType; std::vector<FBOConfig> fboConfigList; FramebufferSRGB framebufferSRGB; FramebufferBlend frameBufferBlend; TestFunction testFunction; RendererTask rendererTask; }; class TestVertexData { public: TestVertexData (Context& context); ~TestVertexData (void); void init (void); void bind (void) const; void unbind (void) const; private: const glw::Functions* m_gl; std::vector<float> m_data; glw::GLuint m_vboHandle; glw::GLuint m_vaoHandle; }; TestVertexData::TestVertexData (Context& context) : m_gl (&context.getRenderContext().getFunctions()) { const glw::GLfloat vertexData[] = { // position // texcoord -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left corner 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right corner 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top right corner -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top left corner 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top right corner -1.0f, -1.0f, 0.0f, 0.0f, 0.0f // bottom left corner }; m_data.resize(DE_LENGTH_OF_ARRAY(vertexData)); for (int idx = 0; idx < (int)m_data.size(); idx++) m_data[idx] = vertexData[idx]; m_gl->genVertexArrays(1, &m_vaoHandle); m_gl->bindVertexArray(m_vaoHandle); m_gl->genBuffers(1, &m_vboHandle); m_gl->bindBuffer(GL_ARRAY_BUFFER, m_vboHandle); m_gl->bufferData(GL_ARRAY_BUFFER, (glw::GLsizei)(m_data.size() * sizeof(glw::GLfloat)), &m_data[0], GL_STATIC_DRAW); m_gl->enableVertexAttribArray(0); m_gl->vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * (glw::GLsizei)sizeof(GL_FLOAT), (glw::GLvoid *)0); m_gl->enableVertexAttribArray(1); m_gl->vertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * (glw::GLsizei)sizeof(GL_FLOAT), (glw::GLvoid *)(3 * sizeof(GL_FLOAT))); m_gl->bindVertexArray(0); m_gl->bindBuffer(GL_ARRAY_BUFFER, 0); GLU_EXPECT_NO_ERROR(m_gl->getError(), "gl error during vertex data setup"); } TestVertexData::~TestVertexData (void) { m_gl->deleteBuffers(1, &m_vboHandle); m_gl->deleteVertexArrays(1, &m_vaoHandle); } void TestVertexData::bind (void) const { m_gl->bindVertexArray(m_vaoHandle); } void TestVertexData::unbind (void) const { m_gl->bindVertexArray(0); } class TestTexture2D { public: TestTexture2D (Context& context, const deUint32 internalFormatValue, const deUint32 transferFormatValue, const deUint32 transferTypeValue, const tcu::Vec4 imageColorValue); ~TestTexture2D (void); int getTextureUnit (void) const; deUint32 getHandle (void) const; void bind (const int textureUnit); void unbind (void) const; private: const glw::Functions* m_gl; glw::GLuint m_handle; const deUint32 m_internalFormat; tcu::TextureFormat m_transferFormat; int m_width; int m_height; tcu::TextureLevel m_imageData; int m_textureUnit; }; TestTexture2D::TestTexture2D (Context& context, const deUint32 internalFormat, const deUint32 transferFormat, const deUint32 transferType, const tcu::Vec4 imageColor) : m_gl (&context.getRenderContext().getFunctions()) , m_internalFormat (internalFormat) , m_transferFormat (tcu::TextureFormat(glu::mapGLTransferFormat(transferFormat, transferType))) , m_width (TestTextureSizes::WIDTH) , m_height (TestTextureSizes::HEIGHT) , m_imageData (tcu::TextureLevel(glu::mapGLInternalFormat(internalFormat), m_width, m_height, 1)) { // fill image data with a solid test color tcu::clear(m_imageData.getAccess(), tcu::Vec4(0.0f)); for (int py = 0; py < m_imageData.getHeight(); py++) { for (int px = 0; px < m_imageData.getWidth(); px++) m_imageData.getAccess().setPixel(imageColor, px, py); } m_gl->genTextures(1, &m_handle); m_gl->bindTexture(GL_TEXTURE_2D, m_handle); m_gl->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); m_gl->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); m_gl->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); m_gl->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); m_gl->texImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_width, m_height, 0, transferFormat, transferType, m_imageData.getAccess().getDataPtr()); m_gl->bindTexture(GL_TEXTURE_2D, 0); } TestTexture2D::~TestTexture2D (void) { m_gl->deleteTextures(1, &m_handle); } int TestTexture2D::getTextureUnit (void) const { return m_textureUnit; } deUint32 TestTexture2D::getHandle (void) const { return m_handle; } void TestTexture2D::bind (const int textureUnit) { m_textureUnit = textureUnit; m_gl->activeTexture(GL_TEXTURE0 + m_textureUnit); m_gl->bindTexture(GL_TEXTURE_2D, m_handle); } void TestTexture2D::unbind (void) const { m_gl->bindTexture(GL_TEXTURE_2D, 0); } class TestFramebuffer { public: TestFramebuffer (void); TestFramebuffer (Context& context, const deUint32 targetType, const deUint32 colorAttachment, glw::GLuint textureAttachmentHandle, const bool isSRGB, const FboType fboType, const int idx); ~TestFramebuffer (void); void setTargetType (const deUint32 targetType); FboType getType (void) const; deUint32 getColorAttachment (void) const; int getIdx (void) const; void bind (void); void unbind (void); typedef de::UniquePtr<glu::Framebuffer> fboUniquePtr; private: const glw::Functions* m_gl; fboUniquePtr m_referenceSource; deUint32 m_targetType; bool m_bound; bool m_isSRGB; FboType m_type; const int m_idx; deUint32 m_colorAttachment; }; TestFramebuffer::TestFramebuffer (Context& context, const deUint32 targetType, const deUint32 colorAttachment, glw::GLuint textureAttachmentHandle, const bool isSRGB, const FboType fboType, const int idx) : m_gl (&context.getRenderContext().getFunctions()) , m_referenceSource (new glu::Framebuffer(context.getRenderContext())) , m_targetType (targetType) , m_bound (false) , m_isSRGB (isSRGB) , m_type (fboType) , m_idx (idx) , m_colorAttachment (colorAttachment) { m_gl->bindFramebuffer(m_targetType, **m_referenceSource); m_gl->framebufferTexture2D(m_targetType, m_colorAttachment, GL_TEXTURE_2D, textureAttachmentHandle, 0); TCU_CHECK(m_gl->checkFramebufferStatus(m_targetType) == GL_FRAMEBUFFER_COMPLETE); if (targetType == GL_DRAW_BUFFER) { glw::GLuint textureAttachments[] = {m_colorAttachment}; m_gl->drawBuffers(DE_LENGTH_OF_ARRAY(textureAttachments), textureAttachments); GLU_EXPECT_NO_ERROR(m_gl->getError(), "glDrawBuffer()"); } if (targetType == GL_READ_BUFFER) { m_gl->readBuffer(m_colorAttachment); GLU_EXPECT_NO_ERROR(m_gl->getError(), "glReadBuffer()"); } m_gl->bindFramebuffer(m_targetType, 0); } TestFramebuffer::~TestFramebuffer (void) { } void TestFramebuffer::setTargetType (const deUint32 targetType) { m_targetType = targetType; } FboType TestFramebuffer::getType (void) const { return m_type; } deUint32 TestFramebuffer::getColorAttachment (void) const { return m_colorAttachment; } int TestFramebuffer::getIdx (void) const { return m_idx; } void TestFramebuffer::bind (void) { if (!m_bound) { m_gl->bindFramebuffer(m_targetType, **m_referenceSource); m_bound = true; } } void TestFramebuffer::unbind (void) { if (m_bound) { m_gl->bindFramebuffer(m_targetType, 0); m_bound = false; } } class TestShaderProgram { public: TestShaderProgram (Context& context, const int samplerTotal, TestFunction testFunction); ~TestShaderProgram (void); glw::GLuint getHandle (void) const; void use (void) const; void unuse (void) const; glu::ShaderProgramInfo getLogInfo (void); private: const glw::Functions* m_gl; de::MovePtr<glu::ShaderProgram> m_referenceSource; const int m_samplerTotal; const int m_shaderStagesTotal; }; TestShaderProgram::TestShaderProgram (Context& context, const int samplerTotal, TestFunction testFunction) : m_gl (&context.getRenderContext().getFunctions()) , m_samplerTotal (samplerTotal) , m_shaderStagesTotal (2) { std::ostringstream shaderFragment; const char* const shaderVertex = "#version 310 es \n" "layout (location = 0) in mediump vec3 aPosition; \n" "layout (location = 1) in mediump vec2 aTexCoord; \n" "out mediump vec2 vs_aTexCoord; \n" "void main () \n" "{ \n" " gl_Position = vec4(aPosition, 1.0f); \n" " vs_aTexCoord = aTexCoord; \n" "} \n"; shaderFragment << "#version 310 es \n" << "in mediump vec2 vs_aTexCoord; \n" << "layout (location = 0) out mediump vec4 fs_aColor0; \n"; for (int samplerIdx = 0; samplerIdx < m_samplerTotal; samplerIdx++) shaderFragment << "uniform sampler2D uTexture" << samplerIdx << "; \n"; shaderFragment << "uniform int uFunctionType; \n"; if (testFunction.hasFunction) shaderFragment << "uniform int uBlendFunctionType; \n" << "uniform mediump float uFactorSrc; \n" << "uniform mediump float uFactorDst; \n" << testFunction.functionDefintion; shaderFragment << "void main () \n" << "{ \n"; for (int samplerIdx = 0; samplerIdx < m_samplerTotal; samplerIdx++) shaderFragment <<" mediump vec4 texelColor" << samplerIdx << " = vec4(0.0f, 0.0f, 0.0f, 1.0f); \n"; shaderFragment << buildSamplingPassType(m_samplerTotal); if (testFunction.hasFunction) shaderFragment << " fs_aColor0 = " << testFunction.functionName << "(texelColor0, texelColor1); \n"; else shaderFragment << " fs_aColor0 = texelColor0; \n"; shaderFragment << "} \n"; m_referenceSource = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(context.getRenderContext(), glu::makeVtxFragSources(shaderVertex, shaderFragment.str()))); if (!m_referenceSource->isOk()) { tcu::TestLog& log = context.getTestContext().getLog(); log << this->getLogInfo(); TCU_FAIL("Failed to compile shaders and link program"); } } TestShaderProgram::~TestShaderProgram (void) { m_referenceSource = de::MovePtr<glu::ShaderProgram>(DE_NULL); m_referenceSource.clear(); } deUint32 TestShaderProgram::getHandle (void) const { return m_referenceSource->getProgram(); } void TestShaderProgram::use (void) const { m_gl->useProgram(this->getHandle()); } void TestShaderProgram::unuse (void) const { m_gl->useProgram(0); } glu::ShaderProgramInfo TestShaderProgram::getLogInfo (void) { glu::ShaderProgramInfo buildInfo; // log shader program info. Only vertex and fragment shaders included buildInfo.program = m_referenceSource->getProgramInfo(); for (int shaderIdx = 0; shaderIdx < m_shaderStagesTotal; shaderIdx++) { glu::ShaderInfo shaderInfo = m_referenceSource->getShaderInfo(static_cast<glu::ShaderType>(static_cast<int>(glu::SHADERTYPE_VERTEX) + static_cast<int>(shaderIdx)), 0); buildInfo.shaders.push_back(shaderInfo); } return buildInfo; } class Renderer { public: Renderer (Context& context); ~Renderer (void); void init (const TestRenderPassConfig& renderPassConfig, const int renderpass); void deinit (void); void setSamplingType (const SamplingType samplerIdx); void setBlendIteration (const int blendIteration); void setFramebufferBlend (const bool blend); void setFramebufferSRGB (const bool sRGB); std::vector<tcu::Vec4> getResultsPreDraw (void) const; std::vector<tcu::Vec4> getResultsPostDraw (void) const; int getBlendConfigCount (void) const; glu::ShaderProgramInfo getShaderProgramInfo (void); void copyFrameBufferTexture (const int srcPx, const int srcPy, const int dstPx, const int dstPy); void draw (void); void storeShaderProgramInfo (void); void logShaderProgramInfo (void); typedef de::SharedPtr<TestTexture2D> TextureSp; typedef de::SharedPtr<TestFramebuffer> FboSp; private: void createFBOwithColorAttachment (const std::vector<FBOConfig> fboConfigList); void setShaderProgramSamplingType (const int samplerIdx); void setShaderBlendFunctionType (void); void setShaderBlendSrcDstValues (void); void bindActiveTexturesSamplers (void); void bindAllRequiredSourceTextures (const TextureSourcesType texturesRequired); void unbindAllSourceTextures (void); void bindFramebuffer (const int framebufferIdx); void unbindFramebuffer (const int framebufferIdx); void enableFramebufferSRGB (void); void enableFramebufferBlend (void); bool isFramebufferAttachmentSRGB (const deUint32 targetType, const deUint32 attachment) const; void readTexels (const int px, const int py, const deUint32 attachment, tcu::Vec4& texelData); void logState (const deUint32 targetType, const deUint32 attachment, const SamplingType samplingType) const; // renderer specific constants initialized during constructor Context& m_context; const TestVertexData m_vertexData; const int m_textureSourceTotal; // additional resources monitored by the renderer std::vector<BlendConfig> m_blendConfigList; std::vector<TextureSp> m_textureSourceList; TestRenderPassConfig m_renderPassConfig; std::vector<TextureSp> m_fboTextureList; TestShaderProgram* m_shaderProgram; std::vector<FboSp> m_framebufferList; std::vector<tcu::Vec4> m_resultsListPreDraw; std::vector<tcu::Vec4> m_resultsListPostDraw; // mutable state variables (internal access only) bool m_hasShaderProgramInfo; int m_renderPass; int m_samplersRequired; bool m_hasFunction; bool m_blittingEnabled; glu::ShaderProgramInfo m_shaderProgramInfo; // mutable state variables (external access via setters) SamplingType m_samplingType; int m_blendIteration; bool m_framebufferBlendEnabled; bool m_framebufferSRGBEnabled; }; Renderer::Renderer (Context& context) : m_context (context) , m_vertexData (context) , m_textureSourceTotal (2) , m_blendConfigList (getBlendingConfigList()) , m_hasShaderProgramInfo (false) { TextureSp textureLinear(new TestTexture2D(m_context, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, getTestColorLinear())); m_textureSourceList.push_back(textureLinear); TextureSp textureSRGB(new TestTexture2D(m_context, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, getTestColorLinear())); m_textureSourceList.push_back(textureSRGB); } Renderer::~Renderer (void) { m_textureSourceList.clear(); this->deinit(); } void Renderer::init (const TestRenderPassConfig& renderPassConfig, const int renderpass) { m_renderPassConfig = renderPassConfig; m_renderPass = renderpass; this->createFBOwithColorAttachment(m_renderPassConfig.fboConfigList); if (m_renderPassConfig.textureSourcesType != TEXTURESOURCESTYPE_NONE) { if (m_renderPassConfig.textureSourcesType == TEXTURESOURCESTYPE_RGBA || m_renderPassConfig.textureSourcesType == TEXTURESOURCESTYPE_SRGBA) m_samplersRequired = 1; else if (m_renderPassConfig.textureSourcesType ==TEXTURESOURCESTYPE_BOTH ) m_samplersRequired = 2; else DE_FATAL("Error: Texture source required not recognised"); m_shaderProgram = new TestShaderProgram(m_context, m_samplersRequired, m_renderPassConfig.testFunction); m_hasFunction = m_renderPassConfig.testFunction.hasFunction; } else m_shaderProgram = DE_NULL; } void Renderer::deinit (void) { if (m_shaderProgram != DE_NULL) { delete m_shaderProgram; m_shaderProgram = DE_NULL; } m_fboTextureList.clear(); m_framebufferList.clear(); } void Renderer::setSamplingType (const SamplingType samplingType) { m_samplingType = samplingType; } void Renderer::setBlendIteration (const int blendIteration) { m_blendIteration = blendIteration; } void Renderer::setFramebufferBlend (const bool blend) { m_framebufferBlendEnabled = blend; } void Renderer::setFramebufferSRGB (const bool sRGB) { m_framebufferSRGBEnabled = sRGB; } std::vector<tcu::Vec4> Renderer::getResultsPreDraw (void) const { return m_resultsListPreDraw; } std::vector<tcu::Vec4> Renderer::getResultsPostDraw (void) const { return m_resultsListPostDraw; } int Renderer::getBlendConfigCount (void) const { return (int)m_blendConfigList.size(); } void Renderer::copyFrameBufferTexture (const int srcPx, const int srcPy, const int dstPx, const int dstPy) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); int fboSrcIdx = -1; int fboDstIdx = -1; deUint32 fboSrcColAttachment = GL_NONE; deUint32 fboDstColAttachment = GL_NONE; for (int idx = 0; idx < (int)m_framebufferList.size(); idx++) this->bindFramebuffer(idx); // cache fbo attachments and idx locations for (int idx = 0; idx < (int)m_framebufferList.size(); idx++) { if (m_framebufferList[idx]->getType() == FBOTYPE_SOURCE) { fboSrcIdx = m_framebufferList[idx]->getIdx(); fboSrcColAttachment = m_framebufferList[fboSrcIdx]->getColorAttachment(); } if (m_framebufferList[idx]->getType() == FBOTYPE_DESTINATION) { fboDstIdx = m_framebufferList[idx]->getIdx(); fboDstColAttachment = m_framebufferList[fboDstIdx]->getColorAttachment(); } } for (int idx = 0; idx < (int)m_framebufferList.size(); idx++) m_framebufferList[idx]->unbind(); // store texel data from both src and dst before performing the copy m_resultsListPreDraw.resize(2); m_framebufferList[fboSrcIdx]->bind(); this->readTexels(0, 0, fboSrcColAttachment, m_resultsListPreDraw[0]); m_framebufferList[fboSrcIdx]->unbind(); m_framebufferList[fboDstIdx]->setTargetType(GL_READ_FRAMEBUFFER); m_framebufferList[fboDstIdx]->bind(); this->readTexels(0, 0, fboDstColAttachment, m_resultsListPreDraw[1]); m_framebufferList[fboDstIdx]->unbind(); m_framebufferList[fboDstIdx]->setTargetType(GL_DRAW_FRAMEBUFFER); m_framebufferList[fboSrcIdx]->bind(); m_framebufferList[fboDstIdx]->bind(); this->enableFramebufferSRGB(); this->enableFramebufferBlend(); gl.blitFramebuffer( srcPx, srcPy, TestTextureSizes::WIDTH, TestTextureSizes::HEIGHT, dstPx, dstPy, TestTextureSizes::WIDTH, TestTextureSizes::HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST); m_resultsListPostDraw.resize(2); this->readTexels(0, 0, fboSrcColAttachment, m_resultsListPostDraw[0]); m_framebufferList[fboSrcIdx]->unbind(); m_framebufferList[fboDstIdx]->unbind(); m_framebufferList[fboDstIdx]->setTargetType(GL_READ_FRAMEBUFFER); m_framebufferList[fboDstIdx]->bind(); this->readTexels(0, 0, fboDstColAttachment, m_resultsListPostDraw[1]); m_framebufferList[fboDstIdx]->unbind(); } void Renderer::draw (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_renderPassConfig.textureSourcesType == TEXTURESOURCESTYPE_NONE) DE_FATAL("Error: Attempted to draw with no texture sources"); // resize results storage with each render pass m_resultsListPreDraw.resize(m_renderPass + 1); m_resultsListPostDraw.resize(m_renderPass + 1); m_shaderProgram->use(); m_vertexData.bind(); for (int idx = 0; idx < (int)m_framebufferList.size(); idx++) this->bindFramebuffer(idx); this->bindAllRequiredSourceTextures(m_renderPassConfig.textureSourcesType); this->bindActiveTexturesSamplers(); this->enableFramebufferSRGB(); this->enableFramebufferBlend(); this->readTexels(0, 0, GL_COLOR_ATTACHMENT0, m_resultsListPreDraw[m_renderPass]); this->setShaderProgramSamplingType(m_samplingType); if (m_hasFunction) { this->setShaderBlendFunctionType(); this->setShaderBlendSrcDstValues(); } gl.drawArrays(GL_TRIANGLES, 0, 6); this->readTexels(0, 0, GL_COLOR_ATTACHMENT0, m_resultsListPostDraw[m_renderPass]); this->logState(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_samplingType); this->unbindAllSourceTextures(); for (int idx = 0; idx < (int)m_framebufferList.size(); idx++) this->unbindFramebuffer(idx); m_vertexData.unbind(); m_shaderProgram->unuse(); } void Renderer::storeShaderProgramInfo (void) { m_shaderProgramInfo = m_shaderProgram->getLogInfo(); m_hasShaderProgramInfo = true; } void Renderer::logShaderProgramInfo (void) { tcu::TestLog& log = m_context.getTestContext().getLog(); if (m_hasShaderProgramInfo) log << m_shaderProgramInfo; } void Renderer::createFBOwithColorAttachment (const std::vector<FBOConfig> fboConfigList) { const int size = (int)fboConfigList.size(); for (int idx = 0; idx < size; idx++) { TextureSp texture(new TestTexture2D(m_context, fboConfigList[idx].textureInternalFormat, GL_RGBA, GL_UNSIGNED_BYTE, fboConfigList[idx].textureColor)); m_fboTextureList.push_back(texture); bool isSRGB; if (fboConfigList[idx].textureInternalFormat == GL_SRGB8_ALPHA8) isSRGB = true; else isSRGB = false; FboSp framebuffer(new TestFramebuffer(m_context, fboConfigList[idx].fboTargetType, fboConfigList[idx].fboColorAttachment, texture->getHandle(), isSRGB, fboConfigList[idx].fboType, idx)); m_framebufferList.push_back(framebuffer); } } void Renderer::setShaderProgramSamplingType (const int samplerIdx) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint location = gl.getUniformLocation(m_shaderProgram->getHandle(), "uFunctionType"); DE_ASSERT(location != (glw::GLuint)-1); gl.uniform1i(location, samplerIdx); } void Renderer::setShaderBlendFunctionType (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); int function = -1; if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_ADD) function = 0; else if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_SUBTRACT) function = 1; else if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_REVERSE_SUBTRACT) function = 2; else DE_FATAL("Error: Blend function not recognised"); glw::GLuint location = gl.getUniformLocation(m_shaderProgram->getHandle(), "uBlendFunctionType"); DE_ASSERT(location != (glw::GLuint)-1); gl.uniform1i(location, function); } void Renderer::setShaderBlendSrcDstValues (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); float funcSrc; if (m_blendConfigList[m_blendIteration].funcSrc == GL_ONE) funcSrc = 1.0f; else funcSrc = 0.0f; float funcDst; if (m_blendConfigList[m_blendIteration].funcDst == GL_ONE) funcDst = 1.0f; else funcDst = 0.0f; glw::GLuint locationSrc = gl.getUniformLocation(m_shaderProgram->getHandle(), "uFactorSrc"); gl.uniform1f(locationSrc, funcSrc); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1f()"); glw::GLuint locationDst = gl.getUniformLocation(m_shaderProgram->getHandle(), "uFactorDst"); gl.uniform1f(locationDst, funcDst); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1f()"); } void Renderer::bindActiveTexturesSamplers (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); for (int idx = 0; idx < m_samplersRequired; idx++) { std::ostringstream stream; stream << "uTexture" << idx; std::string uniformName(stream.str()); glw::GLint location = gl.getUniformLocation(m_shaderProgram->getHandle(), uniformName.c_str()); DE_ASSERT(location != -1); gl.uniform1i(location, m_textureSourceList[idx]->getTextureUnit()); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation()"); } } void Renderer::bindAllRequiredSourceTextures (const TextureSourcesType texturesRequired) { if (texturesRequired == TEXTURESOURCESTYPE_RGBA) m_textureSourceList[0]->bind(0); else if (texturesRequired == TEXTURESOURCESTYPE_SRGBA) m_textureSourceList[1]->bind(0); else if (texturesRequired == TEXTURESOURCESTYPE_BOTH) { m_textureSourceList[0]->bind(0); m_textureSourceList[1]->bind(1); } else DE_FATAL("Error: Invalid sources requested in bind all"); } void Renderer::unbindAllSourceTextures (void) { for (int idx = 0; idx < (int)m_textureSourceList.size(); idx++) m_textureSourceList[idx]->unbind(); } void Renderer::bindFramebuffer (const int framebufferIdx) { m_framebufferList[framebufferIdx]->bind(); } void Renderer::unbindFramebuffer (const int framebufferIdx) { m_framebufferList[framebufferIdx]->unbind(); } void Renderer::enableFramebufferSRGB (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_framebufferSRGBEnabled) gl.enable(GL_FRAMEBUFFER_SRGB); else gl.disable(GL_FRAMEBUFFER_SRGB); } void Renderer::enableFramebufferBlend (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_context.getTestContext().getLog(); std::ostringstream message; message << "Blend settings = "; if (m_framebufferBlendEnabled) { gl.enable(GL_BLEND); gl.blendEquation(m_blendConfigList[m_blendIteration].equation); gl.blendFunc(m_blendConfigList[m_blendIteration].funcSrc, m_blendConfigList[m_blendIteration].funcDst); std::string equation, src, dst; if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_ADD) equation = "GL_FUNC_ADD"; if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_SUBTRACT) equation = "GL_FUNC_SUBTRACT"; if (m_blendConfigList[m_blendIteration].equation == GL_FUNC_REVERSE_SUBTRACT) equation = "GL_FUNC_REVERSE_SUBTRACT"; if (m_blendConfigList[m_blendIteration].funcSrc == GL_ONE) src = "GL_ONE"; else src = "GL_ZERO"; if (m_blendConfigList[m_blendIteration].funcDst == GL_ONE) dst = "GL_ONE"; else dst = "GL_ZERO"; message << "Enabled: equation = " << equation << ", func src = " << src << ", func dst = " << dst; } else { gl.disable(GL_BLEND); message << "Disabled"; } log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; } bool Renderer::isFramebufferAttachmentSRGB (const deUint32 targetType, const deUint32 attachment) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint encodingType; gl.getFramebufferAttachmentParameteriv(targetType, attachment, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &encodingType); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetNamedFramebufferAttachmentParameteriv()"); switch (static_cast<glw::GLenum>(encodingType)) { case GL_SRGB: { return true; break; } case GL_LINEAR: { return false; break; } default: { DE_FATAL("Error: Color attachment format not recognised"); return false; } } } void Renderer::readTexels (const int px, const int py, const deUint32 mode, tcu::Vec4& texelData) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TextureLevel textureRead; // ensure result sampling coordinates are within range of the result color attachment DE_ASSERT((px >= 0) && (px < m_context.getRenderTarget().getWidth())); DE_ASSERT((py >= 0) && (py < m_context.getRenderTarget().getHeight())); gl.readBuffer(mode); textureRead.setStorage(glu::mapGLTransferFormat(GL_RGBA, GL_UNSIGNED_BYTE), TestTextureSizes::WIDTH, TestTextureSizes::HEIGHT); glu::readPixels(m_context.getRenderContext(), px, py, textureRead.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels()"); texelData = textureRead.getAccess().getPixel(px, py); } void Renderer::logState (const deUint32 targetType, const deUint32 attachment, const SamplingType samplingType) const { tcu::TestLog& log = m_context.getTestContext().getLog(); std::ostringstream message; bool fboAttachmentSRGB = this->isFramebufferAttachmentSRGB(targetType, attachment); message.str(""); message << "getFramebufferAttachmentParameteriv() check = "; if (fboAttachmentSRGB) message << "GL_SRGB"; else message << "GL_LINEAR"; log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; message.str(""); message << "Framebuffer color attachment value BEFORE draw call"; logColor(m_context, message.str(), m_resultsListPreDraw[m_renderPass]); message.str(""); message << "Framebuffer color attachment value AFTER draw call"; logColor(m_context, message.str(), m_resultsListPostDraw[m_renderPass]); message.str(""); message << "Sampling type = "; std::string type; if (samplingType == 0) type = "texture()"; else if (samplingType == 1) type = "textureLOD()"; else if (samplingType == 2) type = "textureGrad()"; else if (samplingType == 3) type = "textureOffset()"; else if (samplingType == 4) type = "textureProj()"; else DE_FATAL("Error: Sampling type unregonised"); message << type; log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; message.str(""); if (m_framebufferSRGBEnabled) message << "Framebuffer SRGB = enabled"; else message << "Framebuffer SRGB = disabled"; log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; } class FboSRGBTestCase : public TestCase { public: FboSRGBTestCase (Context& context, const char* const name, const char* const desc); ~FboSRGBTestCase (void); void init (void); void deinit (void); IterateResult iterate (void); void setTestConfig (std::vector<TestRenderPassConfig> renderPassConfigList); virtual void setupTest (void) = 0; virtual bool verifyResult (void) = 0; protected: bool m_hasTestConfig; std::vector<TestRenderPassConfig> m_renderPassConfigList; bool m_testcaseRequiresBlend; std::vector<tcu::Vec4> m_resultsPreDraw; std::vector<tcu::Vec4> m_resultsPostDraw; private: FboSRGBTestCase (const FboSRGBTestCase&); FboSRGBTestCase& operator= (const FboSRGBTestCase&); }; FboSRGBTestCase::FboSRGBTestCase (Context& context, const char* const name, const char* const desc) : TestCase (context, name, desc) , m_hasTestConfig (false) { } FboSRGBTestCase::~FboSRGBTestCase (void) { FboSRGBTestCase::deinit(); } void FboSRGBTestCase::init (void) { // extensions requirements for test if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2))) TCU_THROW(NotSupportedError, "Test requires a context version equal or higher than 3.2"); if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_sRGB_write_control")) TCU_THROW(NotSupportedError, "Test requires extension GL_EXT_sRGB_write_control"); if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_sRGB_decode")) TCU_THROW(NotSupportedError, "Test requires GL_EXT_texture_sRGB_decode extension"); } void FboSRGBTestCase::deinit (void) { } FboSRGBTestCase::IterateResult FboSRGBTestCase::iterate (void) { this->setupTest(); DE_ASSERT(m_hasTestConfig && "Error: Renderer was not supplied a test config"); Renderer renderer(m_context); // loop through each sampling type for (int samplingIdx = 0; samplingIdx < SampligTypeCount::MAX; samplingIdx++) { renderer.setSamplingType(static_cast<SamplingType>(samplingIdx)); // loop through each blend configuration const int blendCount = renderer.getBlendConfigCount(); for (int blendIdx = 0; blendIdx < blendCount; blendIdx++) { // loop through each render pass const int renderPassCount = (int)m_renderPassConfigList.size(); for (int renderPassIdx = 0; renderPassIdx < renderPassCount; renderPassIdx++) { TestRenderPassConfig renderPassConfig = m_renderPassConfigList[renderPassIdx]; renderer.init(renderPassConfig, renderPassIdx); if (blendIdx == 0 && renderPassConfig.rendererTask == RENDERERTASK_DRAW) renderer.storeShaderProgramInfo(); if (renderPassConfig.frameBufferBlend == FRAMEBUFFERBLEND_ENABLED) { renderer.setBlendIteration(blendIdx); renderer.setFramebufferBlend(true); } else renderer.setFramebufferBlend(false); if (renderPassConfig.framebufferSRGB == FRAMEBUFFERSRGB_ENABLED) renderer.setFramebufferSRGB(true); else renderer.setFramebufferSRGB(false); if (renderPassConfig.rendererTask == RENDERERTASK_DRAW) renderer.draw(); else if (renderPassConfig.rendererTask == RENDERERTASK_COPY) renderer.copyFrameBufferTexture(0, 0, 0, 0); else DE_FATAL("Error: render task not recognised"); renderer.deinit(); } // render passes m_resultsPreDraw = renderer.getResultsPreDraw(); m_resultsPostDraw = renderer.getResultsPostDraw(); bool testPassed = this->verifyResult(); if (testPassed) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed"); renderer.logShaderProgramInfo(); return STOP; } if (!m_testcaseRequiresBlend) break; } // blend configs renderer.logShaderProgramInfo(); } // sampling types return STOP; } void FboSRGBTestCase::setTestConfig (std::vector<TestRenderPassConfig> renderPassConfigList) { m_renderPassConfigList = renderPassConfigList; m_hasTestConfig = true; for (int idx = 0; idx < (int)renderPassConfigList.size(); idx++) { if (renderPassConfigList[idx].frameBufferBlend == FRAMEBUFFERBLEND_ENABLED) { m_testcaseRequiresBlend = true; return; } } m_testcaseRequiresBlend = false; } class FboSRGBQueryCase : public TestCase { public: FboSRGBQueryCase (Context& context, const char* const name, const char* const description); ~FboSRGBQueryCase (void); void init (void); void deinit (void); IterateResult iterate (void); }; FboSRGBQueryCase::FboSRGBQueryCase (Context& context, const char* const name, const char* const description) : TestCase (context, name, description) { } FboSRGBQueryCase::~FboSRGBQueryCase (void) { FboSRGBQueryCase::deinit(); } void FboSRGBQueryCase::init (void) { // extension requirements for test if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_sRGB_write_control")) TCU_THROW(NotSupportedError, "Test requires extension GL_EXT_sRGB_write_control"); } void FboSRGBQueryCase::deinit (void) { } FboSRGBQueryCase::IterateResult FboSRGBQueryCase::iterate (void) { // TEST INFO: // API tests which check when querying FRAMEBUFFER_SRGB_EXT capability returns the correct information when using glEnabled() or glDisabled() const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_context.getTestContext().getLog(); const char* const msgPart = ", after disabling = "; for (int idx = 0; idx < static_cast<int>(QUERYTYPE_LAST); idx++) { std::ostringstream message; bool pass = false; message << std::string("Results: After Enabling = "); gl.enable(GL_FRAMEBUFFER_SRGB); switch (static_cast<QueryType>(idx)) { case QUERYTYPE_ISENABLED: { glw::GLboolean enabled[2]; enabled[0] = gl.isEnabled(GL_FRAMEBUFFER_SRGB); gl.disable(GL_FRAMEBUFFER_SRGB); enabled[1] = gl.isEnabled(GL_FRAMEBUFFER_SRGB); message << static_cast<float>(enabled[0]) << msgPart << static_cast<float>(enabled[1]); pass = (enabled[0] && !(enabled[1])) ? true : false; break; } case QUERYTYPE_BOOLEAN: { glw::GLboolean enabled[2]; gl.getBooleanv(GL_FRAMEBUFFER_SRGB,&enabled[0]); gl.disable(GL_FRAMEBUFFER_SRGB); gl.getBooleanv(GL_FRAMEBUFFER_SRGB,&enabled[1]); message << static_cast<float>(enabled[0]) << msgPart << static_cast<float>(enabled[1]); pass = (enabled[0] && !(enabled[1])) ? true : false; break; } case QUERYTYPE_FLOAT: { glw::GLfloat enabled[2]; gl.getFloatv(GL_FRAMEBUFFER_SRGB, &enabled[0]); gl.disable(GL_FRAMEBUFFER_SRGB); gl.getFloatv(GL_FRAMEBUFFER_SRGB, &enabled[1]); message << static_cast<float>(enabled[0]) << msgPart << static_cast<float>(enabled[1]); pass = ((int)enabled[0] && !((int)enabled[1])) ? true : false; break; } case QUERYTYPE_INT: { glw::GLint enabled[2]; gl.getIntegerv(GL_FRAMEBUFFER_SRGB, &enabled[0]); gl.disable(GL_FRAMEBUFFER_SRGB); gl.getIntegerv(GL_FRAMEBUFFER_SRGB, &enabled[1]); message << static_cast<float>(enabled[0]) << msgPart << static_cast<float>(enabled[1]); pass = (enabled[0] && !(enabled[1])) ? true : false; break; } case QUERYTYPE_INT64: { glw::GLint64 enabled[2]; gl.getInteger64v(GL_FRAMEBUFFER_SRGB, &enabled[0]); gl.disable(GL_FRAMEBUFFER_SRGB); gl.getInteger64v(GL_FRAMEBUFFER_SRGB, &enabled[1]); message << static_cast<float>(enabled[0]) << msgPart << static_cast<float>(enabled[1]); pass = (enabled[0] && !(enabled[1])) ? true : false; break; } default: DE_FATAL("Error: Datatype not recognised"); } log << tcu::TestLog::Message << message.str() << tcu::TestLog::EndMessage; if (pass) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed"); return STOP; } } return STOP; } class FboSRGBColAttachCase : public FboSRGBTestCase { public: FboSRGBColAttachCase (Context& context, const char* const name, const char* const description) : FboSRGBTestCase (context, name, description) {} ~FboSRGBColAttachCase (void) {} void setupTest (void); bool verifyResult (void); }; void FboSRGBColAttachCase::setupTest (void) { // TEST INFO: // Check if FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING set to SRGB and FRAMEBUFFER_SRGB_EXT enabled, destination colors are converted from SRGB to linear // before and after blending, finally the result is converted back to SRGB for storage // NOTE: // if fbo pre-draw color set to linaer, color values get linearlized "twice" // (0.2f, 0.3f, 0.4f, 1.0f) when sampled i.e. converted in shader = (0.0331048f, 0.073239f, 0.132868f) // resulting in the follolwing blending equation (0.2f, 0.3f, 0.4f 1.0f) + (0.0331048, 0.073239, 0.132868) = (0.521569f, 0.647059f, 0.756863f, 1.0f) FBOConfig fboConfig0 = FBOConfig(GL_SRGB8_ALPHA8, getTestColorLinear(), GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_SOURCE); FBOConfig fboConfig1 = FBOConfig(GL_RGBA8, getTestColorLinear(), GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_SOURCE); const TestRenderPassConfig renderPassConfigs[] = { TestRenderPassConfig(TEXTURESOURCESTYPE_RGBA, fboConfig0, FRAMEBUFFERSRGB_ENABLED, FRAMEBUFFERBLEND_ENABLED, RENDERERTASK_DRAW), TestRenderPassConfig(TEXTURESOURCESTYPE_BOTH, fboConfig1, FRAMEBUFFERSRGB_DISABLED, FRAMEBUFFERBLEND_DISABLED, getFunctionBlendLinearToSRGBCheck(), RENDERERTASK_DRAW) }; std::vector<TestRenderPassConfig> renderPassConfigList(renderPassConfigs, renderPassConfigs + DE_LENGTH_OF_ARRAY(renderPassConfigs)); this->setTestConfig(renderPassConfigList); } bool FboSRGBColAttachCase::verifyResult (void) { if (tcu::boolAll(tcu::lessThan(tcu::abs(m_resultsPostDraw[0] - m_resultsPostDraw[1]), getEpsilonError())) || tcu::boolAll(tcu::equal(m_resultsPostDraw[0], m_resultsPostDraw[1]))) return true; else return false; } class FboSRGBToggleBlendCase : public FboSRGBTestCase { public: FboSRGBToggleBlendCase (Context& context, const char* const name, const char* const description) : FboSRGBTestCase (context, name, description) {} ~FboSRGBToggleBlendCase (void) {} void setupTest (void); bool verifyResult (void); }; void FboSRGBToggleBlendCase::setupTest (void) { // TEST INFO: // Test to check if changing FRAMEBUFFER_SRGB_EXT from enabled to disabled. Enabled should produce SRGB color whilst disabled // should produce linear color. Test conducted with blending disabled. FBOConfig fboConfig0 = FBOConfig(GL_SRGB8_ALPHA8, getTestColorLinear(), GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_DESTINATION); const TestRenderPassConfig renderPassConfigs[] = { TestRenderPassConfig(TEXTURESOURCESTYPE_RGBA, fboConfig0, FRAMEBUFFERSRGB_ENABLED, FRAMEBUFFERBLEND_DISABLED, TestFunction(false), RENDERERTASK_DRAW), TestRenderPassConfig(TEXTURESOURCESTYPE_RGBA, fboConfig0, FRAMEBUFFERSRGB_DISABLED, FRAMEBUFFERBLEND_DISABLED, TestFunction(false), RENDERERTASK_DRAW) }; std::vector<TestRenderPassConfig> renderPassConfigList(renderPassConfigs, renderPassConfigs + DE_LENGTH_OF_ARRAY(renderPassConfigs)); this->setTestConfig(renderPassConfigList); } bool FboSRGBToggleBlendCase::verifyResult (void) { if (tcu::boolAny(tcu::greaterThan(tcu::abs(m_resultsPostDraw[0] - m_resultsPostDraw[1]), getEpsilonError()))) return true; else return false; } class FboSRGBRenderTargetIgnoreCase : public FboSRGBTestCase { public: FboSRGBRenderTargetIgnoreCase (Context& context, const char* const name, const char* const description) : FboSRGBTestCase (context, name, description) {} ~FboSRGBRenderTargetIgnoreCase (void) {} void setupTest (void); bool verifyResult (void); }; void FboSRGBRenderTargetIgnoreCase::setupTest (void) { // TEST INFO: // Check if render targets that are non-RGB ignore the state of GL_FRAMEBUFFER_SRGB_EXT. Rendering to an fbo with non-sRGB color // attachment should ignore color space conversion, producing linear color. FBOConfig fboConfig0 = FBOConfig(GL_RGBA8, getTestColorBlank(), GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_DESTINATION); const TestRenderPassConfig renderPassConfigs[] = { TestRenderPassConfig(TEXTURESOURCESTYPE_RGBA, fboConfig0, FRAMEBUFFERSRGB_ENABLED, FRAMEBUFFERBLEND_DISABLED, TestFunction(false), RENDERERTASK_DRAW) }; std::vector<TestRenderPassConfig> renderPassConfigList(renderPassConfigs, renderPassConfigs + DE_LENGTH_OF_ARRAY(renderPassConfigs)); this->setTestConfig(renderPassConfigList); } bool FboSRGBRenderTargetIgnoreCase::verifyResult (void) { if (tcu::boolAll(tcu::lessThan(tcu::abs(m_resultsPostDraw[0] - getTestColorLinear()), getEpsilonError())) || tcu::boolAll(tcu::equal(m_resultsPostDraw[0], getTestColorLinear()))) return true; else return false; } class FboSRGBCopyToLinearCase : public FboSRGBTestCase { public: FboSRGBCopyToLinearCase (Context& context, const char* const name, const char* const description) : FboSRGBTestCase (context, name, description) {} ~FboSRGBCopyToLinearCase (void) {} void setupTest (void); bool verifyResult (void); }; void FboSRGBCopyToLinearCase::setupTest (void) { // TEST INFO: // Check if copying from an fbo with an sRGB color attachment to an fbo with a linear color attachment with FRAMEBUFFER_EXT enabled results in // an sRGB to linear conversion FBOConfig fboConfigs[] = { FBOConfig(GL_SRGB8_ALPHA8, getTestColorSRGB(), GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_SOURCE), FBOConfig(GL_RGBA8, getTestColorBlank(), GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FBOTYPE_DESTINATION) }; std::vector<FBOConfig> fboConfigList(fboConfigs, fboConfigs + DE_LENGTH_OF_ARRAY(fboConfigs)); const TestRenderPassConfig renderPassConfigs[] = { TestRenderPassConfig(TEXTURESOURCESTYPE_NONE, fboConfigList, FRAMEBUFFERSRGB_ENABLED, FRAMEBUFFERBLEND_DISABLED, TestFunction(false), RENDERERTASK_COPY) }; std::vector<TestRenderPassConfig> renderPassConfigList(renderPassConfigs, renderPassConfigs + DE_LENGTH_OF_ARRAY(renderPassConfigs)); this->setTestConfig(renderPassConfigList); } bool FboSRGBCopyToLinearCase::verifyResult (void) { logColor(m_context, "pre-copy source fbo color values", m_resultsPreDraw[0]); logColor(m_context, "pre-copy destination fbo color values", m_resultsPreDraw[1]); logColor(m_context, "post-copy source fbo color values", m_resultsPostDraw[0]); logColor(m_context, "post-copy destination fbo color values", m_resultsPostDraw[1]); if (tcu::boolAll(tcu::lessThan(tcu::abs(m_resultsPostDraw[1] - getTestColorLinear()), getEpsilonError())) || tcu::boolAll(tcu::equal(m_resultsPostDraw[1], getTestColorLinear()))) return true; else return false; } class FboSRGBUnsupportedEnumCase : public TestCase { public: FboSRGBUnsupportedEnumCase (Context& context, const char* const name, const char* const description); ~FboSRGBUnsupportedEnumCase (void); void init (void); void deinit (void); bool isInvalidEnum (std::string functionName); IterateResult iterate (void); }; FboSRGBUnsupportedEnumCase::FboSRGBUnsupportedEnumCase (Context& context, const char* const name, const char* const description) : TestCase (context, name, description) { } FboSRGBUnsupportedEnumCase::~FboSRGBUnsupportedEnumCase (void) { FboSRGBUnsupportedEnumCase::deinit(); } void FboSRGBUnsupportedEnumCase::init (void) { // extension requirements for test if (m_context.getContextInfo().isExtensionSupported("GL_EXT_sRGB_write_control")) TCU_THROW(NotSupportedError, "Test requires extension GL_EXT_sRGB_write_control to be unsupported"); } void FboSRGBUnsupportedEnumCase::deinit (void) { } bool FboSRGBUnsupportedEnumCase::isInvalidEnum (std::string functionName) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_context.getTestContext().getLog(); bool isOk = true; glw::GLenum error = GL_NO_ERROR; log << tcu::TestLog::Message << "Checking call to " << functionName << tcu::TestLog::EndMessage; error = gl.getError(); if (error != GL_INVALID_ENUM) { log << tcu::TestLog::Message << " returned wrong value [" << glu::getErrorStr(error) << ", expected " << glu::getErrorStr(GL_INVALID_ENUM) << "]" << tcu::TestLog::EndMessage; isOk = false; } return isOk; } FboSRGBUnsupportedEnumCase::IterateResult FboSRGBUnsupportedEnumCase::iterate (void) { // TEST INFO: // API tests that check calls using enum GL_FRAMEBUFFER_SRGB return GL_INVALID_ENUM when GL_EXT_sRGB_write_control is not supported const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool allPass = true; glw::GLboolean bEnabled = GL_FALSE; glw::GLfloat fEnabled = 0; glw::GLint iEnabled = 0; glw::GLint64 lEnabled = 0; m_context.getTestContext().getLog() << tcu::TestLog::Message << "Check calls using enum GL_FRAMEBUFFER_SRGB return GL_INVALID_ENUM when GL_EXT_sRGB_write_control is not supported\n\n" << tcu::TestLog::EndMessage; gl.enable(GL_FRAMEBUFFER_SRGB); allPass &= isInvalidEnum("glEnable()"); gl.disable(GL_FRAMEBUFFER_SRGB); allPass &= isInvalidEnum("glDisable()"); gl.isEnabled(GL_FRAMEBUFFER_SRGB); allPass &= isInvalidEnum("glIsEnabled()"); gl.getBooleanv(GL_FRAMEBUFFER_SRGB, &bEnabled); allPass &= isInvalidEnum("glGetBooleanv()"); gl.getFloatv(GL_FRAMEBUFFER_SRGB, &fEnabled); allPass &= isInvalidEnum("glGetFloatv()"); gl.getIntegerv(GL_FRAMEBUFFER_SRGB, &iEnabled); allPass &= isInvalidEnum("glGetIntegerv()"); gl.getInteger64v(GL_FRAMEBUFFER_SRGB, &lEnabled); allPass &= isInvalidEnum("glGetInteger64v()"); if (allPass) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return STOP; } } // anonymous FboSRGBWriteControlTests::FboSRGBWriteControlTests (Context& context) : TestCaseGroup (context, "srgb_write_control", "Colorbuffer tests") { } FboSRGBWriteControlTests::~FboSRGBWriteControlTests (void) { } void FboSRGBWriteControlTests::init (void) { this->addChild(new FboSRGBQueryCase (m_context, "framebuffer_srgb_enabled", "srgb enable framebuffer")); this->addChild(new FboSRGBColAttachCase (m_context, "framebuffer_srgb_enabled_col_attach", "srgb enable color attachment and framebuffer")); this->addChild(new FboSRGBToggleBlendCase (m_context, "framebuffer_srgb_enabled_blend", "toggle framebuffer srgb settings with blend disabled")); this->addChild(new FboSRGBRenderTargetIgnoreCase (m_context, "framebuffer_srgb_enabled_render_target_ignore", "enable framebuffer srgb, non-srgb render target should ignore")); this->addChild(new FboSRGBCopyToLinearCase (m_context, "framebuffer_srgb_enabled_copy_to_linear", "no conversion when blittering between framebuffer srgb and linear")); // negative this->addChild(new FboSRGBUnsupportedEnumCase (m_context, "framebuffer_srgb_unsupported_enum", "check error codes for query functions when extension is not supported")); } } } // gles31 } // deqp