blob: 3411ad0d4026cd788f9f4e1532df6610a6370937 [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 Basic Compute Shader Tests.
*//*--------------------------------------------------------------------*/
#include "es31fAtomicCounterTests.hpp"
#include "gluShaderProgram.hpp"
#include "gluObjectWrapper.hpp"
#include "gluRenderContext.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
#include "tcuTestLog.hpp"
#include "deStringUtil.hpp"
#include "deRandom.hpp"
#include "deMemory.h"
#include <vector>
#include <string>
using namespace glw;
using tcu::TestLog;
using std::string;
using std::vector;
namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace
{
class AtomicCounterTest : public TestCase
{
public:
enum Operation
{
OPERATION_INC = (1 << 0),
OPERATION_DEC = (1 << 1),
OPERATION_GET = (1 << 2)
};
enum OffsetType
{
OFFSETTYPE_NONE = 0,
OFFSETTYPE_BASIC,
OFFSETTYPE_REVERSE,
OFFSETTYPE_FIRST_AUTO,
OFFSETTYPE_DEFAULT_AUTO,
OFFSETTYPE_RESET_DEFAULT,
OFFSETTYPE_INVALID,
OFFSETTYPE_INVALID_OVERLAPPING,
OFFSETTYPE_INVALID_DEFAULT
};
enum BindingType
{
BINDINGTYPE_BASIC = 0,
BINDINGTYPE_INVALID,
BINDINGTYPE_INVALID_DEFAULT
};
struct TestSpec
{
TestSpec(void)
: atomicCounterCount(0)
, operations((Operation)0)
, callCount(0)
, useBranches(false)
, threadCount(0)
, offsetType(OFFSETTYPE_NONE)
, bindingType(BINDINGTYPE_BASIC)
{
}
int atomicCounterCount;
Operation operations;
int callCount;
bool useBranches;
int threadCount;
OffsetType offsetType;
BindingType bindingType;
};
AtomicCounterTest(Context &context, const char *name, const char *description, const TestSpec &spec);
~AtomicCounterTest(void);
void init(void);
void deinit(void);
IterateResult iterate(void);
private:
const TestSpec m_spec;
bool checkAndLogCounterValues(TestLog &log, const vector<uint32_t> &counters) const;
bool checkAndLogCallValues(TestLog &log, const vector<uint32_t> &increments, const vector<uint32_t> &decrements,
const vector<uint32_t> &preGets, const vector<uint32_t> &postGets,
const vector<uint32_t> &gets) const;
void splitBuffer(const vector<uint32_t> &buffer, vector<uint32_t> &increments, vector<uint32_t> &decrements,
vector<uint32_t> &preGets, vector<uint32_t> &postGets, vector<uint32_t> &gets) const;
uint32_t getInitialValue(void) const
{
return m_spec.callCount * m_spec.threadCount + 1;
}
static string generateShaderSource(const TestSpec &spec);
static void getCountersValues(vector<uint32_t> &counterValues, const vector<uint32_t> &values, int ndx,
int counterCount);
static bool checkRange(TestLog &log, const vector<uint32_t> &values, const vector<uint32_t> &min,
const vector<uint32_t> &max);
static bool checkUniquenessAndLinearity(TestLog &log, const vector<uint32_t> &values);
static bool checkPath(const vector<uint32_t> &increments, const vector<uint32_t> &decrements, int initialValue,
const TestSpec &spec);
int getOperationCount(void) const;
AtomicCounterTest &operator=(const AtomicCounterTest &);
AtomicCounterTest(const AtomicCounterTest &);
};
int AtomicCounterTest::getOperationCount(void) const
{
int count = 0;
if (m_spec.operations & OPERATION_INC)
count++;
if (m_spec.operations & OPERATION_DEC)
count++;
if (m_spec.operations == OPERATION_GET)
count++;
else if (m_spec.operations & OPERATION_GET)
count += 2;
return count;
}
AtomicCounterTest::AtomicCounterTest(Context &context, const char *name, const char *description, const TestSpec &spec)
: TestCase(context, name, description)
, m_spec(spec)
{
}
AtomicCounterTest::~AtomicCounterTest(void)
{
}
void AtomicCounterTest::init(void)
{
}
void AtomicCounterTest::deinit(void)
{
}
string AtomicCounterTest::generateShaderSource(const TestSpec &spec)
{
std::ostringstream src;
src << "#version 310 es\n"
<< "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n";
{
bool wroteLayout = false;
switch (spec.bindingType)
{
case BINDINGTYPE_INVALID_DEFAULT:
src << "layout(binding=10000";
wroteLayout = true;
break;
default:
// Do nothing
break;
}
switch (spec.offsetType)
{
case OFFSETTYPE_DEFAULT_AUTO:
if (!wroteLayout)
src << "layout(binding=0, ";
else
src << ", ";
src << "offset=4";
wroteLayout = true;
break;
case OFFSETTYPE_RESET_DEFAULT:
DE_ASSERT(spec.atomicCounterCount > 2);
if (!wroteLayout)
src << "layout(binding=0, ";
else
src << ", ";
src << "offset=" << (4 * spec.atomicCounterCount / 2);
wroteLayout = true;
break;
case OFFSETTYPE_INVALID_DEFAULT:
if (!wroteLayout)
src << "layout(binding=0, ";
else
src << ", ";
src << "offset=1";
wroteLayout = true;
break;
default:
// Do nothing
break;
}
if (wroteLayout)
src << ") uniform atomic_uint;\n";
}
src << "layout(binding = 1, std430) buffer Output {\n";
if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
src << " uint preGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
if ((spec.operations & OPERATION_INC) != 0)
src << " uint increment[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
if ((spec.operations & OPERATION_DEC) != 0)
src << " uint decrement[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
src << " uint postGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
if (spec.operations == OPERATION_GET)
src << " uint get[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
src << "} sb_in;\n\n";
for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
{
bool layoutStarted = false;
if (spec.offsetType == OFFSETTYPE_RESET_DEFAULT && counterNdx == spec.atomicCounterCount / 2)
src << "layout(binding=0, offset=0) uniform atomic_uint;\n";
switch (spec.bindingType)
{
case BINDINGTYPE_BASIC:
layoutStarted = true;
src << "layout(binding=0";
break;
case BINDINGTYPE_INVALID:
layoutStarted = true;
src << "layout(binding=10000";
break;
case BINDINGTYPE_INVALID_DEFAULT:
// Nothing
break;
default:
DE_ASSERT(false);
}
switch (spec.offsetType)
{
case OFFSETTYPE_NONE:
if (layoutStarted)
src << ") ";
src << "uniform atomic_uint counter" << counterNdx << ";\n";
break;
case OFFSETTYPE_BASIC:
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=" << (counterNdx * 4) << ") uniform atomic_uint counter" << counterNdx << ";\n";
break;
case OFFSETTYPE_INVALID_DEFAULT:
if (layoutStarted)
src << ") ";
src << "uniform atomic_uint counter" << counterNdx << ";\n";
break;
case OFFSETTYPE_INVALID:
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=" << (1 + counterNdx * 2) << ") uniform atomic_uint counter" << counterNdx << ";\n";
break;
case OFFSETTYPE_INVALID_OVERLAPPING:
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=0) uniform atomic_uint counter" << counterNdx << ";\n";
break;
case OFFSETTYPE_REVERSE:
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=" << (spec.atomicCounterCount - counterNdx - 1) * 4 << ") uniform atomic_uint counter"
<< (spec.atomicCounterCount - counterNdx - 1) << ";\n";
break;
case OFFSETTYPE_FIRST_AUTO:
DE_ASSERT(spec.atomicCounterCount > 2);
if (counterNdx + 1 == spec.atomicCounterCount)
{
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=0) uniform atomic_uint counter0;\n";
}
else if (counterNdx == 0)
{
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=4) uniform atomic_uint counter1;\n";
}
else
{
if (layoutStarted)
src << ") ";
src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
}
break;
case OFFSETTYPE_DEFAULT_AUTO:
if (counterNdx + 1 == spec.atomicCounterCount)
{
if (!layoutStarted)
src << "layout(";
else
src << ", ";
src << "offset=0) uniform atomic_uint counter0;\n";
}
else
{
if (layoutStarted)
src << ") ";
src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
}
break;
case OFFSETTYPE_RESET_DEFAULT:
if (layoutStarted)
src << ") ";
if (counterNdx < spec.atomicCounterCount / 2)
src << "uniform atomic_uint counter" << (counterNdx + spec.atomicCounterCount / 2) << ";\n";
else
src << "uniform atomic_uint counter" << (counterNdx - spec.atomicCounterCount / 2) << ";\n";
break;
default:
DE_ASSERT(false);
}
}
src << "\n"
<< "void main (void)\n"
<< "{\n";
if (spec.callCount > 1)
src << "\tfor (uint i = 0u; i < " << spec.callCount << "u; i++)\n";
src << "\t{\n"
<< "\t\tuint id = (gl_GlobalInvocationID.x";
if (spec.callCount > 1)
src << " * " << spec.callCount << "u";
if (spec.callCount > 1)
src << " + i)";
else
src << ")";
if (spec.atomicCounterCount > 1)
src << " * " << spec.atomicCounterCount << "u";
src << ";\n";
for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
{
if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
src << "\t\tsb_in.preGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
if (spec.useBranches &&
((spec.operations & (OPERATION_INC | OPERATION_DEC)) == (OPERATION_INC | OPERATION_DEC)))
{
src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter" << counterNdx
<< ");\n"
<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
<< "\t\t}\n"
<< "\t\telse\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter" << counterNdx
<< ") + 1u;\n"
<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
<< "\t\t}\n";
}
else
{
if ((spec.operations & OPERATION_INC) != 0)
{
if (spec.useBranches)
{
src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "")
<< ") % 2u) == 0u)\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter"
<< counterNdx << ");\n"
<< "\t\t}\n"
<< "\t\telse\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
<< "\t\t}\n";
}
else
src << "\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter"
<< counterNdx << ");\n";
}
if ((spec.operations & OPERATION_DEC) != 0)
{
if (spec.useBranches)
{
src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "")
<< ") % 2u) == 0u)\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter"
<< counterNdx << ") + 1u;\n"
<< "\t\t}\n"
<< "\t\telse\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
<< "\t\t}\n";
}
else
src << "\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter"
<< counterNdx << ") + 1u;\n";
}
}
if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
src << "\t\tsb_in.postGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
if ((spec.operations == OPERATION_GET) != 0)
{
if (spec.useBranches)
{
src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n"
<< "\t\t}\n"
<< "\t\telse\n"
<< "\t\t{\n"
<< "\t\t\tsb_in.get[id + " << counterNdx << "u] = uint(-1);\n"
<< "\t\t}\n";
}
else
src << "\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
}
}
src << "\t}\n"
<< "}\n";
return src.str();
}
bool AtomicCounterTest::checkAndLogCounterValues(TestLog &log, const vector<uint32_t> &counters) const
{
tcu::ScopedLogSection counterSection(log, "Counter info",
"Show initial value, current value and expected value of each counter.");
bool isOk = true;
// Check that atomic counters have sensible results
for (int counterNdx = 0; counterNdx < (int)counters.size(); counterNdx++)
{
const uint32_t value = counters[counterNdx];
const uint32_t initialValue = getInitialValue();
uint32_t expectedValue = (uint32_t)-1;
if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) == 0)
expectedValue = initialValue + (m_spec.useBranches ? m_spec.threadCount * m_spec.callCount -
m_spec.threadCount * m_spec.callCount / 2 :
m_spec.threadCount * m_spec.callCount);
if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) != 0)
expectedValue = initialValue - (m_spec.useBranches ? m_spec.threadCount * m_spec.callCount -
m_spec.threadCount * m_spec.callCount / 2 :
m_spec.threadCount * m_spec.callCount);
if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) != 0)
expectedValue = initialValue +
(m_spec.useBranches ?
m_spec.threadCount * m_spec.callCount - m_spec.threadCount * m_spec.callCount / 2 :
0) -
(m_spec.useBranches ? m_spec.threadCount * m_spec.callCount / 2 : 0);
if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) == 0)
expectedValue = initialValue;
log << TestLog::Message << "atomic_uint counter" << counterNdx << " initial value: " << initialValue
<< ", value: " << value << ", expected: " << expectedValue << (value == expectedValue ? "" : ", failed!")
<< TestLog::EndMessage;
if (value != expectedValue)
isOk = false;
}
return isOk;
}
void AtomicCounterTest::splitBuffer(const vector<uint32_t> &buffer, vector<uint32_t> &increments,
vector<uint32_t> &decrements, vector<uint32_t> &preGets, vector<uint32_t> &postGets,
vector<uint32_t> &gets) const
{
const int bufferValueCount = m_spec.callCount * m_spec.threadCount * m_spec.atomicCounterCount;
int firstPreGet = -1;
int firstPostGet = -1;
int firstGet = -1;
int firstInc = -1;
int firstDec = -1;
increments.clear();
decrements.clear();
preGets.clear();
postGets.clear();
gets.clear();
if (m_spec.operations == OPERATION_GET)
firstGet = 0;
else if (m_spec.operations == OPERATION_INC)
firstInc = 0;
else if (m_spec.operations == OPERATION_DEC)
firstDec = 0;
else if (m_spec.operations == (OPERATION_GET | OPERATION_INC))
{
firstPreGet = 0;
firstInc = bufferValueCount;
firstPostGet = bufferValueCount * 2;
}
else if (m_spec.operations == (OPERATION_GET | OPERATION_DEC))
{
firstPreGet = 0;
firstDec = bufferValueCount;
firstPostGet = bufferValueCount * 2;
}
else if (m_spec.operations == (OPERATION_GET | OPERATION_DEC | OPERATION_INC))
{
firstPreGet = 0;
firstInc = bufferValueCount;
firstDec = bufferValueCount * 2;
firstPostGet = bufferValueCount * 3;
}
else if (m_spec.operations == (OPERATION_DEC | OPERATION_INC))
{
firstInc = 0;
firstDec = bufferValueCount;
}
else
DE_ASSERT(false);
for (int threadNdx = 0; threadNdx < m_spec.threadCount; threadNdx++)
{
for (int callNdx = 0; callNdx < m_spec.callCount; callNdx++)
{
for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
{
const int id = ((threadNdx * m_spec.callCount) + callNdx) * m_spec.atomicCounterCount + counterNdx;
if (firstInc != -1)
increments.push_back(buffer[firstInc + id]);
if (firstDec != -1)
decrements.push_back(buffer[firstDec + id]);
if (firstPreGet != -1)
preGets.push_back(buffer[firstPreGet + id]);
if (firstPostGet != -1)
postGets.push_back(buffer[firstPostGet + id]);
if (firstGet != -1)
gets.push_back(buffer[firstGet + id]);
}
}
}
}
void AtomicCounterTest::getCountersValues(vector<uint32_t> &counterValues, const vector<uint32_t> &values, int ndx,
int counterCount)
{
counterValues.resize(values.size() / counterCount, 0);
DE_ASSERT(values.size() % counterCount == 0);
for (int valueNdx = 0; valueNdx < (int)counterValues.size(); valueNdx++)
counterValues[valueNdx] = values[valueNdx * counterCount + ndx];
}
bool AtomicCounterTest::checkRange(TestLog &log, const vector<uint32_t> &values, const vector<uint32_t> &min,
const vector<uint32_t> &max)
{
int failedCount = 0;
DE_ASSERT(values.size() == min.size());
DE_ASSERT(values.size() == max.size());
for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
{
if (values[valueNdx] != (uint32_t)-1)
{
if (!deInRange32(values[valueNdx], min[valueNdx], max[valueNdx]))
{
if (failedCount < 20)
log << TestLog::Message << "Value " << values[valueNdx] << " not in range [" << min[valueNdx]
<< ", " << max[valueNdx] << "]." << TestLog::EndMessage;
failedCount++;
}
}
}
if (failedCount > 20)
log << TestLog::Message << "Number of values not in range: " << failedCount << ", displaying first 20 values."
<< TestLog::EndMessage;
return failedCount == 0;
}
bool AtomicCounterTest::checkUniquenessAndLinearity(TestLog &log, const vector<uint32_t> &values)
{
vector<uint32_t> counts;
int failedCount = 0;
uint32_t minValue = (uint32_t)-1;
uint32_t maxValue = 0;
DE_ASSERT(!values.empty());
for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
{
if (values[valueNdx] != (uint32_t)-1)
{
minValue = std::min(minValue, values[valueNdx]);
maxValue = std::max(maxValue, values[valueNdx]);
}
}
counts.resize(maxValue - minValue + 1, 0);
for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
{
if (values[valueNdx] != (uint32_t)-1)
counts[values[valueNdx] - minValue]++;
}
for (int countNdx = 0; countNdx < (int)counts.size(); countNdx++)
{
if (counts[countNdx] != 1)
{
if (failedCount < 20)
log << TestLog::Message << "Value " << (minValue + countNdx) << " is not unique. Returned "
<< counts[countNdx] << " times." << TestLog::EndMessage;
failedCount++;
}
}
if (failedCount > 20)
log << TestLog::Message << "Number of values not unique: " << failedCount << ", displaying first 20 values."
<< TestLog::EndMessage;
return failedCount == 0;
}
bool AtomicCounterTest::checkPath(const vector<uint32_t> &increments, const vector<uint32_t> &decrements,
int initialValue, const TestSpec &spec)
{
const uint32_t lastValue =
initialValue +
(spec.useBranches ? spec.threadCount * spec.callCount - spec.threadCount * spec.callCount / 2 : 0) -
(spec.useBranches ? spec.threadCount * spec.callCount / 2 : 0);
bool isOk = true;
vector<uint32_t> incrementCounts;
vector<uint32_t> decrementCounts;
uint32_t minValue = 0xFFFFFFFFu;
uint32_t maxValue = 0;
for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
{
if (increments[valueNdx] != (uint32_t)-1)
{
minValue = std::min(minValue, increments[valueNdx]);
maxValue = std::max(maxValue, increments[valueNdx]);
}
}
for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
{
if (decrements[valueNdx] != (uint32_t)-1)
{
minValue = std::min(minValue, decrements[valueNdx]);
maxValue = std::max(maxValue, decrements[valueNdx]);
}
}
minValue = std::min(minValue, (uint32_t)initialValue);
maxValue = std::max(maxValue, (uint32_t)initialValue);
incrementCounts.resize(maxValue - minValue + 1, 0);
decrementCounts.resize(maxValue - minValue + 1, 0);
for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
{
if (increments[valueNdx] != (uint32_t)-1)
incrementCounts[increments[valueNdx] - minValue]++;
}
for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
{
if (decrements[valueNdx] != (uint32_t)-1)
decrementCounts[decrements[valueNdx] - minValue]++;
}
int pos = initialValue - minValue;
while (incrementCounts[pos] + decrementCounts[pos] != 0)
{
if (incrementCounts[pos] > 0 && pos >= (int)(lastValue - minValue))
{
// If can increment and incrementation would move us away from result value, increment
incrementCounts[pos]--;
pos++;
}
else if (decrementCounts[pos] > 0)
{
// If can, decrement
decrementCounts[pos]--;
pos--;
}
else if (incrementCounts[pos] > 0)
{
// If increment moves closer to result value and can't decrement, increment
incrementCounts[pos]--;
pos++;
}
else
DE_ASSERT(false);
if (pos < 0 || pos >= (int)incrementCounts.size())
break;
}
if (minValue + pos != lastValue)
isOk = false;
for (int valueNdx = 0; valueNdx < (int)incrementCounts.size(); valueNdx++)
{
if (incrementCounts[valueNdx] != 0)
isOk = false;
}
for (int valueNdx = 0; valueNdx < (int)decrementCounts.size(); valueNdx++)
{
if (decrementCounts[valueNdx] != 0)
isOk = false;
}
return isOk;
}
bool AtomicCounterTest::checkAndLogCallValues(TestLog &log, const vector<uint32_t> &increments,
const vector<uint32_t> &decrements, const vector<uint32_t> &preGets,
const vector<uint32_t> &postGets, const vector<uint32_t> &gets) const
{
bool isOk = true;
for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
{
vector<uint32_t> counterIncrements;
vector<uint32_t> counterDecrements;
vector<uint32_t> counterPreGets;
vector<uint32_t> counterPostGets;
vector<uint32_t> counterGets;
getCountersValues(counterIncrements, increments, counterNdx, m_spec.atomicCounterCount);
getCountersValues(counterDecrements, decrements, counterNdx, m_spec.atomicCounterCount);
getCountersValues(counterPreGets, preGets, counterNdx, m_spec.atomicCounterCount);
getCountersValues(counterPostGets, postGets, counterNdx, m_spec.atomicCounterCount);
getCountersValues(counterGets, gets, counterNdx, m_spec.atomicCounterCount);
if (m_spec.operations == OPERATION_GET)
{
tcu::ScopedLogSection valueCheck(
log, ("counter" + de::toString(counterNdx) + " value check").c_str(),
("Check that counter" + de::toString(counterNdx) + " values haven't changed.").c_str());
int changedValues = 0;
for (int valueNdx = 0; valueNdx < (int)gets.size(); valueNdx++)
{
if ((!m_spec.useBranches || gets[valueNdx] != (uint32_t)-1) && gets[valueNdx] != getInitialValue())
{
if (changedValues < 20)
log << TestLog::Message << "atomicCounter(counter" << counterNdx << ") returned "
<< gets[valueNdx] << " expected " << getInitialValue() << TestLog::EndMessage;
isOk = false;
changedValues++;
}
}
if (changedValues == 0)
log << TestLog::Message << "All values returned by atomicCounter(counter" << counterNdx
<< ") match initial value " << getInitialValue() << "." << TestLog::EndMessage;
else if (changedValues > 20)
log << TestLog::Message << "Total number of invalid values returned by atomicCounter(counter"
<< counterNdx << ") " << changedValues << " displaying first 20 values." << TestLog::EndMessage;
}
else if ((m_spec.operations & (OPERATION_INC | OPERATION_DEC)) == (OPERATION_INC | OPERATION_DEC))
{
tcu::ScopedLogSection valueCheck(log, ("counter" + de::toString(counterNdx) + " path check").c_str(),
("Check that there is order in which counter" + de::toString(counterNdx) +
" increments and decrements could have happened.")
.c_str());
if (!checkPath(counterIncrements, counterDecrements, getInitialValue(), m_spec))
{
isOk = false;
log << TestLog::Message << "No possible order of calls to atomicCounterIncrement(counter" << counterNdx
<< ") and atomicCounterDecrement(counter" << counterNdx << ") found." << TestLog::EndMessage;
}
else
log << TestLog::Message << "Found possible order of calls to atomicCounterIncrement(counter"
<< counterNdx << ") and atomicCounterDecrement(counter" << counterNdx << ")."
<< TestLog::EndMessage;
}
else if ((m_spec.operations & OPERATION_INC) != 0)
{
{
tcu::ScopedLogSection uniquenesCheck(
log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(),
("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.")
.c_str());
if (!checkUniquenessAndLinearity(log, counterIncrements))
{
isOk = false;
log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
<< ") returned non unique values." << TestLog::EndMessage;
}
else
log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
<< ") returned only unique values." << TestLog::EndMessage;
}
if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
{
tcu::ScopedLogSection uniquenesCheck(
log, ("counter" + de::toString(counterNdx) + " check range").c_str(),
("Check that counter" + de::toString(counterNdx) +
" returned only values values between previous and next atomicCounter(counter" +
de::toString(counterNdx) + ").")
.c_str());
if (!checkRange(log, counterIncrements, counterPreGets, counterPostGets))
{
isOk = false;
log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
<< ") returned value that is not between previous and next call to atomicCounter(counter"
<< counterNdx << ")." << TestLog::EndMessage;
}
else
log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
<< ") returned only values between previous and next call to atomicCounter(counter"
<< counterNdx << ")." << TestLog::EndMessage;
}
}
else if ((m_spec.operations & OPERATION_DEC) != 0)
{
{
tcu::ScopedLogSection uniquenesCheck(
log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(),
("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.")
.c_str());
if (!checkUniquenessAndLinearity(log, counterDecrements))
{
isOk = false;
log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
<< ") returned non unique values." << TestLog::EndMessage;
}
else
log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
<< ") returned only unique values." << TestLog::EndMessage;
}
if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
{
tcu::ScopedLogSection uniquenesCheck(
log, ("counter" + de::toString(counterNdx) + " check range").c_str(),
("Check that counter" + de::toString(counterNdx) +
" returned only values values between previous and next atomicCounter(counter" +
de::toString(counterNdx) + ".")
.c_str());
if (!checkRange(log, counterDecrements, counterPostGets, counterPreGets))
{
isOk = false;
log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
<< ") returned value that is not between previous and next call to atomicCounter(counter"
<< counterNdx << ")." << TestLog::EndMessage;
}
else
log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
<< ") returned only values between previous and next call to atomicCounter(counter"
<< counterNdx << ")." << TestLog::EndMessage;
}
}
}
return isOk;
}
TestCase::IterateResult AtomicCounterTest::iterate(void)
{
const glw::Functions &gl = m_context.getRenderContext().getFunctions();
TestLog &log = m_testCtx.getLog();
const glu::Buffer counterBuffer(m_context.getRenderContext());
const glu::Buffer outputBuffer(m_context.getRenderContext());
const glu::ShaderProgram program(m_context.getRenderContext(),
glu::ProgramSources()
<< glu::ShaderSource(glu::SHADERTYPE_COMPUTE, generateShaderSource(m_spec)));
const int32_t counterBufferSize = m_spec.atomicCounterCount * 4;
const int32_t ssoSize = m_spec.atomicCounterCount * m_spec.callCount * m_spec.threadCount * 4 * getOperationCount();
log << program;
if (m_spec.offsetType == OFFSETTYPE_INVALID || m_spec.offsetType == OFFSETTYPE_INVALID_DEFAULT ||
m_spec.bindingType == BINDINGTYPE_INVALID || m_spec.bindingType == BINDINGTYPE_INVALID_DEFAULT ||
m_spec.offsetType == OFFSETTYPE_INVALID_OVERLAPPING)
{
if (program.isOk())
{
log << TestLog::Message << "Expected program to fail, but compilation passed." << TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile succeeded");
return STOP;
}
else
{
log << TestLog::Message << "Compilation failed as expected." << TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Compile failed");
return STOP;
}
}
else if (!program.isOk())
{
log << TestLog::Message << "Compile failed." << TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
return STOP;
}
gl.useProgram(program.getProgram());
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
// Create output buffer
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
gl.bufferData(GL_SHADER_STORAGE_BUFFER, ssoSize, NULL, GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create output buffer");
// Create atomic counter buffer
{
vector<uint32_t> data(m_spec.atomicCounterCount, getInitialValue());
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
gl.bufferData(GL_SHADER_STORAGE_BUFFER, counterBufferSize, &(data[0]), GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create buffer for atomic counters");
}
// Bind output buffer
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *outputBuffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup output buffer");
// Bind atomic counter buffer
gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, *counterBuffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup atomic counter buffer");
// Dispath compute
gl.dispatchCompute(m_spec.threadCount, 1, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
vector<uint32_t> output(ssoSize / 4, 0);
vector<uint32_t> counters(m_spec.atomicCounterCount, 0);
// Read back output buffer
{
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
void *ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(output.size() * sizeof(uint32_t)),
GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
deMemcpy(&(output[0]), ptr, (int)output.size() * sizeof(uint32_t));
if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
{
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
TCU_CHECK_MSG(false, "Mapped buffer corrupted");
}
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
}
// Read back counter buffer
{
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
void *ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(counters.size() * sizeof(uint32_t)),
GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
deMemcpy(&(counters[0]), ptr, (int)counters.size() * sizeof(uint32_t));
if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
{
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
TCU_CHECK_MSG(false, "Mapped buffer corrupted");
}
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
}
bool isOk = true;
if (!checkAndLogCounterValues(log, counters))
isOk = false;
{
vector<uint32_t> increments;
vector<uint32_t> decrements;
vector<uint32_t> preGets;
vector<uint32_t> postGets;
vector<uint32_t> gets;
splitBuffer(output, increments, decrements, preGets, postGets, gets);
if (!checkAndLogCallValues(log, increments, decrements, preGets, postGets, gets))
isOk = false;
}
if (isOk)
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
else
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
string specToTestName(const AtomicCounterTest::TestSpec &spec)
{
std::ostringstream stream;
stream << spec.atomicCounterCount << (spec.atomicCounterCount == 1 ? "_counter" : "_counters");
stream << "_" << spec.callCount << (spec.callCount == 1 ? "_call" : "_calls");
stream << "_" << spec.threadCount << (spec.threadCount == 1 ? "_thread" : "_threads");
return stream.str();
}
string specToTestDescription(const AtomicCounterTest::TestSpec &spec)
{
std::ostringstream stream;
bool firstOperation = 0;
stream << "Test ";
if ((spec.operations & AtomicCounterTest::OPERATION_GET) != 0)
{
stream << "atomicCounter()";
firstOperation = false;
}
if ((spec.operations & AtomicCounterTest::OPERATION_INC) != 0)
{
if (!firstOperation)
stream << ", ";
stream << " atomicCounterIncrement()";
firstOperation = false;
}
if ((spec.operations & AtomicCounterTest::OPERATION_DEC) != 0)
{
if (!firstOperation)
stream << ", ";
stream << " atomicCounterDecrement()";
firstOperation = false;
}
stream << " calls with ";
if (spec.useBranches)
stream << " branches, ";
stream << spec.atomicCounterCount << " atomic counters, " << spec.callCount << " calls and " << spec.threadCount
<< " threads.";
return stream.str();
}
string operationToName(const AtomicCounterTest::Operation &operations, bool useBranch)
{
std::ostringstream stream;
bool first = true;
if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
{
stream << "get";
first = false;
}
if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
{
if (!first)
stream << "_";
stream << "inc";
first = false;
}
if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
{
if (!first)
stream << "_";
stream << "dec";
first = false;
}
if (useBranch)
stream << "_branch";
return stream.str();
}
string operationToDescription(const AtomicCounterTest::Operation &operations, bool useBranch)
{
std::ostringstream stream;
bool firstOperation = 0;
stream << "Test ";
if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
{
stream << "atomicCounter()";
firstOperation = false;
}
if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
{
if (!firstOperation)
stream << ", ";
stream << " atomicCounterIncrement()";
firstOperation = false;
}
if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
{
if (!firstOperation)
stream << ", ";
stream << " atomicCounterDecrement()";
firstOperation = false;
}
if (useBranch)
stream << " calls with branches.";
else
stream << ".";
return stream.str();
}
string layoutTypesToName(const AtomicCounterTest::BindingType &bindingType,
const AtomicCounterTest::OffsetType &offsetType)
{
std::ostringstream stream;
switch (bindingType)
{
case AtomicCounterTest::BINDINGTYPE_BASIC:
// Nothing
break;
case AtomicCounterTest::BINDINGTYPE_INVALID:
stream << "invalid_binding";
break;
default:
DE_ASSERT(false);
}
if (bindingType != AtomicCounterTest::BINDINGTYPE_BASIC && offsetType != AtomicCounterTest::OFFSETTYPE_NONE)
stream << "_";
switch (offsetType)
{
case AtomicCounterTest::OFFSETTYPE_BASIC:
stream << "basic_offset";
break;
case AtomicCounterTest::OFFSETTYPE_REVERSE:
stream << "reverse_offset";
break;
case AtomicCounterTest::OFFSETTYPE_INVALID:
stream << "invalid_offset";
break;
case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
stream << "first_offset_set";
break;
case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
stream << "default_offset_set";
break;
case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
stream << "reset_default_offset";
break;
case AtomicCounterTest::OFFSETTYPE_NONE:
// Do nothing
break;
default:
DE_ASSERT(false);
}
return stream.str();
}
string layoutTypesToDesc(const AtomicCounterTest::BindingType &bindingType,
const AtomicCounterTest::OffsetType &offsetType)
{
std::ostringstream stream;
switch (bindingType)
{
case AtomicCounterTest::BINDINGTYPE_BASIC:
stream << "Test using atomic counters with explicit layout bindings and";
break;
case AtomicCounterTest::BINDINGTYPE_INVALID:
stream << "Test using atomic counters with invalid explicit layout bindings and";
break;
case AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT:
stream << "Test using atomic counters with invalid default layout binding and";
break;
default:
DE_ASSERT(false);
}
switch (offsetType)
{
case AtomicCounterTest::OFFSETTYPE_NONE:
stream << " no explicit offsets.";
break;
case AtomicCounterTest::OFFSETTYPE_BASIC:
stream << "explicit continuos offsets.";
break;
case AtomicCounterTest::OFFSETTYPE_REVERSE:
stream << "reversed explicit offsets.";
break;
case AtomicCounterTest::OFFSETTYPE_INVALID:
stream << "invalid explicit offsets.";
break;
case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
stream << "only first counter with explicit offset.";
break;
case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
stream << "default offset.";
break;
case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
stream << "default offset specified twice.";
break;
default:
DE_ASSERT(false);
}
return stream.str();
}
} // namespace
AtomicCounterTests::AtomicCounterTests(Context &context)
: TestCaseGroup(context, "atomic_counter", "Atomic counter tests")
{
// Runtime use tests
{
const int counterCounts[] = {1, 4, 8};
const int callCounts[] = {1, 5, 100};
const int threadCounts[] = {1, 10, 5000};
const AtomicCounterTest::Operation operations[] = {
AtomicCounterTest::OPERATION_GET,
AtomicCounterTest::OPERATION_INC,
AtomicCounterTest::OPERATION_DEC,
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_GET),
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC | AtomicCounterTest::OPERATION_GET),
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC),
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC |
AtomicCounterTest::OPERATION_GET)};
for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
{
const AtomicCounterTest::Operation operation = operations[operationNdx];
for (int branch = 0; branch < 2; branch++)
{
const bool useBranch = (branch == 1);
TestCaseGroup *operationGroup =
new TestCaseGroup(m_context, operationToName(operation, useBranch).c_str(),
operationToDescription(operation, useBranch).c_str());
for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
{
const int counterCount = counterCounts[counterCountNdx];
for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
{
const int callCount = callCounts[callCountNdx];
for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts);
threadCountNdx++)
{
const int threadCount = threadCounts[threadCountNdx];
if (threadCount * callCount * counterCount > 10000)
continue;
if (useBranch && threadCount * callCount == 1)
continue;
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = counterCount;
spec.operations = operation;
spec.callCount = callCount;
spec.useBranches = useBranch;
spec.threadCount = threadCount;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_BASIC;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_NONE;
operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(),
specToTestDescription(spec).c_str(), spec));
}
}
}
addChild(operationGroup);
}
}
}
{
TestCaseGroup *layoutGroup = new TestCaseGroup(m_context, "layout", "Layout qualifier tests.");
const int counterCounts[] = {1, 8};
const int callCounts[] = {1, 5};
const int threadCounts[] = {1, 1000};
const AtomicCounterTest::Operation operations[] = {
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_GET),
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC | AtomicCounterTest::OPERATION_GET),
(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC)};
const AtomicCounterTest::OffsetType offsetTypes[] = {
AtomicCounterTest::OFFSETTYPE_REVERSE, AtomicCounterTest::OFFSETTYPE_FIRST_AUTO,
AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO, AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT};
for (int offsetTypeNdx = 0; offsetTypeNdx < DE_LENGTH_OF_ARRAY(offsetTypes); offsetTypeNdx++)
{
const AtomicCounterTest::OffsetType offsetType = offsetTypes[offsetTypeNdx];
TestCaseGroup *layoutQualifierGroup = new TestCaseGroup(
m_context, layoutTypesToName(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str(),
layoutTypesToDesc(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str());
for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
{
const AtomicCounterTest::Operation operation = operations[operationNdx];
TestCaseGroup *operationGroup = new TestCaseGroup(m_context, operationToName(operation, false).c_str(),
operationToDescription(operation, false).c_str());
for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
{
const int counterCount = counterCounts[counterCountNdx];
if (offsetType == AtomicCounterTest::OFFSETTYPE_FIRST_AUTO && counterCount < 3)
continue;
if (offsetType == AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO && counterCount < 2)
continue;
if (offsetType == AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT && counterCount < 2)
continue;
if (offsetType == AtomicCounterTest::OFFSETTYPE_REVERSE && counterCount < 2)
continue;
for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
{
const int callCount = callCounts[callCountNdx];
for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts);
threadCountNdx++)
{
const int threadCount = threadCounts[threadCountNdx];
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = counterCount;
spec.operations = operation;
spec.callCount = callCount;
spec.useBranches = false;
spec.threadCount = threadCount;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_BASIC;
spec.offsetType = offsetType;
operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(),
specToTestDescription(spec).c_str(), spec));
}
}
}
layoutQualifierGroup->addChild(operationGroup);
}
layoutGroup->addChild(layoutQualifierGroup);
}
{
TestCaseGroup *invalidGroup = new TestCaseGroup(m_context, "invalid", "Test invalid layouts");
{
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = 1;
spec.operations = AtomicCounterTest::OPERATION_INC;
spec.callCount = 1;
spec.useBranches = false;
spec.threadCount = 1;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_INVALID;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_NONE;
invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_binding",
"Test layout qualifiers with invalid binding.", spec));
}
{
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = 1;
spec.operations = AtomicCounterTest::OPERATION_INC;
spec.callCount = 1;
spec.useBranches = false;
spec.threadCount = 1;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_NONE;
invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_default_binding",
"Test layout qualifiers with invalid default binding.",
spec));
}
{
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = 1;
spec.operations = AtomicCounterTest::OPERATION_INC;
spec.callCount = 1;
spec.useBranches = false;
spec.threadCount = 1;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_BASIC;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_INVALID;
invalidGroup->addChild(new AtomicCounterTest(
m_context, "invalid_offset_align", "Test layout qualifiers with invalid alignment offset.", spec));
}
{
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = 2;
spec.operations = AtomicCounterTest::OPERATION_INC;
spec.callCount = 1;
spec.useBranches = false;
spec.threadCount = 1;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_BASIC;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_INVALID_OVERLAPPING;
invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_offset_overlap",
"Test layout qualifiers with invalid overlapping offset.",
spec));
}
{
AtomicCounterTest::TestSpec spec;
spec.atomicCounterCount = 1;
spec.operations = AtomicCounterTest::OPERATION_INC;
spec.callCount = 1;
spec.useBranches = false;
spec.threadCount = 1;
spec.bindingType = AtomicCounterTest::BINDINGTYPE_BASIC;
spec.offsetType = AtomicCounterTest::OFFSETTYPE_INVALID_DEFAULT;
invalidGroup->addChild(new AtomicCounterTest(
m_context, "invalid_default_offset", "Test layout qualifiers with invalid default offset.", spec));
}
layoutGroup->addChild(invalidGroup);
}
addChild(layoutGroup);
}
}
AtomicCounterTests::~AtomicCounterTests(void)
{
}
void AtomicCounterTests::init(void)
{
}
} // namespace Functional
} // namespace gles31
} // namespace deqp