/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3 Module * ------------------------------------------------- * * Copyright 2015 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 Indexed blend operation tests (GL_EXT_draw_buffers_indexed) *//*--------------------------------------------------------------------*/ #include "es3fDrawBuffersIndexedTests.hpp" #include "gluContextInfo.hpp" #include "gluDrawUtil.hpp" #include "gluObjectWrapper.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "gluStrUtil.hpp" #include "gluTextureUtil.hpp" #include "sglrReferenceUtils.hpp" #include "rrMultisamplePixelBufferAccess.hpp" #include "rrRenderer.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuEither.hpp" #include "tcuImageCompare.hpp" #include "tcuMaybe.hpp" #include "tcuResultCollector.hpp" #include "tcuStringTemplate.hpp" #include "tcuTestLog.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuVector.hpp" #include "tcuVectorUtil.hpp" #include "tcuFloat.hpp" #include "deRandom.hpp" #include "deArrayUtil.hpp" #include "deStringUtil.hpp" #include "deUniquePtr.hpp" #include "deInt32.h" #include #include #include using tcu::BVec4; using tcu::Either; using tcu::IVec2; using tcu::IVec4; using tcu::just; using tcu::Maybe; using tcu::TestLog; using tcu::TextureFormat; using tcu::TextureLevel; using tcu::UVec4; using tcu::Vec2; using tcu::Vec4; using std::map; using std::string; using std::vector; using sglr::rr_util::mapGLBlendEquation; using sglr::rr_util::mapGLBlendEquationAdvanced; using sglr::rr_util::mapGLBlendFunc; namespace deqp { namespace gles3 { namespace Functional { namespace { typedef uint32_t BlendEq; bool isAdvancedBlendEq(BlendEq eq) { switch (eq) { case GL_MULTIPLY: return true; case GL_SCREEN: return true; case GL_OVERLAY: return true; case GL_DARKEN: return true; case GL_LIGHTEN: return true; case GL_COLORDODGE: return true; case GL_COLORBURN: return true; case GL_HARDLIGHT: return true; case GL_SOFTLIGHT: return true; case GL_DIFFERENCE: return true; case GL_EXCLUSION: return true; case GL_HSL_HUE: return true; case GL_HSL_SATURATION: return true; case GL_HSL_COLOR: return true; case GL_HSL_LUMINOSITY: return true; default: return false; } } struct SeparateBlendEq { SeparateBlendEq(BlendEq rgb_, BlendEq alpha_) : rgb(rgb_), alpha(alpha_) { } BlendEq rgb; BlendEq alpha; }; struct BlendFunc { BlendFunc(uint32_t src_, uint32_t dst_) : src(src_), dst(dst_) { } uint32_t src; uint32_t dst; }; struct SeparateBlendFunc { SeparateBlendFunc(BlendFunc rgb_, BlendFunc alpha_) : rgb(rgb_), alpha(alpha_) { } BlendFunc rgb; BlendFunc alpha; }; typedef uint32_t DrawBuffer; struct BlendState { BlendState(void) { } BlendState(const Maybe &enableBlend_, const Maybe> &blendEq_, const Maybe> &blendFunc_, const Maybe &colorMask_) : enableBlend(enableBlend_) , blendEq(blendEq_) , blendFunc(blendFunc_) , colorMask(colorMask_) { } bool isEmpty(void) const { return (!enableBlend) && (!blendEq) && (!blendFunc) && (!colorMask); } Maybe enableBlend; Maybe> blendEq; Maybe> blendFunc; Maybe colorMask; }; static bool checkES32orGL45Support(Context &ctx) { auto ctxType = ctx.getRenderContext().getType(); return contextSupports(ctxType, glu::ApiType::es(3, 2)) || contextSupports(ctxType, glu::ApiType::core(4, 5)); } void setCommonBlendState(const glw::Functions &gl, const BlendState &blend) { if (blend.enableBlend) { if (*blend.enableBlend) gl.enable(GL_BLEND); else gl.disable(GL_BLEND); } if (blend.colorMask) { const BVec4 &mask = *blend.colorMask; gl.colorMask(mask.x(), mask.y(), mask.z(), mask.w()); } if (blend.blendEq) { const Either &blendEq = *blend.blendEq; if (blendEq.is()) gl.blendEquation(blendEq.get()); else if (blendEq.is()) gl.blendEquationSeparate(blendEq.get().rgb, blendEq.get().alpha); else DE_ASSERT(false); } if (blend.blendFunc) { const Either &blendFunc = *blend.blendFunc; if (blendFunc.is()) gl.blendFunc(blendFunc.get().src, blendFunc.get().dst); else if (blendFunc.is()) gl.blendFuncSeparate(blendFunc.get().rgb.src, blendFunc.get().rgb.dst, blendFunc.get().alpha.src, blendFunc.get().alpha.dst); else DE_ASSERT(false); } GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set common blend state."); } void setIndexedBlendState(const glw::Functions &gl, const BlendState &blend, uint32_t index) { if (blend.enableBlend) { if (*blend.enableBlend) gl.enablei(GL_BLEND, index); else gl.disablei(GL_BLEND, index); } if (blend.colorMask) { const BVec4 mask = *blend.colorMask; gl.colorMaski(index, mask.x(), mask.y(), mask.z(), mask.w()); } if (blend.blendEq) { const Either &blendEq = *blend.blendEq; if (blendEq.is()) gl.blendEquationi(index, blendEq.get()); else if (blendEq.is()) gl.blendEquationSeparatei(index, blendEq.get().rgb, blendEq.get().alpha); else DE_ASSERT(false); } if (blend.blendFunc) { const Either &blendFunc = *blend.blendFunc; if (blendFunc.is()) gl.blendFunci(index, blendFunc.get().src, blendFunc.get().dst); else if (blendFunc.is()) gl.blendFuncSeparatei( index, blendFunc.get().rgb.src, blendFunc.get().rgb.dst, blendFunc.get().alpha.src, blendFunc.get().alpha.dst); else DE_ASSERT(false); } GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set draw buffer specifig blend state."); } class DrawBufferInfo { public: DrawBufferInfo(bool render, const IVec2 &size, const BlendState &blendState, const TextureFormat &format); const TextureFormat &getFormat(void) const { return m_format; } const IVec2 &getSize(void) const { return m_size; } const BlendState &getBlendState(void) const { return m_blendState; } bool getRender(void) const { return m_render; } private: bool m_render; IVec2 m_size; TextureFormat m_format; BlendState m_blendState; }; DrawBufferInfo::DrawBufferInfo(bool render, const IVec2 &size, const BlendState &blendState, const TextureFormat &format) : m_render(render) , m_size(size) , m_format(format) , m_blendState(blendState) { } void clearRenderbuffer(const glw::Functions &gl, const tcu::TextureFormat &format, int renderbufferNdx, int renderbufferCount, tcu::TextureLevel &refRenderbuffer) { const tcu::TextureFormatInfo info = tcu::getTextureFormatInfo(format); // Clear each buffer to different color const float redScale = float(renderbufferNdx + 1) / float(renderbufferCount); const float blueScale = float(renderbufferCount - renderbufferNdx) / float(renderbufferCount); const float greenScale = float(((renderbufferCount / 2) + renderbufferNdx) % renderbufferCount) / float(renderbufferCount); // Alpha should never be zero as advanced blend equations assume premultiplied alpha. const float alphaScale = float(1 + (((renderbufferCount / 2) + renderbufferCount - renderbufferNdx) % renderbufferCount)) / float(renderbufferCount); switch (tcu::getTextureChannelClass(format.type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: { const float red = -1000.0f + 2000.0f * redScale; const float green = -1000.0f + 2000.0f * greenScale; const float blue = -1000.0f + 2000.0f * blueScale; const float alpha = -1000.0f + 2000.0f * alphaScale; const Vec4 color(red, green, blue, alpha); tcu::clear(refRenderbuffer, color); gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr()); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: { const int32_t red = int32_t(info.valueMin.x() + (info.valueMax.x() - info.valueMin.x()) * redScale); const int32_t green = int32_t(info.valueMin.y() + (info.valueMax.y() - info.valueMin.y()) * greenScale); const int32_t blue = int32_t(info.valueMin.z() + (info.valueMax.z() - info.valueMin.z()) * blueScale); const int32_t alpha = int32_t(info.valueMin.w() + (info.valueMax.w() - info.valueMin.w()) * alphaScale); const IVec4 color(red, green, blue, alpha); tcu::clear(refRenderbuffer, color); gl.clearBufferiv(GL_COLOR, renderbufferNdx, color.getPtr()); break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const uint32_t red = uint32_t(info.valueMax.x() * redScale); const uint32_t green = uint32_t(info.valueMax.y() * greenScale); const uint32_t blue = uint32_t(info.valueMax.z() * blueScale); const uint32_t alpha = uint32_t(info.valueMax.w() * alphaScale); const UVec4 color(red, green, blue, alpha); tcu::clear(refRenderbuffer, color); gl.clearBufferuiv(GL_COLOR, renderbufferNdx, color.getPtr()); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: { const float red = info.valueMin.x() + (info.valueMax.x() - info.valueMin.x()) * redScale; const float green = info.valueMin.y() + (info.valueMax.y() - info.valueMin.y()) * greenScale; const float blue = info.valueMin.z() + (info.valueMax.z() - info.valueMin.z()) * blueScale; const float alpha = info.valueMin.w() + (info.valueMax.w() - info.valueMin.w()) * alphaScale; const Vec4 color(red, green, blue, alpha); tcu::clear(refRenderbuffer, color); gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr()); break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { const float red = info.valueMax.x() * redScale; const float green = info.valueMax.y() * greenScale; const float blue = info.valueMax.z() * blueScale; const float alpha = info.valueMax.w() * alphaScale; const Vec4 color(red, green, blue, alpha); tcu::clear(refRenderbuffer, color); gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr()); break; } default: DE_ASSERT(false); } GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to clear renderbuffer."); } void genRenderbuffers(const glw::Functions &gl, const vector &drawBuffers, const glu::Framebuffer &framebuffer, const glu::RenderbufferVector &renderbuffers, vector &refRenderbuffers) { vector bufs; bufs.resize(drawBuffers.size()); DE_ASSERT(drawBuffers.size() == renderbuffers.size()); DE_ASSERT(drawBuffers.size() == refRenderbuffers.size()); gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer); for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++) { const DrawBufferInfo &drawBuffer = drawBuffers[renderbufferNdx]; const TextureFormat &format = drawBuffer.getFormat(); const IVec2 &size = drawBuffer.getSize(); const uint32_t glFormat = glu::getInternalFormat(format); bufs[renderbufferNdx] = GL_COLOR_ATTACHMENT0 + renderbufferNdx; refRenderbuffers[renderbufferNdx] = TextureLevel(drawBuffer.getFormat(), size.x(), size.y()); gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffers[renderbufferNdx]); gl.renderbufferStorage(GL_RENDERBUFFER, glFormat, size.x(), size.y()); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + renderbufferNdx, GL_RENDERBUFFER, renderbuffers[renderbufferNdx]); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create renderbuffer."); } gl.drawBuffers((glw::GLsizei)bufs.size(), &(bufs[0])); for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++) { const DrawBufferInfo &drawBuffer = drawBuffers[renderbufferNdx]; const TextureFormat &format = drawBuffer.getFormat(); clearRenderbuffer(gl, format, renderbufferNdx, (int)refRenderbuffers.size(), refRenderbuffers[renderbufferNdx]); } gl.bindRenderbuffer(GL_RENDERBUFFER, 0); gl.bindFramebuffer(GL_FRAMEBUFFER, 0); } Vec4 getFixedPointFormatThreshold(const tcu::TextureFormat &sourceFormat, const tcu::TextureFormat &readPixelsFormat) { DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_FLOATING_POINT); DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_FLOATING_POINT); DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER); DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER); DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER); DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER); const tcu::IVec4 srcBits = tcu::getTextureFormatBitDepth(sourceFormat); const tcu::IVec4 readBits = tcu::getTextureFormatBitDepth(readPixelsFormat); Vec4 threshold = Vec4(0.0f); for (int i = 0; i < 4; i++) { const int bits = de::min(srcBits[i], readBits[i]); if (bits > 0) { threshold[i] = 3.0f / static_cast(((1ul << bits) - 1ul)); } } return threshold; } UVec4 getFloatULPThreshold(const tcu::TextureFormat &sourceFormat, const tcu::TextureFormat &readPixelsFormat) { const tcu::IVec4 srcMantissaBits = tcu::getTextureFormatMantissaBitDepth(sourceFormat); const tcu::IVec4 readMantissaBits = tcu::getTextureFormatMantissaBitDepth(readPixelsFormat); tcu::IVec4 ULPDiff(0); for (int i = 0; i < 4; i++) if (readMantissaBits[i] >= srcMantissaBits[i]) ULPDiff[i] = readMantissaBits[i] - srcMantissaBits[i]; return UVec4(4) * (UVec4(1) << (ULPDiff.cast())); } void verifyRenderbuffer(TestLog &log, tcu::ResultCollector &results, const tcu::TextureFormat &format, int renderbufferNdx, const tcu::TextureLevel &refRenderbuffer, const tcu::TextureLevel &result) { switch (tcu::getTextureChannelClass(format.type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: { const string name = "Renderbuffer" + de::toString(renderbufferNdx); const string desc = "Compare renderbuffer " + de::toString(renderbufferNdx); const UVec4 threshold = getFloatULPThreshold(format, result.getFormat()); if (!tcu::floatUlpThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT)) results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed."); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const string name = "Renderbuffer" + de::toString(renderbufferNdx); const string desc = "Compare renderbuffer " + de::toString(renderbufferNdx); const UVec4 threshold(1, 1, 1, 1); if (!tcu::intThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT)) results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed."); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { const string name = "Renderbuffer" + de::toString(renderbufferNdx); const string desc = "Compare renderbuffer " + de::toString(renderbufferNdx); const Vec4 threshold = getFixedPointFormatThreshold(format, result.getFormat()); if (!tcu::floatThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT)) results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed."); break; } default: DE_ASSERT(false); } } TextureFormat getReadPixelFormat(const TextureFormat &format) { switch (tcu::getTextureChannelClass(format.type)) { case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32); case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: return TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT32); case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: return TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT); default: DE_ASSERT(false); return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); } } void verifyRenderbuffers(TestLog &log, tcu::ResultCollector &results, glu::RenderContext &renderContext, const glu::RenderbufferVector &renderbuffers, const glu::Framebuffer &framebuffer, const vector &refRenderbuffers) { const glw::Functions &gl = renderContext.getFunctions(); DE_ASSERT(renderbuffers.size() == refRenderbuffers.size()); gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer); for (int renderbufferNdx = 0; renderbufferNdx < (int)renderbuffers.size(); renderbufferNdx++) { const TextureLevel &refRenderbuffer = refRenderbuffers[renderbufferNdx]; const int width = refRenderbuffer.getWidth(); const int height = refRenderbuffer.getHeight(); const TextureFormat format = refRenderbuffer.getFormat(); tcu::TextureLevel result(getReadPixelFormat(format), width, height); gl.readBuffer(GL_COLOR_ATTACHMENT0 + renderbufferNdx); glu::readPixels(renderContext, 0, 0, result.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels from renderbuffer failed."); verifyRenderbuffer(log, results, format, renderbufferNdx, refRenderbuffer, result); } gl.bindFramebuffer(GL_FRAMEBUFFER, 0); } static const float s_quadCoords[] = {-0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f}; void setBlendState(rr::FragmentOperationState &fragOps, const BlendState &state) { if (state.blendEq) { if (state.blendEq->is()) { if (isAdvancedBlendEq(state.blendEq->get())) { const rr::BlendEquationAdvanced equation = mapGLBlendEquationAdvanced(state.blendEq->get()); fragOps.blendMode = rr::BLENDMODE_ADVANCED; fragOps.blendEquationAdvaced = equation; } else { const rr::BlendEquation equation = mapGLBlendEquation(state.blendEq->get()); fragOps.blendMode = rr::BLENDMODE_STANDARD; fragOps.blendRGBState.equation = equation; fragOps.blendAState.equation = equation; } } else { DE_ASSERT(state.blendEq->is()); fragOps.blendMode = rr::BLENDMODE_STANDARD; fragOps.blendRGBState.equation = mapGLBlendEquation(state.blendEq->get().rgb); fragOps.blendAState.equation = mapGLBlendEquation(state.blendEq->get().alpha); } } if (state.blendFunc) { if (state.blendFunc->is()) { const rr::BlendFunc srcFunction = mapGLBlendFunc(state.blendFunc->get().src); const rr::BlendFunc dstFunction = mapGLBlendFunc(state.blendFunc->get().dst); fragOps.blendRGBState.srcFunc = srcFunction; fragOps.blendRGBState.dstFunc = dstFunction; fragOps.blendAState.srcFunc = srcFunction; fragOps.blendAState.dstFunc = dstFunction; } else { DE_ASSERT(state.blendFunc->is()); fragOps.blendRGBState.srcFunc = mapGLBlendFunc(state.blendFunc->get().rgb.src); fragOps.blendRGBState.dstFunc = mapGLBlendFunc(state.blendFunc->get().rgb.dst); fragOps.blendAState.srcFunc = mapGLBlendFunc(state.blendFunc->get().alpha.src); fragOps.blendAState.dstFunc = mapGLBlendFunc(state.blendFunc->get().alpha.dst); } } if (state.colorMask) fragOps.colorMask = *state.colorMask; } rr::RenderState createRenderState(const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const DrawBufferInfo &info, int subpixelBits) { const IVec2 size = info.getSize(); rr::RenderState state(rr::ViewportState(rr::WindowRectangle(0, 0, size.x(), size.y())), subpixelBits); state.fragOps.blendMode = rr::BLENDMODE_STANDARD; setBlendState(state.fragOps, preCommonBlendState); setBlendState(state.fragOps, info.getBlendState()); setBlendState(state.fragOps, postCommonBlendState); if (postCommonBlendState.enableBlend) state.fragOps.blendMode = (*(postCommonBlendState.enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE); else if (info.getBlendState().enableBlend) state.fragOps.blendMode = (*(info.getBlendState().enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE); else if (preCommonBlendState.enableBlend) state.fragOps.blendMode = (*(preCommonBlendState.enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE); else state.fragOps.blendMode = rr::BLENDMODE_NONE; if (tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT && tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_FLOATING_POINT) state.fragOps.blendMode = rr::BLENDMODE_NONE; return state; } class VertexShader : public rr::VertexShader { public: VertexShader(void); virtual void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const; }; VertexShader::VertexShader(void) : rr::VertexShader(1, 1) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void VertexShader::shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const { for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { rr::VertexPacket &packet = *packets[packetNdx]; packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx); packet.outputs[0] = 0.5f * (Vec4(1.0f) + rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx)); } } class FragmentShader : public rr::FragmentShader { public: FragmentShader(int drawBufferNdx, const DrawBufferInfo &info); void shadeFragments(rr::FragmentPacket *packets, const int numPackets, const rr::FragmentShadingContext &context) const; private: const int m_drawBufferNdx; const DrawBufferInfo m_info; }; FragmentShader::FragmentShader(int drawBufferNdx, const DrawBufferInfo &info) : rr::FragmentShader(1, 1) , m_drawBufferNdx(drawBufferNdx) , m_info(info) { m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; switch (tcu::getTextureChannelClass(m_info.getFormat().type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: m_outputs[0].type = rr::GENERICVECTYPE_UINT32; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: m_outputs[0].type = rr::GENERICVECTYPE_INT32; break; default: DE_ASSERT(false); } } void FragmentShader::shadeFragments(rr::FragmentPacket *packets, const int numPackets, const rr::FragmentShadingContext &context) const { for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { rr::FragmentPacket &packet = packets[packetNdx]; DE_ASSERT(m_drawBufferNdx >= 0); DE_UNREF(m_info); for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const Vec2 vColor = rr::readVarying(packet, context, 0, fragNdx).xy(); const float values[] = {vColor.x(), vColor.y(), (1.0f - vColor.x()), (1.0f - vColor.y())}; switch (tcu::getTextureChannelClass(m_info.getFormat().type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { const Vec4 color(values[(m_drawBufferNdx + 0) % 4], values[(m_drawBufferNdx + 1) % 4], values[(m_drawBufferNdx + 2) % 4], values[(m_drawBufferNdx + 3) % 4]); rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color); break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const UVec4 color( (uint32_t)(values[(m_drawBufferNdx + 0) % 4]), (uint32_t)(values[(m_drawBufferNdx + 1) % 4]), (uint32_t)(values[(m_drawBufferNdx + 2) % 4]), (uint32_t)(values[(m_drawBufferNdx + 3) % 4])); rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: { const IVec4 color( (int32_t)(values[(m_drawBufferNdx + 0) % 4]), (int32_t)(values[(m_drawBufferNdx + 1) % 4]), (int32_t)(values[(m_drawBufferNdx + 2) % 4]), (int32_t)(values[(m_drawBufferNdx + 3) % 4])); rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color); break; } default: DE_ASSERT(false); } } } } rr::VertexAttrib createVertexAttrib(const float *coords) { rr::VertexAttrib attrib; attrib.type = rr::VERTEXATTRIBTYPE_FLOAT; attrib.size = 2; attrib.pointer = coords; return attrib; } void renderRefQuad(const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, const int subpixelBits, vector &refRenderbuffers) { const rr::Renderer renderer; const rr::PrimitiveList primitives(rr::PRIMITIVETYPE_TRIANGLES, 6, 0); const rr::VertexAttrib vertexAttribs[] = {createVertexAttrib(s_quadCoords)}; for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { if (drawBuffers[drawBufferNdx].getRender()) { const rr::RenderState renderState( createRenderState(preCommonBlendState, postCommonBlendState, drawBuffers[drawBufferNdx], subpixelBits)); const rr::RenderTarget renderTarget( rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refRenderbuffers[drawBufferNdx].getAccess())); const VertexShader vertexShader; const FragmentShader fragmentShader(drawBufferNdx, drawBuffers[drawBufferNdx]); const rr::Program program(&vertexShader, &fragmentShader); const rr::DrawCommand command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), vertexAttribs, primitives); renderer.draw(command); } } } bool requiresAdvancedBlendEq(const BlendState &pre, const BlendState post, const vector &drawBuffers) { bool requiresAdvancedBlendEq = false; if (pre.blendEq && pre.blendEq->is()) requiresAdvancedBlendEq |= isAdvancedBlendEq(pre.blendEq->get()); if (post.blendEq && post.blendEq->is()) requiresAdvancedBlendEq |= isAdvancedBlendEq(post.blendEq->get()); for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { const BlendState &drawBufferBlendState = drawBuffers[drawBufferNdx].getBlendState(); if (drawBufferBlendState.blendEq && drawBufferBlendState.blendEq->is()) requiresAdvancedBlendEq |= isAdvancedBlendEq(drawBufferBlendState.blendEq->get()); } return requiresAdvancedBlendEq; } glu::VertexSource genVertexSource(glu::RenderContext &renderContext) { const bool supportsES32 = glu::contextSupports(renderContext.getType(), glu::ApiType::es(3, 2)); const char *const vertexSource = "${GLSL_VERSION_DECL}\n" "layout(location=0) in highp vec2 i_coord;\n" "out highp vec2 v_color;\n" "void main (void)\n" "{\n" "\tv_color = 0.5 * (vec2(1.0) + i_coord);\n" "\tgl_Position = vec4(i_coord, 0.0, 1.0);\n" "}"; map args; args["GLSL_VERSION_DECL"] = supportsES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_300_ES); return glu::VertexSource(tcu::StringTemplate(vertexSource).specialize(args)); } glu::FragmentSource genFragmentSource(const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, glu::RenderContext &renderContext) { std::ostringstream stream; const bool supportsES32 = glu::contextSupports(renderContext.getType(), glu::ApiType::es(3, 2)); stream << "${GLSL_VERSION_DECL}\n"; if (requiresAdvancedBlendEq(preCommonBlendState, postCommonBlendState, drawBuffers)) { stream << "${GLSL_EXTENSION}" << "layout(blend_support_all_equations) out;\n"; } stream << "in highp vec2 v_color;\n"; for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { const DrawBufferInfo &drawBuffer = drawBuffers[drawBufferNdx]; const TextureFormat &format = drawBuffer.getFormat(); stream << "layout(location=" << drawBufferNdx << ") out highp "; switch (tcu::getTextureChannelClass(format.type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: stream << "vec4"; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: stream << "uvec4"; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: stream << "ivec4"; break; default: DE_ASSERT(false); } stream << " o_drawBuffer" << drawBufferNdx << ";\n"; } stream << "void main (void)\n" << "{\n"; for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { const DrawBufferInfo &drawBuffer = drawBuffers[drawBufferNdx]; const TextureFormat &format = drawBuffer.getFormat(); const char *const values[] = {"v_color.x", "v_color.y", "(1.0 - v_color.x)", "(1.0 - v_color.y)"}; stream << "\to_drawBuffer" << drawBufferNdx; switch (tcu::getTextureChannelClass(format.type)) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: stream << " = vec4(" << values[(drawBufferNdx + 0) % 4] << ", " << values[(drawBufferNdx + 1) % 4] << ", " << values[(drawBufferNdx + 2) % 4] << ", " << values[(drawBufferNdx + 3) % 4] << ");\n"; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: stream << " = uvec4(uint(" << values[(drawBufferNdx + 0) % 4] << "), uint(" << values[(drawBufferNdx + 1) % 4] << "), uint(" << values[(drawBufferNdx + 2) % 4] << "), uint(" << values[(drawBufferNdx + 3) % 4] << "));\n"; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: stream << " = ivec4(int(" << values[(drawBufferNdx + 0) % 4] << "), int(" << values[(drawBufferNdx + 1) % 4] << "), int(" << values[(drawBufferNdx + 2) % 4] << "), int(" << values[(drawBufferNdx + 3) % 4] << "));\n"; break; default: DE_ASSERT(false); } } stream << "}"; map args; args["GLSL_VERSION_DECL"] = supportsES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_300_ES); args["GLSL_EXTENSION"] = supportsES32 ? "\n" : "#extension GL_KHR_blend_equation_advanced : require\n"; return glu::FragmentSource(tcu::StringTemplate(stream.str()).specialize(args)); } glu::ProgramSources genShaderSources(const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, glu::RenderContext &renderContext) { return glu::ProgramSources() << genVertexSource(renderContext) << genFragmentSource(preCommonBlendState, postCommonBlendState, drawBuffers, renderContext); } void renderGLQuad(glu::RenderContext &renderContext, const glu::ShaderProgram &program) { const glu::VertexArrayBinding vertexArrays[] = {glu::VertexArrayBinding( glu::BindingPoint(0), glu::VertexArrayPointer(glu::VTX_COMP_FLOAT, glu::VTX_COMP_CONVERT_NONE, 2, 6, 0, s_quadCoords))}; glu::draw(renderContext, program.getProgram(), 1, vertexArrays, glu::pr::Triangles(6)); } void renderQuad(TestLog &log, glu::RenderContext &renderContext, const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, const glu::Framebuffer &framebuffer, vector &refRenderbuffers) { const glw::Functions &gl = renderContext.getFunctions(); const glu::ShaderProgram program( gl, genShaderSources(preCommonBlendState, postCommonBlendState, drawBuffers, renderContext)); const IVec2 size = drawBuffers[0].getSize(); const bool requiresBlendBarriers = requiresAdvancedBlendEq(preCommonBlendState, postCommonBlendState, drawBuffers); vector bufs; bufs.resize(drawBuffers.size()); for (int bufNdx = 0; bufNdx < (int)bufs.size(); bufNdx++) bufs[bufNdx] = (drawBuffers[bufNdx].getRender() ? GL_COLOR_ATTACHMENT0 + bufNdx : GL_NONE); log << program; gl.viewport(0, 0, size.x(), size.y()); gl.useProgram(program.getProgram()); gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer); setCommonBlendState(gl, preCommonBlendState); for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++) setIndexedBlendState(gl, drawBuffers[renderbufferNdx].getBlendState(), renderbufferNdx); setCommonBlendState(gl, postCommonBlendState); gl.drawBuffers((glw::GLsizei)bufs.size(), &(bufs[0])); if (requiresBlendBarriers) gl.blendBarrier(); renderGLQuad(renderContext, program); if (requiresBlendBarriers) gl.blendBarrier(); gl.drawBuffers(0, 0); gl.bindFramebuffer(GL_FRAMEBUFFER, 0); gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to render"); int subpixelBits = 0; gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits); renderRefQuad(preCommonBlendState, postCommonBlendState, drawBuffers, subpixelBits, refRenderbuffers); } void logBlendState(TestLog &log, const BlendState &blend) { if (blend.enableBlend) { if (*blend.enableBlend) log << TestLog::Message << "Enable blending." << TestLog::EndMessage; else log << TestLog::Message << "Disable blending." << TestLog::EndMessage; } if (blend.colorMask) { const BVec4 mask = *blend.colorMask; log << TestLog::Message << "Set color mask: " << mask << "." << TestLog::EndMessage; } if (blend.blendEq) { const Either &blendEq = *blend.blendEq; if (blendEq.is()) log << TestLog::Message << "Set blend equation: " << glu::getBlendEquationStr(blendEq.get()) << "." << TestLog::EndMessage; else if (blendEq.is()) log << TestLog::Message << "Set blend equation rgb: " << glu::getBlendEquationStr(blendEq.get().rgb) << ", alpha: " << glu::getBlendEquationStr(blendEq.get().alpha) << "." << TestLog::EndMessage; else DE_ASSERT(false); } if (blend.blendFunc) { const Either &blendFunc = *blend.blendFunc; if (blendFunc.is()) log << TestLog::Message << "Set blend function source: " << glu::getBlendFactorStr(blendFunc.get().src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get().dst) << "." << TestLog::EndMessage; else if (blendFunc.is()) { log << TestLog::Message << "Set blend function rgb source: " << glu::getBlendFactorStr(blendFunc.get().rgb.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get().rgb.dst) << "." << TestLog::EndMessage; log << TestLog::Message << "Set blend function alpha source: " << glu::getBlendFactorStr(blendFunc.get().alpha.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get().alpha.dst) << "." << TestLog::EndMessage; } else DE_ASSERT(false); } } void logTestCaseInfo(TestLog &log, const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers) { { tcu::ScopedLogSection drawBuffersSection(log, "DrawBuffers", "Draw buffers"); for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { const tcu::ScopedLogSection drawBufferSection(log, "DrawBuffer" + de::toString(drawBufferNdx), "Draw Buffer " + de::toString(drawBufferNdx)); const DrawBufferInfo &drawBuffer = drawBuffers[drawBufferNdx]; log << TestLog::Message << "Format: " << drawBuffer.getFormat() << TestLog::EndMessage; log << TestLog::Message << "Size: " << drawBuffer.getSize() << TestLog::EndMessage; log << TestLog::Message << "Render: " << (drawBuffer.getRender() ? "true" : "false") << TestLog::EndMessage; } } if (!preCommonBlendState.isEmpty()) { tcu::ScopedLogSection s(log, "PreCommonState", "First set common blend state"); logBlendState(log, preCommonBlendState); } for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++) { if (!drawBuffers[drawBufferNdx].getBlendState().isEmpty()) { const tcu::ScopedLogSection s(log, "DrawBufferState" + de::toString(drawBufferNdx), "Set DrawBuffer " + de::toString(drawBufferNdx) + " state to"); logBlendState(log, drawBuffers[drawBufferNdx].getBlendState()); } } if (!postCommonBlendState.isEmpty()) { tcu::ScopedLogSection s(log, "PostCommonState", "After set common blend state"); logBlendState(log, postCommonBlendState); } } void runTest(TestLog &log, tcu::ResultCollector &results, glu::RenderContext &renderContext, const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers) { const glw::Functions &gl = renderContext.getFunctions(); glu::RenderbufferVector renderbuffers(gl, drawBuffers.size()); glu::Framebuffer framebuffer(gl); vector refRenderbuffers(drawBuffers.size()); logTestCaseInfo(log, preCommonBlendState, postCommonBlendState, drawBuffers); genRenderbuffers(gl, drawBuffers, framebuffer, renderbuffers, refRenderbuffers); renderQuad(log, renderContext, preCommonBlendState, postCommonBlendState, drawBuffers, framebuffer, refRenderbuffers); verifyRenderbuffers(log, results, renderContext, renderbuffers, framebuffer, refRenderbuffers); } class DrawBuffersIndexedTest : public TestCase { public: DrawBuffersIndexedTest(Context &context, const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, const string &name, const string &description); void init(void); IterateResult iterate(void); private: const BlendState m_preCommonBlendState; const BlendState m_postCommonBlendState; const vector m_drawBuffers; }; DrawBuffersIndexedTest::DrawBuffersIndexedTest(Context &context, const BlendState &preCommonBlendState, const BlendState &postCommonBlendState, const vector &drawBuffers, const string &name, const string &description) : TestCase(context, name.c_str(), description.c_str()) , m_preCommonBlendState(preCommonBlendState) , m_postCommonBlendState(postCommonBlendState) , m_drawBuffers(drawBuffers) { } void DrawBuffersIndexedTest::init(void) { const bool supportsES32orGL45 = checkES32orGL45Support(m_context); if (!supportsES32orGL45) { if (requiresAdvancedBlendEq(m_preCommonBlendState, m_postCommonBlendState, m_drawBuffers) && !m_context.getContextInfo().isExtensionSupported("GL_KHR_blend_equation_advanced")) TCU_THROW(NotSupportedError, "Extension GL_KHR_blend_equation_advanced not supported"); if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed")) TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported"); } } TestCase::IterateResult DrawBuffersIndexedTest::iterate(void) { TestLog &log = m_testCtx.getLog(); tcu::ResultCollector results(log); runTest(log, results, m_context.getRenderContext(), m_preCommonBlendState, m_postCommonBlendState, m_drawBuffers); results.setTestContextResult(m_testCtx); return STOP; } BlendEq getRandomBlendEq(de::Random &rng) { const BlendEq eqs[] = {GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX}; return de::getSizedArrayElement(eqs, rng.getUint32() % DE_LENGTH_OF_ARRAY(eqs)); } BlendFunc getRandomBlendFunc(de::Random &rng) { const uint32_t funcs[] = {GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE}; const uint32_t src = de::getSizedArrayElement(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs)); const uint32_t dst = de::getSizedArrayElement(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs)); return BlendFunc(src, dst); } void genRandomBlendState(de::Random &rng, BlendState &blendState) { if (rng.getBool()) blendState.enableBlend = rng.getBool(); if (rng.getBool()) { if (rng.getBool()) blendState.blendEq = getRandomBlendEq(rng); else { const BlendEq rgb = getRandomBlendEq(rng); const BlendEq alpha = getRandomBlendEq(rng); blendState.blendEq = SeparateBlendEq(rgb, alpha); } } if (rng.getBool()) { if (rng.getBool()) blendState.blendFunc = getRandomBlendFunc(rng); else { const BlendFunc rgb = getRandomBlendFunc(rng); const BlendFunc alpha = getRandomBlendFunc(rng); blendState.blendFunc = SeparateBlendFunc(rgb, alpha); } } if (rng.getBool()) { const bool red = rng.getBool(); const bool green = rng.getBool(); const bool blue = rng.getBool(); const bool alpha = rng.getBool(); blendState.colorMask = BVec4(red, blue, green, alpha); } } TextureFormat getRandomFormat(de::Random &rng, Context &context) { const bool supportsES32orGL45 = checkES32orGL45Support(context); const uint32_t glFormats[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGB565, GL_RGBA4, GL_RGB5_A1, GL_RGBA8, GL_RGB10_A2, GL_RGB10_A2UI, GL_R8I, GL_R8UI, GL_R16I, GL_R16UI, GL_R32I, GL_R32UI, GL_RG8I, GL_RG8UI, GL_RG16I, GL_RG16UI, GL_RG32I, GL_RG32UI, GL_RGBA8I, GL_RGBA8UI, GL_RGBA16I, GL_RGBA16UI, GL_RGBA32I, GL_RGBA32UI, GL_RGBA16F, GL_R32F, GL_RG32F, GL_RGBA32F, GL_R11F_G11F_B10F}; if (supportsES32orGL45) return glu::mapGLInternalFormat( de::getArrayElement(glFormats, rng.getUint32() % DE_LENGTH_OF_ARRAY(glFormats))); else { DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(glFormats) == 32); return glu::mapGLInternalFormat( de::getArrayElement(glFormats, rng.getUint32() % (DE_LENGTH_OF_ARRAY(glFormats) - 5))); } } void genRandomTest(de::Random &rng, BlendState &preCommon, BlendState &postCommon, vector &drawBuffers, int maxDrawBufferCount, Context &context) { genRandomBlendState(rng, preCommon); genRandomBlendState(rng, postCommon); for (int drawBufferNdx = 0; drawBufferNdx < maxDrawBufferCount; drawBufferNdx++) { const bool render = rng.getFloat() > 0.1f; const IVec2 size(64, 64); const TextureFormat format(getRandomFormat(rng, context)); BlendState blendState; genRandomBlendState(rng, blendState); // 32bit float formats don't support blending in GLES32 if (format.type == tcu::TextureFormat::FLOAT) { // If format is 32bit float post common can't enable blending if (postCommon.enableBlend && *postCommon.enableBlend) { // Either don't set enable blend or disable blending if (rng.getBool()) postCommon.enableBlend = tcu::Nothing; else postCommon.enableBlend = tcu::just(false); } // If post common doesn't disable blending, per attachment state or // pre common must. if (!postCommon.enableBlend) { // If pre common enables blend per attachment must disable it // If per attachment state changes blend state it must disable it if ((preCommon.enableBlend && *preCommon.enableBlend) || blendState.enableBlend) blendState.enableBlend = tcu::just(false); } } drawBuffers.push_back(DrawBufferInfo(render, size, blendState, format)); } } class MaxDrawBuffersIndexedTest : public TestCase { public: MaxDrawBuffersIndexedTest(Context &contet, int seed); void init(void); IterateResult iterate(void); private: const int m_seed; }; MaxDrawBuffersIndexedTest::MaxDrawBuffersIndexedTest(Context &context, int seed) : TestCase(context, de::toString(seed).c_str(), de::toString(seed).c_str()) , m_seed(deInt32Hash(seed) ^ 1558001307u) { } void MaxDrawBuffersIndexedTest::init(void) { const bool supportsES32orGL45 = checkES32orGL45Support(m_context); if (!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed")) TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported"); } TestCase::IterateResult MaxDrawBuffersIndexedTest::iterate(void) { TestLog &log = m_testCtx.getLog(); tcu::ResultCollector results(log); de::Random rng(m_seed); BlendState preCommonBlendState; BlendState postCommonBlendState; vector drawBuffers; genRandomTest(rng, preCommonBlendState, postCommonBlendState, drawBuffers, 4, m_context); runTest(log, results, m_context.getRenderContext(), preCommonBlendState, postCommonBlendState, drawBuffers); results.setTestContextResult(m_testCtx); return STOP; } class ImplMaxDrawBuffersIndexedTest : public TestCase { public: ImplMaxDrawBuffersIndexedTest(Context &contet, int seed); void init(void); IterateResult iterate(void); private: const int m_seed; }; ImplMaxDrawBuffersIndexedTest::ImplMaxDrawBuffersIndexedTest(Context &context, int seed) : TestCase(context, de::toString(seed).c_str(), de::toString(seed).c_str()) , m_seed(deInt32Hash(seed) ^ 2686315738u) { } void ImplMaxDrawBuffersIndexedTest::init(void) { const bool supportsES32orGL45 = checkES32orGL45Support(m_context); if (!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed")) TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported"); } TestCase::IterateResult ImplMaxDrawBuffersIndexedTest::iterate(void) { TestLog &log = m_testCtx.getLog(); tcu::ResultCollector results(log); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); de::Random rng(m_seed); int32_t maxDrawBuffers = 0; BlendState preCommonBlendState; BlendState postCommonBlendState; vector drawBuffers; gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_MAX_DRAW_BUFFERS) failed"); TCU_CHECK(maxDrawBuffers > 0); genRandomTest(rng, preCommonBlendState, postCommonBlendState, drawBuffers, maxDrawBuffers, m_context); runTest(log, results, m_context.getRenderContext(), preCommonBlendState, postCommonBlendState, drawBuffers); results.setTestContextResult(m_testCtx); return STOP; } enum PrePost { PRE, POST }; TestCase *createDiffTest(Context &context, PrePost prepost, const char *name, const BlendState &commonState, const BlendState &drawBufferState) { const BlendState emptyState = BlendState(tcu::Nothing, tcu::Nothing, tcu::Nothing, tcu::Nothing); if (prepost == PRE) { const BlendState preState = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)), commonState.blendEq, (commonState.blendFunc ? commonState.blendFunc : just(Either(BlendFunc(GL_ONE, GL_ONE)))), tcu::Nothing); vector drawBuffers; drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), emptyState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); return new DrawBuffersIndexedTest(context, preState, emptyState, drawBuffers, name, name); } else if (prepost == POST) { const BlendState preState = BlendState(just(true), tcu::Nothing, Maybe>(BlendFunc(GL_ONE, GL_ONE)), tcu::Nothing); vector drawBuffers; drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), emptyState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); return new DrawBuffersIndexedTest(context, preState, commonState, drawBuffers, name, name); } else { DE_ASSERT(false); return DE_NULL; } } TestCase *createAdvancedEqDiffTest(Context &context, PrePost prepost, const char *name, const BlendState &commonState, const BlendState &drawBufferState) { const BlendState emptyState = BlendState(tcu::Nothing, tcu::Nothing, tcu::Nothing, tcu::Nothing); if (prepost == PRE) { const BlendState preState = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)), commonState.blendEq, (commonState.blendFunc ? commonState.blendFunc : just(Either(BlendFunc(GL_ONE, GL_ONE)))), tcu::Nothing); vector drawBuffers; drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); return new DrawBuffersIndexedTest(context, preState, emptyState, drawBuffers, name, name); } else if (prepost == POST) { const BlendState preState = BlendState(just(true), tcu::Nothing, Maybe>(BlendFunc(GL_ONE, GL_ONE)), tcu::Nothing); vector drawBuffers; drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))); return new DrawBuffersIndexedTest(context, preState, commonState, drawBuffers, name, name); } else { DE_ASSERT(false); return DE_NULL; } } void addDrawBufferCommonTests(TestCaseGroup *root, PrePost prepost) { const BlendState emptyState = BlendState(Maybe(), Maybe>(), Maybe>(), Maybe()); { const BlendState disableState = BlendState(just(false), Maybe>(), Maybe>(), Maybe()); const BlendState enableState = BlendState(just(true), Maybe>(), Maybe>(), Maybe()); root->addChild( createDiffTest(root->getContext(), prepost, "common_enable_buffer_enable", enableState, enableState)); root->addChild( createDiffTest(root->getContext(), prepost, "common_disable_buffer_disable", disableState, disableState)); root->addChild( createDiffTest(root->getContext(), prepost, "common_disable_buffer_enable", disableState, enableState)); root->addChild( createDiffTest(root->getContext(), prepost, "common_enable_buffer_disable", enableState, disableState)); } { const BlendState eqStateA = BlendState(tcu::Nothing, Maybe>(GL_FUNC_ADD), Maybe>(), Maybe()); const BlendState eqStateB = BlendState(tcu::Nothing, Maybe>(GL_FUNC_SUBTRACT), Maybe>(), Maybe()); const BlendState separateEqStateA = BlendState( tcu::Nothing, Maybe>(SeparateBlendEq(GL_FUNC_ADD, GL_FUNC_SUBTRACT)), Maybe>(), Maybe()); const BlendState separateEqStateB = BlendState( tcu::Nothing, Maybe>(SeparateBlendEq(GL_FUNC_SUBTRACT, GL_FUNC_ADD)), Maybe>(), Maybe()); const BlendState advancedEqStateA = BlendState(tcu::Nothing, Maybe>(GL_DIFFERENCE), Maybe>(), Maybe()); const BlendState advancedEqStateB = BlendState(tcu::Nothing, Maybe>(GL_SCREEN), Maybe>(), Maybe()); root->addChild( createDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_blend_eq", eqStateA, eqStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_separate_blend_eq", eqStateA, separateEqStateB)); root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_advanced_blend_eq", eqStateA, advancedEqStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_blend_eq", separateEqStateA, eqStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_separate_blend_eq", separateEqStateA, separateEqStateB)); root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_advanced_blend_eq", separateEqStateA, advancedEqStateB)); root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_blend_eq", advancedEqStateA, eqStateB)); root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_separate_blend_eq", advancedEqStateA, separateEqStateB)); root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_advanced_blend_eq", advancedEqStateA, advancedEqStateB)); } { const BlendState funcStateA = BlendState( tcu::Nothing, Maybe>(), Maybe>(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA)), Maybe()); const BlendState funcStateB = BlendState( tcu::Nothing, Maybe>(), Maybe>(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA)), Maybe()); const BlendState separateFuncStateA = BlendState( tcu::Nothing, Maybe>(), Maybe>(SeparateBlendFunc( BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA), BlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA))), Maybe()); const BlendState separateFuncStateB = BlendState( tcu::Nothing, Maybe>(), Maybe>(SeparateBlendFunc( BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA), BlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA))), Maybe()); root->addChild( createDiffTest(root->getContext(), prepost, "common_blend_func_buffer_blend_func", funcStateA, funcStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_func_buffer_separate_blend_func", funcStateA, separateFuncStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_func_buffer_blend_func", separateFuncStateA, funcStateB)); root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_func_buffer_separate_blend_func", separateFuncStateA, separateFuncStateB)); } { const BlendState commonColorMaskState = BlendState(tcu::Nothing, Maybe>(), Maybe>(), Maybe(BVec4(true, false, true, false))); const BlendState bufferColorMaskState = BlendState(tcu::Nothing, Maybe>(), Maybe>(), Maybe(BVec4(false, true, false, true))); root->addChild(createDiffTest(root->getContext(), prepost, "common_color_mask_buffer_color_mask", commonColorMaskState, bufferColorMaskState)); } } void addRandomMaxTest(TestCaseGroup *root) { for (int i = 0; i < 20; i++) root->addChild(new MaxDrawBuffersIndexedTest(root->getContext(), i)); } void addRandomImplMaxTest(TestCaseGroup *root) { for (int i = 0; i < 20; i++) root->addChild(new ImplMaxDrawBuffersIndexedTest(root->getContext(), i)); } } // namespace TestCaseGroup *createDrawBuffersIndexedTests(Context &context) { const BlendState emptyState = BlendState(Maybe(), Maybe>(), Maybe>(), Maybe()); TestCaseGroup *const group = new TestCaseGroup(context, "draw_buffers_indexed", "Test for indexed draw buffers. GL_EXT_draw_buffers_indexed."); TestCaseGroup *const preGroup = new TestCaseGroup( context, "overwrite_common", "Set common state and overwrite it with draw buffer blend state."); TestCaseGroup *const postGroup = new TestCaseGroup(context, "overwrite_indexed", "Set indexed blend state and overwrite it with common state."); TestCaseGroup *const randomGroup = new TestCaseGroup(context, "random", "Random indexed blend state tests."); TestCaseGroup *const maxGroup = new TestCaseGroup(context, "max_required_draw_buffers", "Random tests using minimum maximum number of draw buffers."); TestCaseGroup *const maxImplGroup = new TestCaseGroup(context, "max_implementation_draw_buffers", "Random tests using maximum number of draw buffers reported by implementation."); group->addChild(preGroup); group->addChild(postGroup); group->addChild(randomGroup); randomGroup->addChild(maxGroup); randomGroup->addChild(maxImplGroup); addDrawBufferCommonTests(preGroup, PRE); addDrawBufferCommonTests(postGroup, POST); addRandomMaxTest(maxGroup); addRandomImplMaxTest(maxImplGroup); return group; } } // namespace Functional } // namespace gles3 } // namespace deqp