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