blob: 7484ea37609a6cc92fab2196e79e31266c9ed81f [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program Reference Renderer
* -----------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Reference implementation for per-fragment operations.
*//*--------------------------------------------------------------------*/
#include "rrFragmentOperations.hpp"
#include "tcuVectorUtil.hpp"
#include "tcuTextureUtil.hpp"
#include <limits>
using de::clamp;
using de::max;
using de::min;
using tcu::clamp;
using tcu::IVec2;
using tcu::IVec4;
using tcu::max;
using tcu::min;
using tcu::UVec4;
using tcu::Vec3;
using tcu::Vec4;
namespace rr
{
// Return oldValue with the bits indicated by mask replaced by corresponding bits of newValue.
static inline int maskedBitReplace(int oldValue, int newValue, uint32_t mask)
{
return (oldValue & ~mask) | (newValue & mask);
}
static inline bool isInsideRect(const IVec2 &point, const WindowRectangle &rect)
{
return de::inBounds(point.x(), rect.left, rect.left + rect.width) &&
de::inBounds(point.y(), rect.bottom, rect.bottom + rect.height);
}
static inline Vec4 unpremultiply(const Vec4 &v)
{
if (v.w() > 0.0f)
return Vec4(v.x() / v.w(), v.y() / v.w(), v.z() / v.w(), v.w());
else
{
DE_ASSERT(v.x() == 0.0f && v.y() == 0.0f && v.z() == 0.0f);
return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
}
}
void clearMultisampleColorBuffer(const tcu::PixelBufferAccess &dst, const Vec4 &v, const WindowRectangle &r)
{
tcu::clear(tcu::getSubregion(dst, 0, r.left, r.bottom, dst.getWidth(), r.width, r.height), v);
}
void clearMultisampleColorBuffer(const tcu::PixelBufferAccess &dst, const IVec4 &v, const WindowRectangle &r)
{
tcu::clear(tcu::getSubregion(dst, 0, r.left, r.bottom, dst.getWidth(), r.width, r.height), v);
}
void clearMultisampleColorBuffer(const tcu::PixelBufferAccess &dst, const UVec4 &v, const WindowRectangle &r)
{
tcu::clear(tcu::getSubregion(dst, 0, r.left, r.bottom, dst.getWidth(), r.width, r.height), v.cast<int>());
}
void clearMultisampleDepthBuffer(const tcu::PixelBufferAccess &dst, float v, const WindowRectangle &r)
{
tcu::clearDepth(tcu::getSubregion(dst, 0, r.left, r.bottom, dst.getWidth(), r.width, r.height), v);
}
void clearMultisampleStencilBuffer(const tcu::PixelBufferAccess &dst, int v, const WindowRectangle &r)
{
tcu::clearStencil(tcu::getSubregion(dst, 0, r.left, r.bottom, dst.getWidth(), r.width, r.height), v);
}
FragmentProcessor::FragmentProcessor(void) : m_sampleRegister()
{
}
void FragmentProcessor::executeScissorTest(int fragNdxOffset, int numSamplesPerFragment, const Fragment *inputFragments,
const WindowRectangle &scissorRect)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragNdx = fragNdxOffset + regSampleNdx / numSamplesPerFragment;
if (!isInsideRect(inputFragments[fragNdx].pixelCoord, scissorRect))
m_sampleRegister[regSampleNdx].isAlive = false;
}
}
}
void FragmentProcessor::executeStencilCompare(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const StencilState &stencilState,
int numStencilBits, const tcu::ConstPixelBufferAccess &stencilBuffer)
{
#define SAMPLE_REGISTER_STENCIL_COMPARE(COMPARE_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
int fragSampleNdx = regSampleNdx % numSamplesPerFragment; \
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment]; \
int stencilBufferValue = \
stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()); \
int maskedRef = stencilState.compMask & clampedStencilRef; \
int maskedBuf = stencilState.compMask & stencilBufferValue; \
DE_UNREF(maskedRef); \
DE_UNREF(maskedBuf); \
\
m_sampleRegister[regSampleNdx].stencilPassed = (COMPARE_EXPRESSION); \
} \
}
int clampedStencilRef = de::clamp(stencilState.ref, 0, (1 << numStencilBits) - 1);
switch (stencilState.func)
{
case TESTFUNC_NEVER:
SAMPLE_REGISTER_STENCIL_COMPARE(false) break;
case TESTFUNC_ALWAYS:
SAMPLE_REGISTER_STENCIL_COMPARE(true) break;
case TESTFUNC_LESS:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef < maskedBuf) break;
case TESTFUNC_LEQUAL:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef <= maskedBuf) break;
case TESTFUNC_GREATER:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef > maskedBuf) break;
case TESTFUNC_GEQUAL:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef >= maskedBuf) break;
case TESTFUNC_EQUAL:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef == maskedBuf) break;
case TESTFUNC_NOTEQUAL:
SAMPLE_REGISTER_STENCIL_COMPARE(maskedRef != maskedBuf) break;
default:
DE_ASSERT(false);
}
#undef SAMPLE_REGISTER_STENCIL_COMPARE
}
void FragmentProcessor::executeStencilSFail(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const StencilState &stencilState,
int numStencilBits, const tcu::PixelBufferAccess &stencilBuffer)
{
#define SAMPLE_REGISTER_SFAIL(SFAIL_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive && !m_sampleRegister[regSampleNdx].stencilPassed) \
{ \
int fragSampleNdx = regSampleNdx % numSamplesPerFragment; \
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment]; \
int stencilBufferValue = \
stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()); \
\
stencilBuffer.setPixStencil( \
maskedBitReplace(stencilBufferValue, (SFAIL_EXPRESSION), stencilState.writeMask), fragSampleNdx, \
frag.pixelCoord.x(), frag.pixelCoord.y()); \
m_sampleRegister[regSampleNdx].isAlive = false; \
} \
}
int clampedStencilRef = de::clamp(stencilState.ref, 0, (1 << numStencilBits) - 1);
switch (stencilState.sFail)
{
case STENCILOP_KEEP:
SAMPLE_REGISTER_SFAIL(stencilBufferValue) break;
case STENCILOP_ZERO:
SAMPLE_REGISTER_SFAIL(0) break;
case STENCILOP_REPLACE:
SAMPLE_REGISTER_SFAIL(clampedStencilRef) break;
case STENCILOP_INCR:
SAMPLE_REGISTER_SFAIL(de::clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1)) break;
case STENCILOP_DECR:
SAMPLE_REGISTER_SFAIL(de::clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1)) break;
case STENCILOP_INCR_WRAP:
SAMPLE_REGISTER_SFAIL((stencilBufferValue + 1) & ((1 << numStencilBits) - 1)) break;
case STENCILOP_DECR_WRAP:
SAMPLE_REGISTER_SFAIL((stencilBufferValue - 1) & ((1 << numStencilBits) - 1)) break;
case STENCILOP_INVERT:
SAMPLE_REGISTER_SFAIL((~stencilBufferValue) & ((1 << numStencilBits) - 1)) break;
default:
DE_ASSERT(false);
}
#undef SAMPLE_REGISTER_SFAIL
}
void FragmentProcessor::executeDepthBoundsTest(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const float minDepthBound,
const float maxDepthBound,
const tcu::ConstPixelBufferAccess &depthBuffer)
{
if (depthBuffer.getFormat().type == tcu::TextureFormat::FLOAT ||
depthBuffer.getFormat().type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; ++regSampleNdx)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
const float depthBufferValue =
depthBuffer.getPixDepth(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
if (!de::inRange(depthBufferValue, minDepthBound, maxDepthBound))
m_sampleRegister[regSampleNdx].isAlive = false;
}
}
}
else
{
/* Convert float bounds to target buffer format for comparison */
uint32_t minDepthBoundUint, maxDepthBoundUint;
{
uint32_t buffer[2];
DE_ASSERT(sizeof(buffer) >= (size_t)depthBuffer.getFormat().getPixelSize());
tcu::PixelBufferAccess access(depthBuffer.getFormat(), 1, 1, 1, &buffer);
access.setPixDepth(minDepthBound, 0, 0, 0);
minDepthBoundUint = access.getPixelUint(0, 0, 0).x();
}
{
uint32_t buffer[2];
tcu::PixelBufferAccess access(depthBuffer.getFormat(), 1, 1, 1, &buffer);
access.setPixDepth(maxDepthBound, 0, 0, 0);
maxDepthBoundUint = access.getPixelUint(0, 0, 0).x();
}
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; ++regSampleNdx)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
const uint32_t depthBufferValue =
depthBuffer.getPixelUint(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()).x();
if (!de::inRange(depthBufferValue, minDepthBoundUint, maxDepthBoundUint))
m_sampleRegister[regSampleNdx].isAlive = false;
}
}
}
}
void FragmentProcessor::executeDepthCompare(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, TestFunc depthFunc,
const tcu::ConstPixelBufferAccess &depthBuffer)
{
#define SAMPLE_REGISTER_DEPTH_COMPARE_F(COMPARE_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
int fragSampleNdx = regSampleNdx % numSamplesPerFragment; \
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment]; \
float depthBufferValue = depthBuffer.getPixDepth(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()); \
float sampleDepthFloat = frag.sampleDepths[fragSampleNdx]; \
float sampleDepth = de::clamp(sampleDepthFloat, 0.0f, 1.0f); \
\
m_sampleRegister[regSampleNdx].depthPassed = (COMPARE_EXPRESSION); \
\
DE_UNREF(depthBufferValue); \
DE_UNREF(sampleDepth); \
} \
}
#define SAMPLE_REGISTER_DEPTH_COMPARE_UI(COMPARE_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
int fragSampleNdx = regSampleNdx % numSamplesPerFragment; \
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment]; \
uint32_t depthBufferValue = \
depthBuffer.getPixelUint(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()).x(); \
float sampleDepthFloat = frag.sampleDepths[fragSampleNdx]; \
\
/* Convert input float to target buffer format for comparison */ \
\
uint32_t buffer[2]; \
\
DE_ASSERT(sizeof(buffer) >= (size_t)depthBuffer.getFormat().getPixelSize()); \
\
tcu::PixelBufferAccess access(depthBuffer.getFormat(), 1, 1, 1, &buffer); \
access.setPixDepth(sampleDepthFloat, 0, 0, 0); \
uint32_t sampleDepth = access.getPixelUint(0, 0, 0).x(); \
\
m_sampleRegister[regSampleNdx].depthPassed = (COMPARE_EXPRESSION); \
\
DE_UNREF(depthBufferValue); \
DE_UNREF(sampleDepth); \
} \
}
if (depthBuffer.getFormat().type == tcu::TextureFormat::FLOAT ||
depthBuffer.getFormat().type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV)
{
switch (depthFunc)
{
case TESTFUNC_NEVER:
SAMPLE_REGISTER_DEPTH_COMPARE_F(false) break;
case TESTFUNC_ALWAYS:
SAMPLE_REGISTER_DEPTH_COMPARE_F(true) break;
case TESTFUNC_LESS:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth < depthBufferValue) break;
case TESTFUNC_LEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth <= depthBufferValue) break;
case TESTFUNC_GREATER:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth > depthBufferValue) break;
case TESTFUNC_GEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth >= depthBufferValue) break;
case TESTFUNC_EQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth == depthBufferValue) break;
case TESTFUNC_NOTEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_F(sampleDepth != depthBufferValue) break;
default:
DE_ASSERT(false);
}
}
else
{
switch (depthFunc)
{
case TESTFUNC_NEVER:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(false) break;
case TESTFUNC_ALWAYS:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(true) break;
case TESTFUNC_LESS:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth < depthBufferValue) break;
case TESTFUNC_LEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth <= depthBufferValue) break;
case TESTFUNC_GREATER:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth > depthBufferValue) break;
case TESTFUNC_GEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth >= depthBufferValue) break;
case TESTFUNC_EQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth == depthBufferValue) break;
case TESTFUNC_NOTEQUAL:
SAMPLE_REGISTER_DEPTH_COMPARE_UI(sampleDepth != depthBufferValue) break;
default:
DE_ASSERT(false);
}
}
#undef SAMPLE_REGISTER_DEPTH_COMPARE_F
#undef SAMPLE_REGISTER_DEPTH_COMPARE_UI
}
void FragmentProcessor::executeDepthWrite(int fragNdxOffset, int numSamplesPerFragment, const Fragment *inputFragments,
const tcu::PixelBufferAccess &depthBuffer)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive && m_sampleRegister[regSampleNdx].depthPassed)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
const float clampedDepth = de::clamp(frag.sampleDepths[fragSampleNdx], 0.0f, 1.0f);
depthBuffer.setPixDepth(clampedDepth, fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
}
}
}
void FragmentProcessor::executeStencilDpFailAndPass(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const StencilState &stencilState,
int numStencilBits, const tcu::PixelBufferAccess &stencilBuffer)
{
#define SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive && (CONDITION)) \
{ \
int fragSampleNdx = regSampleNdx % numSamplesPerFragment; \
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment]; \
int stencilBufferValue = \
stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()); \
\
stencilBuffer.setPixStencil(maskedBitReplace(stencilBufferValue, (EXPRESSION), stencilState.writeMask), \
fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y()); \
} \
}
#define SWITCH_DPFAIL_OR_DPPASS(OP_NAME, CONDITION) \
switch (stencilState.OP_NAME) \
{ \
case STENCILOP_KEEP: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, stencilBufferValue) break; \
case STENCILOP_ZERO: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, 0) break; \
case STENCILOP_REPLACE: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, clampedStencilRef) break; \
case STENCILOP_INCR: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, de::clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1)) \
break; \
case STENCILOP_DECR: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, de::clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1)) \
break; \
case STENCILOP_INCR_WRAP: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, (stencilBufferValue + 1) & ((1 << numStencilBits) - 1)) break; \
case STENCILOP_DECR_WRAP: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, (stencilBufferValue - 1) & ((1 << numStencilBits) - 1)) break; \
case STENCILOP_INVERT: \
SAMPLE_REGISTER_DPFAIL_OR_DPPASS(CONDITION, (~stencilBufferValue) & ((1 << numStencilBits) - 1)) break; \
default: \
DE_ASSERT(false); \
}
int clampedStencilRef = de::clamp(stencilState.ref, 0, (1 << numStencilBits) - 1);
SWITCH_DPFAIL_OR_DPPASS(dpFail, !m_sampleRegister[regSampleNdx].depthPassed)
SWITCH_DPFAIL_OR_DPPASS(dpPass, m_sampleRegister[regSampleNdx].depthPassed)
#undef SWITCH_DPFAIL_OR_DPPASS
#undef SAMPLE_REGISTER_DPFAIL_OR_DPPASS
}
void FragmentProcessor::executeBlendFactorComputeRGB(const Vec4 &blendColor, const BlendState &blendRGBState)
{
#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
const Vec4 &src = m_sampleRegister[regSampleNdx].clampedBlendSrcColor; \
const Vec4 &src1 = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color; \
const Vec4 &dst = m_sampleRegister[regSampleNdx].clampedBlendDstColor; \
DE_UNREF(src); \
DE_UNREF(src1); \
DE_UNREF(dst); \
\
m_sampleRegister[regSampleNdx].FACTOR_NAME = (FACTOR_EXPRESSION); \
} \
}
#define SWITCH_SRC_OR_DST_FACTOR_RGB(FUNC_NAME, FACTOR_NAME) \
switch (blendRGBState.FUNC_NAME) \
{ \
case BLENDFUNC_ZERO: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(0.0f)) break; \
case BLENDFUNC_ONE: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f)) break; \
case BLENDFUNC_SRC_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src.swizzle(0, 1, 2)) break; \
case BLENDFUNC_ONE_MINUS_SRC_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f) - src.swizzle(0, 1, 2)) break; \
case BLENDFUNC_DST_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, dst.swizzle(0, 1, 2)) break; \
case BLENDFUNC_ONE_MINUS_DST_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f) - dst.swizzle(0, 1, 2)) break; \
case BLENDFUNC_SRC_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(src.w())) break; \
case BLENDFUNC_ONE_MINUS_SRC_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f - src.w())) break; \
case BLENDFUNC_DST_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(dst.w())) break; \
case BLENDFUNC_ONE_MINUS_DST_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f - dst.w())) break; \
case BLENDFUNC_CONSTANT_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, blendColor.swizzle(0, 1, 2)) break; \
case BLENDFUNC_ONE_MINUS_CONSTANT_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f) - blendColor.swizzle(0, 1, 2)) break; \
case BLENDFUNC_CONSTANT_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(blendColor.w())) break; \
case BLENDFUNC_ONE_MINUS_CONSTANT_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f - blendColor.w())) break; \
case BLENDFUNC_SRC_ALPHA_SATURATE: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(de::min(src.w(), 1.0f - dst.w()))) break; \
case BLENDFUNC_SRC1_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src1.swizzle(0, 1, 2)) break; \
case BLENDFUNC_ONE_MINUS_SRC1_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f) - src1.swizzle(0, 1, 2)) break; \
case BLENDFUNC_SRC1_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(src1.w())) break; \
case BLENDFUNC_ONE_MINUS_SRC1_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, Vec3(1.0f - src1.w())) break; \
default: \
DE_ASSERT(false); \
}
SWITCH_SRC_OR_DST_FACTOR_RGB(srcFunc, blendSrcFactorRGB)
SWITCH_SRC_OR_DST_FACTOR_RGB(dstFunc, blendDstFactorRGB)
#undef SWITCH_SRC_OR_DST_FACTOR_RGB
#undef SAMPLE_REGISTER_BLEND_FACTOR
}
void FragmentProcessor::executeBlendFactorComputeA(const Vec4 &blendColor, const BlendState &blendAState)
{
#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
const Vec4 &src = m_sampleRegister[regSampleNdx].clampedBlendSrcColor; \
const Vec4 &src1 = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color; \
const Vec4 &dst = m_sampleRegister[regSampleNdx].clampedBlendDstColor; \
DE_UNREF(src); \
DE_UNREF(src1); \
DE_UNREF(dst); \
\
m_sampleRegister[regSampleNdx].FACTOR_NAME = (FACTOR_EXPRESSION); \
} \
}
#define SWITCH_SRC_OR_DST_FACTOR_A(FUNC_NAME, FACTOR_NAME) \
switch (blendAState.FUNC_NAME) \
{ \
case BLENDFUNC_ZERO: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 0.0f) break; \
case BLENDFUNC_ONE: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f) break; \
case BLENDFUNC_SRC_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src.w()) break; \
case BLENDFUNC_ONE_MINUS_SRC_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - src.w()) break; \
case BLENDFUNC_DST_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, dst.w()) break; \
case BLENDFUNC_ONE_MINUS_DST_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - dst.w()) break; \
case BLENDFUNC_SRC_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src.w()) break; \
case BLENDFUNC_ONE_MINUS_SRC_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - src.w()) break; \
case BLENDFUNC_DST_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, dst.w()) break; \
case BLENDFUNC_ONE_MINUS_DST_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - dst.w()) break; \
case BLENDFUNC_CONSTANT_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, blendColor.w()) break; \
case BLENDFUNC_ONE_MINUS_CONSTANT_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - blendColor.w()) break; \
case BLENDFUNC_CONSTANT_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, blendColor.w()) break; \
case BLENDFUNC_ONE_MINUS_CONSTANT_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - blendColor.w()) break; \
case BLENDFUNC_SRC_ALPHA_SATURATE: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f) break; \
case BLENDFUNC_SRC1_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src1.w()) break; \
case BLENDFUNC_ONE_MINUS_SRC1_COLOR: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - src1.w()) break; \
case BLENDFUNC_SRC1_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, src1.w()) break; \
case BLENDFUNC_ONE_MINUS_SRC1_ALPHA: \
SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, 1.0f - src1.w()) break; \
default: \
DE_ASSERT(false); \
}
SWITCH_SRC_OR_DST_FACTOR_A(srcFunc, blendSrcFactorA)
SWITCH_SRC_OR_DST_FACTOR_A(dstFunc, blendDstFactorA)
#undef SWITCH_SRC_OR_DST_FACTOR_A
#undef SAMPLE_REGISTER_BLEND_FACTOR
}
void FragmentProcessor::executeBlend(const BlendState &blendRGBState, const BlendState &blendAState)
{
#define SAMPLE_REGISTER_BLENDED_COLOR(COLOR_NAME, COLOR_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
SampleData &sample = m_sampleRegister[regSampleNdx]; \
const Vec4 &srcColor = sample.clampedBlendSrcColor; \
const Vec4 &dstColor = sample.clampedBlendDstColor; \
\
sample.COLOR_NAME = (COLOR_EXPRESSION); \
} \
}
switch (blendRGBState.equation)
{
case BLENDEQUATION_ADD:
SAMPLE_REGISTER_BLENDED_COLOR(blendedRGB, srcColor.swizzle(0, 1, 2) * sample.blendSrcFactorRGB +
dstColor.swizzle(0, 1, 2) * sample.blendDstFactorRGB)
break;
case BLENDEQUATION_SUBTRACT:
SAMPLE_REGISTER_BLENDED_COLOR(blendedRGB, srcColor.swizzle(0, 1, 2) * sample.blendSrcFactorRGB -
dstColor.swizzle(0, 1, 2) * sample.blendDstFactorRGB)
break;
case BLENDEQUATION_REVERSE_SUBTRACT:
SAMPLE_REGISTER_BLENDED_COLOR(blendedRGB, dstColor.swizzle(0, 1, 2) * sample.blendDstFactorRGB -
srcColor.swizzle(0, 1, 2) * sample.blendSrcFactorRGB)
break;
case BLENDEQUATION_MIN:
SAMPLE_REGISTER_BLENDED_COLOR(blendedRGB, min(srcColor.swizzle(0, 1, 2), dstColor.swizzle(0, 1, 2))) break;
case BLENDEQUATION_MAX:
SAMPLE_REGISTER_BLENDED_COLOR(blendedRGB, max(srcColor.swizzle(0, 1, 2), dstColor.swizzle(0, 1, 2))) break;
default:
DE_ASSERT(false);
}
switch (blendAState.equation)
{
case BLENDEQUATION_ADD:
SAMPLE_REGISTER_BLENDED_COLOR(blendedA,
srcColor.w() * sample.blendSrcFactorA + dstColor.w() * sample.blendDstFactorA)
break;
case BLENDEQUATION_SUBTRACT:
SAMPLE_REGISTER_BLENDED_COLOR(blendedA,
srcColor.w() * sample.blendSrcFactorA - dstColor.w() * sample.blendDstFactorA)
break;
case BLENDEQUATION_REVERSE_SUBTRACT:
SAMPLE_REGISTER_BLENDED_COLOR(blendedA,
dstColor.w() * sample.blendDstFactorA - srcColor.w() * sample.blendSrcFactorA)
break;
case BLENDEQUATION_MIN:
SAMPLE_REGISTER_BLENDED_COLOR(blendedA, min(srcColor.w(), dstColor.w())) break;
case BLENDEQUATION_MAX:
SAMPLE_REGISTER_BLENDED_COLOR(blendedA, max(srcColor.w(), dstColor.w())) break;
default:
DE_ASSERT(false);
}
#undef SAMPLE_REGISTER_BLENDED_COLOR
}
namespace advblend
{
inline float multiply(float src, float dst)
{
return src * dst;
}
inline float screen(float src, float dst)
{
return src + dst - src * dst;
}
inline float darken(float src, float dst)
{
return de::min(src, dst);
}
inline float lighten(float src, float dst)
{
return de::max(src, dst);
}
inline float difference(float src, float dst)
{
return de::abs(dst - src);
}
inline float exclusion(float src, float dst)
{
return src + dst - 2.0f * src * dst;
}
inline float overlay(float src, float dst)
{
if (dst <= 0.5f)
return 2.0f * src * dst;
else
return 1.0f - 2.0f * (1.0f - src) * (1.0f - dst);
}
inline float colordodge(float src, float dst)
{
if (dst <= 0.0f)
return 0.0f;
else if (src < 1.0f)
return de::min(1.0f, dst / (1.0f - src));
else
return 1.0f;
}
inline float colorburn(float src, float dst)
{
if (dst >= 1.0f)
return 1.0f;
else if (src > 0.0f)
return 1.0f - de::min(1.0f, (1.0f - dst) / src);
else
return 0.0f;
}
inline float hardlight(float src, float dst)
{
if (src <= 0.5f)
return 2.0f * src * dst;
else
return 1.0f - 2.0f * (1.0f - src) * (1.0f - dst);
}
inline float softlight(float src, float dst)
{
if (src <= 0.5f)
return dst - (1.0f - 2.0f * src) * dst * (1.0f - dst);
else if (dst <= 0.25f)
return dst + (2.0f * src - 1.0f) * dst * ((16.0f * dst - 12.0f) * dst + 3.0f);
else
return dst + (2.0f * src - 1.0f) * (deFloatSqrt(dst) - dst);
}
inline float minComp(const Vec3 &v)
{
return de::min(de::min(v.x(), v.y()), v.z());
}
inline float maxComp(const Vec3 &v)
{
return de::max(de::max(v.x(), v.y()), v.z());
}
inline float luminosity(const Vec3 &rgb)
{
return dot(rgb, Vec3(0.3f, 0.59f, 0.11f));
}
inline float saturation(const Vec3 &rgb)
{
return maxComp(rgb) - minComp(rgb);
}
Vec3 setLum(const Vec3 &cbase, const Vec3 &clum)
{
const float lbase = luminosity(cbase);
const float llum = luminosity(clum);
const float ldiff = llum - lbase;
const Vec3 color = cbase + Vec3(ldiff);
const float minC = minComp(color);
const float maxC = maxComp(color);
if (minC < 0.0f)
return llum + ((color - llum) * llum / (llum != minC ? (llum - minC) : 1.0f));
else if (maxC > 1.0f)
return llum + ((color - llum) * (1.0f - llum) / (llum != maxC ? (maxC - llum) : 1.0f));
else
return color;
}
Vec3 setLumSat(const Vec3 &cbase, const Vec3 &csat, const Vec3 &clum)
{
const float minbase = minComp(cbase);
const float sbase = saturation(cbase);
const float ssat = saturation(csat);
Vec3 color = Vec3(0.0f);
if (sbase > 0.0f)
color = (cbase - minbase) * ssat / sbase;
return setLum(color, clum);
}
} // namespace advblend
void FragmentProcessor::executeAdvancedBlend(BlendEquationAdvanced equation)
{
using namespace advblend;
#define SAMPLE_REGISTER_ADV_BLEND(FUNCTION_NAME) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
SampleData &sample = m_sampleRegister[regSampleNdx]; \
const Vec4 &srcColor = sample.clampedBlendSrcColor; \
const Vec4 &dstColor = sample.clampedBlendDstColor; \
const Vec3 &bias = sample.blendSrcFactorRGB; \
const float p0 = sample.blendSrcFactorA; \
const float r = FUNCTION_NAME(srcColor[0], dstColor[0]) * p0 + bias[0]; \
const float g = FUNCTION_NAME(srcColor[1], dstColor[1]) * p0 + bias[1]; \
const float b = FUNCTION_NAME(srcColor[2], dstColor[2]) * p0 + bias[2]; \
\
sample.blendedRGB = Vec3(r, g, b); \
} \
}
#define SAMPLE_REGISTER_ADV_BLEND_HSL(COLOR_EXPRESSION) \
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++) \
{ \
if (m_sampleRegister[regSampleNdx].isAlive) \
{ \
SampleData &sample = m_sampleRegister[regSampleNdx]; \
const Vec3 srcColor = sample.clampedBlendSrcColor.swizzle(0, 1, 2); \
const Vec3 dstColor = sample.clampedBlendDstColor.swizzle(0, 1, 2); \
const Vec3 &bias = sample.blendSrcFactorRGB; \
const float p0 = sample.blendSrcFactorA; \
\
sample.blendedRGB = (COLOR_EXPRESSION)*p0 + bias; \
} \
}
// Pre-compute factors & compute alpha \todo [2014-03-18 pyry] Re-using variable names.
// \note clampedBlend*Color contains clamped & unpremultiplied colors
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
SampleData &sample = m_sampleRegister[regSampleNdx];
const Vec4 &srcColor = sample.clampedBlendSrcColor;
const Vec4 &dstColor = sample.clampedBlendDstColor;
const float srcA = srcColor.w();
const float dstA = dstColor.w();
const float p0 = srcA * dstA;
const float p1 = srcA * (1.0f - dstA);
const float p2 = dstA * (1.0f - srcA);
const Vec3 bias(srcColor[0] * p1 + dstColor[0] * p2, srcColor[1] * p1 + dstColor[1] * p2,
srcColor[2] * p1 + dstColor[2] * p2);
sample.blendSrcFactorRGB = bias;
sample.blendSrcFactorA = p0;
sample.blendedA = p0 + p1 + p2;
}
}
switch (equation)
{
case BLENDEQUATION_ADVANCED_MULTIPLY:
SAMPLE_REGISTER_ADV_BLEND(multiply);
break;
case BLENDEQUATION_ADVANCED_SCREEN:
SAMPLE_REGISTER_ADV_BLEND(screen);
break;
case BLENDEQUATION_ADVANCED_OVERLAY:
SAMPLE_REGISTER_ADV_BLEND(overlay);
break;
case BLENDEQUATION_ADVANCED_DARKEN:
SAMPLE_REGISTER_ADV_BLEND(darken);
break;
case BLENDEQUATION_ADVANCED_LIGHTEN:
SAMPLE_REGISTER_ADV_BLEND(lighten);
break;
case BLENDEQUATION_ADVANCED_COLORDODGE:
SAMPLE_REGISTER_ADV_BLEND(colordodge);
break;
case BLENDEQUATION_ADVANCED_COLORBURN:
SAMPLE_REGISTER_ADV_BLEND(colorburn);
break;
case BLENDEQUATION_ADVANCED_HARDLIGHT:
SAMPLE_REGISTER_ADV_BLEND(hardlight);
break;
case BLENDEQUATION_ADVANCED_SOFTLIGHT:
SAMPLE_REGISTER_ADV_BLEND(softlight);
break;
case BLENDEQUATION_ADVANCED_DIFFERENCE:
SAMPLE_REGISTER_ADV_BLEND(difference);
break;
case BLENDEQUATION_ADVANCED_EXCLUSION:
SAMPLE_REGISTER_ADV_BLEND(exclusion);
break;
case BLENDEQUATION_ADVANCED_HSL_HUE:
SAMPLE_REGISTER_ADV_BLEND_HSL(setLumSat(srcColor, dstColor, dstColor));
break;
case BLENDEQUATION_ADVANCED_HSL_SATURATION:
SAMPLE_REGISTER_ADV_BLEND_HSL(setLumSat(dstColor, srcColor, dstColor));
break;
case BLENDEQUATION_ADVANCED_HSL_COLOR:
SAMPLE_REGISTER_ADV_BLEND_HSL(setLum(srcColor, dstColor));
break;
case BLENDEQUATION_ADVANCED_HSL_LUMINOSITY:
SAMPLE_REGISTER_ADV_BLEND_HSL(setLum(dstColor, srcColor));
break;
default:
DE_ASSERT(false);
}
#undef SAMPLE_REGISTER_ADV_BLEND
#undef SAMPLE_REGISTER_ADV_BLEND_HSL
}
void FragmentProcessor::executeColorWrite(int fragNdxOffset, int numSamplesPerFragment, const Fragment *inputFragments,
bool isSRGB, const tcu::PixelBufferAccess &colorBuffer)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
Vec4 combinedColor;
combinedColor.xyz() = m_sampleRegister[regSampleNdx].blendedRGB;
combinedColor.w() = m_sampleRegister[regSampleNdx].blendedA;
if (isSRGB)
combinedColor = tcu::linearToSRGB(combinedColor);
colorBuffer.setPixel(combinedColor, fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
}
}
}
void FragmentProcessor::executeRGBA8ColorWrite(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments,
const tcu::PixelBufferAccess &colorBuffer)
{
const int fragStride = 4;
const int xStride = colorBuffer.getRowPitch();
const int yStride = colorBuffer.getSlicePitch();
uint8_t *const basePtr = (uint8_t *)colorBuffer.getDataPtr();
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
uint8_t *dstPtr =
basePtr + fragSampleNdx * fragStride + frag.pixelCoord.x() * xStride + frag.pixelCoord.y() * yStride;
dstPtr[0] = tcu::floatToU8(m_sampleRegister[regSampleNdx].blendedRGB.x());
dstPtr[1] = tcu::floatToU8(m_sampleRegister[regSampleNdx].blendedRGB.y());
dstPtr[2] = tcu::floatToU8(m_sampleRegister[regSampleNdx].blendedRGB.z());
dstPtr[3] = tcu::floatToU8(m_sampleRegister[regSampleNdx].blendedA);
}
}
}
void FragmentProcessor::executeMaskedColorWrite(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const Vec4 &colorMaskFactor,
const Vec4 &colorMaskNegationFactor, bool isSRGB,
const tcu::PixelBufferAccess &colorBuffer)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
Vec4 originalColor = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
Vec4 newColor;
newColor.xyz() = m_sampleRegister[regSampleNdx].blendedRGB;
newColor.w() = m_sampleRegister[regSampleNdx].blendedA;
if (isSRGB)
newColor = tcu::linearToSRGB(newColor);
newColor = colorMaskFactor * newColor + colorMaskNegationFactor * originalColor;
colorBuffer.setPixel(newColor, fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
}
}
}
void FragmentProcessor::executeSignedValueWrite(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const tcu::BVec4 &colorMask,
const tcu::PixelBufferAccess &colorBuffer)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
const IVec4 originalValue =
colorBuffer.getPixelInt(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
colorBuffer.setPixel(tcu::select(m_sampleRegister[regSampleNdx].signedValue, originalValue, colorMask),
fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
}
}
}
void FragmentProcessor::executeUnsignedValueWrite(int fragNdxOffset, int numSamplesPerFragment,
const Fragment *inputFragments, const tcu::BVec4 &colorMask,
const tcu::PixelBufferAccess &colorBuffer)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[fragNdxOffset + regSampleNdx / numSamplesPerFragment];
const UVec4 originalValue =
colorBuffer.getPixelUint(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
colorBuffer.setPixel(tcu::select(m_sampleRegister[regSampleNdx].unsignedValue, originalValue, colorMask),
fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
}
}
}
void FragmentProcessor::render(const rr::MultisamplePixelBufferAccess &msColorBuffer,
const rr::MultisamplePixelBufferAccess &msDepthBuffer,
const rr::MultisamplePixelBufferAccess &msStencilBuffer, const Fragment *inputFragments,
int numFragments, FaceType fragmentFacing, const FragmentOperationState &state)
{
DE_ASSERT(fragmentFacing < FACETYPE_LAST);
DE_ASSERT(state.numStencilBits < 32); // code bitshifts numStencilBits, avoid undefined behavior
const tcu::PixelBufferAccess &colorBuffer = msColorBuffer.raw();
const tcu::PixelBufferAccess &depthBuffer = msDepthBuffer.raw();
const tcu::PixelBufferAccess &stencilBuffer = msStencilBuffer.raw();
bool hasDepth = depthBuffer.getWidth() > 0 && depthBuffer.getHeight() > 0 && depthBuffer.getDepth() > 0;
bool hasStencil = stencilBuffer.getWidth() > 0 && stencilBuffer.getHeight() > 0 && stencilBuffer.getDepth() > 0;
bool doDepthBoundsTest = hasDepth && state.depthBoundsTestEnabled;
bool doDepthTest = hasDepth && state.depthTestEnabled;
bool doStencilTest = hasStencil && state.stencilTestEnabled;
tcu::TextureChannelClass colorbufferClass = tcu::getTextureChannelClass(msColorBuffer.raw().getFormat().type);
rr::GenericVecType fragmentDataType =
(colorbufferClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER) ?
(rr::GENERICVECTYPE_INT32) :
((colorbufferClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER) ? (rr::GENERICVECTYPE_UINT32) :
(rr::GENERICVECTYPE_FLOAT));
DE_ASSERT((!hasDepth || colorBuffer.getWidth() == depthBuffer.getWidth()) &&
(!hasStencil || colorBuffer.getWidth() == stencilBuffer.getWidth()));
DE_ASSERT((!hasDepth || colorBuffer.getHeight() == depthBuffer.getHeight()) &&
(!hasStencil || colorBuffer.getHeight() == stencilBuffer.getHeight()));
DE_ASSERT((!hasDepth || colorBuffer.getDepth() == depthBuffer.getDepth()) &&
(!hasStencil || colorBuffer.getDepth() == stencilBuffer.getDepth()));
// Combined formats must be separated beforehand
DE_ASSERT(!hasDepth || (!tcu::isCombinedDepthStencilType(depthBuffer.getFormat().type) &&
depthBuffer.getFormat().order == tcu::TextureFormat::D));
DE_ASSERT(!hasStencil || (!tcu::isCombinedDepthStencilType(stencilBuffer.getFormat().type) &&
stencilBuffer.getFormat().order == tcu::TextureFormat::S));
int numSamplesPerFragment = colorBuffer.getWidth();
int totalNumSamples = numFragments * numSamplesPerFragment;
int numSampleGroups =
(totalNumSamples - 1) / SAMPLE_REGISTER_SIZE + 1; // \note totalNumSamples/SAMPLE_REGISTER_SIZE rounded up.
const StencilState &stencilState = state.stencilStates[fragmentFacing];
Vec4 colorMaskFactor(state.colorMask[0] ? 1.0f : 0.0f, state.colorMask[1] ? 1.0f : 0.0f,
state.colorMask[2] ? 1.0f : 0.0f, state.colorMask[3] ? 1.0f : 0.0f);
Vec4 colorMaskNegationFactor(state.colorMask[0] ? 0.0f : 1.0f, state.colorMask[1] ? 0.0f : 1.0f,
state.colorMask[2] ? 0.0f : 1.0f, state.colorMask[3] ? 0.0f : 1.0f);
bool sRGBTarget = state.sRGBEnabled && tcu::isSRGB(colorBuffer.getFormat());
DE_ASSERT(SAMPLE_REGISTER_SIZE % numSamplesPerFragment == 0);
// Divide the fragments' samples into groups of size SAMPLE_REGISTER_SIZE, and perform
// the per-sample operations for one group at a time.
for (int sampleGroupNdx = 0; sampleGroupNdx < numSampleGroups; sampleGroupNdx++)
{
// The index of the fragment of the sample at the beginning of m_sampleRegisters.
int groupFirstFragNdx = (sampleGroupNdx * SAMPLE_REGISTER_SIZE) / numSamplesPerFragment;
// Initialize sample data in the sample register.
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
int fragNdx = groupFirstFragNdx + regSampleNdx / numSamplesPerFragment;
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
if (fragNdx < numFragments)
{
m_sampleRegister[regSampleNdx].isAlive =
(inputFragments[fragNdx].coverage & (1u << fragSampleNdx)) != 0;
m_sampleRegister[regSampleNdx].depthPassed =
true; // \note This will stay true if depth test is disabled.
}
else
m_sampleRegister[regSampleNdx].isAlive = false;
}
// Scissor test.
if (state.scissorTestEnabled)
executeScissorTest(groupFirstFragNdx, numSamplesPerFragment, inputFragments, state.scissorRectangle);
// Depth bounds test.
if (doDepthBoundsTest)
executeDepthBoundsTest(groupFirstFragNdx, numSamplesPerFragment, inputFragments, state.minDepthBound,
state.maxDepthBound, depthBuffer);
// Stencil test.
if (doStencilTest)
{
executeStencilCompare(groupFirstFragNdx, numSamplesPerFragment, inputFragments, stencilState,
state.numStencilBits, stencilBuffer);
executeStencilSFail(groupFirstFragNdx, numSamplesPerFragment, inputFragments, stencilState,
state.numStencilBits, stencilBuffer);
}
// Depth test.
// \note Current value of isAlive is needed for dpPass and dpFail, so it's only updated after them and not right after depth test.
if (doDepthTest)
{
executeDepthCompare(groupFirstFragNdx, numSamplesPerFragment, inputFragments, state.depthFunc, depthBuffer);
if (state.depthMask)
executeDepthWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, depthBuffer);
}
// Do dpFail and dpPass stencil writes.
if (doStencilTest)
executeStencilDpFailAndPass(groupFirstFragNdx, numSamplesPerFragment, inputFragments, stencilState,
state.numStencilBits, stencilBuffer);
// Kill the samples that failed depth test.
if (doDepthTest)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
m_sampleRegister[regSampleNdx].isAlive =
m_sampleRegister[regSampleNdx].isAlive && m_sampleRegister[regSampleNdx].depthPassed;
}
// Paint fragments to target
switch (fragmentDataType)
{
case rr::GENERICVECTYPE_FLOAT:
{
// Select min/max clamping values for blending factors and operands
Vec4 minClampValue;
Vec4 maxClampValue;
if (colorbufferClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
{
minClampValue = Vec4(0.0f);
maxClampValue = Vec4(1.0f);
}
else if (colorbufferClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT)
{
minClampValue = Vec4(-1.0f);
maxClampValue = Vec4(1.0f);
}
else
{
// No clamping
minClampValue = Vec4(-std::numeric_limits<float>::infinity());
maxClampValue = Vec4(std::numeric_limits<float>::infinity());
}
// Blend calculation - only if using blend.
if (state.blendMode == BLENDMODE_STANDARD)
{
// Put dst color to register, doing srgb-to-linear conversion if needed.
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[groupFirstFragNdx + regSampleNdx / numSamplesPerFragment];
Vec4 dstColor = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
m_sampleRegister[regSampleNdx].clampedBlendSrcColor =
clamp(frag.value.get<float>(), minClampValue, maxClampValue);
m_sampleRegister[regSampleNdx].clampedBlendSrc1Color =
clamp(frag.value1.get<float>(), minClampValue, maxClampValue);
m_sampleRegister[regSampleNdx].clampedBlendDstColor =
clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, minClampValue, maxClampValue);
}
}
// Calculate blend factors to register.
executeBlendFactorComputeRGB(state.blendColor, state.blendRGBState);
executeBlendFactorComputeA(state.blendColor, state.blendAState);
// Compute blended color.
executeBlend(state.blendRGBState, state.blendAState);
}
else if (state.blendMode == BLENDMODE_ADVANCED)
{
// Unpremultiply colors for blending, and do sRGB->linear if necessary
// \todo [2014-03-17 pyry] Re-consider clampedBlend*Color var names
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
int fragSampleNdx = regSampleNdx % numSamplesPerFragment;
const Fragment &frag = inputFragments[groupFirstFragNdx + regSampleNdx / numSamplesPerFragment];
const Vec4 srcColor = frag.value.get<float>();
const Vec4 dstColor =
colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
m_sampleRegister[regSampleNdx].clampedBlendSrcColor =
unpremultiply(clamp(srcColor, minClampValue, maxClampValue));
m_sampleRegister[regSampleNdx].clampedBlendDstColor = unpremultiply(
clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, minClampValue, maxClampValue));
}
}
executeAdvancedBlend(state.blendEquationAdvaced);
}
else
{
// Not using blend - just put values to register as-is.
DE_ASSERT(state.blendMode == BLENDMODE_NONE);
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const Fragment &frag = inputFragments[groupFirstFragNdx + regSampleNdx / numSamplesPerFragment];
m_sampleRegister[regSampleNdx].blendedRGB = frag.value.get<float>().xyz();
m_sampleRegister[regSampleNdx].blendedA = frag.value.get<float>().w();
}
}
}
// Clamp result values in sample register
if (colorbufferClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT)
{
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
m_sampleRegister[regSampleNdx].blendedRGB =
clamp(m_sampleRegister[regSampleNdx].blendedRGB, minClampValue.swizzle(0, 1, 2),
maxClampValue.swizzle(0, 1, 2));
m_sampleRegister[regSampleNdx].blendedA =
clamp(m_sampleRegister[regSampleNdx].blendedA, minClampValue.w(), maxClampValue.w());
}
}
}
// Finally, write the colors to the color buffer.
if (state.colorMask[0] && state.colorMask[1] && state.colorMask[2] && state.colorMask[3])
{
if (colorBuffer.getFormat() ==
tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))
executeRGBA8ColorWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, colorBuffer);
else
executeColorWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, sRGBTarget,
colorBuffer);
}
else if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
executeMaskedColorWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, colorMaskFactor,
colorMaskNegationFactor, sRGBTarget, colorBuffer);
break;
}
case rr::GENERICVECTYPE_INT32:
// Write fragments
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const Fragment &frag = inputFragments[groupFirstFragNdx + regSampleNdx / numSamplesPerFragment];
m_sampleRegister[regSampleNdx].signedValue = frag.value.get<int32_t>();
}
}
if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
executeSignedValueWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, state.colorMask,
colorBuffer);
break;
case rr::GENERICVECTYPE_UINT32:
// Write fragments
for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
{
if (m_sampleRegister[regSampleNdx].isAlive)
{
const Fragment &frag = inputFragments[groupFirstFragNdx + regSampleNdx / numSamplesPerFragment];
m_sampleRegister[regSampleNdx].unsignedValue = frag.value.get<uint32_t>();
}
}
if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
executeUnsignedValueWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, state.colorMask,
colorBuffer);
break;
default:
DE_ASSERT(false);
}
}
}
} // namespace rr