/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 3.1 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 "es31fDrawBuffersIndexedTests.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 <string>
#include <vector>
#include <map>
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 gles31
{
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<bool>& enableBlend_,
const Maybe<Either<BlendEq, SeparateBlendEq> >& blendEq_,
const Maybe<Either<BlendFunc, SeparateBlendFunc> >& blendFunc_,
const Maybe<BVec4>& colorMask_)
: enableBlend (enableBlend_)
, blendEq (blendEq_)
, blendFunc (blendFunc_)
, colorMask (colorMask_)
{
}
bool isEmpty (void) const
{
return (!enableBlend) && (!blendEq) && (!blendFunc) && (!colorMask);
}
Maybe<bool> enableBlend;
Maybe<Either<BlendEq, SeparateBlendEq> > blendEq;
Maybe<Either<BlendFunc, SeparateBlendFunc> > blendFunc;
Maybe<BVec4> colorMask;
};
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, SeparateBlendEq>& blendEq = *blend.blendEq;
if (blendEq.is<BlendEq>())
gl.blendEquation(blendEq.get<BlendEq>());
else if (blendEq.is<SeparateBlendEq>())
gl.blendEquationSeparate(blendEq.get<SeparateBlendEq>().rgb, blendEq.get<SeparateBlendEq>().alpha);
else
DE_ASSERT(false);
}
if (blend.blendFunc)
{
const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
if (blendFunc.is<BlendFunc>())
gl.blendFunc(blendFunc.get<BlendFunc>().src, blendFunc.get<BlendFunc>().dst);
else if (blendFunc.is<SeparateBlendFunc>())
gl.blendFuncSeparate(blendFunc.get<SeparateBlendFunc>().rgb.src, blendFunc.get<SeparateBlendFunc>().rgb.dst, blendFunc.get<SeparateBlendFunc>().alpha.src, blendFunc.get<SeparateBlendFunc>().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, SeparateBlendEq>& blendEq = *blend.blendEq;
if (blendEq.is<BlendEq>())
gl.blendEquationi(index, blendEq.get<BlendEq>());
else if (blendEq.is<SeparateBlendEq>())
gl.blendEquationSeparatei(index, blendEq.get<SeparateBlendEq>().rgb, blendEq.get<SeparateBlendEq>().alpha);
else
DE_ASSERT(false);
}
if (blend.blendFunc)
{
const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
if (blendFunc.is<BlendFunc>())
gl.blendFunci(index, blendFunc.get<BlendFunc>().src, blendFunc.get<BlendFunc>().dst);
else if (blendFunc.is<SeparateBlendFunc>())
gl.blendFuncSeparatei(index, blendFunc.get<SeparateBlendFunc>().rgb.src, blendFunc.get<SeparateBlendFunc>().rgb.dst, blendFunc.get<SeparateBlendFunc>().alpha.src, blendFunc.get<SeparateBlendFunc>().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<DrawBufferInfo>& drawBuffers,
const glu::Framebuffer& framebuffer,
const glu::RenderbufferVector& renderbuffers,
vector<TextureLevel>& refRenderbuffers)
{
vector<deUint32> 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<deUint64, 4>(1) << (tcu::min(srcBits, readBits).cast<deUint64>())) - tcu::Vector<deUint64, 4>(1)).cast<float>();
}
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<deUint32>()));
}
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<TextureLevel>& 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<BlendEq>())
{
if (isAdvancedBlendEq(state.blendEq->get<BlendEq>()))
{
const rr::BlendEquationAdvanced equation = mapGLBlendEquationAdvanced(state.blendEq->get<BlendEq>());
fragOps.blendMode = rr::BLENDMODE_ADVANCED;
fragOps.blendEquationAdvaced = equation;
}
else
{
const rr::BlendEquation equation = mapGLBlendEquation(state.blendEq->get<BlendEq>());
fragOps.blendMode = rr::BLENDMODE_STANDARD;
fragOps.blendRGBState.equation = equation;
fragOps.blendAState.equation = equation;
}
}
else
{
DE_ASSERT(state.blendEq->is<SeparateBlendEq>());
fragOps.blendMode = rr::BLENDMODE_STANDARD;
fragOps.blendRGBState.equation = mapGLBlendEquation(state.blendEq->get<SeparateBlendEq>().rgb);
fragOps.blendAState.equation = mapGLBlendEquation(state.blendEq->get<SeparateBlendEq>().alpha);
}
}
if (state.blendFunc)
{
if (state.blendFunc->is<BlendFunc>())
{
const rr::BlendFunc srcFunction = mapGLBlendFunc(state.blendFunc->get<BlendFunc>().src);
const rr::BlendFunc dstFunction = mapGLBlendFunc(state.blendFunc->get<BlendFunc>().dst);
fragOps.blendRGBState.srcFunc = srcFunction;
fragOps.blendRGBState.dstFunc = dstFunction;
fragOps.blendAState.srcFunc = srcFunction;
fragOps.blendAState.dstFunc = dstFunction;
}
else
{
DE_ASSERT(state.blendFunc->is<SeparateBlendFunc>());
fragOps.blendRGBState.srcFunc = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().rgb.src);
fragOps.blendRGBState.dstFunc = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().rgb.dst);
fragOps.blendAState.srcFunc = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().alpha.src);
fragOps.blendAState.dstFunc = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().alpha.dst);
}
}
if (state.colorMask)
fragOps.colorMask = *state.colorMask;
}
rr::RenderState createRenderState (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const DrawBufferInfo& info)
{
const IVec2 size = info.getSize();
rr::RenderState state (rr::ViewportState(rr::WindowRectangle(0, 0, size.x(), size.y())));
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<float>(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<DrawBufferInfo>& drawBuffers,
vector<TextureLevel>& 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]));
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<DrawBufferInfo>& drawBuffers)
{
bool requiresAdvancedBlendEq = false;
if (pre.blendEq && pre.blendEq->is<BlendEq>())
requiresAdvancedBlendEq |= isAdvancedBlendEq(pre.blendEq->get<BlendEq>());
if (post.blendEq && post.blendEq->is<BlendEq>())
requiresAdvancedBlendEq |= isAdvancedBlendEq(post.blendEq->get<BlendEq>());
for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
{
const BlendState& drawBufferBlendState = drawBuffers[drawBufferNdx].getBlendState();
if (drawBufferBlendState.blendEq && drawBufferBlendState.blendEq->is<BlendEq>())
requiresAdvancedBlendEq |= isAdvancedBlendEq(drawBufferBlendState.blendEq->get<BlendEq>());
}
return requiresAdvancedBlendEq;
}
glu::VertexSource genVertexSource (glu::RenderContext& renderContext)
{
const bool isES32 = 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<string, string> args;
args["GLSL_VERSION_DECL"] = isES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES);
return glu::VertexSource(tcu::StringTemplate(vertexSource).specialize(args));
}
glu::FragmentSource genFragmentSource (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const vector<DrawBufferInfo>& drawBuffers, glu::RenderContext& renderContext)
{
std::ostringstream stream;
const bool isES32 = 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<string, string> args;
args["GLSL_VERSION_DECL"] = isES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES);
args["GLSL_EXTENSION"] = isES32 ? "\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<DrawBufferInfo>& 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<DrawBufferInfo>& drawBuffers,
const glu::Framebuffer& framebuffer,
vector<TextureLevel>& 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<deUint32> 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");
renderRefQuad(preCommonBlendState, postCommonBlendState, drawBuffers, 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, SeparateBlendEq>& blendEq = *blend.blendEq;
if (blendEq.is<BlendEq>())
log << TestLog::Message << "Set blend equation: " << glu::getBlendEquationStr(blendEq.get<BlendEq>()) << "." << TestLog::EndMessage;
else if (blendEq.is<SeparateBlendEq>())
log << TestLog::Message << "Set blend equation rgb: " << glu::getBlendEquationStr(blendEq.get<SeparateBlendEq>().rgb) << ", alpha: " << glu::getBlendEquationStr(blendEq.get<SeparateBlendEq>().alpha) << "." << TestLog::EndMessage;
else
DE_ASSERT(false);
}
if (blend.blendFunc)
{
const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
if (blendFunc.is<BlendFunc>())
log << TestLog::Message << "Set blend function source: " << glu::getBlendFactorStr(blendFunc.get<BlendFunc>().src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<BlendFunc>().dst) << "." << TestLog::EndMessage;
else if (blendFunc.is<SeparateBlendFunc>())
{
log << TestLog::Message << "Set blend function rgb source: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().rgb.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().rgb.dst) << "." << TestLog::EndMessage;
log << TestLog::Message << "Set blend function alpha source: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().alpha.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().alpha.dst) << "." << TestLog::EndMessage;
}
else
DE_ASSERT(false);
}
}
void logTestCaseInfo (TestLog& log,
const BlendState& preCommonBlendState,
const BlendState& postCommonBlendState,
const vector<DrawBufferInfo>& 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<DrawBufferInfo>& drawBuffers)
{
const glw::Functions& gl = renderContext.getFunctions();
glu::RenderbufferVector renderbuffers (gl, drawBuffers.size());
glu::Framebuffer framebuffer (gl);
vector<TextureLevel> 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<DrawBufferInfo>& drawBuffers,
const string& name,
const string& description);
void init (void);
IterateResult iterate (void);
private:
const BlendState m_preCommonBlendState;
const BlendState m_postCommonBlendState;
const vector<DrawBufferInfo> m_drawBuffers;
};
DrawBuffersIndexedTest::DrawBuffersIndexedTest (Context& context,
const BlendState& preCommonBlendState,
const BlendState& postCommonBlendState,
const vector<DrawBufferInfo>& 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 isES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
if (!isES32)
{
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<DE_LENGTH_OF_ARRAY(eqs)>(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<DE_LENGTH_OF_ARRAY(funcs)>(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs));
const deUint32 dst = de::getSizedArrayElement<DE_LENGTH_OF_ARRAY(funcs)>(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 isES32 = glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2));
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 (isES32)
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<DrawBufferInfo>& 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<bool>();
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 isES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
if (!isES32 && !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<DrawBufferInfo> 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 isES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
if (!isES32 && !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<DrawBufferInfo> 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<bool>(), tcu::nothing<Either<BlendEq, SeparateBlendEq> >(), tcu::nothing<Either<BlendFunc, SeparateBlendFunc> >(), tcu::nothing<BVec4>());
if (prepost == PRE)
{
const BlendState preState = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)),
commonState.blendEq,
(commonState.blendFunc ? commonState.blendFunc : just(Either<BlendFunc, SeparateBlendFunc>(BlendFunc(GL_ONE, GL_ONE)))),
tcu::nothing<BVec4>());
vector<DrawBufferInfo> 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<Either<BlendEq, SeparateBlendEq> >(),
Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
tcu::nothing<BVec4>());
vector<DrawBufferInfo> 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<bool>(), tcu::nothing<Either<BlendEq, SeparateBlendEq> >(), tcu::nothing<Either<BlendFunc, SeparateBlendFunc> >(), tcu::nothing<BVec4>());
if (prepost == PRE)
{
const BlendState preState = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)),
commonState.blendEq,
(commonState.blendFunc ? commonState.blendFunc : just(Either<BlendFunc, SeparateBlendFunc>(BlendFunc(GL_ONE, GL_ONE)))),
tcu::nothing<BVec4>());
vector<DrawBufferInfo> 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<Either<BlendEq, SeparateBlendEq> >(),
Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
tcu::nothing<BVec4>());
vector<DrawBufferInfo> 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<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
{
const BlendState disableState = BlendState(just(false), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState enableState = BlendState(just(true), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
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<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_ADD), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState eqStateB = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_SUBTRACT), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState separateEqStateA = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_ADD, GL_FUNC_SUBTRACT)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState separateEqStateB = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_SUBTRACT, GL_FUNC_ADD)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState advancedEqStateA = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_DIFFERENCE), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
const BlendState advancedEqStateB = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_SCREEN), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
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<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA)), Maybe<BVec4>());
const BlendState funcStateB = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA)), Maybe<BVec4>());
const BlendState separateFuncStateA = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(SeparateBlendFunc(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA), BlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA))), Maybe<BVec4>());
const BlendState separateFuncStateB = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(SeparateBlendFunc(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA), BlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA))), Maybe<BVec4>());
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<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>(BVec4(true, false, true, false)));
const BlendState bufferColorMaskState = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>(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<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
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
} // gles31
} // deqp