blob: e7c291177b84cdd36485dcc3c1a7115c845911ad [file] [log] [blame] [edit]
/*-------------------------------------------------------------------------
* 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 Buffer test utilities.
*//*--------------------------------------------------------------------*/
#include "glsBufferTestUtil.hpp"
#include "tcuRandomValueIterator.hpp"
#include "tcuSurface.hpp"
#include "tcuImageCompare.hpp"
#include "tcuVector.hpp"
#include "tcuFormatUtil.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "gluPixelTransfer.hpp"
#include "gluRenderContext.hpp"
#include "gluStrUtil.hpp"
#include "gluShaderProgram.hpp"
#include "deMemory.h"
#include "deStringUtil.hpp"
#include "deArrayUtil.hpp"
#include <algorithm>
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
namespace deqp
{
namespace gls
{
namespace BufferTestUtil
{
enum
{
VERIFY_QUAD_SIZE = 8, //!< Quad size in VertexArrayVerifier
MAX_LINES_PER_INDEX_ARRAY_DRAW = 128, //!< Maximum number of lines per one draw in IndexArrayVerifier
INDEX_ARRAY_DRAW_VIEWPORT_WIDTH = 128,
INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT = 128
};
using std::set;
using std::string;
using std::vector;
using tcu::TestLog;
// Helper functions.
void fillWithRandomBytes(uint8_t *ptr, int numBytes, uint32_t seed)
{
std::copy(tcu::RandomValueIterator<uint8_t>::begin(seed, numBytes), tcu::RandomValueIterator<uint8_t>::end(), ptr);
}
bool compareByteArrays(tcu::TestLog &log, const uint8_t *resPtr, const uint8_t *refPtr, int numBytes)
{
bool isOk = true;
const int maxSpanLen = 8;
const int maxDiffSpans = 4;
int numDiffSpans = 0;
int diffSpanStart = -1;
int ndx = 0;
log << TestLog::Section("Verify", "Verification result");
for (; ndx < numBytes; ndx++)
{
if (resPtr[ndx] != refPtr[ndx])
{
if (diffSpanStart < 0)
diffSpanStart = ndx;
isOk = false;
}
else if (diffSpanStart >= 0)
{
if (numDiffSpans < maxDiffSpans)
{
int len = ndx - diffSpanStart;
int printLen = de::min(len, maxSpanLen);
log << TestLog::Message << len << " byte difference at offset " << diffSpanStart << "\n"
<< " expected "
<< tcu::formatArray(tcu::Format::HexIterator<uint8_t>(refPtr + diffSpanStart),
tcu::Format::HexIterator<uint8_t>(refPtr + diffSpanStart + printLen))
<< "\n"
<< " got "
<< tcu::formatArray(tcu::Format::HexIterator<uint8_t>(resPtr + diffSpanStart),
tcu::Format::HexIterator<uint8_t>(resPtr + diffSpanStart + printLen))
<< TestLog::EndMessage;
}
else
log << TestLog::Message << "(output too long, truncated)" << TestLog::EndMessage;
numDiffSpans += 1;
diffSpanStart = -1;
}
}
if (diffSpanStart >= 0)
{
if (numDiffSpans < maxDiffSpans)
{
int len = ndx - diffSpanStart;
int printLen = de::min(len, maxSpanLen);
log << TestLog::Message << len << " byte difference at offset " << diffSpanStart << "\n"
<< " expected "
<< tcu::formatArray(tcu::Format::HexIterator<uint8_t>(refPtr + diffSpanStart),
tcu::Format::HexIterator<uint8_t>(refPtr + diffSpanStart + printLen))
<< "\n"
<< " got "
<< tcu::formatArray(tcu::Format::HexIterator<uint8_t>(resPtr + diffSpanStart),
tcu::Format::HexIterator<uint8_t>(resPtr + diffSpanStart + printLen))
<< TestLog::EndMessage;
}
else
log << TestLog::Message << "(output too long, truncated)" << TestLog::EndMessage;
}
log << TestLog::Message << (isOk ? "Verification passed." : "Verification FAILED!") << TestLog::EndMessage;
log << TestLog::EndSection;
return isOk;
}
const char *getBufferTargetName(uint32_t target)
{
switch (target)
{
case GL_ARRAY_BUFFER:
return "array";
case GL_COPY_READ_BUFFER:
return "copy_read";
case GL_COPY_WRITE_BUFFER:
return "copy_write";
case GL_ELEMENT_ARRAY_BUFFER:
return "element_array";
case GL_PIXEL_PACK_BUFFER:
return "pixel_pack";
case GL_PIXEL_UNPACK_BUFFER:
return "pixel_unpack";
case GL_TEXTURE_BUFFER:
return "texture";
case GL_TRANSFORM_FEEDBACK_BUFFER:
return "transform_feedback";
case GL_UNIFORM_BUFFER:
return "uniform";
default:
DE_ASSERT(false);
return DE_NULL;
}
}
const char *getUsageHintName(uint32_t hint)
{
switch (hint)
{
case GL_STREAM_DRAW:
return "stream_draw";
case GL_STREAM_READ:
return "stream_read";
case GL_STREAM_COPY:
return "stream_copy";
case GL_STATIC_DRAW:
return "static_draw";
case GL_STATIC_READ:
return "static_read";
case GL_STATIC_COPY:
return "static_copy";
case GL_DYNAMIC_DRAW:
return "dynamic_draw";
case GL_DYNAMIC_READ:
return "dynamic_read";
case GL_DYNAMIC_COPY:
return "dynamic_copy";
default:
DE_ASSERT(false);
return DE_NULL;
}
}
// BufferCase
BufferCase::BufferCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const char *name,
const char *description)
: TestCase(testCtx, name, description)
, CallLogWrapper(renderCtx.getFunctions(), testCtx.getLog())
, m_renderCtx(renderCtx)
{
}
BufferCase::~BufferCase(void)
{
enableLogging(false);
BufferCase::deinit();
}
void BufferCase::init(void)
{
enableLogging(true);
}
void BufferCase::deinit(void)
{
for (set<uint32_t>::const_iterator bufIter = m_allocatedBuffers.begin(); bufIter != m_allocatedBuffers.end();
bufIter++)
glDeleteBuffers(1, &(*bufIter));
}
uint32_t BufferCase::genBuffer(void)
{
uint32_t buf = 0;
glGenBuffers(1, &buf);
if (buf != 0)
{
try
{
m_allocatedBuffers.insert(buf);
}
catch (const std::exception &)
{
glDeleteBuffers(1, &buf);
throw;
}
}
return buf;
}
void BufferCase::deleteBuffer(uint32_t buffer)
{
glDeleteBuffers(1, &buffer);
m_allocatedBuffers.erase(buffer);
}
void BufferCase::checkError(void)
{
glw::GLenum err = glGetError();
if (err != GL_NO_ERROR)
throw tcu::TestError(string("Got ") + glu::getErrorStr(err).toString());
}
// ReferenceBuffer
void ReferenceBuffer::setSize(int numBytes)
{
m_data.resize(numBytes);
}
void ReferenceBuffer::setData(int numBytes, const uint8_t *bytes)
{
m_data.resize(numBytes);
std::copy(bytes, bytes + numBytes, m_data.begin());
}
void ReferenceBuffer::setSubData(int offset, int numBytes, const uint8_t *bytes)
{
DE_ASSERT(de::inBounds(offset, 0, (int)m_data.size()) &&
de::inRange(offset + numBytes, offset, (int)m_data.size()));
std::copy(bytes, bytes + numBytes, m_data.begin() + offset);
}
// BufferWriterBase
BufferWriterBase::BufferWriterBase(glu::RenderContext &renderCtx, tcu::TestLog &log)
: CallLogWrapper(renderCtx.getFunctions(), log)
, m_renderCtx(renderCtx)
{
enableLogging(true);
}
void BufferWriterBase::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes, uint32_t targetHint)
{
DE_UNREF(targetHint);
write(buffer, offset, numBytes, bytes);
}
// BufferWriter
BufferWriter::BufferWriter(glu::RenderContext &renderCtx, tcu::TestLog &log, WriteType writeType) : m_writer(DE_NULL)
{
switch (writeType)
{
case WRITE_BUFFER_SUB_DATA:
m_writer = new BufferSubDataWriter(renderCtx, log);
break;
case WRITE_BUFFER_WRITE_MAP:
m_writer = new BufferWriteMapWriter(renderCtx, log);
break;
default:
TCU_FAIL("Unsupported writer");
}
}
BufferWriter::~BufferWriter(void)
{
delete m_writer;
}
void BufferWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes)
{
DE_ASSERT(numBytes >= getMinSize());
DE_ASSERT(offset % getAlignment() == 0);
DE_ASSERT((offset + numBytes) % getAlignment() == 0);
return m_writer->write(buffer, offset, numBytes, bytes);
}
void BufferWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes, uint32_t targetHint)
{
DE_ASSERT(numBytes >= getMinSize());
DE_ASSERT(offset % getAlignment() == 0);
DE_ASSERT((offset + numBytes) % getAlignment() == 0);
return m_writer->write(buffer, offset, numBytes, bytes, targetHint);
}
// BufferSubDataWriter
void BufferSubDataWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes)
{
write(buffer, offset, numBytes, bytes, GL_ARRAY_BUFFER);
}
void BufferSubDataWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes, uint32_t target)
{
glBindBuffer(target, buffer);
glBufferSubData(target, offset, numBytes, bytes);
glBindBuffer(target, 0);
GLU_CHECK();
}
// BufferWriteMapWriter
void BufferWriteMapWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes)
{
write(buffer, offset, numBytes, bytes, GL_ARRAY_BUFFER);
}
void BufferWriteMapWriter::write(uint32_t buffer, int offset, int numBytes, const uint8_t *bytes, uint32_t target)
{
glBindBuffer(target, buffer);
void *ptr = glMapBufferRange(target, offset, numBytes, GL_MAP_WRITE_BIT);
GLU_CHECK_MSG("glMapBufferRange");
deMemcpy(ptr, bytes, numBytes);
glUnmapBuffer(target);
glBindBuffer(target, 0);
GLU_CHECK();
}
// BufferVerifierBase
BufferVerifierBase::BufferVerifierBase(glu::RenderContext &renderCtx, tcu::TestLog &log)
: CallLogWrapper(renderCtx.getFunctions(), log)
, m_renderCtx(renderCtx)
, m_log(log)
{
enableLogging(true);
}
bool BufferVerifierBase::verify(uint32_t buffer, const uint8_t *reference, int offset, int numBytes,
uint32_t targetHint)
{
DE_UNREF(targetHint);
return verify(buffer, reference, offset, numBytes);
}
// BufferVerifier
BufferVerifier::BufferVerifier(glu::RenderContext &renderCtx, tcu::TestLog &log, VerifyType verifyType)
: m_verifier(DE_NULL)
{
switch (verifyType)
{
case VERIFY_AS_VERTEX_ARRAY:
m_verifier = new VertexArrayVerifier(renderCtx, log);
break;
case VERIFY_AS_INDEX_ARRAY:
m_verifier = new IndexArrayVerifier(renderCtx, log);
break;
case VERIFY_BUFFER_READ_MAP:
m_verifier = new BufferMapVerifier(renderCtx, log);
break;
default:
TCU_FAIL("Unsupported verifier");
}
}
BufferVerifier::~BufferVerifier(void)
{
delete m_verifier;
}
bool BufferVerifier::verify(uint32_t buffer, const uint8_t *reference, int offset, int numBytes)
{
DE_ASSERT(numBytes >= getMinSize());
DE_ASSERT(offset % getAlignment() == 0);
DE_ASSERT((offset + numBytes) % getAlignment() == 0);
return m_verifier->verify(buffer, reference, offset, numBytes);
}
bool BufferVerifier::verify(uint32_t buffer, const uint8_t *reference, int offset, int numBytes, uint32_t targetHint)
{
DE_ASSERT(numBytes >= getMinSize());
DE_ASSERT(offset % getAlignment() == 0);
DE_ASSERT((offset + numBytes) % getAlignment() == 0);
return m_verifier->verify(buffer, reference, offset, numBytes, targetHint);
}
// BufferMapVerifier
bool BufferMapVerifier::verify(uint32_t buffer, const uint8_t *reference, int offset, int numBytes)
{
return verify(buffer, reference, offset, numBytes, GL_ARRAY_BUFFER);
}
bool BufferMapVerifier::verify(uint32_t buffer, const uint8_t *reference, int offset, int numBytes, uint32_t target)
{
const uint8_t *mapPtr = DE_NULL;
bool isOk = false;
glBindBuffer(target, buffer);
mapPtr = (const uint8_t *)glMapBufferRange(target, offset, numBytes, GL_MAP_READ_BIT);
GLU_CHECK_MSG("glMapBufferRange");
TCU_CHECK(mapPtr);
isOk = compareByteArrays(m_log, mapPtr, reference + offset, numBytes);
glUnmapBuffer(target);
GLU_CHECK_MSG("glUnmapBuffer");
glBindBuffer(target, 0);
return isOk;
}
// VertexArrayVerifier
VertexArrayVerifier::VertexArrayVerifier(glu::RenderContext &renderCtx, tcu::TestLog &log)
: BufferVerifierBase(renderCtx, log)
, m_program(DE_NULL)
, m_posLoc(0)
, m_byteVecLoc(0)
, m_vao(0)
{
const glu::ContextType ctxType = renderCtx.getType();
const glu::GLSLVersion glslVersion =
glu::isContextTypeES(ctxType) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
DE_ASSERT(glu::isGLSLVersionSupported(ctxType, glslVersion));
m_program = new glu::ShaderProgram(m_renderCtx,
glu::makeVtxFragSources(string(glu::getGLSLVersionDeclaration(glslVersion)) +
"\n"
"in highp vec2 a_position;\n"
"in mediump vec3 a_byteVec;\n"
"out mediump vec3 v_byteVec;\n"
"void main (void)\n"
"{\n"
" gl_Position = vec4(a_position, 0.0, 1.0);\n"
" v_byteVec = a_byteVec;\n"
"}\n",
string(glu::getGLSLVersionDeclaration(glslVersion)) +
"\n"
"in mediump vec3 v_byteVec;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = vec4(v_byteVec, 1.0);\n"
"}\n"));
if (!m_program->isOk())
{
m_log << *m_program;
delete m_program;
TCU_FAIL("Compile failed");
}
const glw::Functions &gl = m_renderCtx.getFunctions();
m_posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position");
m_byteVecLoc = gl.getAttribLocation(m_program->getProgram(), "a_byteVec");
gl.genVertexArrays(1, &m_vao);
gl.genBuffers(1, &m_positionBuf);
gl.genBuffers(1, &m_indexBuf);
GLU_EXPECT_NO_ERROR(gl.getError(), "Initialization failed");
}
VertexArrayVerifier::~VertexArrayVerifier(void)
{
const glw::Functions &gl = m_renderCtx.getFunctions();
if (m_vao)
gl.deleteVertexArrays(1, &m_vao);
if (m_positionBuf)
gl.deleteBuffers(1, &m_positionBuf);
if (m_indexBuf)
gl.deleteBuffers(1, &m_indexBuf);
delete m_program;
}
static void computePositions(vector<tcu::Vec2> &positions, int gridSizeX, int gridSizeY)
{
positions.resize(gridSizeX * gridSizeY * 4);
for (int y = 0; y < gridSizeY; y++)
for (int x = 0; x < gridSizeX; x++)
{
float sx0 = (float)(x + 0) / (float)gridSizeX;
float sy0 = (float)(y + 0) / (float)gridSizeY;
float sx1 = (float)(x + 1) / (float)gridSizeX;
float sy1 = (float)(y + 1) / (float)gridSizeY;
float fx0 = 2.0f * sx0 - 1.0f;
float fy0 = 2.0f * sy0 - 1.0f;
float fx1 = 2.0f * sx1 - 1.0f;
float fy1 = 2.0f * sy1 - 1.0f;
int baseNdx = (y * gridSizeX + x) * 4;
positions[baseNdx + 0] = tcu::Vec2(fx0, fy0);
positions[baseNdx + 1] = tcu::Vec2(fx0, fy1);
positions[baseNdx + 2] = tcu::Vec2(fx1, fy0);
positions[baseNdx + 3] = tcu::Vec2(fx1, fy1);
}
}
static void computeIndices(vector<uint16_t> &indices, int gridSizeX, int gridSizeY)
{
indices.resize(3 * 2 * gridSizeX * gridSizeY);
for (int quadNdx = 0; quadNdx < gridSizeX * gridSizeY; quadNdx++)
{
int v00 = quadNdx * 4 + 0;
int v01 = quadNdx * 4 + 1;
int v10 = quadNdx * 4 + 2;
int v11 = quadNdx * 4 + 3;
DE_ASSERT(v11 < (1 << 16));
indices[quadNdx * 6 + 0] = (uint16_t)v10;
indices[quadNdx * 6 + 1] = (uint16_t)v00;
indices[quadNdx * 6 + 2] = (uint16_t)v01;
indices[quadNdx * 6 + 3] = (uint16_t)v10;
indices[quadNdx * 6 + 4] = (uint16_t)v01;
indices[quadNdx * 6 + 5] = (uint16_t)v11;
}
}
static inline tcu::Vec4 fetchVtxColor(const uint8_t *ptr, int vtxNdx)
{
return tcu::RGBA(*(ptr + vtxNdx * 3 + 0), *(ptr + vtxNdx * 3 + 1), *(ptr + vtxNdx * 3 + 2), 255).toVec();
}
static void renderQuadGridReference(tcu::Surface &dst, int numQuads, int rowLength, const uint8_t *inPtr)
{
using tcu::Vec4;
dst.setSize(rowLength * VERIFY_QUAD_SIZE,
(numQuads / rowLength + (numQuads % rowLength != 0 ? 1 : 0)) * VERIFY_QUAD_SIZE);
tcu::PixelBufferAccess dstAccess = dst.getAccess();
tcu::clear(dstAccess, tcu::IVec4(0, 0, 0, 0xff));
for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
{
int x0 = (quadNdx % rowLength) * VERIFY_QUAD_SIZE;
int y0 = (quadNdx / rowLength) * VERIFY_QUAD_SIZE;
Vec4 v00 = fetchVtxColor(inPtr, quadNdx * 4 + 0);
Vec4 v10 = fetchVtxColor(inPtr, quadNdx * 4 + 1);
Vec4 v01 = fetchVtxColor(inPtr, quadNdx * 4 + 2);
Vec4 v11 = fetchVtxColor(inPtr, quadNdx * 4 + 3);
for (int y = 0; y < VERIFY_QUAD_SIZE; y++)
for (int x = 0; x < VERIFY_QUAD_SIZE; x++)
{
float fx = ((float)x + 0.5f) / (float)VERIFY_QUAD_SIZE;
float fy = ((float)y + 0.5f) / (float)VERIFY_QUAD_SIZE;
bool tri = fx + fy <= 1.0f;
float tx = tri ? fx : (1.0f - fx);
float ty = tri ? fy : (1.0f - fy);
const Vec4 &t0 = tri ? v00 : v11;
const Vec4 &t1 = tri ? v01 : v10;
const Vec4 &t2 = tri ? v10 : v01;
Vec4 color = t0 + (t1 - t0) * tx + (t2 - t0) * ty;
dstAccess.setPixel(color, x0 + x, y0 + y);
}
}
}
bool VertexArrayVerifier::verify(uint32_t buffer, const uint8_t *refPtr, int offset, int numBytes)
{
const tcu::RenderTarget &renderTarget = m_renderCtx.getRenderTarget();
const int numBytesInVtx = 3;
const int numBytesInQuad = numBytesInVtx * 4;
int maxQuadsX = de::min(128, renderTarget.getWidth() / VERIFY_QUAD_SIZE);
int maxQuadsY = de::min(128, renderTarget.getHeight() / VERIFY_QUAD_SIZE);
int maxQuadsPerBatch = maxQuadsX * maxQuadsY;
int numVerified = 0;
uint32_t program = m_program->getProgram();
tcu::RGBA threshold = renderTarget.getPixelFormat().getColorThreshold() + tcu::RGBA(3, 3, 3, 3);
bool isOk = true;
vector<tcu::Vec2> positions;
vector<uint16_t> indices;
tcu::Surface rendered;
tcu::Surface reference;
DE_ASSERT(numBytes >= numBytesInQuad); // Can't render full quad with smaller buffers.
computePositions(positions, maxQuadsX, maxQuadsY);
computeIndices(indices, maxQuadsX, maxQuadsY);
// Reset buffer bindings.
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
// Setup rendering state.
glViewport(0, 0, maxQuadsX * VERIFY_QUAD_SIZE, maxQuadsY * VERIFY_QUAD_SIZE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glUseProgram(program);
glBindVertexArray(m_vao);
// Upload positions
glBindBuffer(GL_ARRAY_BUFFER, m_positionBuf);
glBufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(positions.size() * sizeof(positions[0])), &positions[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(m_posLoc);
glVertexAttribPointer(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, DE_NULL);
// Upload indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuf);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (glw::GLsizeiptr)(indices.size() * sizeof(indices[0])), &indices[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(m_byteVecLoc);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
while (numVerified < numBytes)
{
int numRemaining = numBytes - numVerified;
bool isLeftoverBatch = numRemaining < numBytesInQuad;
int numBytesToVerify =
isLeftoverBatch ? numBytesInQuad :
de::min(maxQuadsPerBatch * numBytesInQuad, numRemaining - numRemaining % numBytesInQuad);
int curOffset = isLeftoverBatch ? (numBytes - numBytesInQuad) : numVerified;
int numQuads = numBytesToVerify / numBytesInQuad;
int numCols = de::min(maxQuadsX, numQuads);
int numRows = numQuads / maxQuadsX + (numQuads % maxQuadsX != 0 ? 1 : 0);
string imageSetDesc = string("Bytes ") + de::toString(offset + curOffset) + " to " +
de::toString(offset + curOffset + numBytesToVerify - 1);
DE_ASSERT(numBytesToVerify > 0 && numBytesToVerify % numBytesInQuad == 0);
DE_ASSERT(de::inBounds(curOffset, 0, numBytes));
DE_ASSERT(de::inRange(curOffset + numBytesToVerify, curOffset, numBytes));
// Render batch.
glClear(GL_COLOR_BUFFER_BIT);
glVertexAttribPointer(m_byteVecLoc, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0,
(const glw::GLvoid *)(uintptr_t)(offset + curOffset));
glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, DE_NULL);
renderQuadGridReference(reference, numQuads, numCols, refPtr + offset + curOffset);
rendered.setSize(numCols * VERIFY_QUAD_SIZE, numRows * VERIFY_QUAD_SIZE);
glu::readPixels(m_renderCtx, 0, 0, rendered.getAccess());
if (!tcu::pixelThresholdCompare(m_log, "RenderResult", imageSetDesc.c_str(), reference, rendered, threshold,
tcu::COMPARE_LOG_RESULT))
{
isOk = false;
break;
}
numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
}
glBindVertexArray(0);
return isOk;
}
// IndexArrayVerifier
IndexArrayVerifier::IndexArrayVerifier(glu::RenderContext &renderCtx, tcu::TestLog &log)
: BufferVerifierBase(renderCtx, log)
, m_program(DE_NULL)
, m_posLoc(0)
, m_colorLoc(0)
{
const glu::ContextType ctxType = renderCtx.getType();
const glu::GLSLVersion glslVersion =
glu::isContextTypeES(ctxType) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
DE_ASSERT(glu::isGLSLVersionSupported(ctxType, glslVersion));
m_program = new glu::ShaderProgram(m_renderCtx,
glu::makeVtxFragSources(string(glu::getGLSLVersionDeclaration(glslVersion)) +
"\n"
"in highp vec2 a_position;\n"
"in mediump vec3 a_color;\n"
"out mediump vec3 v_color;\n"
"void main (void)\n"
"{\n"
" gl_Position = vec4(a_position, 0.0, 1.0);\n"
" v_color = a_color;\n"
"}\n",
string(glu::getGLSLVersionDeclaration(glslVersion)) +
"\n"
"in mediump vec3 v_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = vec4(v_color, 1.0);\n"
"}\n"));
if (!m_program->isOk())
{
m_log << *m_program;
delete m_program;
TCU_FAIL("Compile failed");
}
const glw::Functions &gl = m_renderCtx.getFunctions();
m_posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position");
m_colorLoc = gl.getAttribLocation(m_program->getProgram(), "a_color");
gl.genVertexArrays(1, &m_vao);
gl.genBuffers(1, &m_positionBuf);
gl.genBuffers(1, &m_colorBuf);
GLU_EXPECT_NO_ERROR(gl.getError(), "Initialization failed");
}
IndexArrayVerifier::~IndexArrayVerifier(void)
{
const glw::Functions &gl = m_renderCtx.getFunctions();
if (m_vao)
gl.deleteVertexArrays(1, &m_vao);
if (m_positionBuf)
gl.deleteBuffers(1, &m_positionBuf);
if (m_colorBuf)
gl.deleteBuffers(1, &m_colorBuf);
delete m_program;
}
static void computeIndexVerifierPositions(std::vector<tcu::Vec2> &dst)
{
const int numPosX = 16;
const int numPosY = 16;
dst.resize(numPosX * numPosY);
for (int y = 0; y < numPosY; y++)
{
for (int x = 0; x < numPosX; x++)
{
float xf = float(x) / float(numPosX - 1);
float yf = float(y) / float(numPosY - 1);
dst[y * numPosX + x] = tcu::Vec2(2.0f * xf - 1.0f, 2.0f * yf - 1.0f);
}
}
}
static void computeIndexVerifierColors(std::vector<tcu::Vec3> &dst)
{
const int numColors = 256;
const float minVal = 0.1f;
const float maxVal = 0.5f;
de::Random rnd(0xabc231);
dst.resize(numColors);
for (std::vector<tcu::Vec3>::iterator i = dst.begin(); i != dst.end(); ++i)
{
i->x() = rnd.getFloat(minVal, maxVal);
i->y() = rnd.getFloat(minVal, maxVal);
i->z() = rnd.getFloat(minVal, maxVal);
}
}
template <typename T>
static void execVertexFetch(T *dst, const T *src, const uint8_t *indices, int numIndices)
{
for (int i = 0; i < numIndices; ++i)
dst[i] = src[indices[i]];
}
bool IndexArrayVerifier::verify(uint32_t buffer, const uint8_t *refPtr, int offset, int numBytes)
{
const tcu::RenderTarget &renderTarget = m_renderCtx.getRenderTarget();
const int viewportW = de::min<int>(INDEX_ARRAY_DRAW_VIEWPORT_WIDTH, renderTarget.getWidth());
const int viewportH = de::min<int>(INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT, renderTarget.getHeight());
const int minBytesPerBatch = 2;
const tcu::RGBA threshold(0, 0, 0, 0);
std::vector<tcu::Vec2> positions;
std::vector<tcu::Vec3> colors;
std::vector<tcu::Vec2> fetchedPos(MAX_LINES_PER_INDEX_ARRAY_DRAW + 1);
std::vector<tcu::Vec3> fetchedColor(MAX_LINES_PER_INDEX_ARRAY_DRAW + 1);
tcu::Surface indexBufferImg(viewportW, viewportH);
tcu::Surface referenceImg(viewportW, viewportH);
int numVerified = 0;
bool isOk = true;
DE_STATIC_ASSERT(sizeof(tcu::Vec2) == sizeof(float) * 2);
DE_STATIC_ASSERT(sizeof(tcu::Vec3) == sizeof(float) * 3);
computeIndexVerifierPositions(positions);
computeIndexVerifierColors(colors);
// Reset buffer bindings.
glBindVertexArray(m_vao);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
// Setup rendering state.
glViewport(0, 0, viewportW, viewportH);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glUseProgram(m_program->getProgram());
glEnableVertexAttribArray(m_posLoc);
glEnableVertexAttribArray(m_colorLoc);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
while (numVerified < numBytes)
{
int numRemaining = numBytes - numVerified;
bool isLeftoverBatch = numRemaining < minBytesPerBatch;
int numBytesToVerify =
isLeftoverBatch ? minBytesPerBatch : de::min(MAX_LINES_PER_INDEX_ARRAY_DRAW + 1, numRemaining);
int curOffset = isLeftoverBatch ? (numBytes - minBytesPerBatch) : numVerified;
string imageSetDesc = string("Bytes ") + de::toString(offset + curOffset) + " to " +
de::toString(offset + curOffset + numBytesToVerify - 1);
// Step 1: Render using index buffer.
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, m_positionBuf);
glBufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(positions.size() * sizeof(positions[0])), &positions[0],
GL_STREAM_DRAW);
glVertexAttribPointer(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, DE_NULL);
glBindBuffer(GL_ARRAY_BUFFER, m_colorBuf);
glBufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(colors.size() * sizeof(colors[0])), &colors[0], GL_STREAM_DRAW);
glVertexAttribPointer(m_colorLoc, 3, GL_FLOAT, GL_FALSE, 0, DE_NULL);
glDrawElements(GL_LINE_STRIP, numBytesToVerify, GL_UNSIGNED_BYTE, (void *)(uintptr_t)(offset + curOffset));
glu::readPixels(m_renderCtx, 0, 0, indexBufferImg.getAccess());
// Step 2: Do manual fetch and render without index buffer.
execVertexFetch(&fetchedPos[0], &positions[0], refPtr + offset + curOffset, numBytesToVerify);
execVertexFetch(&fetchedColor[0], &colors[0], refPtr + offset + curOffset, numBytesToVerify);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, m_positionBuf);
glBufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(fetchedPos.size() * sizeof(fetchedPos[0])), &fetchedPos[0],
GL_STREAM_DRAW);
glVertexAttribPointer(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, DE_NULL);
glBindBuffer(GL_ARRAY_BUFFER, m_colorBuf);
glBufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(fetchedColor.size() * sizeof(fetchedColor[0])),
&fetchedColor[0], GL_STREAM_DRAW);
glVertexAttribPointer(m_colorLoc, 3, GL_FLOAT, GL_FALSE, 0, DE_NULL);
glDrawArrays(GL_LINE_STRIP, 0, numBytesToVerify);
glu::readPixels(m_renderCtx, 0, 0, referenceImg.getAccess());
if (!tcu::pixelThresholdCompare(m_log, "RenderResult", imageSetDesc.c_str(), referenceImg, indexBufferImg,
threshold, tcu::COMPARE_LOG_RESULT))
{
isOk = false;
break;
}
numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
}
glBindVertexArray(0);
return isOk;
}
const char *getWriteTypeDescription(WriteType write)
{
static const char *s_desc[] = {"glBufferSubData()", "glMapBufferRange()", "transform feedback",
"glReadPixels() into PBO binding"};
return de::getSizedArrayElement<WRITE_LAST>(s_desc, write);
}
const char *getVerifyTypeDescription(VerifyType verify)
{
static const char *s_desc[] = {"rendering as vertex data", "rendering as index data",
"reading in shader as uniform buffer data", "using as PBO and uploading to texture",
"reading back using glMapBufferRange()"};
return de::getSizedArrayElement<VERIFY_LAST>(s_desc, verify);
}
} // namespace BufferTestUtil
} // namespace gls
} // namespace deqp