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.
1129 lines
35 KiB
1129 lines
35 KiB
/*------------------------------------------------------------------------- |
|
* drawElements Quality Program EGL 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 Rendering tests for different config and api combinations. |
|
* \todo [2013-03-19 pyry] GLES1 and VG support. |
|
*//*--------------------------------------------------------------------*/ |
|
|
|
#include "teglRenderTests.hpp" |
|
#include "teglRenderCase.hpp" |
|
|
|
#include "tcuRenderTarget.hpp" |
|
#include "tcuTestLog.hpp" |
|
#include "tcuImageCompare.hpp" |
|
#include "tcuTextureUtil.hpp" |
|
#include "tcuSurface.hpp" |
|
|
|
#include "egluDefs.hpp" |
|
#include "egluUtil.hpp" |
|
|
|
#include "eglwLibrary.hpp" |
|
#include "eglwEnums.hpp" |
|
|
|
#include "gluShaderProgram.hpp" |
|
|
|
#include "glwFunctions.hpp" |
|
#include "glwEnums.hpp" |
|
|
|
#include "deRandom.hpp" |
|
#include "deSharedPtr.hpp" |
|
#include "deSemaphore.hpp" |
|
#include "deThread.hpp" |
|
#include "deString.h" |
|
|
|
#include "rrRenderer.hpp" |
|
#include "rrFragmentOperations.hpp" |
|
|
|
#include <algorithm> |
|
#include <iterator> |
|
#include <memory> |
|
#include <set> |
|
|
|
namespace deqp |
|
{ |
|
namespace egl |
|
{ |
|
|
|
using std::string; |
|
using std::vector; |
|
using std::set; |
|
|
|
using tcu::Vec4; |
|
|
|
using tcu::TestLog; |
|
|
|
using namespace glw; |
|
using namespace eglw; |
|
|
|
static const tcu::Vec4 CLEAR_COLOR = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); |
|
static const float CLEAR_DEPTH = 1.0f; |
|
static const int CLEAR_STENCIL = 0; |
|
|
|
namespace |
|
{ |
|
|
|
enum PrimitiveType |
|
{ |
|
PRIMITIVETYPE_TRIANGLE = 0, //!< Triangles, requires 3 coordinates per primitive |
|
// PRIMITIVETYPE_POINT, //!< Points, requires 1 coordinate per primitive (w is used as size) |
|
// PRIMITIVETYPE_LINE, //!< Lines, requires 2 coordinates per primitive |
|
|
|
PRIMITIVETYPE_LAST |
|
}; |
|
|
|
enum BlendMode |
|
{ |
|
BLENDMODE_NONE = 0, //!< No blending |
|
BLENDMODE_ADDITIVE, //!< Blending with ONE, ONE |
|
BLENDMODE_SRC_OVER, //!< Blending with SRC_ALPHA, ONE_MINUS_SRC_ALPHA |
|
|
|
BLENDMODE_LAST |
|
}; |
|
|
|
enum DepthMode |
|
{ |
|
DEPTHMODE_NONE = 0, //!< No depth test or depth writes |
|
DEPTHMODE_LESS, //!< Depth test with less & depth write |
|
|
|
DEPTHMODE_LAST |
|
}; |
|
|
|
enum StencilMode |
|
{ |
|
STENCILMODE_NONE = 0, //!< No stencil test or write |
|
STENCILMODE_LEQUAL_INC, //!< Stencil test with LEQUAL, increment on pass |
|
|
|
STENCILMODE_LAST |
|
}; |
|
|
|
struct DrawPrimitiveOp |
|
{ |
|
PrimitiveType type; |
|
int count; |
|
vector<Vec4> positions; |
|
vector<Vec4> colors; |
|
BlendMode blend; |
|
DepthMode depth; |
|
StencilMode stencil; |
|
int stencilRef; |
|
}; |
|
|
|
static bool isANarrowScreenSpaceTriangle (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2) |
|
{ |
|
// to clip space |
|
const tcu::Vec2 csp0 = p0.swizzle(0, 1) / p0.w(); |
|
const tcu::Vec2 csp1 = p1.swizzle(0, 1) / p1.w(); |
|
const tcu::Vec2 csp2 = p2.swizzle(0, 1) / p2.w(); |
|
|
|
const tcu::Vec2 e01 = (csp1 - csp0); |
|
const tcu::Vec2 e02 = (csp2 - csp0); |
|
|
|
const float minimumVisibleArea = 0.4f; // must cover at least 10% of the surface |
|
const float visibleArea = de::abs(e01.x() * e02.y() - e02.x() * e01.y()) * 0.5f; |
|
|
|
return visibleArea < minimumVisibleArea; |
|
} |
|
|
|
void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp) |
|
{ |
|
const int minStencilRef = 0; |
|
const int maxStencilRef = 8; |
|
const int minPrimitives = 2; |
|
const int maxPrimitives = 4; |
|
|
|
const float maxTriOffset = 1.0f; |
|
const float minDepth = -1.0f; // \todo [pyry] Reference doesn't support Z clipping yet |
|
const float maxDepth = 1.0f; |
|
|
|
const float minRGB = 0.2f; |
|
const float maxRGB = 0.9f; |
|
const float minAlpha = 0.3f; |
|
const float maxAlpha = 1.0f; |
|
|
|
drawOp.type = (PrimitiveType)rnd.getInt(0, PRIMITIVETYPE_LAST-1); |
|
drawOp.count = rnd.getInt(minPrimitives, maxPrimitives); |
|
drawOp.blend = (BlendMode)rnd.getInt(0, BLENDMODE_LAST-1); |
|
drawOp.depth = (DepthMode)rnd.getInt(0, DEPTHMODE_LAST-1); |
|
drawOp.stencil = (StencilMode)rnd.getInt(0, STENCILMODE_LAST-1); |
|
drawOp.stencilRef = rnd.getInt(minStencilRef, maxStencilRef); |
|
|
|
if (drawOp.type == PRIMITIVETYPE_TRIANGLE) |
|
{ |
|
drawOp.positions.resize(drawOp.count*3); |
|
drawOp.colors.resize(drawOp.count*3); |
|
|
|
for (int triNdx = 0; triNdx < drawOp.count; triNdx++) |
|
{ |
|
const float cx = rnd.getFloat(-1.0f, 1.0f); |
|
const float cy = rnd.getFloat(-1.0f, 1.0f); |
|
|
|
for (int coordNdx = 0; coordNdx < 3; coordNdx++) |
|
{ |
|
tcu::Vec4& position = drawOp.positions[triNdx*3 + coordNdx]; |
|
tcu::Vec4& color = drawOp.colors[triNdx*3 + coordNdx]; |
|
|
|
position.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
position.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
position.z() = rnd.getFloat(minDepth, maxDepth); |
|
position.w() = 1.0f; |
|
|
|
color.x() = rnd.getFloat(minRGB, maxRGB); |
|
color.y() = rnd.getFloat(minRGB, maxRGB); |
|
color.z() = rnd.getFloat(minRGB, maxRGB); |
|
color.w() = rnd.getFloat(minAlpha, maxAlpha); |
|
} |
|
|
|
// avoid generating narrow triangles |
|
{ |
|
const int maxAttempts = 40; |
|
int numAttempts = 0; |
|
tcu::Vec4& p0 = drawOp.positions[triNdx*3 + 0]; |
|
tcu::Vec4& p1 = drawOp.positions[triNdx*3 + 1]; |
|
tcu::Vec4& p2 = drawOp.positions[triNdx*3 + 2]; |
|
|
|
while (isANarrowScreenSpaceTriangle(p0, p1, p2)) |
|
{ |
|
p1.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
p1.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
p1.z() = rnd.getFloat(minDepth, maxDepth); |
|
p1.w() = 1.0f; |
|
|
|
p2.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
p2.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset); |
|
p2.z() = rnd.getFloat(minDepth, maxDepth); |
|
p2.w() = 1.0f; |
|
|
|
if (++numAttempts > maxAttempts) |
|
{ |
|
DE_ASSERT(false); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
else |
|
DE_ASSERT(false); |
|
} |
|
|
|
// Reference rendering code |
|
|
|
class ReferenceShader : public rr::VertexShader, public rr::FragmentShader |
|
{ |
|
public: |
|
enum |
|
{ |
|
VaryingLoc_Color = 0 |
|
}; |
|
|
|
ReferenceShader () |
|
: rr::VertexShader (2, 1) // color and pos in => color out |
|
, rr::FragmentShader(1, 1) // color in => color out |
|
{ |
|
this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; |
|
this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT; |
|
|
|
this->rr::VertexShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; |
|
this->rr::VertexShader::m_outputs[0].flatshade = false; |
|
|
|
this->rr::FragmentShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; |
|
this->rr::FragmentShader::m_inputs[0].flatshade = false; |
|
|
|
this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; |
|
} |
|
|
|
void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const |
|
{ |
|
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) |
|
{ |
|
const int positionAttrLoc = 0; |
|
const int colorAttrLoc = 1; |
|
|
|
rr::VertexPacket& packet = *packets[packetNdx]; |
|
|
|
// Transform to position |
|
packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx); |
|
|
|
// Pass color to FS |
|
packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx); |
|
} |
|
} |
|
|
|
void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const |
|
{ |
|
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) |
|
{ |
|
rr::FragmentPacket& packet = packets[packetNdx]; |
|
|
|
for (int fragNdx = 0; fragNdx < 4; ++fragNdx) |
|
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx)); |
|
} |
|
} |
|
}; |
|
|
|
void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp) |
|
{ |
|
state.cullMode = rr::CULLMODE_NONE; |
|
|
|
if (drawOp.blend != BLENDMODE_NONE) |
|
{ |
|
state.fragOps.blendMode = rr::BLENDMODE_STANDARD; |
|
|
|
switch (drawOp.blend) |
|
{ |
|
case BLENDMODE_ADDITIVE: |
|
state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_ONE; |
|
state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE; |
|
state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD; |
|
state.fragOps.blendAState = state.fragOps.blendRGBState; |
|
break; |
|
|
|
case BLENDMODE_SRC_OVER: |
|
state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_SRC_ALPHA; |
|
state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA; |
|
state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD; |
|
state.fragOps.blendAState = state.fragOps.blendRGBState; |
|
break; |
|
|
|
default: |
|
DE_ASSERT(false); |
|
} |
|
} |
|
|
|
if (drawOp.depth != DEPTHMODE_NONE) |
|
{ |
|
state.fragOps.depthTestEnabled = true; |
|
|
|
DE_ASSERT(drawOp.depth == DEPTHMODE_LESS); |
|
state.fragOps.depthFunc = rr::TESTFUNC_LESS; |
|
} |
|
|
|
if (drawOp.stencil != STENCILMODE_NONE) |
|
{ |
|
state.fragOps.stencilTestEnabled = true; |
|
|
|
DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC); |
|
state.fragOps.stencilStates[0].func = rr::TESTFUNC_LEQUAL; |
|
state.fragOps.stencilStates[0].sFail = rr::STENCILOP_KEEP; |
|
state.fragOps.stencilStates[0].dpFail = rr::STENCILOP_INCR; |
|
state.fragOps.stencilStates[0].dpPass = rr::STENCILOP_INCR; |
|
state.fragOps.stencilStates[0].ref = drawOp.stencilRef; |
|
state.fragOps.stencilStates[1] = state.fragOps.stencilStates[0]; |
|
} |
|
} |
|
|
|
tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits) |
|
{ |
|
using tcu::TextureFormat; |
|
|
|
DE_ASSERT(de::inBounds(colorBits.redBits, 0, 0xff) && |
|
de::inBounds(colorBits.greenBits, 0, 0xff) && |
|
de::inBounds(colorBits.blueBits, 0, 0xff) && |
|
de::inBounds(colorBits.alphaBits, 0, 0xff)); |
|
|
|
#define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A)) |
|
|
|
// \note [pyry] This may not hold true on some implementations - best effort guess only. |
|
switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits)) |
|
{ |
|
case PACK_FMT(8,8,8,8): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); |
|
case PACK_FMT(8,8,8,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8); |
|
case PACK_FMT(4,4,4,4): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_4444); |
|
case PACK_FMT(5,5,5,1): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551); |
|
case PACK_FMT(5,6,5,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_SHORT_565); |
|
|
|
// \note Defaults to RGBA8 |
|
default: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); |
|
} |
|
|
|
#undef PACK_FMT |
|
} |
|
|
|
tcu::TextureFormat getDepthFormat (const int depthBits) |
|
{ |
|
switch (depthBits) |
|
{ |
|
case 0: return tcu::TextureFormat(); |
|
case 8: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8); |
|
case 16: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16); |
|
case 24: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24); |
|
case 32: |
|
default: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT); |
|
} |
|
} |
|
|
|
tcu::TextureFormat getStencilFormat (int stencilBits) |
|
{ |
|
switch (stencilBits) |
|
{ |
|
case 0: return tcu::TextureFormat(); |
|
case 8: |
|
default: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8); |
|
} |
|
} |
|
|
|
void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples) |
|
{ |
|
const int width = dst.getWidth(); |
|
const int height = dst.getHeight(); |
|
|
|
tcu::TextureLevel colorBuffer; |
|
tcu::TextureLevel depthBuffer; |
|
tcu::TextureLevel stencilBuffer; |
|
|
|
rr::Renderer referenceRenderer; |
|
rr::VertexAttrib attributes[2]; |
|
const ReferenceShader shader; |
|
|
|
attributes[0].type = rr::VERTEXATTRIBTYPE_FLOAT; |
|
attributes[0].size = 4; |
|
attributes[0].stride = 0; |
|
attributes[0].instanceDivisor = 0; |
|
|
|
attributes[1].type = rr::VERTEXATTRIBTYPE_FLOAT; |
|
attributes[1].size = 4; |
|
attributes[1].stride = 0; |
|
attributes[1].instanceDivisor = 0; |
|
|
|
// Initialize buffers. |
|
colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height); |
|
rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height)); |
|
|
|
if (depthBits > 0) |
|
{ |
|
depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height); |
|
rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height)); |
|
} |
|
|
|
if (stencilBits > 0) |
|
{ |
|
stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height); |
|
rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height)); |
|
} |
|
|
|
const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()), |
|
rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()), |
|
rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess())); |
|
|
|
for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++) |
|
{ |
|
// Translate state |
|
rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()))); |
|
toReferenceRenderState(renderState, *drawOp); |
|
|
|
DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE); |
|
|
|
attributes[0].pointer = &drawOp->positions[0]; |
|
attributes[1].pointer = &drawOp->colors[0]; |
|
|
|
referenceRenderer.draw( |
|
rr::DrawCommand( |
|
renderState, |
|
renderTarget, |
|
rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)), |
|
2, |
|
attributes, |
|
rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0))); |
|
} |
|
|
|
rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())); |
|
} |
|
|
|
// API rendering code |
|
|
|
class Program |
|
{ |
|
public: |
|
Program (void) {} |
|
virtual ~Program (void) {} |
|
|
|
virtual void setup (void) const = DE_NULL; |
|
}; |
|
|
|
typedef de::SharedPtr<Program> ProgramSp; |
|
|
|
static glu::ProgramSources getProgramSourcesES2 (void) |
|
{ |
|
static const char* s_vertexSrc = |
|
"attribute highp vec4 a_position;\n" |
|
"attribute mediump vec4 a_color;\n" |
|
"varying mediump vec4 v_color;\n" |
|
"void main (void)\n" |
|
"{\n" |
|
" gl_Position = a_position;\n" |
|
" v_color = a_color;\n" |
|
"}\n"; |
|
|
|
static const char* s_fragmentSrc = |
|
"varying mediump vec4 v_color;\n" |
|
"void main (void)\n" |
|
"{\n" |
|
" gl_FragColor = v_color;\n" |
|
"}\n"; |
|
|
|
return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc); |
|
} |
|
|
|
class GLES2Program : public Program |
|
{ |
|
public: |
|
GLES2Program (const glw::Functions& gl) |
|
: m_gl (gl) |
|
, m_program (gl, getProgramSourcesES2()) |
|
, m_positionLoc (0) |
|
, m_colorLoc (0) |
|
{ |
|
|
|
m_positionLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_position"); |
|
m_colorLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_color"); |
|
} |
|
|
|
~GLES2Program (void) |
|
{ |
|
} |
|
|
|
void setup (void) const |
|
{ |
|
m_gl.useProgram(m_program.getProgram()); |
|
m_gl.enableVertexAttribArray(m_positionLoc); |
|
m_gl.enableVertexAttribArray(m_colorLoc); |
|
GLU_CHECK_GLW_MSG(m_gl, "Program setup failed"); |
|
} |
|
|
|
int getPositionLoc (void) const { return m_positionLoc; } |
|
int getColorLoc (void) const { return m_colorLoc; } |
|
|
|
private: |
|
const glw::Functions& m_gl; |
|
glu::ShaderProgram m_program; |
|
int m_positionLoc; |
|
int m_colorLoc; |
|
}; |
|
|
|
void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil) |
|
{ |
|
gl.clearColor(color.x(), color.y(), color.z(), color.w()); |
|
gl.clearDepthf(depth); |
|
gl.clearStencil(stencil); |
|
gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); |
|
} |
|
|
|
void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp) |
|
{ |
|
const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program); |
|
|
|
switch (drawOp.blend) |
|
{ |
|
case BLENDMODE_NONE: |
|
gl.disable(GL_BLEND); |
|
break; |
|
|
|
case BLENDMODE_ADDITIVE: |
|
gl.enable(GL_BLEND); |
|
gl.blendFunc(GL_ONE, GL_ONE); |
|
break; |
|
|
|
case BLENDMODE_SRC_OVER: |
|
gl.enable(GL_BLEND); |
|
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
break; |
|
|
|
default: |
|
DE_ASSERT(false); |
|
} |
|
|
|
switch (drawOp.depth) |
|
{ |
|
case DEPTHMODE_NONE: |
|
gl.disable(GL_DEPTH_TEST); |
|
break; |
|
|
|
case DEPTHMODE_LESS: |
|
gl.enable(GL_DEPTH_TEST); |
|
break; |
|
|
|
default: |
|
DE_ASSERT(false); |
|
} |
|
|
|
switch (drawOp.stencil) |
|
{ |
|
case STENCILMODE_NONE: |
|
gl.disable(GL_STENCIL_TEST); |
|
break; |
|
|
|
case STENCILMODE_LEQUAL_INC: |
|
gl.enable(GL_STENCIL_TEST); |
|
gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u); |
|
gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR); |
|
break; |
|
|
|
default: |
|
DE_ASSERT(false); |
|
} |
|
|
|
gl.disable(GL_DITHER); |
|
|
|
gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]); |
|
gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]); |
|
|
|
DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE); |
|
gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3); |
|
} |
|
|
|
static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst) |
|
{ |
|
gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr()); |
|
} |
|
|
|
Program* createProgram (const glw::Functions& gl, EGLint api) |
|
{ |
|
switch (api) |
|
{ |
|
case EGL_OPENGL_ES2_BIT: return new GLES2Program(gl); |
|
case EGL_OPENGL_ES3_BIT_KHR: return new GLES2Program(gl); |
|
default: |
|
throw tcu::NotSupportedError("Unsupported API"); |
|
} |
|
} |
|
|
|
void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp) |
|
{ |
|
switch (api) |
|
{ |
|
case EGL_OPENGL_ES2_BIT: drawGLES2(gl, program, drawOp); break; |
|
case EGL_OPENGL_ES3_BIT_KHR: drawGLES2(gl, program, drawOp); break; |
|
default: |
|
throw tcu::NotSupportedError("Unsupported API"); |
|
} |
|
} |
|
|
|
void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil) |
|
{ |
|
switch (api) |
|
{ |
|
case EGL_OPENGL_ES2_BIT: clearGLES2(gl, color, depth, stencil); break; |
|
case EGL_OPENGL_ES3_BIT_KHR: clearGLES2(gl, color, depth, stencil); break; |
|
default: |
|
throw tcu::NotSupportedError("Unsupported API"); |
|
} |
|
} |
|
|
|
static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst) |
|
{ |
|
switch (api) |
|
{ |
|
case EGL_OPENGL_ES2_BIT: readPixelsGLES2(gl, dst); break; |
|
case EGL_OPENGL_ES3_BIT_KHR: readPixelsGLES2(gl, dst); break; |
|
default: |
|
throw tcu::NotSupportedError("Unsupported API"); |
|
} |
|
} |
|
|
|
static void finish (const glw::Functions& gl, EGLint api) |
|
{ |
|
switch (api) |
|
{ |
|
case EGL_OPENGL_ES2_BIT: |
|
case EGL_OPENGL_ES3_BIT_KHR: |
|
gl.finish(); |
|
break; |
|
|
|
default: |
|
throw tcu::NotSupportedError("Unsupported API"); |
|
} |
|
} |
|
|
|
tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config) |
|
{ |
|
tcu::PixelFormat fmt; |
|
fmt.redBits = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE); |
|
fmt.greenBits = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE); |
|
fmt.blueBits = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE); |
|
fmt.alphaBits = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE); |
|
return fmt; |
|
} |
|
|
|
} // anonymous |
|
|
|
// SingleThreadRenderCase |
|
|
|
class SingleThreadRenderCase : public MultiContextRenderCase |
|
{ |
|
public: |
|
SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi); |
|
|
|
void init (void); |
|
|
|
private: |
|
virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); |
|
|
|
glw::Functions m_gl; |
|
}; |
|
|
|
// SingleThreadColorClearCase |
|
|
|
SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) |
|
: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) |
|
{ |
|
} |
|
|
|
void SingleThreadRenderCase::init (void) |
|
{ |
|
MultiContextRenderCase::init(); |
|
m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); |
|
} |
|
|
|
void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) |
|
{ |
|
const Library& egl = m_eglTestCtx.getLibrary(); |
|
const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); |
|
const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); |
|
const int numContexts = (int)contexts.size(); |
|
const int drawsPerCtx = 2; |
|
const int numIters = 2; |
|
const float threshold = 0.02f; |
|
|
|
const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); |
|
const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); |
|
const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); |
|
const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); |
|
|
|
TestLog& log = m_testCtx.getLog(); |
|
|
|
tcu::Surface refFrame (width, height); |
|
tcu::Surface frame (width, height); |
|
|
|
de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); |
|
vector<ProgramSp> programs (contexts.size()); |
|
vector<DrawPrimitiveOp> drawOps; |
|
|
|
// Log basic information about config. |
|
log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; |
|
|
|
// Generate draw ops. |
|
drawOps.resize(numContexts*drawsPerCtx*numIters); |
|
for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) |
|
randomizeDrawOp(rnd, *drawOp); |
|
|
|
// Create and setup programs per context |
|
for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) |
|
{ |
|
EGLint api = contexts[ctxNdx].first; |
|
EGLContext context = contexts[ctxNdx].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); |
|
programs[ctxNdx]->setup(); |
|
} |
|
|
|
// Clear to black using first context. |
|
{ |
|
EGLint api = contexts[0].first; |
|
EGLContext context = contexts[0].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); |
|
finish(m_gl, api); |
|
} |
|
|
|
// Render. |
|
for (int iterNdx = 0; iterNdx < numIters; iterNdx++) |
|
{ |
|
for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) |
|
{ |
|
EGLint api = contexts[ctxNdx].first; |
|
EGLContext context = contexts[ctxNdx].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++) |
|
{ |
|
const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx]; |
|
draw(m_gl, api, *programs[ctxNdx], drawOp); |
|
} |
|
|
|
finish(m_gl, api); |
|
} |
|
} |
|
|
|
// Read pixels using first context. \todo [pyry] Randomize? |
|
{ |
|
EGLint api = contexts[0].first; |
|
EGLContext context = contexts[0].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
readPixels(m_gl, api, frame); |
|
} |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
|
|
|
// Render reference. |
|
// \note Reference image is always generated using single-sampling. |
|
renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); |
|
|
|
// Compare images |
|
{ |
|
bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); |
|
|
|
if (!imagesOk) |
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); |
|
} |
|
} |
|
|
|
// MultiThreadRenderCase |
|
|
|
class MultiThreadRenderCase : public MultiContextRenderCase |
|
{ |
|
public: |
|
MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi); |
|
|
|
void init (void); |
|
|
|
private: |
|
virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); |
|
|
|
glw::Functions m_gl; |
|
}; |
|
|
|
class RenderTestThread; |
|
|
|
typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp; |
|
typedef de::SharedPtr<de::Semaphore> SemaphoreSp; |
|
|
|
struct DrawOpPacket |
|
{ |
|
DrawOpPacket (void) |
|
: drawOps (DE_NULL) |
|
, numOps (0) |
|
{ |
|
} |
|
|
|
const DrawPrimitiveOp* drawOps; |
|
int numOps; |
|
SemaphoreSp wait; |
|
SemaphoreSp signal; |
|
}; |
|
|
|
class RenderTestThread : public de::Thread |
|
{ |
|
public: |
|
RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets) |
|
: m_egl (egl) |
|
, m_display (display) |
|
, m_surface (surface) |
|
, m_context (context) |
|
, m_api (api) |
|
, m_gl (gl) |
|
, m_program (program) |
|
, m_packets (packets) |
|
{ |
|
} |
|
|
|
void run (void) |
|
{ |
|
for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++) |
|
{ |
|
// Wait until it is our turn. |
|
packetIter->wait->decrement(); |
|
|
|
// Acquire context. |
|
EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context)); |
|
|
|
// Execute rendering. |
|
for (int ndx = 0; ndx < packetIter->numOps; ndx++) |
|
draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]); |
|
|
|
finish(m_gl, m_api); |
|
|
|
// Release context. |
|
EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
|
|
|
// Signal completion. |
|
packetIter->signal->increment(); |
|
} |
|
} |
|
|
|
private: |
|
const Library& m_egl; |
|
EGLDisplay m_display; |
|
EGLSurface m_surface; |
|
EGLContext m_context; |
|
EGLint m_api; |
|
const glw::Functions& m_gl; |
|
const Program& m_program; |
|
const std::vector<DrawOpPacket>& m_packets; |
|
}; |
|
|
|
MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) |
|
: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) |
|
{ |
|
} |
|
|
|
void MultiThreadRenderCase::init (void) |
|
{ |
|
MultiContextRenderCase::init(); |
|
m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); |
|
} |
|
|
|
void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) |
|
{ |
|
const Library& egl = m_eglTestCtx.getLibrary(); |
|
const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); |
|
const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); |
|
const int numContexts = (int)contexts.size(); |
|
const int opsPerPacket = 2; |
|
const int packetsPerThread = 2; |
|
const int numThreads = numContexts; |
|
const int numPackets = numThreads * packetsPerThread; |
|
const float threshold = 0.02f; |
|
|
|
const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); |
|
const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); |
|
const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); |
|
const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); |
|
|
|
TestLog& log = m_testCtx.getLog(); |
|
|
|
tcu::Surface refFrame (width, height); |
|
tcu::Surface frame (width, height); |
|
|
|
de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); |
|
|
|
// Resources that need cleanup |
|
vector<ProgramSp> programs (numContexts); |
|
vector<SemaphoreSp> semaphores (numPackets+1); |
|
vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket); |
|
vector<vector<DrawOpPacket> > packets (numThreads); |
|
vector<RenderTestThreadSp> threads (numThreads); |
|
|
|
// Log basic information about config. |
|
log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; |
|
log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; |
|
|
|
// Initialize semaphores. |
|
for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem) |
|
*sem = SemaphoreSp(new de::Semaphore(0)); |
|
|
|
// Create draw ops. |
|
for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) |
|
randomizeDrawOp(rnd, *drawOp); |
|
|
|
// Create packets. |
|
for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) |
|
{ |
|
packets[threadNdx].resize(packetsPerThread); |
|
|
|
for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++) |
|
{ |
|
DrawOpPacket& packet = packets[threadNdx][packetNdx]; |
|
|
|
// Threads take turns with packets. |
|
packet.wait = semaphores[packetNdx*numThreads + threadNdx]; |
|
packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1]; |
|
packet.numOps = opsPerPacket; |
|
packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket]; |
|
} |
|
} |
|
|
|
// Create and setup programs per context |
|
for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) |
|
{ |
|
EGLint api = contexts[ctxNdx].first; |
|
EGLContext context = contexts[ctxNdx].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); |
|
programs[ctxNdx]->setup(); |
|
|
|
// Release context |
|
EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
|
} |
|
|
|
// Clear to black using first context. |
|
{ |
|
EGLint api = contexts[0].first; |
|
EGLContext context = contexts[0].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); |
|
finish(m_gl, api); |
|
|
|
// Release context |
|
EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
|
} |
|
|
|
// Create and launch threads (actual rendering starts once first semaphore is signaled). |
|
for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) |
|
{ |
|
threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx])); |
|
threads[threadNdx]->start(); |
|
} |
|
|
|
// Signal start and wait until complete. |
|
semaphores.front()->increment(); |
|
semaphores.back()->decrement(); |
|
|
|
// Read pixels using first context. \todo [pyry] Randomize? |
|
{ |
|
EGLint api = contexts[0].first; |
|
EGLContext context = contexts[0].second; |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); |
|
|
|
readPixels(m_gl, api, frame); |
|
} |
|
|
|
EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
|
|
|
// Join threads. |
|
for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) |
|
threads[threadNdx]->join(); |
|
|
|
// Render reference. |
|
renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); |
|
|
|
// Compare images |
|
{ |
|
bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); |
|
|
|
if (!imagesOk) |
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); |
|
} |
|
} |
|
|
|
RenderTests::RenderTests (EglTestContext& eglTestCtx) |
|
: TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs") |
|
{ |
|
} |
|
|
|
RenderTests::~RenderTests (void) |
|
{ |
|
} |
|
|
|
struct RenderGroupSpec |
|
{ |
|
const char* name; |
|
const char* desc; |
|
EGLint apiBits; |
|
eglu::ConfigFilter baseFilter; |
|
int numContextsPerApi; |
|
}; |
|
|
|
template <deUint32 Bits> |
|
static bool renderable (const eglu::CandidateConfig& c) |
|
{ |
|
return (c.renderableType() & Bits) == Bits; |
|
} |
|
|
|
template <class RenderClass> |
|
static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last) |
|
{ |
|
for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++) |
|
{ |
|
tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc); |
|
group->addChild(configGroup); |
|
|
|
vector<RenderFilterList> filterLists; |
|
eglu::FilterList baseFilters; |
|
baseFilters << groupIter->baseFilter; |
|
getDefaultRenderFilterLists(filterLists, baseFilters); |
|
|
|
for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++) |
|
configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi)); |
|
} |
|
} |
|
|
|
void RenderTests::init (void) |
|
{ |
|
static const RenderGroupSpec singleContextCases[] = |
|
{ |
|
{ |
|
"gles2", |
|
"Primitive rendering using GLES2", |
|
EGL_OPENGL_ES2_BIT, |
|
renderable<EGL_OPENGL_ES2_BIT>, |
|
1 |
|
}, |
|
{ |
|
"gles3", |
|
"Primitive rendering using GLES3", |
|
EGL_OPENGL_ES3_BIT, |
|
renderable<EGL_OPENGL_ES3_BIT>, |
|
1 |
|
}, |
|
}; |
|
|
|
static const RenderGroupSpec multiContextCases[] = |
|
{ |
|
{ |
|
"gles2", |
|
"Primitive rendering using multiple GLES2 contexts to shared surface", |
|
EGL_OPENGL_ES2_BIT, |
|
renderable<EGL_OPENGL_ES2_BIT>, |
|
3 |
|
}, |
|
{ |
|
"gles3", |
|
"Primitive rendering using multiple GLES3 contexts to shared surface", |
|
EGL_OPENGL_ES3_BIT, |
|
renderable<EGL_OPENGL_ES3_BIT>, |
|
3 |
|
}, |
|
{ |
|
"gles2_gles3", |
|
"Primitive rendering using multiple APIs to shared surface", |
|
EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT, |
|
renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>, |
|
1 |
|
}, |
|
}; |
|
|
|
tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering"); |
|
addChild(singleContextGroup); |
|
createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]); |
|
|
|
tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface"); |
|
addChild(multiContextGroup); |
|
createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); |
|
|
|
tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface"); |
|
addChild(multiThreadGroup); |
|
createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); |
|
} |
|
|
|
} // egl |
|
} // deqp
|
|
|