You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
627 lines
22 KiB
627 lines
22 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL (ES) Module
|
|
* -----------------------------------------------
|
|
*
|
|
* 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 Shader - render state interaction case.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "glsFragOpInteractionCase.hpp"
|
|
|
|
#include "glsRandomShaderProgram.hpp"
|
|
#include "glsFragmentOpUtil.hpp"
|
|
#include "glsInteractionTestUtil.hpp"
|
|
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluContextInfo.hpp"
|
|
|
|
#include "rsgShader.hpp"
|
|
#include "rsgProgramGenerator.hpp"
|
|
#include "rsgUtils.hpp"
|
|
|
|
#include "sglrContext.hpp"
|
|
#include "sglrReferenceContext.hpp"
|
|
#include "sglrGLContext.hpp"
|
|
#include "sglrContextUtil.hpp"
|
|
|
|
#include "tcuRenderTarget.hpp"
|
|
#include "tcuImageCompare.hpp"
|
|
|
|
#include "deRandom.hpp"
|
|
#include "deString.h"
|
|
#include "deStringUtil.hpp"
|
|
|
|
#include "glwEnums.hpp"
|
|
|
|
#include "gluDrawUtil.hpp"
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gls
|
|
{
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
using tcu::Vec2;
|
|
using tcu::Vec4;
|
|
using tcu::IVec2;
|
|
using tcu::IVec4;
|
|
|
|
using gls::InteractionTestUtil::RenderState;
|
|
using gls::InteractionTestUtil::StencilState;
|
|
|
|
enum
|
|
{
|
|
NUM_ITERATIONS = 5,
|
|
NUM_COMMANDS_PER_ITERATION = 5,
|
|
VIEWPORT_WIDTH = 64,
|
|
VIEWPORT_HEIGHT = 64
|
|
};
|
|
|
|
namespace
|
|
{
|
|
|
|
static void computeVertexLayout (const vector<rsg::ShaderInput*>& attributes, int numVertices, vector<glu::VertexArrayBinding>* layout, int* stride)
|
|
{
|
|
DE_ASSERT(layout->empty());
|
|
|
|
int curOffset = 0;
|
|
|
|
for (vector<rsg::ShaderInput*>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
|
|
{
|
|
const rsg::ShaderInput* attrib = *iter;
|
|
const rsg::Variable* var = attrib->getVariable();
|
|
const rsg::VariableType& type = var->getType();
|
|
const int numComps = type.getNumElements();
|
|
|
|
TCU_CHECK_INTERNAL(type.getBaseType() == rsg::VariableType::TYPE_FLOAT && de::inRange(type.getNumElements(), 1, 4));
|
|
|
|
layout->push_back(glu::va::Float(var->getName(), numComps, numVertices, 0 /* computed later */, (const float*)(deUintptr)curOffset));
|
|
|
|
curOffset += numComps * (int)sizeof(float);
|
|
}
|
|
|
|
for (vector<glu::VertexArrayBinding>::iterator vaIter = layout->begin(); vaIter != layout->end(); ++vaIter)
|
|
vaIter->pointer.stride = curOffset;
|
|
|
|
*stride = curOffset;
|
|
}
|
|
|
|
class VertexDataStorage
|
|
{
|
|
public:
|
|
VertexDataStorage (const vector<rsg::ShaderInput*>& attributes, int numVertices);
|
|
|
|
int getDataSize (void) const { return (int)m_data.size(); }
|
|
void* getBasePtr (void) { return m_data.empty() ? DE_NULL : &m_data[0]; }
|
|
const void* getBasePtr (void) const { return m_data.empty() ? DE_NULL : &m_data[0]; }
|
|
|
|
const std::vector<glu::VertexArrayBinding>& getLayout (void) const { return m_layout; }
|
|
|
|
int getNumEntries (void) const { return (int)m_layout.size(); }
|
|
const glu::VertexArrayBinding& getLayoutEntry (int ndx) const { return m_layout[ndx]; }
|
|
|
|
private:
|
|
std::vector<deUint8> m_data;
|
|
std::vector<glu::VertexArrayBinding> m_layout;
|
|
};
|
|
|
|
VertexDataStorage::VertexDataStorage (const vector<rsg::ShaderInput*>& attributes, int numVertices)
|
|
{
|
|
int stride = 0;
|
|
computeVertexLayout(attributes, numVertices, &m_layout, &stride);
|
|
m_data.resize(stride * numVertices);
|
|
}
|
|
|
|
static inline glu::VertexArrayBinding getEntryWithPointer (const VertexDataStorage& data, int ndx)
|
|
{
|
|
const glu::VertexArrayBinding& entry = data.getLayoutEntry(ndx);
|
|
return glu::VertexArrayBinding(entry.binding, glu::VertexArrayPointer(entry.pointer.componentType,
|
|
entry.pointer.convert,
|
|
entry.pointer.numComponents,
|
|
entry.pointer.numElements,
|
|
entry.pointer.stride,
|
|
(const void*)((deUintptr)entry.pointer.data+(deUintptr)data.getBasePtr())));
|
|
}
|
|
|
|
template<int Size>
|
|
static void setVertex (const glu::VertexArrayPointer& pointer, int vertexNdx, const tcu::Vector<float, Size>& value)
|
|
{
|
|
// \todo [2013-12-14 pyry] Implement other modes.
|
|
DE_ASSERT(pointer.componentType == glu::VTX_COMP_FLOAT && pointer.convert == glu::VTX_COMP_CONVERT_NONE);
|
|
DE_ASSERT(pointer.numComponents == Size);
|
|
DE_ASSERT(de::inBounds(vertexNdx, 0, pointer.numElements));
|
|
|
|
float* dst = (float*)((deUint8*)pointer.data + pointer.stride*vertexNdx);
|
|
|
|
for (int ndx = 0; ndx < Size; ndx++)
|
|
dst[ndx] = value[ndx];
|
|
}
|
|
|
|
template<int Size>
|
|
static tcu::Vector<float, Size> interpolateRange (const rsg::ConstValueRangeAccess& range, const tcu::Vector<float, Size>& t)
|
|
{
|
|
tcu::Vector<float, Size> result;
|
|
|
|
for (int ndx = 0; ndx < Size; ndx++)
|
|
result[ndx] = range.getMin().component(ndx).asFloat()*(1.0f - t[ndx]) + range.getMax().component(ndx).asFloat()*t[ndx];
|
|
|
|
return result;
|
|
}
|
|
|
|
struct Quad
|
|
{
|
|
tcu::IVec2 posA;
|
|
tcu::IVec2 posB;
|
|
};
|
|
|
|
struct RenderCommand
|
|
{
|
|
Quad quad;
|
|
float depth;
|
|
RenderState state;
|
|
|
|
RenderCommand (void) : depth(0.0f) {}
|
|
};
|
|
|
|
static Quad getRandomQuad (de::Random& rnd, int targetW, int targetH)
|
|
{
|
|
// \note In viewport coordinates.
|
|
// \todo [2012-12-18 pyry] Out-of-bounds values.
|
|
const int maxOutOfBounds = 0;
|
|
const float minSize = 0.5f;
|
|
|
|
const int minW = deCeilFloatToInt32(minSize * (float)targetW);
|
|
const int minH = deCeilFloatToInt32(minSize * (float)targetH);
|
|
const int maxW = targetW + 2*maxOutOfBounds;
|
|
const int maxH = targetH + 2*maxOutOfBounds;
|
|
|
|
const int width = rnd.getInt(minW, maxW);
|
|
const int height = rnd.getInt(minH, maxH);
|
|
const int x = rnd.getInt(-maxOutOfBounds, targetW+maxOutOfBounds-width);
|
|
const int y = rnd.getInt(-maxOutOfBounds, targetH+maxOutOfBounds-height);
|
|
|
|
const bool flipX = rnd.getBool();
|
|
const bool flipY = rnd.getBool();
|
|
|
|
Quad quad;
|
|
|
|
quad.posA = tcu::IVec2(flipX ? (x+width-1) : x, flipY ? (y+height-1) : y);
|
|
quad.posB = tcu::IVec2(flipX ? x : (x+width-1), flipY ? y : (y+height-1));
|
|
|
|
return quad;
|
|
}
|
|
|
|
static float getRandomDepth (de::Random& rnd)
|
|
{
|
|
// \note Not using depth 1.0 since clearing with 1.0 and rendering with 1.0 may not be same value.
|
|
static const float depthValues[] = { 0.0f, 0.2f, 0.4f, 0.5f, 0.51f, 0.6f, 0.8f, 0.95f };
|
|
return rnd.choose<float>(DE_ARRAY_BEGIN(depthValues), DE_ARRAY_END(depthValues));
|
|
}
|
|
|
|
static void computeRandomRenderCommand (de::Random& rnd, RenderCommand& command, glu::ApiType apiType, int targetW, int targetH)
|
|
{
|
|
command.quad = getRandomQuad(rnd, targetW, targetH);
|
|
command.depth = getRandomDepth(rnd);
|
|
gls::InteractionTestUtil::computeRandomRenderState(rnd, command.state, apiType, targetW, targetH);
|
|
}
|
|
|
|
static void setRenderState (sglr::Context& ctx, const RenderState& state)
|
|
{
|
|
if (state.scissorTestEnabled)
|
|
{
|
|
ctx.enable(GL_SCISSOR_TEST);
|
|
ctx.scissor(state.scissorRectangle.left, state.scissorRectangle.bottom,
|
|
state.scissorRectangle.width, state.scissorRectangle.height);
|
|
}
|
|
else
|
|
ctx.disable(GL_SCISSOR_TEST);
|
|
|
|
if (state.stencilTestEnabled)
|
|
{
|
|
ctx.enable(GL_STENCIL_TEST);
|
|
|
|
for (int face = 0; face < rr::FACETYPE_LAST; face++)
|
|
{
|
|
deUint32 glFace = face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
|
|
const StencilState& sParams = state.stencil[face];
|
|
|
|
ctx.stencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
|
|
ctx.stencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
|
|
ctx.stencilMaskSeparate(glFace, sParams.writeMask);
|
|
}
|
|
}
|
|
else
|
|
ctx.disable(GL_STENCIL_TEST);
|
|
|
|
if (state.depthTestEnabled)
|
|
{
|
|
ctx.enable(GL_DEPTH_TEST);
|
|
ctx.depthFunc(state.depthFunc);
|
|
ctx.depthMask(state.depthWriteMask ? GL_TRUE : GL_FALSE);
|
|
}
|
|
else
|
|
ctx.disable(GL_DEPTH_TEST);
|
|
|
|
if (state.blendEnabled)
|
|
{
|
|
ctx.enable(GL_BLEND);
|
|
ctx.blendEquationSeparate(state.blendRGBState.equation, state.blendAState.equation);
|
|
ctx.blendFuncSeparate(state.blendRGBState.srcFunc, state.blendRGBState.dstFunc, state.blendAState.srcFunc, state.blendAState.dstFunc);
|
|
ctx.blendColor(state.blendColor.x(), state.blendColor.y(), state.blendColor.z(), state.blendColor.w());
|
|
}
|
|
else
|
|
ctx.disable(GL_BLEND);
|
|
|
|
if (state.ditherEnabled)
|
|
ctx.enable(GL_DITHER);
|
|
else
|
|
ctx.disable(GL_DITHER);
|
|
|
|
ctx.colorMask(state.colorMask[0] ? GL_TRUE : GL_FALSE,
|
|
state.colorMask[1] ? GL_TRUE : GL_FALSE,
|
|
state.colorMask[2] ? GL_TRUE : GL_FALSE,
|
|
state.colorMask[3] ? GL_TRUE : GL_FALSE);
|
|
}
|
|
|
|
static void renderQuad (sglr::Context& ctx, const glu::VertexArrayPointer& posPtr, const Quad& quad, const float depth)
|
|
{
|
|
const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
|
|
|
|
const bool flipX = quad.posB.x() < quad.posA.x();
|
|
const bool flipY = quad.posB.y() < quad.posA.y();
|
|
const int viewportX = de::min(quad.posA.x(), quad.posB.x());
|
|
const int viewportY = de::min(quad.posA.y(), quad.posB.y());
|
|
const int viewportW = de::abs(quad.posA.x()-quad.posB.x())+1;
|
|
const int viewportH = de::abs(quad.posA.y()-quad.posB.y())+1;
|
|
|
|
const Vec2 pA (flipX ? 1.0f : -1.0f, flipY ? 1.0f : -1.0f);
|
|
const Vec2 pB (flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f);
|
|
|
|
setVertex(posPtr, 0, Vec4(pA.x(), pA.y(), depth, 1.0f));
|
|
setVertex(posPtr, 1, Vec4(pB.x(), pA.y(), depth, 1.0f));
|
|
setVertex(posPtr, 2, Vec4(pA.x(), pB.y(), depth, 1.0f));
|
|
setVertex(posPtr, 3, Vec4(pB.x(), pB.y(), depth, 1.0f));
|
|
|
|
ctx.viewport(viewportX, viewportY, viewportW, viewportH);
|
|
ctx.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_SHORT, &indices[0]);
|
|
}
|
|
|
|
static void render (sglr::Context& ctx, const glu::VertexArrayPointer& posPtr, const RenderCommand& cmd)
|
|
{
|
|
setRenderState(ctx, cmd.state);
|
|
renderQuad(ctx, posPtr, cmd.quad, cmd.depth);
|
|
}
|
|
|
|
static void setupAttributes (sglr::Context& ctx, const VertexDataStorage& vertexData, deUint32 program)
|
|
{
|
|
for (int attribNdx = 0; attribNdx < vertexData.getNumEntries(); ++attribNdx)
|
|
{
|
|
const glu::VertexArrayBinding bindingPtr = getEntryWithPointer(vertexData, attribNdx);
|
|
const int attribLoc = bindingPtr.binding.type == glu::BindingPoint::BPTYPE_NAME ? ctx.getAttribLocation(program, bindingPtr.binding.name.c_str()) : bindingPtr.binding.location;
|
|
|
|
DE_ASSERT(bindingPtr.pointer.componentType == glu::VTX_COMP_FLOAT);
|
|
|
|
if (attribLoc >= 0)
|
|
{
|
|
ctx.enableVertexAttribArray(attribLoc);
|
|
ctx.vertexAttribPointer(attribLoc, bindingPtr.pointer.numComponents, GL_FLOAT, GL_FALSE, bindingPtr.pointer.stride, bindingPtr.pointer.data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void setUniformValue (sglr::Context& ctx, int location, rsg::ConstValueAccess value)
|
|
{
|
|
DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(float));
|
|
DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(int));
|
|
|
|
switch (value.getType().getBaseType())
|
|
{
|
|
case rsg::VariableType::TYPE_FLOAT:
|
|
switch (value.getType().getNumElements())
|
|
{
|
|
case 1: ctx.uniform1fv(location, 1, (float*)value.value().getValuePtr()); break;
|
|
case 2: ctx.uniform2fv(location, 1, (float*)value.value().getValuePtr()); break;
|
|
case 3: ctx.uniform3fv(location, 1, (float*)value.value().getValuePtr()); break;
|
|
case 4: ctx.uniform4fv(location, 1, (float*)value.value().getValuePtr()); break;
|
|
default: TCU_FAIL("Unsupported type"); break;
|
|
}
|
|
break;
|
|
|
|
case rsg::VariableType::TYPE_INT:
|
|
case rsg::VariableType::TYPE_BOOL:
|
|
case rsg::VariableType::TYPE_SAMPLER_2D:
|
|
case rsg::VariableType::TYPE_SAMPLER_CUBE:
|
|
switch (value.getType().getNumElements())
|
|
{
|
|
case 1: ctx.uniform1iv(location, 1, (int*)value.value().getValuePtr()); break;
|
|
case 2: ctx.uniform2iv(location, 1, (int*)value.value().getValuePtr()); break;
|
|
case 3: ctx.uniform3iv(location, 1, (int*)value.value().getValuePtr()); break;
|
|
case 4: ctx.uniform4iv(location, 1, (int*)value.value().getValuePtr()); break;
|
|
default: TCU_FAIL("Unsupported type"); break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw tcu::InternalError("Unsupported type", "", __FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
static int findShaderInputIndex (const vector<rsg::ShaderInput*>& vars, const char* name)
|
|
{
|
|
for (int ndx = 0; ndx < (int)vars.size(); ++ndx)
|
|
{
|
|
if (deStringEqual(vars[ndx]->getVariable()->getName(), name))
|
|
return ndx;
|
|
}
|
|
|
|
throw tcu::InternalError(string(name) + " not found in shader inputs");
|
|
}
|
|
|
|
static float getWellBehavingChannelColor (float v, int numBits)
|
|
{
|
|
DE_ASSERT(de::inRange(numBits, 0, 32));
|
|
|
|
// clear color may not be accurately representable in the target format. If the clear color is
|
|
// on a representable value mapping range border, it could be rounded differently by the GL and in
|
|
// SGLR adding an unexpected error source. However, selecting an accurately representable background
|
|
// color would effectively disable dithering. To allow dithering and to prevent undefined rounding
|
|
// direction from affecting results, round accurate color to target color format with 8 sub-units
|
|
// (3 bits). If the selected sub-unit value is 3 or 4 (bordering 0.5), replace it with 2 and 5,
|
|
// respectively.
|
|
|
|
if (numBits == 0 || v <= 0.0f || v >= 1.0f)
|
|
{
|
|
// already accurately representable
|
|
return v;
|
|
}
|
|
else
|
|
{
|
|
const deUint64 numSubBits = 3;
|
|
const deUint64 subUnitBorderLo = (1u << (numSubBits - 1u)) - 1u;
|
|
const deUint64 subUnitBorderHi = 1u << (numSubBits - 1u);
|
|
const deUint64 maxFixedValue = (1u << (numBits + numSubBits)) - 1u;
|
|
const deUint64 fixedValue = deRoundFloatToInt64(v * (float)maxFixedValue);
|
|
|
|
const deUint64 units = fixedValue >> numSubBits;
|
|
const deUint64 subUnits = fixedValue & ((1u << numSubBits) - 1u);
|
|
|
|
const deUint64 tweakedSubUnits = (subUnits == subUnitBorderLo) ? (subUnitBorderLo - 1)
|
|
: (subUnits == subUnitBorderHi) ? (subUnitBorderHi + 1)
|
|
: (subUnits);
|
|
const deUint64 tweakedValue = (units << numSubBits) | (tweakedSubUnits);
|
|
|
|
return float(tweakedValue) / float(maxFixedValue);
|
|
}
|
|
}
|
|
|
|
static tcu::Vec4 getWellBehavingColor (const tcu::Vec4& accurateColor, const tcu::PixelFormat& format)
|
|
{
|
|
return tcu::Vec4(getWellBehavingChannelColor(accurateColor[0], format.redBits),
|
|
getWellBehavingChannelColor(accurateColor[1], format.greenBits),
|
|
getWellBehavingChannelColor(accurateColor[2], format.blueBits),
|
|
getWellBehavingChannelColor(accurateColor[3], format.alphaBits));
|
|
}
|
|
|
|
} // anonymous
|
|
|
|
struct FragOpInteractionCase::ReferenceContext
|
|
{
|
|
const sglr::ReferenceContextLimits limits;
|
|
sglr::ReferenceContextBuffers buffers;
|
|
sglr::ReferenceContext context;
|
|
|
|
ReferenceContext (glu::RenderContext& renderCtx, int width, int height)
|
|
: limits (renderCtx)
|
|
, buffers (renderCtx.getRenderTarget().getPixelFormat(), renderCtx.getRenderTarget().getDepthBits(), renderCtx.getRenderTarget().getStencilBits(), width, height)
|
|
, context (limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer())
|
|
{
|
|
}
|
|
};
|
|
|
|
FragOpInteractionCase::FragOpInteractionCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const rsg::ProgramParameters& params)
|
|
: TestCase (testCtx, name, "")
|
|
, m_renderCtx (renderCtx)
|
|
, m_ctxInfo (ctxInfo)
|
|
, m_params (params)
|
|
, m_vertexShader (rsg::Shader::TYPE_VERTEX)
|
|
, m_fragmentShader (rsg::Shader::TYPE_FRAGMENT)
|
|
, m_program (DE_NULL)
|
|
, m_glCtx (DE_NULL)
|
|
, m_referenceCtx (DE_NULL)
|
|
, m_glProgram (0)
|
|
, m_refProgram (0)
|
|
, m_iterNdx (0)
|
|
{
|
|
}
|
|
|
|
FragOpInteractionCase::~FragOpInteractionCase (void)
|
|
{
|
|
FragOpInteractionCase::deinit();
|
|
}
|
|
|
|
void FragOpInteractionCase::init (void)
|
|
{
|
|
de::Random rnd (m_params.seed ^ 0x232faac);
|
|
const int viewportW = de::min<int>(m_renderCtx.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
|
|
const int viewportH = de::min<int>(m_renderCtx.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
|
|
const int viewportX = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportW);
|
|
const int viewportY = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportH);
|
|
|
|
rsg::ProgramGenerator generator;
|
|
|
|
generator.generate(m_params, m_vertexShader, m_fragmentShader);
|
|
rsg::computeUnifiedUniforms(m_vertexShader, m_fragmentShader, m_unifiedUniforms);
|
|
|
|
try
|
|
{
|
|
DE_ASSERT(!m_program);
|
|
m_program = new gls::RandomShaderProgram(m_vertexShader, m_fragmentShader, (int)m_unifiedUniforms.size(), m_unifiedUniforms.empty() ? DE_NULL : &m_unifiedUniforms[0]);
|
|
|
|
DE_ASSERT(!m_referenceCtx);
|
|
m_referenceCtx = new ReferenceContext(m_renderCtx, viewportW, viewportH);
|
|
|
|
DE_ASSERT(!m_glCtx);
|
|
m_glCtx = new sglr::GLContext(m_renderCtx, m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, IVec4(viewportX, viewportY, viewportW, viewportH));
|
|
|
|
m_refProgram = m_referenceCtx->context.createProgram(m_program);
|
|
m_glProgram = m_glCtx->createProgram(m_program);
|
|
|
|
m_viewportSize = tcu::IVec2(viewportW, viewportH);
|
|
m_iterNdx = 0;
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
|
}
|
|
catch (...)
|
|
{
|
|
// Save some memory by cleaning up stuff.
|
|
FragOpInteractionCase::deinit();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void FragOpInteractionCase::deinit (void)
|
|
{
|
|
delete m_referenceCtx;
|
|
m_referenceCtx = DE_NULL;
|
|
|
|
delete m_glCtx;
|
|
m_glCtx = DE_NULL;
|
|
|
|
delete m_program;
|
|
m_program = DE_NULL;
|
|
}
|
|
|
|
FragOpInteractionCase::IterateResult FragOpInteractionCase::iterate (void)
|
|
{
|
|
de::Random rnd (m_params.seed ^ deInt32Hash(m_iterNdx));
|
|
const tcu::ScopedLogSection section (m_testCtx.getLog(), string("Iter") + de::toString(m_iterNdx), string("Iteration ") + de::toString(m_iterNdx));
|
|
|
|
const int positionNdx = findShaderInputIndex(m_vertexShader.getInputs(), "dEQP_Position");
|
|
|
|
const int numVertices = 4;
|
|
VertexDataStorage vertexData (m_vertexShader.getInputs(), numVertices);
|
|
std::vector<rsg::VariableValue> uniformValues;
|
|
std::vector<RenderCommand> renderCmds (NUM_COMMANDS_PER_ITERATION);
|
|
|
|
tcu::Surface rendered (m_viewportSize.x(), m_viewportSize.y());
|
|
tcu::Surface reference (m_viewportSize.x(), m_viewportSize.y());
|
|
|
|
const tcu::Vec4 vtxInterpFactors[] =
|
|
{
|
|
tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f),
|
|
tcu::Vec4(1.0f, 0.0f, 0.5f, 0.5f),
|
|
tcu::Vec4(0.0f, 1.0f, 0.5f, 0.5f),
|
|
tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)
|
|
};
|
|
|
|
rsg::computeUniformValues(rnd, uniformValues, m_unifiedUniforms);
|
|
|
|
for (int attribNdx = 0; attribNdx < (int)m_vertexShader.getInputs().size(); ++attribNdx)
|
|
{
|
|
if (attribNdx == positionNdx)
|
|
continue;
|
|
|
|
const rsg::ShaderInput* shaderIn = m_vertexShader.getInputs()[attribNdx];
|
|
const rsg::VariableType& varType = shaderIn->getVariable()->getType();
|
|
const rsg::ConstValueRangeAccess valueRange = shaderIn->getValueRange();
|
|
const int numComponents = varType.getNumElements();
|
|
const glu::VertexArrayBinding layoutEntry = getEntryWithPointer(vertexData, attribNdx);
|
|
|
|
DE_ASSERT(varType.getBaseType() == rsg::VariableType::TYPE_FLOAT);
|
|
|
|
for (int vtxNdx = 0; vtxNdx < 4; vtxNdx++)
|
|
{
|
|
const int fNdx = (attribNdx+vtxNdx+m_iterNdx)%DE_LENGTH_OF_ARRAY(vtxInterpFactors);
|
|
const tcu::Vec4& f = vtxInterpFactors[fNdx];
|
|
|
|
switch (numComponents)
|
|
{
|
|
case 1: setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<1>())); break;
|
|
case 2: setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<2>())); break;
|
|
case 3: setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<3>())); break;
|
|
case 4: setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<4>())); break;
|
|
default:
|
|
DE_ASSERT(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (vector<RenderCommand>::iterator cmdIter = renderCmds.begin(); cmdIter != renderCmds.end(); ++cmdIter)
|
|
computeRandomRenderCommand(rnd, *cmdIter, m_renderCtx.getType().getAPI(), m_viewportSize.x(), m_viewportSize.y());
|
|
|
|
// Workaround for inaccurate barycentric/depth computation in current reference renderer:
|
|
// Small bias is added to the draw call depths, in increasing order, to avoid accuracy issues in depth comparison.
|
|
for (int cmdNdx = 0; cmdNdx < (int)renderCmds.size(); cmdNdx++)
|
|
renderCmds[cmdNdx].depth += 0.0231725f * float(cmdNdx);
|
|
|
|
{
|
|
const glu::VertexArrayPointer posPtr = getEntryWithPointer(vertexData, positionNdx).pointer;
|
|
|
|
sglr::Context* const contexts[] = { m_glCtx, &m_referenceCtx->context };
|
|
const deUint32 programs[] = { m_glProgram, m_refProgram };
|
|
tcu::PixelBufferAccess readDst[] = { rendered.getAccess(), reference.getAccess() };
|
|
|
|
const tcu::Vec4 accurateClearColor = tcu::Vec4(0.0f, 0.25f, 0.5f, 1.0f);
|
|
const tcu::Vec4 clearColor = getWellBehavingColor(accurateClearColor, m_renderCtx.getRenderTarget().getPixelFormat());
|
|
|
|
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(contexts); ndx++)
|
|
{
|
|
sglr::Context& ctx = *contexts[ndx];
|
|
const deUint32 program = programs[ndx];
|
|
|
|
setupAttributes(ctx, vertexData, program);
|
|
|
|
ctx.disable (GL_SCISSOR_TEST);
|
|
ctx.colorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
ctx.depthMask (GL_TRUE);
|
|
ctx.stencilMask (~0u);
|
|
ctx.clearColor (clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
|
|
ctx.clear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
|
|
|
|
ctx.useProgram (program);
|
|
|
|
for (vector<rsg::VariableValue>::const_iterator uniformIter = uniformValues.begin(); uniformIter != uniformValues.end(); ++uniformIter)
|
|
setUniformValue(ctx, ctx.getUniformLocation(program, uniformIter->getVariable()->getName()), uniformIter->getValue());
|
|
|
|
for (vector<RenderCommand>::const_iterator cmdIter = renderCmds.begin(); cmdIter != renderCmds.end(); ++cmdIter)
|
|
render(ctx, posPtr, *cmdIter);
|
|
|
|
GLU_EXPECT_NO_ERROR(ctx.getError(), "Rendering failed");
|
|
|
|
ctx.readPixels(0, 0, m_viewportSize.x(), m_viewportSize.y(), GL_RGBA, GL_UNSIGNED_BYTE, readDst[ndx].getDataPtr());
|
|
}
|
|
}
|
|
|
|
{
|
|
const tcu::RGBA threshold = m_renderCtx.getRenderTarget().getPixelFormat().getColorThreshold()+tcu::RGBA(3,3,3,3);
|
|
const bool compareOk = tcu::bilinearCompare(m_testCtx.getLog(), "CompareResult", "Image comparison result", reference.getAccess(), rendered.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
|
|
|
|
if (!compareOk)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
|
|
return STOP;
|
|
}
|
|
}
|
|
|
|
m_iterNdx += 1;
|
|
return (m_iterNdx < NUM_ITERATIONS) ? CONTINUE : STOP;
|
|
}
|
|
|
|
} // gls
|
|
} // deqp
|