blob: e49e581cd1fc2c6eb99f63c4184ed42bdf86c527 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 3.1 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 Drawing tests.
*//*--------------------------------------------------------------------*/
#include "es31fDrawTests.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deMemory.h"
#include "tcuRenderTarget.hpp"
#include "tcuVectorUtil.hpp"
#include "sglrGLContext.hpp"
#include "glsDrawTest.hpp"
#include "gluStrUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluCallLogWrapper.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include <set>
namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace
{
enum TestIterationType
{
TYPE_DRAW_COUNT, // !< test with 1, 5, and 25 primitives
TYPE_INSTANCE_COUNT, // !< test with 1, 4, and 11 instances
TYPE_LAST
};
static const char *s_commonVertexShaderSource = "#version 310 es\n"
"in highp vec4 a_position;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
"}\n";
static const char *s_commonFragmentShaderSource = "#version 310 es\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"void main (void)\n"
"{\n"
" fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
static const char *s_colorVertexShaderSource = "#version 310 es\n"
"in highp vec4 a_position;\n"
"in highp vec4 a_color;\n"
"out highp vec4 v_color;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = a_color;\n"
"}\n";
static const char *s_colorFragmentShaderSource = "#version 310 es\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"in highp vec4 v_color;\n"
"void main (void)\n"
"{\n"
" fragColor = v_color;\n"
"}\n";
struct DrawElementsCommand
{
uint32_t count;
uint32_t primCount;
uint32_t firstIndex;
int32_t baseVertex;
uint32_t reservedMustBeZero;
};
DE_STATIC_ASSERT(5 * sizeof(uint32_t) == sizeof(DrawElementsCommand)); // tight packing
struct DrawArraysCommand
{
uint32_t count;
uint32_t primCount;
uint32_t first;
uint32_t reservedMustBeZero;
};
DE_STATIC_ASSERT(4 * sizeof(uint32_t) == sizeof(DrawArraysCommand)); // tight packing
// Verifies image contains only yellow or greeen, or a linear combination
// of these colors.
static bool verifyImageYellowGreen(const tcu::Surface &image, tcu::TestLog &log)
{
using tcu::TestLog;
const int colorThreshold = 20;
tcu::Surface error(image.getWidth(), image.getHeight());
bool isOk = true;
for (int y = 0; y < image.getHeight(); y++)
for (int x = 0; x < image.getWidth(); x++)
{
const tcu::RGBA pixel = image.getPixel(x, y);
bool pixelOk = true;
// Any pixel with !(G ~= 255) is faulty (not a linear combinations of green and yellow)
if (de::abs(pixel.getGreen() - 255) > colorThreshold)
pixelOk = false;
// Any pixel with !(B ~= 0) is faulty (not a linear combinations of green and yellow)
if (de::abs(pixel.getBlue() - 0) > colorThreshold)
pixelOk = false;
error.setPixel(x, y, (pixelOk) ? (tcu::RGBA(0, 255, 0, 255)) : (tcu::RGBA(255, 0, 0, 255)));
isOk = isOk && pixelOk;
}
if (!isOk)
{
log << TestLog::Message << "Image verification failed." << TestLog::EndMessage;
log << TestLog::ImageSet("Verfication result", "Result of rendering")
<< TestLog::Image("Result", "Result", image) << TestLog::Image("ErrorMask", "Error mask", error)
<< TestLog::EndImageSet;
}
else
{
log << TestLog::ImageSet("Verfication result", "Result of rendering")
<< TestLog::Image("Result", "Result", image) << TestLog::EndImageSet;
}
return isOk;
}
static void addTestIterations(gls::DrawTest *test, gls::DrawTestSpec &spec, TestIterationType type)
{
if (type == TYPE_DRAW_COUNT)
{
spec.primitiveCount = 1;
test->addIteration(spec, "draw count = 1");
spec.primitiveCount = 5;
test->addIteration(spec, "draw count = 5");
spec.primitiveCount = 25;
test->addIteration(spec, "draw count = 25");
}
else if (type == TYPE_INSTANCE_COUNT)
{
spec.instanceCount = 1;
test->addIteration(spec, "instance count = 1");
spec.instanceCount = 4;
test->addIteration(spec, "instance count = 4");
spec.instanceCount = 11;
test->addIteration(spec, "instance count = 11");
}
else
DE_ASSERT(false);
}
static void genBasicSpec(gls::DrawTestSpec &spec, glu::ContextType contextType, gls::DrawTestSpec::DrawMethod method)
{
spec.apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
spec.primitive = gls::DrawTestSpec::PRIMITIVE_TRIANGLES;
spec.primitiveCount = 5;
spec.drawMethod = method;
spec.indexType = gls::DrawTestSpec::INDEXTYPE_LAST;
spec.indexPointerOffset = 0;
spec.indexStorage = gls::DrawTestSpec::STORAGE_LAST;
spec.first = 0;
spec.indexMin = 0;
spec.indexMax = 0;
spec.instanceCount = 1;
spec.indirectOffset = 0;
spec.attribs.resize(2);
spec.attribs[0].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[0].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[0].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[0].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[0].componentCount = 4;
spec.attribs[0].offset = 0;
spec.attribs[0].stride = 0;
spec.attribs[0].normalize = false;
spec.attribs[0].instanceDivisor = 0;
spec.attribs[0].useDefaultAttribute = false;
spec.attribs[1].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[1].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[1].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[1].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[1].componentCount = 2;
spec.attribs[1].offset = 0;
spec.attribs[1].stride = 0;
spec.attribs[1].normalize = false;
spec.attribs[1].instanceDivisor = 0;
spec.attribs[1].useDefaultAttribute = false;
}
static std::string sizeToString(int size)
{
if (size < 1024)
return de::toString(size) + " byte(s)";
if (size < 1024 * 1024)
return de::toString(size / 1024) + " KB";
return de::toString(size / 1024 / 1024) + " MB";
}
class AttributeGroup : public TestCaseGroup
{
public:
AttributeGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod,
gls::DrawTestSpec::Primitive primitive, gls::DrawTestSpec::IndexType indexType,
gls::DrawTestSpec::Storage indexStorage);
~AttributeGroup(void);
void init(void);
private:
gls::DrawTestSpec::DrawMethod m_method;
gls::DrawTestSpec::Primitive m_primitive;
gls::DrawTestSpec::IndexType m_indexType;
gls::DrawTestSpec::Storage m_indexStorage;
};
AttributeGroup::AttributeGroup(Context &context, const char *name, const char *descr,
gls::DrawTestSpec::DrawMethod drawMethod, gls::DrawTestSpec::Primitive primitive,
gls::DrawTestSpec::IndexType indexType, gls::DrawTestSpec::Storage indexStorage)
: TestCaseGroup(context, name, descr)
, m_method(drawMethod)
, m_primitive(primitive)
, m_indexType(indexType)
, m_indexStorage(indexStorage)
{
}
AttributeGroup::~AttributeGroup(void)
{
}
void AttributeGroup::init(void)
{
// Single attribute
{
gls::DrawTest *test =
new gls::DrawTest(m_testCtx, m_context.getRenderContext(), "single_attribute", "Single attribute array.");
glu::ContextType contextType = m_context.getRenderContext().getType();
gls::DrawTestSpec spec;
spec.apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
spec.primitive = m_primitive;
spec.primitiveCount = 5;
spec.drawMethod = m_method;
spec.indexType = m_indexType;
spec.indexPointerOffset = 0;
spec.indexStorage = m_indexStorage;
spec.first = 0;
spec.indexMin = 0;
spec.indexMax = 0;
spec.instanceCount = 1;
spec.indirectOffset = 0;
spec.attribs.resize(1);
spec.attribs[0].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[0].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[0].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[0].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[0].componentCount = 2;
spec.attribs[0].offset = 0;
spec.attribs[0].stride = 0;
spec.attribs[0].normalize = false;
spec.attribs[0].instanceDivisor = 0;
spec.attribs[0].useDefaultAttribute = false;
addTestIterations(test, spec, TYPE_DRAW_COUNT);
this->addChild(test);
}
// Multiple attribute
{
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), "multiple_attributes",
"Multiple attribute arrays.");
glu::ContextType contextType = m_context.getRenderContext().getType();
gls::DrawTestSpec spec;
spec.apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
spec.primitive = m_primitive;
spec.primitiveCount = 5;
spec.drawMethod = m_method;
spec.indexType = m_indexType;
spec.indexPointerOffset = 0;
spec.indexStorage = m_indexStorage;
spec.first = 0;
spec.indexMin = 0;
spec.indexMax = 0;
spec.instanceCount = 1;
spec.indirectOffset = 0;
spec.attribs.resize(2);
spec.attribs[0].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[0].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[0].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[0].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[0].componentCount = 4;
spec.attribs[0].offset = 0;
spec.attribs[0].stride = 0;
spec.attribs[0].normalize = false;
spec.attribs[0].instanceDivisor = 0;
spec.attribs[0].useDefaultAttribute = false;
spec.attribs[1].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[1].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[1].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[1].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[1].componentCount = 2;
spec.attribs[1].offset = 0;
spec.attribs[1].stride = 0;
spec.attribs[1].normalize = false;
spec.attribs[1].instanceDivisor = 0;
spec.attribs[1].useDefaultAttribute = false;
addTestIterations(test, spec, TYPE_DRAW_COUNT);
this->addChild(test);
}
// Multiple attribute, second one divided
{
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), "instanced_attributes",
"Instanced attribute array.");
glu::ContextType contextType = m_context.getRenderContext().getType();
gls::DrawTestSpec spec;
spec.apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
spec.primitive = m_primitive;
spec.primitiveCount = 5;
spec.drawMethod = m_method;
spec.indexType = m_indexType;
spec.indexPointerOffset = 0;
spec.indexStorage = m_indexStorage;
spec.first = 0;
spec.indexMin = 0;
spec.indexMax = 0;
spec.instanceCount = 1;
spec.indirectOffset = 0;
spec.attribs.resize(3);
spec.attribs[0].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[0].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[0].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[0].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[0].componentCount = 4;
spec.attribs[0].offset = 0;
spec.attribs[0].stride = 0;
spec.attribs[0].normalize = false;
spec.attribs[0].instanceDivisor = 0;
spec.attribs[0].useDefaultAttribute = false;
// Add another position component so the instances wont be drawn on each other
spec.attribs[1].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[1].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[1].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[1].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[1].componentCount = 2;
spec.attribs[1].offset = 0;
spec.attribs[1].stride = 0;
spec.attribs[1].normalize = false;
spec.attribs[1].instanceDivisor = 1;
spec.attribs[1].useDefaultAttribute = false;
spec.attribs[1].additionalPositionAttribute = true;
// Instanced color
spec.attribs[2].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[2].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[2].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[2].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[2].componentCount = 3;
spec.attribs[2].offset = 0;
spec.attribs[2].stride = 0;
spec.attribs[2].normalize = false;
spec.attribs[2].instanceDivisor = 1;
spec.attribs[2].useDefaultAttribute = false;
addTestIterations(test, spec, TYPE_INSTANCE_COUNT);
this->addChild(test);
}
// Multiple attribute, second one default
{
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), "default_attribute",
"Attribute specified with glVertexAttrib*.");
glu::ContextType contextType = m_context.getRenderContext().getType();
gls::DrawTestSpec spec;
spec.apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
spec.primitive = m_primitive;
spec.primitiveCount = 5;
spec.drawMethod = m_method;
spec.indexType = m_indexType;
spec.indexPointerOffset = 0;
spec.indexStorage = m_indexStorage;
spec.first = 0;
spec.indexMin = 0;
spec.indexMax = 0;
spec.instanceCount = 1;
spec.indirectOffset = 0;
spec.attribs.resize(2);
spec.attribs[0].inputType = gls::DrawTestSpec::INPUTTYPE_FLOAT;
spec.attribs[0].outputType = gls::DrawTestSpec::OUTPUTTYPE_VEC2;
spec.attribs[0].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[0].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[0].componentCount = 2;
spec.attribs[0].offset = 0;
spec.attribs[0].stride = 0;
spec.attribs[0].normalize = false;
spec.attribs[0].instanceDivisor = 0;
spec.attribs[0].useDefaultAttribute = false;
struct IOPair
{
gls::DrawTestSpec::InputType input;
gls::DrawTestSpec::OutputType output;
int componentCount;
} iopairs[] = {
{gls::DrawTestSpec::INPUTTYPE_FLOAT, gls::DrawTestSpec::OUTPUTTYPE_VEC2, 4},
{gls::DrawTestSpec::INPUTTYPE_FLOAT, gls::DrawTestSpec::OUTPUTTYPE_VEC4, 2},
{gls::DrawTestSpec::INPUTTYPE_INT, gls::DrawTestSpec::OUTPUTTYPE_IVEC3, 4},
{gls::DrawTestSpec::INPUTTYPE_UNSIGNED_INT, gls::DrawTestSpec::OUTPUTTYPE_UVEC2, 4},
};
for (int ioNdx = 0; ioNdx < DE_LENGTH_OF_ARRAY(iopairs); ++ioNdx)
{
const std::string desc = gls::DrawTestSpec::inputTypeToString(iopairs[ioNdx].input) +
de::toString(iopairs[ioNdx].componentCount) + " to " +
gls::DrawTestSpec::outputTypeToString(iopairs[ioNdx].output);
spec.attribs[1].inputType = iopairs[ioNdx].input;
spec.attribs[1].outputType = iopairs[ioNdx].output;
spec.attribs[1].storage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.attribs[1].usage = gls::DrawTestSpec::USAGE_STATIC_DRAW;
spec.attribs[1].componentCount = iopairs[ioNdx].componentCount;
spec.attribs[1].offset = 0;
spec.attribs[1].stride = 0;
spec.attribs[1].normalize = false;
spec.attribs[1].instanceDivisor = 0;
spec.attribs[1].useDefaultAttribute = true;
test->addIteration(spec, desc.c_str());
}
this->addChild(test);
}
}
class IndexGroup : public TestCaseGroup
{
public:
IndexGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod);
~IndexGroup(void);
void init(void);
private:
gls::DrawTestSpec::DrawMethod m_method;
};
IndexGroup::IndexGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod)
: TestCaseGroup(context, name, descr)
, m_method(drawMethod)
{
}
IndexGroup::~IndexGroup(void)
{
}
void IndexGroup::init(void)
{
struct IndexTest
{
gls::DrawTestSpec::IndexType type;
int offsets[3];
};
const IndexTest tests[] = {
{gls::DrawTestSpec::INDEXTYPE_BYTE, {0, 1, -1}},
{gls::DrawTestSpec::INDEXTYPE_SHORT, {0, 2, -1}},
{gls::DrawTestSpec::INDEXTYPE_INT, {0, 4, -1}},
};
gls::DrawTestSpec spec;
genBasicSpec(spec, m_context.getRenderContext().getType(), m_method);
spec.indexStorage = gls::DrawTestSpec::STORAGE_BUFFER;
for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(tests); ++testNdx)
{
const IndexTest &indexTest = tests[testNdx];
const std::string name = std::string("index_") + gls::DrawTestSpec::indexTypeToString(indexTest.type);
const std::string desc = std::string("index ") + gls::DrawTestSpec::indexTypeToString(indexTest.type);
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), name.c_str(), desc.c_str());
spec.indexType = indexTest.type;
for (int iterationNdx = 0;
iterationNdx < DE_LENGTH_OF_ARRAY(indexTest.offsets) && indexTest.offsets[iterationNdx] != -1;
++iterationNdx)
{
const std::string iterationDesc =
std::string("first vertex ") +
de::toString(indexTest.offsets[iterationNdx] / gls::DrawTestSpec::indexTypeSize(indexTest.type));
spec.indexPointerOffset = indexTest.offsets[iterationNdx];
test->addIteration(spec, iterationDesc.c_str());
}
addChild(test);
}
}
class BaseVertexGroup : public TestCaseGroup
{
public:
BaseVertexGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod);
~BaseVertexGroup(void);
void init(void);
private:
gls::DrawTestSpec::DrawMethod m_method;
};
BaseVertexGroup::BaseVertexGroup(Context &context, const char *name, const char *descr,
gls::DrawTestSpec::DrawMethod drawMethod)
: TestCaseGroup(context, name, descr)
, m_method(drawMethod)
{
}
BaseVertexGroup::~BaseVertexGroup(void)
{
}
void BaseVertexGroup::init(void)
{
struct IndexTest
{
bool positiveBase;
gls::DrawTestSpec::IndexType type;
int baseVertex[2];
};
const IndexTest tests[] = {
{true, gls::DrawTestSpec::INDEXTYPE_BYTE, {1, 2}}, {true, gls::DrawTestSpec::INDEXTYPE_SHORT, {1, 2}},
{true, gls::DrawTestSpec::INDEXTYPE_INT, {1, 2}}, {false, gls::DrawTestSpec::INDEXTYPE_BYTE, {-1, -2}},
{false, gls::DrawTestSpec::INDEXTYPE_SHORT, {-1, -2}}, {false, gls::DrawTestSpec::INDEXTYPE_INT, {-1, -2}},
};
gls::DrawTestSpec spec;
genBasicSpec(spec, m_context.getRenderContext().getType(), m_method);
spec.indexStorage = gls::DrawTestSpec::STORAGE_BUFFER;
for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(tests); ++testNdx)
{
const IndexTest &indexTest = tests[testNdx];
const std::string name = std::string("index_") + (indexTest.positiveBase ? "" : "neg_") +
gls::DrawTestSpec::indexTypeToString(indexTest.type);
const std::string desc = std::string("index ") + gls::DrawTestSpec::indexTypeToString(indexTest.type);
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), name.c_str(), desc.c_str());
spec.indexType = indexTest.type;
for (int iterationNdx = 0; iterationNdx < DE_LENGTH_OF_ARRAY(indexTest.baseVertex); ++iterationNdx)
{
const std::string iterationDesc =
std::string("base vertex ") + de::toString(indexTest.baseVertex[iterationNdx]);
spec.baseVertex = indexTest.baseVertex[iterationNdx];
test->addIteration(spec, iterationDesc.c_str());
}
addChild(test);
}
}
class FirstGroup : public TestCaseGroup
{
public:
FirstGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod);
~FirstGroup(void);
void init(void);
private:
gls::DrawTestSpec::DrawMethod m_method;
};
FirstGroup::FirstGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod)
: TestCaseGroup(context, name, descr)
, m_method(drawMethod)
{
}
FirstGroup::~FirstGroup(void)
{
}
void FirstGroup::init(void)
{
const int firsts[] = {1, 3, 17};
gls::DrawTestSpec spec;
genBasicSpec(spec, m_context.getRenderContext().getType(), m_method);
for (int firstNdx = 0; firstNdx < DE_LENGTH_OF_ARRAY(firsts); ++firstNdx)
{
const std::string name = std::string("first_") + de::toString(firsts[firstNdx]);
const std::string desc = std::string("first ") + de::toString(firsts[firstNdx]);
gls::DrawTest *test = new gls::DrawTest(m_testCtx, m_context.getRenderContext(), name.c_str(), desc.c_str());
spec.first = firsts[firstNdx];
addTestIterations(test, spec, TYPE_DRAW_COUNT);
this->addChild(test);
}
}
class MethodGroup : public TestCaseGroup
{
public:
MethodGroup(Context &context, const char *name, const char *descr, gls::DrawTestSpec::DrawMethod drawMethod);
~MethodGroup(void);
void init(void);
private:
gls::DrawTestSpec::DrawMethod m_method;
};
MethodGroup::MethodGroup(Context &context, const char *name, const char *descr,
gls::DrawTestSpec::DrawMethod drawMethod)
: TestCaseGroup(context, name, descr)
, m_method(drawMethod)
{
}
MethodGroup::~MethodGroup(void)
{
}
void MethodGroup::init(void)
{
const bool indexed = (m_method == gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INDIRECT);
const bool hasFirst = (m_method == gls::DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INDIRECT);
const gls::DrawTestSpec::Primitive primitive[] = {
gls::DrawTestSpec::PRIMITIVE_POINTS, gls::DrawTestSpec::PRIMITIVE_TRIANGLES,
gls::DrawTestSpec::PRIMITIVE_TRIANGLE_FAN, gls::DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP,
gls::DrawTestSpec::PRIMITIVE_LINES, gls::DrawTestSpec::PRIMITIVE_LINE_STRIP,
gls::DrawTestSpec::PRIMITIVE_LINE_LOOP};
if (hasFirst)
{
// First-tests
this->addChild(new FirstGroup(m_context, "first", "First tests", m_method));
}
if (indexed)
{
// Index-tests
this->addChild(new IndexGroup(m_context, "indices", "Index tests", m_method));
this->addChild(new BaseVertexGroup(m_context, "base_vertex", "Base vertex tests", m_method));
}
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(primitive); ++ndx)
{
const std::string name = gls::DrawTestSpec::primitiveToString(primitive[ndx]);
const std::string desc = gls::DrawTestSpec::primitiveToString(primitive[ndx]);
this->addChild(new AttributeGroup(m_context, name.c_str(), desc.c_str(), m_method, primitive[ndx],
gls::DrawTestSpec::INDEXTYPE_SHORT, gls::DrawTestSpec::STORAGE_BUFFER));
}
}
class GridProgram : public sglr::ShaderProgram
{
public:
GridProgram(void);
void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const;
void shadeFragments(rr::FragmentPacket *packets, const int numPackets,
const rr::FragmentShadingContext &context) const;
};
GridProgram::GridProgram(void)
: sglr::ShaderProgram(sglr::pdec::ShaderProgramDeclaration()
<< sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexAttribute("a_offset", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexAttribute("a_color", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexSource("#version 310 es\n"
"in highp vec4 a_position;\n"
"in highp vec4 a_offset;\n"
"in highp vec4 a_color;\n"
"out highp vec4 v_color;\n"
"void main(void)\n"
"{\n"
" gl_Position = a_position + a_offset;\n"
" v_color = a_color;\n"
"}\n")
<< sglr::pdec::FragmentSource("#version 310 es\n"
"layout(location = 0) out highp vec4 dEQP_FragColor;\n"
"in highp vec4 v_color;\n"
"void main(void)\n"
"{\n"
" dEQP_FragColor = v_color;\n"
"}\n"))
{
}
void GridProgram::shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets,
const int numPackets) const
{
for (int ndx = 0; ndx < numPackets; ++ndx)
{
packets[ndx]->position =
rr::readVertexAttribFloat(inputs[0], packets[ndx]->instanceNdx, packets[ndx]->vertexNdx) +
rr::readVertexAttribFloat(inputs[1], packets[ndx]->instanceNdx, packets[ndx]->vertexNdx);
packets[ndx]->outputs[0] =
rr::readVertexAttribFloat(inputs[2], packets[ndx]->instanceNdx, packets[ndx]->vertexNdx);
}
}
void GridProgram::shadeFragments(rr::FragmentPacket *packets, const int numPackets,
const rr::FragmentShadingContext &context) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0,
rr::readTriangleVarying<float>(packets[packetNdx], context, 0, fragNdx));
}
class InstancedGridRenderTest : public TestCase
{
public:
InstancedGridRenderTest(Context &context, const char *name, const char *desc, int gridSide, bool useIndices);
~InstancedGridRenderTest(void);
IterateResult iterate(void);
private:
void renderTo(sglr::Context &ctx, sglr::ShaderProgram &program, tcu::Surface &dst);
const int m_gridSide;
const bool m_useIndices;
};
InstancedGridRenderTest::InstancedGridRenderTest(Context &context, const char *name, const char *desc, int gridSide,
bool useIndices)
: TestCase(context, name, desc)
, m_gridSide(gridSide)
, m_useIndices(useIndices)
{
}
InstancedGridRenderTest::~InstancedGridRenderTest(void)
{
}
InstancedGridRenderTest::IterateResult InstancedGridRenderTest::iterate(void)
{
const int renderTargetWidth = de::min(1024, m_context.getRenderTarget().getWidth());
const int renderTargetHeight = de::min(1024, m_context.getRenderTarget().getHeight());
sglr::GLContext ctx(m_context.getRenderContext(), m_testCtx.getLog(),
sglr::GLCONTEXT_LOG_CALLS | sglr::GLCONTEXT_LOG_PROGRAMS,
tcu::IVec4(0, 0, renderTargetWidth, renderTargetHeight));
tcu::Surface surface(renderTargetWidth, renderTargetHeight);
GridProgram program;
// render
renderTo(ctx, program, surface);
// verify image
// \note the green/yellow pattern is only for clarity. The test will only verify that all instances were drawn by looking for anything non-green/yellow.
if (verifyImageYellowGreen(surface, m_testCtx.getLog()))
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result image invalid");
return STOP;
}
void InstancedGridRenderTest::renderTo(sglr::Context &ctx, sglr::ShaderProgram &program, tcu::Surface &dst)
{
const tcu::Vec4 green(0, 1, 0, 1);
const tcu::Vec4 yellow(1, 1, 0, 1);
uint32_t vaoID = 0;
uint32_t positionBuf = 0;
uint32_t offsetBuf = 0;
uint32_t colorBuf = 0;
uint32_t indexBuf = 0;
uint32_t drawIndirectBuf = 0;
uint32_t programID = ctx.createProgram(&program);
int32_t posLocation = ctx.getAttribLocation(programID, "a_position");
int32_t offsetLocation = ctx.getAttribLocation(programID, "a_offset");
int32_t colorLocation = ctx.getAttribLocation(programID, "a_color");
float cellW = 2.0f / (float)m_gridSide;
float cellH = 2.0f / (float)m_gridSide;
const tcu::Vec4 vertexPositions[] = {
tcu::Vec4(0, 0, 0, 1), tcu::Vec4(cellW, 0, 0, 1), tcu::Vec4(0, cellH, 0, 1),
tcu::Vec4(0, cellH, 0, 1), tcu::Vec4(cellW, 0, 0, 1), tcu::Vec4(cellW, cellH, 0, 1),
};
const uint16_t indices[] = {0, 4, 3, 2, 1, 5};
std::vector<tcu::Vec4> offsets;
for (int x = 0; x < m_gridSide; ++x)
for (int y = 0; y < m_gridSide; ++y)
offsets.push_back(tcu::Vec4((float)x * cellW - 1.0f, (float)y * cellW - 1.0f, 0, 0));
std::vector<tcu::Vec4> colors;
for (int x = 0; x < m_gridSide; ++x)
for (int y = 0; y < m_gridSide; ++y)
colors.push_back(((x + y) % 2 == 0) ? (green) : (yellow));
ctx.genVertexArrays(1, &vaoID);
ctx.bindVertexArray(vaoID);
ctx.genBuffers(1, &positionBuf);
ctx.bindBuffer(GL_ARRAY_BUFFER, positionBuf);
ctx.bufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
ctx.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
ctx.vertexAttribDivisor(posLocation, 0);
ctx.enableVertexAttribArray(posLocation);
ctx.genBuffers(1, &offsetBuf);
ctx.bindBuffer(GL_ARRAY_BUFFER, offsetBuf);
ctx.bufferData(GL_ARRAY_BUFFER, offsets.size() * sizeof(tcu::Vec4), &offsets[0], GL_STATIC_DRAW);
ctx.vertexAttribPointer(offsetLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
ctx.vertexAttribDivisor(offsetLocation, 1);
ctx.enableVertexAttribArray(offsetLocation);
ctx.genBuffers(1, &colorBuf);
ctx.bindBuffer(GL_ARRAY_BUFFER, colorBuf);
ctx.bufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(tcu::Vec4), &colors[0], GL_STATIC_DRAW);
ctx.vertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
ctx.vertexAttribDivisor(colorLocation, 1);
ctx.enableVertexAttribArray(colorLocation);
if (m_useIndices)
{
ctx.genBuffers(1, &indexBuf);
ctx.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
ctx.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
ctx.genBuffers(1, &drawIndirectBuf);
ctx.bindBuffer(GL_DRAW_INDIRECT_BUFFER, drawIndirectBuf);
if (m_useIndices)
{
DrawElementsCommand command;
command.count = 6;
command.primCount = m_gridSide * m_gridSide;
command.firstIndex = 0;
command.baseVertex = 0;
command.reservedMustBeZero = 0;
ctx.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(command), &command, GL_STATIC_DRAW);
}
else
{
DrawArraysCommand command;
command.count = 6;
command.primCount = m_gridSide * m_gridSide;
command.first = 0;
command.reservedMustBeZero = 0;
ctx.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(command), &command, GL_STATIC_DRAW);
}
ctx.clearColor(0, 0, 0, 1);
ctx.clear(GL_COLOR_BUFFER_BIT);
ctx.viewport(0, 0, dst.getWidth(), dst.getHeight());
ctx.useProgram(programID);
if (m_useIndices)
ctx.drawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, DE_NULL);
else
ctx.drawArraysIndirect(GL_TRIANGLES, DE_NULL);
ctx.useProgram(0);
glu::checkError(ctx.getError(), "", __FILE__, __LINE__);
ctx.deleteBuffers(1, &drawIndirectBuf);
if (m_useIndices)
ctx.deleteBuffers(1, &indexBuf);
ctx.deleteBuffers(1, &colorBuf);
ctx.deleteBuffers(1, &offsetBuf);
ctx.deleteBuffers(1, &positionBuf);
ctx.deleteVertexArrays(1, &vaoID);
ctx.deleteProgram(programID);
ctx.finish();
ctx.readPixels(dst, 0, 0, dst.getWidth(), dst.getHeight());
glu::checkError(ctx.getError(), "", __FILE__, __LINE__);
}
class InstancingGroup : public TestCaseGroup
{
public:
InstancingGroup(Context &context, const char *name, const char *descr);
~InstancingGroup(void);
void init(void);
};
InstancingGroup::InstancingGroup(Context &context, const char *name, const char *descr)
: TestCaseGroup(context, name, descr)
{
}
InstancingGroup::~InstancingGroup(void)
{
}
void InstancingGroup::init(void)
{
const int gridWidths[] = {
2, 5, 10, 32, 100,
};
// drawArrays
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(gridWidths); ++ndx)
{
const std::string name = std::string("draw_arrays_indirect_grid_") + de::toString(gridWidths[ndx]) + "x" +
de::toString(gridWidths[ndx]);
const std::string desc = std::string("DrawArraysIndirect, Grid size ") + de::toString(gridWidths[ndx]) + "x" +
de::toString(gridWidths[ndx]);
this->addChild(new InstancedGridRenderTest(m_context, name.c_str(), desc.c_str(), gridWidths[ndx], false));
}
// drawElements
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(gridWidths); ++ndx)
{
const std::string name = std::string("draw_elements_indirect_grid_") + de::toString(gridWidths[ndx]) + "x" +
de::toString(gridWidths[ndx]);
const std::string desc = std::string("DrawElementsIndirect, Grid size ") + de::toString(gridWidths[ndx]) + "x" +
de::toString(gridWidths[ndx]);
this->addChild(new InstancedGridRenderTest(m_context, name.c_str(), desc.c_str(), gridWidths[ndx], true));
}
}
class ComputeShaderGeneratedCase : public TestCase
{
public:
enum DrawMethod
{
DRAWMETHOD_DRAWARRAYS,
DRAWMETHOD_DRAWELEMENTS,
DRAWMETHOD_LAST
};
ComputeShaderGeneratedCase(Context &context, const char *name, const char *desc, DrawMethod method, bool computeCmd,
bool computeData, bool computeIndices, int gridSize, int drawCallCount);
~ComputeShaderGeneratedCase(void);
void init(void);
void deinit(void);
IterateResult iterate(void);
std::string genComputeSource(bool computeCmd, bool computeData, bool computeIndices) const;
private:
void createDrawCommand(void);
void createDrawData(void);
void createDrawIndices(void);
virtual void runComputeShader(void) = 0;
void renderTo(tcu::Surface &image);
protected:
uint32_t calcDrawBufferSize(void) const;
uint32_t calcIndexBufferSize(void) const;
const DrawMethod m_drawMethod;
const bool m_computeCmd;
const bool m_computeData;
const bool m_computeIndices;
const int m_commandSize;
const int m_numDrawCmds;
const int m_gridSize;
glw::GLuint m_cmdBufferID;
glw::GLuint m_dataBufferID;
glw::GLuint m_indexBufferID;
private:
glu::ShaderProgram *m_shaderProgram;
};
ComputeShaderGeneratedCase::ComputeShaderGeneratedCase(Context &context, const char *name, const char *desc,
DrawMethod method, bool computeCmd, bool computeData,
bool computeIndices, int gridSize, int drawCallCount)
: TestCase(context, name, desc)
, m_drawMethod(method)
, m_computeCmd(computeCmd)
, m_computeData(computeData)
, m_computeIndices(computeIndices)
, m_commandSize((method == DRAWMETHOD_DRAWARRAYS) ? ((int)sizeof(DrawArraysCommand)) :
((int)sizeof(DrawElementsCommand)))
, m_numDrawCmds(drawCallCount)
, m_gridSize(gridSize)
, m_cmdBufferID(0)
, m_dataBufferID(0)
, m_indexBufferID(0)
, m_shaderProgram(DE_NULL)
{
const int triangleCount = m_gridSize * m_gridSize * 2;
DE_ASSERT(method < DRAWMETHOD_LAST);
DE_ASSERT(!computeIndices || method == DRAWMETHOD_DRAWELEMENTS);
DE_ASSERT(triangleCount % m_numDrawCmds == 0);
DE_UNREF(triangleCount);
}
ComputeShaderGeneratedCase::~ComputeShaderGeneratedCase(void)
{
deinit();
}
void ComputeShaderGeneratedCase::init(void)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
// generate basic shader
m_shaderProgram = new glu::ShaderProgram(m_context.getRenderContext(),
glu::ProgramSources() << glu::VertexSource(s_colorVertexShaderSource)
<< glu::FragmentSource(s_colorFragmentShaderSource));
m_testCtx.getLog() << *m_shaderProgram;
if (!m_shaderProgram->isOk())
throw tcu::TestError("Failed to compile shader.");
// gen buffers
gl.genBuffers(1, &m_cmdBufferID);
gl.genBuffers(1, &m_dataBufferID);
gl.genBuffers(1, &m_indexBufferID);
// check the SSBO buffers are of legal size
{
const uint64_t drawBufferElementSize = sizeof(tcu::Vec4);
const uint64_t indexBufferElementSize = sizeof(uint32_t);
const int commandBufferSize = m_commandSize * m_numDrawCmds;
int64_t maxSSBOSize = 0;
gl.getInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxSSBOSize);
if (m_computeData && (uint64_t)calcDrawBufferSize() * drawBufferElementSize > (uint64_t)maxSSBOSize)
throw tcu::NotSupportedError("GL_MAX_SHADER_STORAGE_BLOCK_SIZE is too small for vertex attrib buffers");
if (m_computeIndices && (uint64_t)calcIndexBufferSize() * indexBufferElementSize > (uint64_t)maxSSBOSize)
throw tcu::NotSupportedError("GL_MAX_SHADER_STORAGE_BLOCK_SIZE is too small for index buffers");
if (m_computeCmd && (uint64_t)commandBufferSize > (uint64_t)maxSSBOSize)
throw tcu::NotSupportedError("GL_MAX_SHADER_STORAGE_BLOCK_SIZE is too small for command buffers");
}
}
void ComputeShaderGeneratedCase::deinit(void)
{
if (m_cmdBufferID)
{
m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_cmdBufferID);
m_cmdBufferID = 0;
}
if (m_dataBufferID)
{
m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_dataBufferID);
m_dataBufferID = 0;
}
if (m_indexBufferID)
{
m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_indexBufferID);
m_indexBufferID = 0;
}
if (m_shaderProgram)
{
delete m_shaderProgram;
m_shaderProgram = DE_NULL;
}
}
ComputeShaderGeneratedCase::IterateResult ComputeShaderGeneratedCase::iterate(void)
{
const int renderTargetWidth = de::min(1024, m_context.getRenderTarget().getWidth());
const int renderTargetHeight = de::min(1024, m_context.getRenderTarget().getHeight());
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
tcu::Surface surface(renderTargetWidth, renderTargetHeight);
m_testCtx.getLog() << tcu::TestLog::Message << "Preparing to draw " << m_gridSize << " x " << m_gridSize << " grid."
<< tcu::TestLog::EndMessage;
try
{
// Gen command buffer
if (!m_computeCmd)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Uploading draw command buffer." << tcu::TestLog::EndMessage;
createDrawCommand();
}
// Gen data buffer
if (!m_computeData)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Uploading draw data buffer." << tcu::TestLog::EndMessage;
createDrawData();
}
// Gen index buffer
if (!m_computeIndices && m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Uploading draw index buffer." << tcu::TestLog::EndMessage;
createDrawIndices();
}
// Run compute shader
{
m_testCtx.getLog() << tcu::TestLog::Message << "Filling following buffers using compute shader:\n"
<< ((m_computeCmd) ? ("\tcommand buffer\n") : (""))
<< ((m_computeData) ? ("\tdata buffer\n") : (""))
<< ((m_computeIndices) ? ("\tindex buffer\n") : ("")) << tcu::TestLog::EndMessage;
runComputeShader();
}
// Ensure data is written to the buffers before we try to read it
{
const glw::GLuint barriers = ((m_computeCmd) ? (GL_COMMAND_BARRIER_BIT) : (0)) |
((m_computeData) ? (GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT) : (0)) |
((m_computeIndices) ? (GL_ELEMENT_ARRAY_BARRIER_BIT) : (0));
m_testCtx.getLog() << tcu::TestLog::Message
<< "Memory barrier. Barriers = " << glu::getMemoryBarrierFlagsStr(barriers)
<< tcu::TestLog::EndMessage;
gl.memoryBarrier(barriers);
}
// Draw from buffers
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing from buffers with " << m_numDrawCmds << " draw call(s)."
<< tcu::TestLog::EndMessage;
renderTo(surface);
}
catch (glu::OutOfMemoryError &)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Got GL_OUT_OF_MEMORY." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Got GL_OUT_OF_MEMORY");
m_testCtx.setTerminateAfter(true); // Do not rely on implementation to be able to recover from OOM
return STOP;
}
// verify image
// \note the green/yellow pattern is only for clarity. The test will only verify that all grid cells were drawn by looking for anything non-green/yellow.
if (verifyImageYellowGreen(surface, m_testCtx.getLog()))
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result image invalid");
return STOP;
}
std::string ComputeShaderGeneratedCase::genComputeSource(bool computeCmd, bool computeData, bool computeIndices) const
{
const int cmdLayoutBinding = 0;
const int dataLayoutBinding = (computeCmd) ? (1) : (0);
const int indexLayoutBinding = (computeCmd && computeData) ? (2) : (computeCmd || computeData) ? (1) : (0);
std::ostringstream buf;
buf << "#version 310 es\n\n"
<< "precision highp int;\n"
<< "precision highp float;\n\n";
if (computeCmd && m_drawMethod == DRAWMETHOD_DRAWARRAYS)
buf << "struct DrawArraysIndirectCommand {\n"
<< " uint count;\n"
<< " uint primCount;\n"
<< " uint first;\n"
<< " uint reservedMustBeZero;\n"
<< "};\n\n";
else if (computeCmd && m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
buf << "struct DrawElementsIndirectCommand {\n"
<< " uint count;\n"
<< " uint primCount;\n"
<< " uint firstIndex;\n"
<< " int baseVertex;\n"
<< " uint reservedMustBeZero;\n"
<< "};\n\n";
buf << "layout(local_size_x = 1, local_size_y = 1) in;\n"
<< "layout(std430) buffer;\n\n";
if (computeCmd)
buf << "layout(binding = " << cmdLayoutBinding << ") writeonly buffer CommandBuffer {\n"
<< " "
<< ((m_drawMethod == DRAWMETHOD_DRAWARRAYS) ? ("DrawArraysIndirectCommand") :
("DrawElementsIndirectCommand"))
<< " commands[];\n"
<< "};\n";
if (computeData)
buf << "layout(binding = " << dataLayoutBinding << ") writeonly buffer DataBuffer {\n"
<< " vec4 attribs[];\n"
<< "};\n";
if (computeIndices)
buf << "layout(binding = " << indexLayoutBinding << ") writeonly buffer IndexBuffer {\n"
<< " uint indices[];\n"
<< "};\n";
buf << "\n"
<< "void main() {\n"
<< " const uint gridSize = " << m_gridSize << "u;\n"
<< " const uint triangleCount = gridSize * gridSize * 2u;\n"
<< "\n";
if (computeCmd)
{
buf << " // command\n"
<< " if (gl_GlobalInvocationID.x < " << m_numDrawCmds
<< "u && gl_GlobalInvocationID.y == 0u && gl_GlobalInvocationID.z == 0u) {\n"
<< " const uint numDrawCallTris = triangleCount / " << m_numDrawCmds << "u;\n"
<< " uint firstTri = gl_GlobalInvocationID.x * numDrawCallTris;\n\n"
<< " commands[gl_GlobalInvocationID.x].count = numDrawCallTris*3u;\n"
<< " commands[gl_GlobalInvocationID.x].primCount = 1u;\n";
if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
{
buf << " commands[gl_GlobalInvocationID.x].first = firstTri*3u;\n";
}
else if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
{
buf << " commands[gl_GlobalInvocationID.x].firstIndex = firstTri*3u;\n";
buf << " commands[gl_GlobalInvocationID.x].baseVertex = 0;\n";
}
buf << " commands[gl_GlobalInvocationID.x].reservedMustBeZero = 0u;\n"
<< " }\n"
<< "\n";
}
if (computeData)
{
buf << " // vertex attribs\n"
<< " const vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
<< " const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n";
if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
{
buf << " if (gl_GlobalInvocationID.x < gridSize && gl_GlobalInvocationID.y < gridSize && "
"gl_GlobalInvocationID.z == 0u) {\n"
<< " uint y = gl_GlobalInvocationID.x;\n"
<< " uint x = gl_GlobalInvocationID.y;\n"
<< " float posX = (float(x) / float(gridSize)) * 2.0 - 1.0;\n"
<< " float posXp1 = (float(x+1u) / float(gridSize)) * 2.0 - 1.0;\n"
<< " float posY = (float(y) / float(gridSize)) * 2.0 - 1.0;\n"
<< " float posYp1 = (float(y+1u) / float(gridSize)) * 2.0 - 1.0;\n"
<< " vec4 color = ((x + y)%2u != 0u) ? (yellow) : (green);\n"
<< "\n"
<< " attribs[((y * gridSize + x) * 6u + 0u) * 2u + 0u] = vec4(posX, posY, 0.0, 1.0);\n"
<< " attribs[((y * gridSize + x) * 6u + 1u) * 2u + 0u] = vec4(posXp1, posY, 0.0, 1.0);\n"
<< " attribs[((y * gridSize + x) * 6u + 2u) * 2u + 0u] = vec4(posXp1, posYp1, 0.0, 1.0);\n"
<< " attribs[((y * gridSize + x) * 6u + 3u) * 2u + 0u] = vec4(posX, posY, 0.0, 1.0);\n"
<< " attribs[((y * gridSize + x) * 6u + 4u) * 2u + 0u] = vec4(posXp1, posYp1, 0.0, 1.0);\n"
<< " attribs[((y * gridSize + x) * 6u + 5u) * 2u + 0u] = vec4(posX, posYp1, 0.0, 1.0);\n"
<< "\n"
<< " attribs[((y * gridSize + x) * 6u + 0u) * 2u + 1u] = color;\n"
<< " attribs[((y * gridSize + x) * 6u + 1u) * 2u + 1u] = color;\n"
<< " attribs[((y * gridSize + x) * 6u + 2u) * 2u + 1u] = color;\n"
<< " attribs[((y * gridSize + x) * 6u + 3u) * 2u + 1u] = color;\n"
<< " attribs[((y * gridSize + x) * 6u + 4u) * 2u + 1u] = color;\n"
<< " attribs[((y * gridSize + x) * 6u + 5u) * 2u + 1u] = color;\n"
<< " }\n";
}
else if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
{
buf << " if (gl_GlobalInvocationID.x < gridSize+1u && gl_GlobalInvocationID.y < gridSize+1u && "
"gl_GlobalInvocationID.z == 0u) {\n"
<< " uint y = gl_GlobalInvocationID.x;\n"
<< " uint x = gl_GlobalInvocationID.y;\n"
<< " float posX = (float(x) / float(gridSize)) * 2.0 - 1.0;\n"
<< " float posY = (float(y) / float(gridSize)) * 2.0 - 1.0;\n"
<< "\n"
<< " attribs[(y * (gridSize+1u) + x) * 4u + 0u] = vec4(posX, posY, 0.0, 1.0);\n"
<< " attribs[(y * (gridSize+1u) + x) * 4u + 1u] = green;\n"
<< " attribs[(y * (gridSize+1u) + x) * 4u + 2u] = vec4(posX, posY, 0.0, 1.0);\n"
<< " attribs[(y * (gridSize+1u) + x) * 4u + 3u] = yellow;\n"
<< " }\n";
}
buf << "\n";
}
if (computeIndices)
{
buf << " // indices\n"
<< " if (gl_GlobalInvocationID.x < gridSize && gl_GlobalInvocationID.y < gridSize && "
"gl_GlobalInvocationID.z == 0u) {\n"
<< " uint y = gl_GlobalInvocationID.x;\n"
<< " uint x = gl_GlobalInvocationID.y;\n"
<< " uint color = ((x + y)%2u);\n"
<< "\n"
<< " indices[(y * gridSize + x) * 6u + 0u] = ((y+0u) * (gridSize+1u) + (x+0u)) * 2u + color;\n"
<< " indices[(y * gridSize + x) * 6u + 1u] = ((y+1u) * (gridSize+1u) + (x+0u)) * 2u + color;\n"
<< " indices[(y * gridSize + x) * 6u + 2u] = ((y+1u) * (gridSize+1u) + (x+1u)) * 2u + color;\n"
<< " indices[(y * gridSize + x) * 6u + 3u] = ((y+0u) * (gridSize+1u) + (x+0u)) * 2u + color;\n"
<< " indices[(y * gridSize + x) * 6u + 4u] = ((y+1u) * (gridSize+1u) + (x+1u)) * 2u + color;\n"
<< " indices[(y * gridSize + x) * 6u + 5u] = ((y+0u) * (gridSize+1u) + (x+1u)) * 2u + color;\n"
<< " }\n"
<< "\n";
}
buf << "}\n";
return buf.str();
}
void ComputeShaderGeneratedCase::createDrawCommand(void)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
const int triangleCount = m_gridSize * m_gridSize * 2;
const uint32_t numDrawCallTris = triangleCount / m_numDrawCmds;
if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
{
std::vector<DrawArraysCommand> cmds;
for (int ndx = 0; ndx < m_numDrawCmds; ++ndx)
{
const uint32_t firstTri = ndx * numDrawCallTris;
DrawArraysCommand data;
data.count = numDrawCallTris * 3;
data.primCount = 1;
data.first = firstTri * 3;
data.reservedMustBeZero = 0;
cmds.push_back(data);
}
DE_ASSERT((int)(sizeof(DrawArraysCommand) * cmds.size()) == m_numDrawCmds * m_commandSize);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, m_cmdBufferID);
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, (glw::GLsizeiptr)(sizeof(DrawArraysCommand) * cmds.size()), &cmds[0],
GL_STATIC_DRAW);
}
else if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
{
std::vector<DrawElementsCommand> cmds;
for (int ndx = 0; ndx < m_numDrawCmds; ++ndx)
{
const uint32_t firstTri = ndx * numDrawCallTris;
DrawElementsCommand data;
data.count = numDrawCallTris * 3;
data.primCount = 1;
data.firstIndex = firstTri * 3;
data.baseVertex = 0;
data.reservedMustBeZero = 0;
cmds.push_back(data);
}
DE_ASSERT((int)(sizeof(DrawElementsCommand) * cmds.size()) == m_numDrawCmds * m_commandSize);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, m_cmdBufferID);
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, (glw::GLsizeiptr)(sizeof(DrawElementsCommand) * cmds.size()), &cmds[0],
GL_STATIC_DRAW);
}
else
DE_ASSERT(false);
glu::checkError(gl.getError(), "create draw command", __FILE__, __LINE__);
}
void ComputeShaderGeneratedCase::createDrawData(void)
{
const tcu::Vec4 yellow(1.0f, 1.0f, 0.0f, 1.0f);
const tcu::Vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
{
// Store elements in the order they are drawn. Interleave color.
std::vector<tcu::Vec4> buffer(m_gridSize * m_gridSize * 6 * 2);
DE_ASSERT(buffer.size() == calcDrawBufferSize());
for (int y = 0; y < m_gridSize; ++y)
for (int x = 0; x < m_gridSize; ++x)
{
const float posX = ((float)x / (float)m_gridSize) * 2.0f - 1.0f;
const float posY = ((float)y / (float)m_gridSize) * 2.0f - 1.0f;
const float cellSize = 2.0f / (float)m_gridSize;
const tcu::Vec4 &color = ((x + y) % 2) ? (yellow) : (green);
buffer[((y * m_gridSize + x) * 6 + 0) * 2 + 0] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 1) * 2 + 0] = tcu::Vec4(posX + cellSize, posY, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 2) * 2 + 0] =
tcu::Vec4(posX + cellSize, posY + cellSize, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 3) * 2 + 0] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 4) * 2 + 0] =
tcu::Vec4(posX + cellSize, posY + cellSize, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 5) * 2 + 0] = tcu::Vec4(posX, posY + cellSize, 0.0f, 1.0f);
buffer[((y * m_gridSize + x) * 6 + 0) * 2 + 1] = color;
buffer[((y * m_gridSize + x) * 6 + 1) * 2 + 1] = color;
buffer[((y * m_gridSize + x) * 6 + 2) * 2 + 1] = color;
buffer[((y * m_gridSize + x) * 6 + 3) * 2 + 1] = color;
buffer[((y * m_gridSize + x) * 6 + 4) * 2 + 1] = color;
buffer[((y * m_gridSize + x) * 6 + 5) * 2 + 1] = color;
}
gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBufferID);
gl.bufferData(GL_ARRAY_BUFFER, (int)(buffer.size() * sizeof(tcu::Vec4)), buffer[0].getPtr(), GL_STATIC_DRAW);
}
else if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
{
// Elements are indexed by index buffer. Interleave color. Two vertices per position since 2 colors
std::vector<tcu::Vec4> buffer((m_gridSize + 1) * (m_gridSize + 1) * 4);
DE_ASSERT(buffer.size() == calcDrawBufferSize());
for (int y = 0; y < m_gridSize + 1; ++y)
for (int x = 0; x < m_gridSize + 1; ++x)
{
const float posX = ((float)x / (float)m_gridSize) * 2.0f - 1.0f;
const float posY = ((float)y / (float)m_gridSize) * 2.0f - 1.0f;
buffer[(y * (m_gridSize + 1) + x) * 4 + 0] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
buffer[(y * (m_gridSize + 1) + x) * 4 + 1] = green;
buffer[(y * (m_gridSize + 1) + x) * 4 + 2] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
buffer[(y * (m_gridSize + 1) + x) * 4 + 3] = yellow;
}
gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBufferID);
gl.bufferData(GL_ARRAY_BUFFER, (int)(buffer.size() * sizeof(tcu::Vec4)), buffer[0].getPtr(), GL_STATIC_DRAW);
}
else
DE_ASSERT(false);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
void ComputeShaderGeneratedCase::createDrawIndices(void)
{
DE_ASSERT(m_drawMethod == DRAWMETHOD_DRAWELEMENTS);
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
std::vector<uint32_t> buffer(m_gridSize * m_gridSize * 6);
DE_ASSERT(buffer.size() == calcIndexBufferSize());
for (int y = 0; y < m_gridSize; ++y)
for (int x = 0; x < m_gridSize; ++x)
{
const int color = ((x + y) % 2);
buffer[(y * m_gridSize + x) * 6 + 0] = ((y + 0) * (m_gridSize + 1) + (x + 0)) * 2 + color;
buffer[(y * m_gridSize + x) * 6 + 1] = ((y + 1) * (m_gridSize + 1) + (x + 0)) * 2 + color;
buffer[(y * m_gridSize + x) * 6 + 2] = ((y + 1) * (m_gridSize + 1) + (x + 1)) * 2 + color;
buffer[(y * m_gridSize + x) * 6 + 3] = ((y + 0) * (m_gridSize + 1) + (x + 0)) * 2 + color;
buffer[(y * m_gridSize + x) * 6 + 4] = ((y + 1) * (m_gridSize + 1) + (x + 1)) * 2 + color;
buffer[(y * m_gridSize + x) * 6 + 5] = ((y + 0) * (m_gridSize + 1) + (x + 1)) * 2 + color;
}
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferID);
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (int)(buffer.size() * sizeof(uint32_t)), &buffer[0], GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
void ComputeShaderGeneratedCase::renderTo(tcu::Surface &dst)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
const int32_t positionLoc = gl.getAttribLocation(m_shaderProgram->getProgram(), "a_position");
const int32_t colorLoc = gl.getAttribLocation(m_shaderProgram->getProgram(), "a_color");
uint32_t vaoID = 0;
gl.genVertexArrays(1, &vaoID);
gl.bindVertexArray(vaoID);
// Setup buffers
gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBufferID);
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 8 * (int)sizeof(float), DE_NULL);
gl.vertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, 8 * (int)sizeof(float),
glu::BufferOffsetAsPointer(4 * sizeof(float)));
gl.enableVertexAttribArray(positionLoc);
gl.enableVertexAttribArray(colorLoc);
DE_ASSERT(positionLoc != -1);
DE_ASSERT(colorLoc != -1);
if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferID);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, m_cmdBufferID);
// draw
gl.clearColor(0, 0, 0, 1);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
gl.useProgram(m_shaderProgram->getProgram());
for (int drawCmdNdx = 0; drawCmdNdx < m_numDrawCmds; ++drawCmdNdx)
{
const void *offset = glu::BufferOffsetAsPointer(drawCmdNdx * m_commandSize);
if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
gl.drawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, offset);
else if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
gl.drawArraysIndirect(GL_TRIANGLES, offset);
else
DE_ASSERT(false);
}
gl.useProgram(0);
// free
gl.deleteVertexArrays(1, &vaoID);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.finish();
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
uint32_t ComputeShaderGeneratedCase::calcDrawBufferSize(void) const
{
// returns size in "vec4"s
if (m_drawMethod == DRAWMETHOD_DRAWARRAYS)
return m_gridSize * m_gridSize * 6 * 2;
else if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
return (m_gridSize + 1) * (m_gridSize + 1) * 4;
else
DE_ASSERT(false);
return 0;
}
uint32_t ComputeShaderGeneratedCase::calcIndexBufferSize(void) const
{
if (m_drawMethod == DRAWMETHOD_DRAWELEMENTS)
return m_gridSize * m_gridSize * 6;
else
return 0;
}
class ComputeShaderGeneratedCombinedCase : public ComputeShaderGeneratedCase
{
public:
ComputeShaderGeneratedCombinedCase(Context &context, const char *name, const char *desc, DrawMethod method,
bool computeCmd, bool computeData, bool computeIndices, int gridSize,
int numDrawCalls);
~ComputeShaderGeneratedCombinedCase(void);
void init(void);
void deinit(void);
private:
void runComputeShader(void);
glu::ShaderProgram *m_computeProgram;
};
ComputeShaderGeneratedCombinedCase::ComputeShaderGeneratedCombinedCase(Context &context, const char *name,
const char *desc, DrawMethod method,
bool computeCmd, bool computeData,
bool computeIndices, int gridSize,
int numDrawCalls)
: ComputeShaderGeneratedCase(context, name, desc, method, computeCmd, computeData, computeIndices, gridSize,
numDrawCalls)
, m_computeProgram(DE_NULL)
{
}
ComputeShaderGeneratedCombinedCase::~ComputeShaderGeneratedCombinedCase(void)
{
deinit();
}
void ComputeShaderGeneratedCombinedCase::init(void)
{
// generate compute shader
m_computeProgram = new glu::ShaderProgram(
m_context.getRenderContext(),
glu::ProgramSources() << glu::ComputeSource(genComputeSource(m_computeCmd, m_computeData, m_computeIndices)));
m_testCtx.getLog() << *m_computeProgram;
if (!m_computeProgram->isOk())
throw tcu::TestError("Failed to compile compute shader.");
// init parent
ComputeShaderGeneratedCase::init();
}
void ComputeShaderGeneratedCombinedCase::deinit(void)
{
// deinit parent
ComputeShaderGeneratedCase::deinit();
if (m_computeProgram)
{
delete m_computeProgram;
m_computeProgram = DE_NULL;
}
}
void ComputeShaderGeneratedCombinedCase::runComputeShader(void)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
const bool indexed = (m_drawMethod == DRAWMETHOD_DRAWELEMENTS);
const tcu::IVec3 nullSize(0, 0, 0);
const tcu::IVec3 commandDispatchSize = (m_computeCmd) ? (tcu::IVec3(m_numDrawCmds, 1, 1)) : (nullSize);
const tcu::IVec3 drawElementsDataBufferDispatchSize =
(m_computeData) ? (tcu::IVec3(m_gridSize + 1, m_gridSize + 1, 1)) : (nullSize);
const tcu::IVec3 drawArraysDataBufferDispatchSize =
(m_computeData) ? (tcu::IVec3(m_gridSize, m_gridSize, 1)) : (nullSize);
const tcu::IVec3 indexBufferDispatchSize =
(m_computeIndices && indexed) ? (tcu::IVec3(m_gridSize, m_gridSize, 1)) : (nullSize);
const tcu::IVec3 dataBufferDispatchSize = (m_drawMethod == DRAWMETHOD_DRAWELEMENTS) ?
(drawElementsDataBufferDispatchSize) :
(drawArraysDataBufferDispatchSize);
const tcu::IVec3 dispatchSize =
tcu::max(tcu::max(commandDispatchSize, dataBufferDispatchSize), indexBufferDispatchSize);
gl.useProgram(m_computeProgram->getProgram());
glu::checkError(gl.getError(), "use compute shader", __FILE__, __LINE__);
// setup buffers
if (m_computeCmd)
{
const int bindingPoint = 0;
const int bufferSize = m_commandSize * m_numDrawCmds;
m_testCtx.getLog() << tcu::TestLog::Message << "Binding command buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_cmdBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for command buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
}
if (m_computeData)
{
const int bindingPoint = (m_computeCmd) ? (1) : (0);
const int bufferSize = (int)(calcDrawBufferSize() * sizeof(tcu::Vec4));
m_testCtx.getLog() << tcu::TestLog::Message << "Binding data buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_dataBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for data buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
}
if (m_computeIndices)
{
const int bindingPoint = (m_computeCmd && m_computeData) ? (2) : (m_computeCmd || m_computeData) ? (1) : (0);
const int bufferSize = (int)(calcIndexBufferSize() * sizeof(uint32_t));
m_testCtx.getLog() << tcu::TestLog::Message << "Binding index buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_indexBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for index buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
}
glu::checkError(gl.getError(), "setup buffers", __FILE__, __LINE__);
// calculate
m_testCtx.getLog() << tcu::TestLog::Message << "Dispatching compute, size = " << dispatchSize
<< tcu::TestLog::EndMessage;
gl.dispatchCompute(dispatchSize.x(), dispatchSize.y(), dispatchSize.z());
glu::checkError(gl.getError(), "calculate", __FILE__, __LINE__);
}
class ComputeShaderGeneratedSeparateCase : public ComputeShaderGeneratedCase
{
public:
ComputeShaderGeneratedSeparateCase(Context &context, const char *name, const char *desc, DrawMethod method,
bool computeCmd, bool computeData, bool computeIndices, int gridSize,
int numDrawCalls);
~ComputeShaderGeneratedSeparateCase(void);
void init(void);
void deinit(void);
private:
std::string genCmdComputeSource(void);
std::string genDataComputeSource(void);
std::string genIndexComputeSource(void);
void runComputeShader(void);
glu::ShaderProgram *m_computeCmdProgram;
glu::ShaderProgram *m_computeDataProgram;
glu::ShaderProgram *m_computeIndicesProgram;
};
ComputeShaderGeneratedSeparateCase::ComputeShaderGeneratedSeparateCase(Context &context, const char *name,
const char *desc, DrawMethod method,
bool computeCmd, bool computeData,
bool computeIndices, int gridSize,
int numDrawCalls)
: ComputeShaderGeneratedCase(context, name, desc, method, computeCmd, computeData, computeIndices, gridSize,
numDrawCalls)
, m_computeCmdProgram(DE_NULL)
, m_computeDataProgram(DE_NULL)
, m_computeIndicesProgram(DE_NULL)
{
}
ComputeShaderGeneratedSeparateCase::~ComputeShaderGeneratedSeparateCase(void)
{
deinit();
}
void ComputeShaderGeneratedSeparateCase::init(void)
{
// generate cmd compute shader
if (m_computeCmd)
{
m_computeCmdProgram = new glu::ShaderProgram(
m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genCmdComputeSource()));
m_testCtx.getLog() << *m_computeCmdProgram;
if (!m_computeCmdProgram->isOk())
throw tcu::TestError("Failed to compile command compute shader.");
}
// generate data compute shader
if (m_computeData)
{
m_computeDataProgram = new glu::ShaderProgram(
m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genDataComputeSource()));
m_testCtx.getLog() << *m_computeDataProgram;
if (!m_computeDataProgram->isOk())
throw tcu::TestError("Failed to compile data compute shader.");
}
// generate index compute shader
if (m_computeIndices)
{
m_computeIndicesProgram = new glu::ShaderProgram(
m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genIndexComputeSource()));
m_testCtx.getLog() << *m_computeIndicesProgram;
if (!m_computeIndicesProgram->isOk())
throw tcu::TestError("Failed to compile data compute shader.");
}
// init parent
ComputeShaderGeneratedCase::init();
}
void ComputeShaderGeneratedSeparateCase::deinit(void)
{
// deinit parent
ComputeShaderGeneratedCase::deinit();
if (m_computeCmdProgram)
{
delete m_computeCmdProgram;
m_computeCmdProgram = DE_NULL;
}
if (m_computeDataProgram)
{
delete m_computeDataProgram;
m_computeDataProgram = DE_NULL;
}
if (m_computeIndicesProgram)
{
delete m_computeIndicesProgram;
m_computeIndicesProgram = DE_NULL;
}
}
std::string ComputeShaderGeneratedSeparateCase::genCmdComputeSource(void)
{
return ComputeShaderGeneratedCase::genComputeSource(true, false, false);
}
std::string ComputeShaderGeneratedSeparateCase::genDataComputeSource(void)
{
return ComputeShaderGeneratedCase::genComputeSource(false, true, false);
}
std::string ComputeShaderGeneratedSeparateCase::genIndexComputeSource(void)
{
return ComputeShaderGeneratedCase::genComputeSource(false, false, true);
}
void ComputeShaderGeneratedSeparateCase::runComputeShader(void)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
// Compute command
if (m_computeCmd)
{
const int bindingPoint = 0;
const tcu::IVec3 commandDispatchSize(m_numDrawCmds, 1, 1);
const int bufferSize = m_commandSize * m_numDrawCmds;
gl.useProgram(m_computeCmdProgram->getProgram());
// setup buffers
m_testCtx.getLog() << tcu::TestLog::Message << "Binding command buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_cmdBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for command buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
// calculate
m_testCtx.getLog() << tcu::TestLog::Message << "Dispatching command compute, size = " << commandDispatchSize
<< tcu::TestLog::EndMessage;
gl.dispatchCompute(commandDispatchSize.x(), commandDispatchSize.y(), commandDispatchSize.z());
glu::checkError(gl.getError(), "calculate cmd", __FILE__, __LINE__);
}
// Compute data
if (m_computeData)
{
const int bindingPoint = 0;
const tcu::IVec3 drawElementsDataBufferDispatchSize(m_gridSize + 1, m_gridSize + 1, 1);
const tcu::IVec3 drawArraysDataBufferDispatchSize(m_gridSize, m_gridSize, 1);
const tcu::IVec3 dataBufferDispatchSize = (m_drawMethod == DRAWMETHOD_DRAWELEMENTS) ?
(drawElementsDataBufferDispatchSize) :
(drawArraysDataBufferDispatchSize);
const int bufferSize = (int)(calcDrawBufferSize() * sizeof(tcu::Vec4));
gl.useProgram(m_computeDataProgram->getProgram());
// setup buffers
m_testCtx.getLog() << tcu::TestLog::Message << "Binding data buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_dataBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for data buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
// calculate
m_testCtx.getLog() << tcu::TestLog::Message << "Dispatching data compute, size = " << dataBufferDispatchSize
<< tcu::TestLog::EndMessage;
gl.dispatchCompute(dataBufferDispatchSize.x(), dataBufferDispatchSize.y(), dataBufferDispatchSize.z());
glu::checkError(gl.getError(), "calculate data", __FILE__, __LINE__);
}
// Compute indices
if (m_computeIndices)
{
const int bindingPoint = 0;
const tcu::IVec3 indexBufferDispatchSize(m_gridSize, m_gridSize, 1);
const int bufferSize = (int)(calcIndexBufferSize() * sizeof(uint32_t));
DE_ASSERT(m_drawMethod == DRAWMETHOD_DRAWELEMENTS);
gl.useProgram(m_computeIndicesProgram->getProgram());
// setup buffers
m_testCtx.getLog() << tcu::TestLog::Message << "Binding index buffer to binding point " << bindingPoint
<< tcu::TestLog::EndMessage;
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_indexBufferID);
m_testCtx.getLog() << tcu::TestLog::Message << "Allocating memory for index buffer, size "
<< sizeToString(bufferSize) << "." << tcu::TestLog::EndMessage;
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_DRAW);
// calculate
m_testCtx.getLog() << tcu::TestLog::Message << "Dispatching index compute, size = " << indexBufferDispatchSize
<< tcu::TestLog::EndMessage;
gl.dispatchCompute(indexBufferDispatchSize.x(), indexBufferDispatchSize.y(), indexBufferDispatchSize.z());
glu::checkError(gl.getError(), "calculate indices", __FILE__, __LINE__);
}
glu::checkError(gl.getError(), "post dispatch", __FILE__, __LINE__);
}
class ComputeShaderGeneratedGroup : public TestCaseGroup
{
public:
ComputeShaderGeneratedGroup(Context &context, const char *name, const char *descr);
~ComputeShaderGeneratedGroup(void);
void init(void);
};
ComputeShaderGeneratedGroup::ComputeShaderGeneratedGroup(Context &context, const char *name, const char *descr)
: TestCaseGroup(context, name, descr)
{
}
ComputeShaderGeneratedGroup::~ComputeShaderGeneratedGroup(void)
{
}
void ComputeShaderGeneratedGroup::init(void)
{
const int gridSize = 8;
tcu::TestCaseGroup *const separateGroup =
new tcu::TestCaseGroup(m_testCtx, "separate", "Use separate compute shaders for each buffer");
tcu::TestCaseGroup *const combinedGroup =
new tcu::TestCaseGroup(m_testCtx, "combined", "Use combined compute shader for all buffers");
tcu::TestCaseGroup *const largeGroup = new tcu::TestCaseGroup(m_testCtx, "large", "Draw shapes with large buffers");
this->addChild(separateGroup);
this->addChild(combinedGroup);
this->addChild(largeGroup);
// .separate
{
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawarrays_compute_cmd", "Command from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, true, false, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawarrays_compute_data", "Data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, false, true, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawarrays_compute_cmd_and_data", "Command and data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, true, true, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_cmd", "Command from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, false, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_data", "Data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, false, true, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_indices", "Indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, false, false, true, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_cmd_and_data", "Command and data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, true, false, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_cmd_and_indices", "Command and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, false, true, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_data_and_indices", "Data and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, false, true, true, gridSize, 1));
separateGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, "drawelements_compute_cmd_and_data_and_indices", "Command, data and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, true, true, gridSize, 1));
}
// .combined
{
combinedGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, "drawarrays_compute_cmd_and_data", "Command and data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, true, true, false, gridSize, 1));
combinedGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, "drawelements_compute_cmd_and_data", "Command and data from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, true, false, gridSize, 1));
combinedGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, "drawelements_compute_cmd_and_indices", "Command and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, false, true, gridSize, 1));
combinedGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, "drawelements_compute_data_and_indices", "Data and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, false, true, true, gridSize, 1));
combinedGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, "drawelements_compute_cmd_and_data_and_indices", "Command, data and indices from compute shader",
ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true, true, true, gridSize, 1));
}
// .large
{
struct TestSpec
{
int gridSize;
int numDrawCommands;
};
struct TestMethod
{
ComputeShaderGeneratedCase::DrawMethod method;
bool separateCompute;
};
static const TestSpec specs[] = {
{100, 1}, // !< drawArrays array size ~ 1.9 MB
{200, 1}, // !< drawArrays array size ~ 7.7 MB
{500, 1}, // !< drawArrays array size ~ 48 MB
{1000, 1}, // !< drawArrays array size ~ 192 MB
{1200, 1}, // !< drawArrays array size ~ 277 MB
{1500, 1}, // !< drawArrays array size ~ 430 MB
{100, 8}, // !< drawArrays array size ~ 1.9 MB
{200, 8}, // !< drawArrays array size ~ 7.7 MB
{500, 8}, // !< drawArrays array size ~ 48 MB
{1000, 8}, // !< drawArrays array size ~ 192 MB
{1200, 8}, // !< drawArrays array size ~ 277 MB
{1500, 8}, // !< drawArrays array size ~ 430 MB
{100, 200}, // !< 50 cells per draw call
{200, 800}, // !< 50 cells per draw call
{500, 2500}, // !< 100 cells per draw call
{1000, 5000}, // !< 250 cells per draw call
};
static const TestMethod methods[] = {
{ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, true},
{ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS, false},
{ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, true},
{ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS, false},
};
for (int methodNdx = 0; methodNdx < DE_LENGTH_OF_ARRAY(methods); ++methodNdx)
for (int specNdx = 0; specNdx < DE_LENGTH_OF_ARRAY(specs); ++specNdx)
{
const std::string name =
std::string("") +
((methods[methodNdx].method == ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS) ?
("drawarrays") :
("drawelements")) +
((methods[methodNdx].separateCompute) ? ("_separate") : ("_combined")) + "_grid_" +
de::toString(specs[specNdx].gridSize) + "x" + de::toString(specs[specNdx].gridSize) +
"_drawcount_" + de::toString(specs[specNdx].numDrawCommands);
const std::string desc =
std::string("Draw grid with ") +
((methods[methodNdx].method == ComputeShaderGeneratedCase::DRAWMETHOD_DRAWARRAYS) ?
("drawarrays indirect") :
("drawelements indirect")) +
" calculating buffers in " + ((methods[methodNdx].separateCompute) ? ("separate") : ("combined")) +
" compute shader." + " Grid size is " + de::toString(specs[specNdx].gridSize) + "x" +
de::toString(specs[specNdx].gridSize) + ", draw count is " +
de::toString(specs[specNdx].numDrawCommands);
const bool computeIndices =
(methods[methodNdx].method == ComputeShaderGeneratedCase::DRAWMETHOD_DRAWELEMENTS);
if (methods[methodNdx].separateCompute)
largeGroup->addChild(new ComputeShaderGeneratedSeparateCase(
m_context, name.c_str(), desc.c_str(), methods[methodNdx].method, false, true, computeIndices,
specs[specNdx].gridSize, specs[specNdx].numDrawCommands));
else
largeGroup->addChild(new ComputeShaderGeneratedCombinedCase(
m_context, name.c_str(), desc.c_str(), methods[methodNdx].method, false, true, computeIndices,
specs[specNdx].gridSize, specs[specNdx].numDrawCommands));
}
}
}
class RandomGroup : public TestCaseGroup
{
public:
RandomGroup(Context &context, const char *name, const char *descr);
~RandomGroup(void);
void init(void);
};
template <int SIZE>
struct UniformWeightArray
{
float weights[SIZE];
UniformWeightArray(void)
{
for (int i = 0; i < SIZE; ++i)
weights[i] = 1.0f;
}
};
RandomGroup::RandomGroup(Context &context, const char *name, const char *descr) : TestCaseGroup(context, name, descr)
{
}
RandomGroup::~RandomGroup(void)
{
}
void RandomGroup::init(void)
{
const int numAttempts = 100;
const int attribCounts[] = {1, 2, 5};
const float attribWeights[] = {30, 10, 1};
const int primitiveCounts[] = {1, 5, 64};
const float primitiveCountWeights[] = {20, 10, 1};
const int indexOffsets[] = {0, 7, 13};
const float indexOffsetWeights[] = {20, 20, 1};
const int firsts[] = {0, 7, 13};
const float firstWeights[] = {20, 20, 1};
const int instanceCounts[] = {1, 2, 16, 17};
const float instanceWeights[] = {20, 10, 5, 1};
const int indexMins[] = {0, 1, 3, 8};
const int indexMaxs[] = {4, 8, 128, 257};
const float indexWeights[] = {50, 50, 50, 50};
const int offsets[] = {0, 1, 5, 12};
const float offsetWeights[] = {50, 10, 10, 10};
const int strides[] = {0, 7, 16, 17};
const float strideWeights[] = {50, 10, 10, 10};
const int instanceDivisors[] = {0, 1, 3, 129};
const float instanceDivisorWeights[] = {70, 30, 10, 10};
const int indirectOffsets[] = {0, 1, 2};
const float indirectOffsetWeigths[] = {2, 1, 1};
const int baseVertices[] = {0, 1, -2, 4, 3};
const float baseVertexWeigths[] = {4, 1, 1, 1, 1};
gls::DrawTestSpec::Primitive primitives[] = {
gls::DrawTestSpec::PRIMITIVE_POINTS, gls::DrawTestSpec::PRIMITIVE_TRIANGLES,
gls::DrawTestSpec::PRIMITIVE_TRIANGLE_FAN, gls::DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP,
gls::DrawTestSpec::PRIMITIVE_LINES, gls::DrawTestSpec::PRIMITIVE_LINE_STRIP,
gls::DrawTestSpec::PRIMITIVE_LINE_LOOP};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(primitives)> primitiveWeights;
gls::DrawTestSpec::DrawMethod drawMethods[] = {
gls::DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INDIRECT,
gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INDIRECT,
};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(drawMethods)> drawMethodWeights;
gls::DrawTestSpec::IndexType indexTypes[] = {
gls::DrawTestSpec::INDEXTYPE_BYTE,
gls::DrawTestSpec::INDEXTYPE_SHORT,
gls::DrawTestSpec::INDEXTYPE_INT,
};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(indexTypes)> indexTypeWeights;
gls::DrawTestSpec::InputType inputTypes[] = {
gls::DrawTestSpec::INPUTTYPE_FLOAT,
gls::DrawTestSpec::INPUTTYPE_FIXED,
gls::DrawTestSpec::INPUTTYPE_BYTE,
gls::DrawTestSpec::INPUTTYPE_SHORT,
gls::DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE,
gls::DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT,
gls::DrawTestSpec::INPUTTYPE_INT,
gls::DrawTestSpec::INPUTTYPE_UNSIGNED_INT,
gls::DrawTestSpec::INPUTTYPE_HALF,
gls::DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10,
gls::DrawTestSpec::INPUTTYPE_INT_2_10_10_10,
};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(inputTypes)> inputTypeWeights;
gls::DrawTestSpec::OutputType outputTypes[] = {
gls::DrawTestSpec::OUTPUTTYPE_FLOAT, gls::DrawTestSpec::OUTPUTTYPE_VEC2, gls::DrawTestSpec::OUTPUTTYPE_VEC3,
gls::DrawTestSpec::OUTPUTTYPE_VEC4, gls::DrawTestSpec::OUTPUTTYPE_INT, gls::DrawTestSpec::OUTPUTTYPE_UINT,
gls::DrawTestSpec::OUTPUTTYPE_IVEC2, gls::DrawTestSpec::OUTPUTTYPE_IVEC3, gls::DrawTestSpec::OUTPUTTYPE_IVEC4,
gls::DrawTestSpec::OUTPUTTYPE_UVEC2, gls::DrawTestSpec::OUTPUTTYPE_UVEC3, gls::DrawTestSpec::OUTPUTTYPE_UVEC4,
};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(outputTypes)> outputTypeWeights;
gls::DrawTestSpec::Usage usages[] = {
gls::DrawTestSpec::USAGE_DYNAMIC_DRAW, gls::DrawTestSpec::USAGE_STATIC_DRAW,
gls::DrawTestSpec::USAGE_STREAM_DRAW, gls::DrawTestSpec::USAGE_STREAM_READ,
gls::DrawTestSpec::USAGE_STREAM_COPY, gls::DrawTestSpec::USAGE_STATIC_READ,
gls::DrawTestSpec::USAGE_STATIC_COPY, gls::DrawTestSpec::USAGE_DYNAMIC_READ,
gls::DrawTestSpec::USAGE_DYNAMIC_COPY,
};
const UniformWeightArray<DE_LENGTH_OF_ARRAY(usages)> usageWeights;
std::set<uint32_t> insertedHashes;
size_t insertedCount = 0;
glu::ContextType contextType = m_context.getRenderContext().getType();
glu::ApiType apiType = glu::isContextTypeES(contextType) ? glu::ApiType::es(3, 1) : contextType.getAPI();
for (int ndx = 0; ndx < numAttempts; ++ndx)
{
de::Random random(0xc551393 + ndx); // random does not depend on previous cases
int attributeCount = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(attribCounts), DE_ARRAY_END(attribCounts), attribWeights);
int drawCommandSize;
gls::DrawTestSpec spec;
spec.apiType = apiType;
spec.primitive = random.chooseWeighted<gls::DrawTestSpec::Primitive>(
DE_ARRAY_BEGIN(primitives), DE_ARRAY_END(primitives), primitiveWeights.weights);
spec.primitiveCount = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(primitiveCounts), DE_ARRAY_END(primitiveCounts), primitiveCountWeights);
spec.drawMethod = random.chooseWeighted<gls::DrawTestSpec::DrawMethod>(
DE_ARRAY_BEGIN(drawMethods), DE_ARRAY_END(drawMethods), drawMethodWeights.weights);
if (spec.drawMethod == gls::DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INDIRECT)
drawCommandSize = sizeof(uint32_t[4]);
else if (spec.drawMethod == gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INDIRECT)
drawCommandSize = sizeof(uint32_t[5]);
else
{
DE_ASSERT(false);
return;
}
spec.indexType = random.chooseWeighted<gls::DrawTestSpec::IndexType>(
DE_ARRAY_BEGIN(indexTypes), DE_ARRAY_END(indexTypes), indexTypeWeights.weights);
spec.indexPointerOffset = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(indexOffsets), DE_ARRAY_END(indexOffsets), indexOffsetWeights);
spec.indexStorage = gls::DrawTestSpec::STORAGE_BUFFER;
spec.first = random.chooseWeighted<int, const int *, const float *>(DE_ARRAY_BEGIN(firsts),
DE_ARRAY_END(firsts), firstWeights);
spec.indexMin = random.chooseWeighted<int, const int *, const float *>(DE_ARRAY_BEGIN(indexMins),
DE_ARRAY_END(indexMins), indexWeights);
spec.indexMax = random.chooseWeighted<int, const int *, const float *>(DE_ARRAY_BEGIN(indexMaxs),
DE_ARRAY_END(indexMaxs), indexWeights);
spec.instanceCount = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(instanceCounts), DE_ARRAY_END(instanceCounts), instanceWeights);
spec.indirectOffset = random.chooseWeighted<int, const int *, const float *>(DE_ARRAY_BEGIN(indirectOffsets),
DE_ARRAY_END(indirectOffsets),
indirectOffsetWeigths) *
drawCommandSize;
spec.baseVertex = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(baseVertices), DE_ARRAY_END(baseVertices), baseVertexWeigths);
// check spec is legal
if (!spec.valid())
continue;
for (int attrNdx = 0; attrNdx < attributeCount;)
{
bool valid;
gls::DrawTestSpec::AttributeSpec attribSpec;
attribSpec.inputType = random.chooseWeighted<gls::DrawTestSpec::InputType>(
DE_ARRAY_BEGIN(inputTypes), DE_ARRAY_END(inputTypes), inputTypeWeights.weights);
attribSpec.outputType = random.chooseWeighted<gls::DrawTestSpec::OutputType>(
DE_ARRAY_BEGIN(outputTypes), DE_ARRAY_END(outputTypes), outputTypeWeights.weights);
attribSpec.storage = gls::DrawTestSpec::STORAGE_BUFFER;
attribSpec.usage = random.chooseWeighted<gls::DrawTestSpec::Usage>(
DE_ARRAY_BEGIN(usages), DE_ARRAY_END(usages), usageWeights.weights);
attribSpec.componentCount = random.getInt(1, 4);
attribSpec.offset = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(offsets), DE_ARRAY_END(offsets), offsetWeights);
attribSpec.stride = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(strides), DE_ARRAY_END(strides), strideWeights);
attribSpec.normalize = random.getBool();
attribSpec.instanceDivisor = random.chooseWeighted<int, const int *, const float *>(
DE_ARRAY_BEGIN(instanceDivisors), DE_ARRAY_END(instanceDivisors), instanceDivisorWeights);
attribSpec.useDefaultAttribute = random.getBool();
// check spec is legal
valid = attribSpec.valid(spec.apiType);
// we do not want interleaved elements. (Might result in some weird floating point values)
if (attribSpec.stride &&
attribSpec.componentCount * gls::DrawTestSpec::inputTypeSize(attribSpec.inputType) > attribSpec.stride)
valid = false;
// try again if not valid
if (valid)
{
spec.attribs.push_back(attribSpec);
++attrNdx;
}
}
// Do not collapse all vertex positions to a single positions
if (spec.primitive != gls::DrawTestSpec::PRIMITIVE_POINTS)
spec.attribs[0].instanceDivisor = 0;
// Is render result meaningful?
{
// Only one vertex
if (spec.drawMethod == gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_RANGED &&
spec.indexMin == spec.indexMax && spec.primitive != gls::DrawTestSpec::PRIMITIVE_POINTS)
continue;
if (spec.attribs[0].useDefaultAttribute && spec.primitive != gls::DrawTestSpec::PRIMITIVE_POINTS)
continue;
// Triangle only on one axis
if (spec.primitive == gls::DrawTestSpec::PRIMITIVE_TRIANGLES ||
spec.primitive == gls::DrawTestSpec::PRIMITIVE_TRIANGLE_FAN ||
spec.primitive == gls::DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP)
{
if (spec.attribs[0].componentCount == 1)
continue;
if (spec.attribs[0].outputType == gls::DrawTestSpec::OUTPUTTYPE_FLOAT ||
spec.attribs[0].outputType == gls::DrawTestSpec::OUTPUTTYPE_INT ||
spec.attribs[0].outputType == gls::DrawTestSpec::OUTPUTTYPE_UINT)
continue;
if (spec.drawMethod == gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_RANGED &&
(spec.indexMax - spec.indexMin) < 2)
continue;
}
}
// Add case
{
uint32_t hash = spec.hash();
for (int attrNdx = 0; attrNdx < attributeCount; ++attrNdx)
hash = (hash << 2) ^ (uint32_t)spec.attribs[attrNdx].hash();
if (insertedHashes.find(hash) == insertedHashes.end())
{
// Only aligned cases
if (spec.isCompatibilityTest() != gls::DrawTestSpec::COMPATIBILITY_UNALIGNED_OFFSET &&
spec.isCompatibilityTest() != gls::DrawTestSpec::COMPATIBILITY_UNALIGNED_STRIDE)
this->addChild(new gls::DrawTest(m_testCtx, m_context.getRenderContext(), spec,
de::toString(insertedCount).c_str(), spec.getDesc().c_str()));
insertedHashes.insert(hash);
++insertedCount;
}
}
}
}
class BadCommandBufferCase : public TestCase
{
public:
enum
{
CommandSize = 20
};
BadCommandBufferCase(Context &context, const char *name, const char *desc, uint32_t alignment, uint32_t bufferSize,
bool writeCommandToBuffer, uint32_t m_expectedError);
~BadCommandBufferCase(void);
IterateResult iterate(void);
private:
const uint32_t m_alignment;
const uint32_t m_bufferSize;
const bool m_writeCommandToBuffer;
const uint32_t m_expectedError;
};
BadCommandBufferCase::BadCommandBufferCase(Context &context, const char *name, const char *desc, uint32_t alignment,
uint32_t bufferSize, bool writeCommandToBuffer, uint32_t expectedError)
: TestCase(context, name, desc)
, m_alignment(alignment)
, m_bufferSize(bufferSize)
, m_writeCommandToBuffer(writeCommandToBuffer)
, m_expectedError(expectedError)
{
}
BadCommandBufferCase::~BadCommandBufferCase(void)
{
}
BadCommandBufferCase::IterateResult BadCommandBufferCase::iterate(void)
{
const tcu::Vec4 vertexPositions[] = {
tcu::Vec4(0, 0, 0, 1),
tcu::Vec4(1, 0, 0, 1),
tcu::Vec4(0, 1, 0, 1),
};
const uint16_t indices[] = {
0,
2,
1,
};
DE_STATIC_ASSERT(CommandSize == sizeof(DrawElementsCommand));
sglr::GLContext gl(m_context.getRenderContext(), m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS,
tcu::IVec4(0, 0, 1, 1));
uint32_t vaoID = 0;
uint32_t positionBuf = 0;
uint32_t indexBuf = 0;
uint32_t drawIndirectBuf = 0;
uint32_t error;
glu::ShaderProgram program(m_context.getRenderContext(), glu::ProgramSources()
<< glu::VertexSource(s_commonVertexShaderSource)
<< glu::FragmentSource(s_commonFragmentShaderSource));
uint32_t programID = program.getProgram();
int32_t posLocation = gl.getAttribLocation(programID, "a_position");
DrawElementsCommand drawCommand;
drawCommand.count = 3;
drawCommand.primCount = 1;
drawCommand.firstIndex = 0;
drawCommand.baseVertex = 0;
drawCommand.reservedMustBeZero = 0;
std::vector<int8_t> drawCommandBuffer;
drawCommandBuffer.resize(m_bufferSize);
deMemset(&drawCommandBuffer[0], 0, (int)drawCommandBuffer.size());
if (m_writeCommandToBuffer)
{
DE_ASSERT(drawCommandBuffer.size() >= sizeof(drawCommand) + m_alignment);
deMemcpy(&drawCommandBuffer[m_alignment], &drawCommand, sizeof(drawCommand));
}
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.genVertexArrays(1, &vaoID);
gl.bindVertexArray(vaoID);
gl.genBuffers(1, &positionBuf);
gl.bindBuffer(GL_ARRAY_BUFFER, positionBuf);
gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.vertexAttribDivisor(posLocation, 0);
gl.enableVertexAttribArray(posLocation);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.genBuffers(1, &indexBuf);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.genBuffers(1, &drawIndirectBuf);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, drawIndirectBuf);
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, drawCommandBuffer.size(), &drawCommandBuffer[0], GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.viewport(0, 0, 1, 1);
gl.useProgram(programID);
gl.drawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void *)(uintptr_t)m_alignment);
error = gl.getError();
gl.useProgram(0);
gl.deleteBuffers(1, &drawIndirectBuf);
gl.deleteBuffers(1, &indexBuf);
gl.deleteBuffers(1, &positionBuf);
gl.deleteVertexArrays(1, &vaoID);
m_testCtx.getLog() << tcu::TestLog::Message << "drawElementsIndirect generated " << glu::getErrorStr(error)
<< ", expecting " << glu::getErrorStr(m_expectedError) << "." << tcu::TestLog::EndMessage;
if (error == m_expectedError)
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
{
m_testCtx.getLog() << tcu::TestLog::Message << "\tUnexpected error." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected error.");
}
return STOP;
}
class BadAlignmentCase : public BadCommandBufferCase
{
public:
BadAlignmentCase(Context &context, const char *name, const char *desc, uint32_t alignment);
~BadAlignmentCase(void);
};
BadAlignmentCase::BadAlignmentCase(Context &context, const char *name, const char *desc, uint32_t alignment)
: BadCommandBufferCase(context, name, desc, alignment, CommandSize + alignment, true, GL_INVALID_VALUE)
{
}
BadAlignmentCase::~BadAlignmentCase(void)
{
}
class BadBufferRangeCase : public BadCommandBufferCase
{
public:
BadBufferRangeCase(Context &context, const char *name, const char *desc, uint32_t offset);
~BadBufferRangeCase(void);
};
BadBufferRangeCase::BadBufferRangeCase(Context &context, const char *name, const char *desc, uint32_t offset)
: BadCommandBufferCase(context, name, desc, offset, CommandSize, false, GL_INVALID_OPERATION)
{
}
BadBufferRangeCase::~BadBufferRangeCase(void)
{
}
class BadStateCase : public TestCase
{
public:
enum CaseType
{
CASE_CLIENT_BUFFER_VERTEXATTR = 0,
CASE_CLIENT_BUFFER_COMMAND,
CASE_DEFAULT_VAO,
CASE_CLIENT_LAST
};
BadStateCase(Context &context, const char *name, const char *desc, CaseType type);
~BadStateCase(void);
void init(void);
void deinit(void);
IterateResult iterate(void);
private:
const CaseType m_caseType;
};
BadStateCase::BadStateCase(Context &context, const char *name, const char *desc, CaseType type)
: TestCase(context, name, desc)
, m_caseType(type)
{
DE_ASSERT(type < CASE_CLIENT_LAST);
}
BadStateCase::~BadStateCase(void)
{
deinit();
}
void BadStateCase::init(void)
{
if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
{
if (m_caseType == CASE_CLIENT_BUFFER_VERTEXATTR)
throw tcu::NotSupportedError(
"The negative test for vertex attrib array in the client memory is not supported in the GL context");
if (m_caseType == CASE_DEFAULT_VAO)
throw tcu::NotSupportedError(
"The negative test for use with default vao is not supported in the GL context");
}
}
void BadStateCase::deinit(void)
{
}
BadStateCase::IterateResult BadStateCase::iterate(void)
{
const tcu::Vec4 vertexPositions[] = {
tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f),
tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f),
tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f),
};
const uint16_t indices[] = {
0,
2,
1,
};
sglr::GLContext gl(m_context.getRenderContext(), m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS,
tcu::IVec4(0, 0, 1, 1));
uint32_t error;
glu::ShaderProgram program(m_context.getRenderContext(), glu::ProgramSources()
<< glu::VertexSource(s_commonVertexShaderSource)
<< glu::FragmentSource(s_commonFragmentShaderSource));
uint32_t vaoID = 0;
uint32_t dataBufferID = 0;
uint32_t indexBufferID = 0;
uint32_t cmdBufferID = 0;
const uint32_t programID = program.getProgram();
const int32_t posLocation = gl.getAttribLocation(programID, "a_position");
DrawElementsCommand drawCommand;
drawCommand.count = 3;
drawCommand.primCount = 1;
drawCommand.firstIndex = 0;
drawCommand.baseVertex = 0;
drawCommand.reservedMustBeZero = 0;
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
if (m_caseType == CASE_CLIENT_BUFFER_VERTEXATTR)
{
// \note We use default VAO since we use client pointers. Trying indirect draw with default VAO is also an error. => This test does two illegal operations
gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, vertexPositions);
gl.enableVertexAttribArray(posLocation);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
else if (m_caseType == CASE_CLIENT_BUFFER_COMMAND)
{
gl.genVertexArrays(1, &vaoID);
gl.bindVertexArray(vaoID);
gl.genBuffers(1, &dataBufferID);
gl.bindBuffer(GL_ARRAY_BUFFER, dataBufferID);
gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.enableVertexAttribArray(posLocation);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
else if (m_caseType == CASE_DEFAULT_VAO)
{
gl.genBuffers(1, &dataBufferID);
gl.bindBuffer(GL_ARRAY_BUFFER, dataBufferID);
gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.enableVertexAttribArray(posLocation);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
else
DE_ASSERT(false);
gl.genBuffers(1, &indexBufferID);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
if (m_caseType != CASE_CLIENT_BUFFER_COMMAND)
{
gl.genBuffers(1, &cmdBufferID);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, cmdBufferID);
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(drawCommand), &drawCommand, GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
gl.viewport(0, 0, 1, 1);
gl.useProgram(programID);
gl.drawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT,
(m_caseType != CASE_CLIENT_BUFFER_COMMAND) ? (DE_NULL) : (&drawCommand));
error = gl.getError();
gl.bindVertexArray(0);
gl.useProgram(0);
if (error == GL_INVALID_OPERATION)
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
{
m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected error. Expected GL_INVALID_OPERATION, got "
<< glu::getErrorStr(error) << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected error.");
}
return STOP;
}
class BadDrawModeCase : public TestCase
{
public:
enum DrawType
{
DRAW_ARRAYS = 0,
DRAW_ELEMENTS,
DRAW_ELEMENTS_BAD_INDEX,
DRAW_LAST
};
BadDrawModeCase(Context &context, const char *name, const char *desc, DrawType type);
~BadDrawModeCase(void);
void init(void);
void deinit(void);
IterateResult iterate(void);
private:
const DrawType m_drawType;
};
BadDrawModeCase::BadDrawModeCase(Context &context, const char *name, const char *desc, DrawType type)
: TestCase(context, name, desc)
, m_drawType(type)
{
DE_ASSERT(type < DRAW_LAST);
}
BadDrawModeCase::~BadDrawModeCase(void)
{
deinit();
}
void BadDrawModeCase::init(void)
{
}
void BadDrawModeCase::deinit(void)
{
}
BadDrawModeCase::IterateResult BadDrawModeCase::iterate(void)
{
const tcu::Vec4 vertexPositions[] = {
tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f),
tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f),
tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f),
};
const uint16_t indices[] = {
0,
2,
1,
};
sglr::GLContext gl(m_context.getRenderContext(), m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS,
tcu::IVec4(0, 0, 1, 1));
uint32_t error;
glu::ShaderProgram program(m_context.getRenderContext(), glu::ProgramSources()
<< glu::VertexSource(s_commonVertexShaderSource)
<< glu::FragmentSource(s_commonFragmentShaderSource));
uint32_t vaoID = 0;
uint32_t dataBufferID = 0;
uint32_t indexBufferID = 0;
uint32_t cmdBufferID = 0;
const uint32_t programID = program.getProgram();
const int32_t posLocation = gl.getAttribLocation(programID, "a_position");
const glw::GLenum mode = (m_drawType == DRAW_ELEMENTS_BAD_INDEX) ? (GL_TRIANGLES) : (0x123);
const glw::GLenum indexType = (m_drawType == DRAW_ELEMENTS_BAD_INDEX) ? (0x123) : (GL_UNSIGNED_SHORT);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
// vao
gl.genVertexArrays(1, &vaoID);
gl.bindVertexArray(vaoID);
// va
gl.genBuffers(1, &dataBufferID);
gl.bindBuffer(GL_ARRAY_BUFFER, dataBufferID);
gl.bufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.enableVertexAttribArray(posLocation);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
// index
if (m_drawType == DRAW_ELEMENTS || m_drawType == DRAW_ELEMENTS_BAD_INDEX)
{
gl.genBuffers(1, &indexBufferID);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
}
// cmd
gl.genBuffers(1, &cmdBufferID);
gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, cmdBufferID);
if (m_drawType == DRAW_ELEMENTS || m_drawType == DRAW_ELEMENTS_BAD_INDEX)
{
DrawElementsCommand drawCommand;
drawCommand.count = 3;
drawCommand.primCount = 1;
drawCommand.firstIndex = 0;
drawCommand.baseVertex = 0;
drawCommand.reservedMustBeZero = 0;
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(drawCommand), &drawCommand, GL_STATIC_DRAW);
}
else if (m_drawType == DRAW_ARRAYS)
{
DrawArraysCommand drawCommand;
drawCommand.count = 3;
drawCommand.primCount = 1;
drawCommand.first = 0;
drawCommand.reservedMustBeZero = 0;
gl.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(drawCommand), &drawCommand, GL_STATIC_DRAW);
}
else
DE_ASSERT(false);
glu::checkError(gl.getError(), "", __FILE__, __LINE__);
gl.viewport(0, 0, 1, 1);
gl.useProgram(programID);
if (m_drawType == DRAW_ELEMENTS || m_drawType == DRAW_ELEMENTS_BAD_INDEX)
gl.drawElementsIndirect(mode, indexType, DE_NULL);
else if (m_drawType == DRAW_ARRAYS)
gl.drawArraysIndirect(mode, DE_NULL);
else
DE_ASSERT(false);
error = gl.getError();
gl.useProgram(0);
if (error == GL_INVALID_ENUM)
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
{
m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected error. Expected GL_INVALID_ENUM, got "
<< glu::getErrorStr(error) << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected error.");
}
return STOP;
}
class NegativeGroup : public TestCaseGroup
{
public:
NegativeGroup(Context &context, const char *name, const char *descr);
~NegativeGroup(void);
void init(void);
};
NegativeGroup::NegativeGroup(Context &context, const char *name, const char *descr)
: TestCaseGroup(context, name, descr)
{
}
NegativeGroup::~NegativeGroup(void)
{
}
void NegativeGroup::init(void)
{
// invalid alignment
addChild(new BadAlignmentCase(m_context, "command_bad_alignment_1", "Bad command alignment", 1));
addChild(new BadAlignmentCase(m_context, "command_bad_alignment_2", "Bad command alignment", 2));
addChild(new BadAlignmentCase(m_context, "command_bad_alignment_3", "Bad command alignment", 3));
// command only partially or not at all in the buffer
addChild(new BadBufferRangeCase(m_context, "command_offset_partially_in_buffer",
"Command not fully in the buffer range", BadBufferRangeCase::CommandSize - 16));
addChild(new BadBufferRangeCase(m_context, "command_offset_not_in_buffer", "Command not in the buffer range",
BadBufferRangeCase::CommandSize));
addChild(new BadBufferRangeCase(m_context, "command_offset_not_in_buffer_unsigned32_wrap",
"Command not in the buffer range", 0xFFFFFFFC));
addChild(new BadBufferRangeCase(m_context, "command_offset_not_in_buffer_signed32_wrap",
"Command not in the buffer range", 0x7FFFFFFC));
// use with client data and default vao
addChild(new BadStateCase(m_context, "client_vertex_attrib_array", "Vertex attrib array in the client memory",
BadStateCase::CASE_CLIENT_BUFFER_VERTEXATTR));
addChild(new BadStateCase(m_context, "client_command_array", "Command array in the client memory",
BadStateCase::CASE_CLIENT_BUFFER_COMMAND));
addChild(new BadStateCase(m_context, "default_vao", "Use with default vao", BadStateCase::CASE_DEFAULT_VAO));
// invalid mode & type
addChild(new BadDrawModeCase(m_context, "invalid_mode_draw_arrays", "Call DrawArraysIndirect with bad mode",
BadDrawModeCase::DRAW_ARRAYS));
addChild(new BadDrawModeCase(m_context, "invalid_mode_draw_elements", "Call DrawelementsIndirect with bad mode",
BadDrawModeCase::DRAW_ELEMENTS));
addChild(new BadDrawModeCase(m_context, "invalid_type_draw_elements", "Call DrawelementsIndirect with bad type",
BadDrawModeCase::DRAW_ELEMENTS_BAD_INDEX));
}
} // namespace
DrawTests::DrawTests(Context &context) : TestCaseGroup(context, "draw_indirect", "Indirect drawing tests")
{
}
DrawTests::~DrawTests(void)
{
}
void DrawTests::init(void)
{
// Basic
{
const gls::DrawTestSpec::DrawMethod basicMethods[] = {
gls::DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INDIRECT,
gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INDIRECT,
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(basicMethods); ++ndx)
{
const std::string name = gls::DrawTestSpec::drawMethodToString(basicMethods[ndx]);
const std::string desc = gls::DrawTestSpec::drawMethodToString(basicMethods[ndx]);
this->addChild(new MethodGroup(m_context, name.c_str(), desc.c_str(), basicMethods[ndx]));
}
}
// extreme instancing
this->addChild(new InstancingGroup(m_context, "instancing", "draw tests with a large instance count."));
// compute shader generated commands
this->addChild(new ComputeShaderGeneratedGroup(m_context, "compute_interop",
"draw tests with a draw command generated in compute shader."));
// Random
this->addChild(new RandomGroup(m_context, "random", "random draw commands."));
// negative
this->addChild(new NegativeGroup(m_context, "negative", "invalid draw commands with defined error codes."));
}
} // namespace Functional
} // namespace gles31
} // namespace deqp