You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1547 lines
46 KiB
1547 lines
46 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|