T input[NUM_ELEMENTS];
T compare[NUM_ELEMENTS];
T output[NUM_ELEMENTS];
+ T invocationHitCount[NUM_ELEMENTS];
deInt32 index;
};
{
m_ptr->input[i] = static_cast<dataTypeT>(rnd.getUint64());
m_ptr->output[i] = pattern;
+ m_ptr->invocationHitCount[i] = 0;
}
m_ptr->index = 0;
" ${DATATYPE} inputValues[${N}];\n"
" ${DATATYPE} compareValues[${N}];\n"
" ${DATATYPE} outputValues[${N}];\n"
+ " ${DATATYPE} invocationHitCount[${N}];\n"
" int index;\n"
"} buf;\n");
specializations["N"] = de::toString((int)NUM_ELEMENTS);
specializations["COMPARE_ARG"] = m_atomicOp == ATOMIC_OP_COMP_SWAP ? "buf.compareValues[idx], " : "";
- const tcu::StringTemplate shaderTemplateSrc(
+ const tcu::StringTemplate nonVertexShaderTemplateSrc(
"int idx = atomicAdd(buf.index, 1);\n"
"buf.outputValues[idx] = ${ATOMICOP}(buf.inoutValues[idx % (${N}/2)], ${COMPARE_ARG}buf.inputValues[idx]);\n");
+ const tcu::StringTemplate vertexShaderTemplateSrc(
+ "int idx = gl_VertexIndex;\n"
+ "if (atomicAdd(buf.invocationHitCount[idx], 1) == 0)\n"
+ "{\n"
+ " buf.outputValues[idx] = ${ATOMICOP}(buf.inoutValues[idx % (${N}/2)], ${COMPARE_ARG}buf.inputValues[idx]);\n"
+ "}\n");
+
m_shaderSpec.outputs.push_back(Symbol("outData", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
m_shaderSpec.globalDeclarations = shaderTemplateGlobal.specialize(specializations);
- m_shaderSpec.source = shaderTemplateSrc.specialize(specializations);
m_shaderSpec.glslVersion = glu::GLSL_VERSION_450;
+ m_shaderSpec.source = m_shaderType == glu::SHADERTYPE_VERTEX ?
+ vertexShaderTemplateSrc.specialize(specializations) : nonVertexShaderTemplateSrc.specialize(specializations);
}
void addAtomicOperationTests (tcu::TestCaseGroup* atomicOperationTestsGroup)
}
{
- tcu::TestLog& log = m_context.getTestContext().getLog();
- tcu::TestStatus testResult = tcu::TestStatus::pass("Pass");
- std::vector<int> numHits (numCounters, 0); // Number of hits per counter.
- std::vector<deUint32> counterValues (numCounters);
- std::vector<std::vector<bool> > counterMasks (numCounters);
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ tcu::TestStatus testResult = tcu::TestStatus::pass("Pass");
+ std::vector<int> numHits (numCounters, 0); // Number of hits per counter.
+ std::vector<deUint32> counterValues (numCounters);
+ std::vector<std::map<deUint32, int> > resultValueHitCountMaps (numCounters);
+
for (int opNdx = 0; opNdx < numOps; opNdx++)
numHits[m_opIndices[opNdx]] += 1;
const deUint32 refCount = (deUint32)(numHits[counterNdx]*numInvocations);
const deUint32 resCount = counterValues[counterNdx];
- if (refCount != resCount)
+ bool foundInvalidCtrValue = false;
+
+ if(resCount < refCount)
+ {
+ log << tcu::TestLog::Message << "ERROR: atomic counter " << counterNdx << " has value " << resCount
+ << ", expected value greater than or equal to " << refCount
+ << tcu::TestLog::EndMessage;
+
+ foundInvalidCtrValue = true;
+ }
+ else if (refCount == 0 && resCount != 0)
{
log << tcu::TestLog::Message << "ERROR: atomic counter " << counterNdx << " has value " << resCount
<< ", expected " << refCount
<< tcu::TestLog::EndMessage;
+ foundInvalidCtrValue = true;
+ }
+
+ if (foundInvalidCtrValue == true)
+ {
if (testResult.getCode() == QP_TEST_RESULT_PASS)
testResult = tcu::TestStatus::fail("Invalid atomic counter value");
}
}
- // Allocate bitmasks - one bit per each valid result value
- for (int counterNdx = 0; counterNdx < numCounters; counterNdx++)
- {
- const int counterValue = numHits[counterNdx]*numInvocations;
- counterMasks[counterNdx].resize(counterValue, false);
- }
-
// Verify result values from shaders
for (int invocationNdx = 0; invocationNdx < numInvocations; invocationNdx++)
{
{
const int counterNdx = m_opIndices[opNdx];
const deUint32 resValue = outValues[opNdx*numInvocations + invocationNdx];
- const bool rangeOk = de::inBounds(resValue, 0u, (deUint32)counterMasks[counterNdx].size());
- const bool notSeen = rangeOk && !counterMasks[counterNdx][resValue];
- const bool isOk = rangeOk && notSeen;
+ const bool rangeOk = de::inBounds(resValue, 0u, counterValues[counterNdx]);
+
+ if (resultValueHitCountMaps[counterNdx].count(resValue) == 0)
+ resultValueHitCountMaps[counterNdx][resValue] = 1;
+ else
+ resultValueHitCountMaps[counterNdx][resValue] += 1;
- if (!isOk)
+ if (!rangeOk)
{
log << tcu::TestLog::Message << "ERROR: at invocation " << invocationNdx
<< ", op " << opNdx << ": got invalid result value "
if (testResult.getCode() == QP_TEST_RESULT_PASS)
testResult = tcu::TestStatus::fail("Invalid result value");
}
- else
- {
- // Mark as used - no other invocation should see this value from same counter.
- counterMasks[counterNdx][resValue] = true;
- }
}
}
- if (testResult.getCode() == QP_TEST_RESULT_PASS)
+ for (int ctrIdx = 0; ctrIdx < numCounters; ctrIdx++)
{
- // Consistency check - all masks should be 1 now
- for (int counterNdx = 0; counterNdx < numCounters; counterNdx++)
+ std::map<deUint32, int>::iterator hitCountItr;
+ for (hitCountItr = resultValueHitCountMaps[ctrIdx].begin(); hitCountItr != resultValueHitCountMaps[ctrIdx].end(); hitCountItr++)
{
- for (std::vector<bool>::const_iterator i = counterMasks[counterNdx].begin(); i != counterMasks[counterNdx].end(); i++)
- TCU_CHECK_INTERNAL(*i);
+ if(hitCountItr->second > 1)
+ {
+ log << tcu::TestLog::Message << "ERROR: Duplicate result value from counter " << ctrIdx << "."
+ <<" Value " << hitCountItr->first << " found " << hitCountItr->second << " times."
+ << tcu::TestLog::EndMessage;
+
+ if (testResult.getCode() == QP_TEST_RESULT_PASS)
+ testResult = tcu::TestStatus::fail("Invalid result value");
+ }
}
}