blob: fde30e8fd55170e5a4a06991fe24bb0d19d51545 [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::vector;
using std::string;
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<deUint32>& counters) const;
bool checkAndLogCallValues (TestLog& log, const vector<deUint32>& increments, const vector<deUint32>& decrements, const vector<deUint32>& preGets, const vector<deUint32>& postGets, const vector<deUint32>& gets) const;
void splitBuffer (const vector<deUint32>& buffer, vector<deUint32>& increments, vector<deUint32>& decrements, vector<deUint32>& preGets, vector<deUint32>& postGets, vector<deUint32>& gets) const;
deUint32 getInitialValue (void) const { return m_spec.callCount * m_spec.threadCount + 1; }
static string generateShaderSource (const TestSpec& spec);
static void getCountersValues (vector<deUint32>& counterValues, const vector<deUint32>& values, int ndx, int counterCount);
static bool checkRange (TestLog& log, const vector<deUint32>& values, const vector<deUint32>& min, const vector<deUint32>& max);
static bool checkUniquenessAndLinearity (TestLog& log, const vector<deUint32>& values);
static bool checkPath (const vector<deUint32>& increments, const vector<deUint32>& 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<deUint32>& 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 deUint32 value = counters[counterNdx];
const deUint32 initialValue = getInitialValue();
deUint32 expectedValue = (deUint32)-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<deUint32>& buffer, vector<deUint32>& increments, vector<deUint32>& decrements, vector<deUint32>& preGets, vector<deUint32>& postGets, vector<deUint32>& 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<deUint32>& counterValues, const vector<deUint32>& 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<deUint32>& values, const vector<deUint32>& min, const vector<deUint32>& 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] != (deUint32)-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<deUint32>& values)
{
vector<deUint32> counts;
int failedCount = 0;
deUint32 minValue = (deUint32)-1;
deUint32 maxValue = 0;
DE_ASSERT(!values.empty());
for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
{
if (values[valueNdx] != (deUint32)-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] != (deUint32)-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<deUint32>& increments, const vector<deUint32>& decrements, int initialValue, const TestSpec& spec)
{
const deUint32 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<deUint32> incrementCounts;
vector<deUint32> decrementCounts;
deUint32 minValue = 0xFFFFFFFFu;
deUint32 maxValue = 0;
for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
{
if (increments[valueNdx] != (deUint32)-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] != (deUint32)-1)
{
minValue = std::min(minValue, decrements[valueNdx]);
maxValue = std::max(maxValue, decrements[valueNdx]);
}
}
minValue = std::min(minValue, (deUint32)initialValue);
maxValue = std::max(maxValue, (deUint32)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] != (deUint32)-1)
incrementCounts[increments[valueNdx] - minValue]++;
}
for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
{
if (decrements[valueNdx] != (deUint32)-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<deUint32>& increments, const vector<deUint32>& decrements, const vector<deUint32>& preGets, const vector<deUint32>& postGets, const vector<deUint32>& gets) const
{
bool isOk = true;
for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
{
vector<deUint32> counterIncrements;
vector<deUint32> counterDecrements;
vector<deUint32> counterPreGets;
vector<deUint32> counterPostGets;
vector<deUint32> 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] != (deUint32)-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 deInt32 counterBufferSize = m_spec.atomicCounterCount * 4;
const deInt32 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<deUint32> 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<deUint32> output(ssoSize/4, 0);
vector<deUint32> 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(deUint32)), GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
deMemcpy(&(output[0]), ptr, (int)output.size() * sizeof(deUint32));
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(deUint32)), GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
deMemcpy(&(counters[0]), ptr, (int)counters.size() * sizeof(deUint32));
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<deUint32> increments;
vector<deUint32> decrements;
vector<deUint32> preGets;
vector<deUint32> postGets;
vector<deUint32> 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();
}
} // Anonymous
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)
{
}
} // Functional
} // gles31
} // deqp