From: Matthew Netsch Date: Thu, 2 Jun 2022 21:22:12 +0000 (+0000) Subject: Merge vk-gl-cts/opengl-es-cts-3.2.8 into vk-gl-cts/main X-Git-Tag: upstream/1.3.5~312 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=219e8b8065b875326720bc45513cd2cff462c2ae;p=platform%2Fupstream%2FVK-GL-CTS.git Merge vk-gl-cts/opengl-es-cts-3.2.8 into vk-gl-cts/main Change-Id: I011f48b25cfd0f2b951425ea770220a6dcaa3e1a --- 219e8b8065b875326720bc45513cd2cff462c2ae diff --cc modules/gles3/functional/es3fDrawBuffersIndexedTests.cpp index 7310de8,0000000..d6e3957 mode 100644,000000..100644 --- a/modules/gles3/functional/es3fDrawBuffersIndexedTests.cpp +++ b/modules/gles3/functional/es3fDrawBuffersIndexedTests.cpp @@@ -1,1675 -1,0 +1,1687 @@@ +/*------------------------------------------------------------------------- + * 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::Maybe; +using tcu::TestLog; +using tcu::TextureFormat; +using tcu::TextureLevel; +using tcu::UVec4; +using tcu::Vec2; +using tcu::Vec4; +using tcu::just; + +using std::string; +using std::vector; +using std::map; + +using sglr::rr_util::mapGLBlendEquation; +using sglr::rr_util::mapGLBlendFunc; +using sglr::rr_util::mapGLBlendEquationAdvanced; + +namespace deqp +{ +namespace gles3 +{ +namespace Functional +{ +namespace +{ + +typedef deUint32 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 (deUint32 src_, deUint32 dst_) + : src (src_) + , dst (dst_) + { + } + + deUint32 src; + deUint32 dst; +}; + +struct SeparateBlendFunc +{ + SeparateBlendFunc (BlendFunc rgb_, BlendFunc alpha_) + : rgb (rgb_) + , alpha (alpha_) + { + } + + BlendFunc rgb; + BlendFunc alpha; +}; + +typedef deUint32 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, deUint32 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 deInt32 red = deInt32(info.valueMin.x() + (info.valueMax.x() - info.valueMin.x()) * redScale); + const deInt32 green = deInt32(info.valueMin.y() + (info.valueMax.y() - info.valueMin.y()) * greenScale); + const deInt32 blue = deInt32(info.valueMin.z() + (info.valueMax.z() - info.valueMin.z()) * blueScale); + const deInt32 alpha = deInt32(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 deUint32 red = deUint32(info.valueMax.x() * redScale); + const deUint32 green = deUint32(info.valueMax.y() * greenScale); + const deUint32 blue = deUint32(info.valueMax.z() * blueScale); + const deUint32 alpha = deUint32(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(DE_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 deUint32 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); + - return Vec4(3.0f) / ((tcu::Vector(1) << (tcu::min(srcBits, readBits).cast())) - tcu::Vector(1)).cast(); ++ 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(DE_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 ((deUint32)(values[(m_drawBufferNdx + 0) % 4]), + (deUint32)(values[(m_drawBufferNdx + 1) % 4]), + (deUint32)(values[(m_drawBufferNdx + 2) % 4]), + (deUint32)(values[(m_drawBufferNdx + 3) % 4])); + + rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color); + break; + } + + case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: + { + const IVec4 color ((deInt32)(values[(m_drawBufferNdx + 0) % 4]), + (deInt32)(values[(m_drawBufferNdx + 1) % 4]), + (deInt32)(values[(m_drawBufferNdx + 2) % 4]), + (deInt32)(values[(m_drawBufferNdx + 3) % 4])); + + rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color); + break; + } + + default: + DE_ASSERT(DE_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(DE_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(DE_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 deUint32 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 deUint32 src = de::getSizedArrayElement(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs)); + const deUint32 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 deUint32 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); + deInt32 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)); +} + +} // anonymous + +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; +} + +} // Functional +} // gles3 +} // deqp