Add SPIR-V tests for return values of atomic functions
authorSlawomir Cygan <slawomir.cygan@intel.com>
Wed, 21 Mar 2018 16:32:50 +0000 (17:32 +0100)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Tue, 15 May 2018 09:50:12 +0000 (05:50 -0400)
Most of atomic functions return values before applying
atomic operation - this change adds tests for that.

Return values are captured to separate buffer and
matched to the final result (and arguments).

Components: Vulkan

VK-GL-CTS Issue: 1092

New Tests:
dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.*
Affects:
dEQP-VK.spirv_assembly.instruction.compute.opatomic*

Change-Id: I96390ed1b694f0aa2bd6d92868da46fef3d5e249

android/cts/master/vk-master.txt
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
external/vulkancts/mustpass/1.1.2/vk-default-no-waivers.txt
external/vulkancts/mustpass/1.1.2/vk-default.txt

index 0118846..570f162 100755 (executable)
@@ -208275,6 +208275,11 @@ dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.idec
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.load
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.store
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.compex
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iadd
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.isub
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iinc
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.idec
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.compex
 dEQP-VK.spirv_assembly.instruction.compute.opline.all
 dEQP-VK.spirv_assembly.instruction.compute.opmoduleprocessed.all
 dEQP-VK.spirv_assembly.instruction.compute.opnoline.all
index f474ac5..a883cfb 100644 (file)
@@ -29,6 +29,7 @@
 #include "deSharedPtr.hpp"
 #include "tcuTestLog.hpp"
 #include "tcuVector.hpp"
+#include "tcuTestLog.hpp"
 #include "vkMemUtil.hpp"
 #include "vktSpvAsmUtils.hpp"
 
@@ -60,6 +61,7 @@ enum BufferType
 {
        BUFFERTYPE_INPUT = 0,
        BUFFERTYPE_EXPECTED,
+       BUFFERTYPE_ATOMIC_RET,
 
        BUFFERTYPE_LAST
 };
@@ -138,6 +140,17 @@ public:
                                }
                        }
                }
+               else if (m_type == BUFFERTYPE_ATOMIC_RET)
+               {
+                       bytes.resize(m_numInputElements * sizeof(deInt32), 0xff);
+
+                       if (m_opAtomic == OPATOMIC_COMPEX)
+                       {
+                               deInt32* const bytesAsInt = reinterpret_cast<deInt32* const>(&bytes.front());
+                               for (size_t ndx = 0; ndx < m_numInputElements; ndx++)
+                                       bytesAsInt[ndx] = inputInts[ndx] % 2;
+                       }
+               }
                else
                        DE_FATAL("Unknown buffer type");
        }
@@ -146,6 +159,7 @@ public:
        {
                switch (m_type)
                {
+                       case BUFFERTYPE_ATOMIC_RET:
                        case BUFFERTYPE_INPUT:
                                return m_numInputElements * sizeof(deInt32);
                        case BUFFERTYPE_EXPECTED:
@@ -156,6 +170,98 @@ public:
                }
        }
 
+       template <int OpAtomic>
+       static bool compareWithRetvals (const std::vector<BufferSp>& inputs, const std::vector<AllocationSp>& outputAllocs, const std::vector<BufferSp>& expectedOutputs, tcu::TestLog& log)
+       {
+               if (outputAllocs.size() != 2 || inputs.size() != 1)
+                       DE_FATAL("Wrong number of buffers to compare");
+
+               for (size_t i = 0; i < outputAllocs.size(); ++i)
+               {
+                       const deUint32* values = reinterpret_cast<deUint32*>(outputAllocs[i]->getHostPtr());
+
+                       if (i == 1 && OpAtomic != OPATOMIC_COMPEX)
+                       {
+                               // BUFFERTYPE_ATOMIC_RET for arithmetic operations must be verified manually by matching return values to inputs
+                               std::vector<deUint8>    inputBytes;
+                               inputs[0]->getBytes(inputBytes);
+
+                               const deUint32*                 inputValues                     = reinterpret_cast<deUint32*>(&inputBytes.front());
+                               const size_t                    inputValuesCount        = inputBytes.size() / sizeof(deUint32);
+
+                               // result of all atomic operations
+                               const deUint32                  resultValue                     = *reinterpret_cast<deUint32*>(outputAllocs[0]->getHostPtr());
+
+                               if (!compareRetVals<OpAtomic>(inputValues, inputValuesCount, resultValue, values))
+                               {
+                                       log << tcu::TestLog::Message << "Wrong contents of buffer with return values after atomic operation." << tcu::TestLog::EndMessage;
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+                               const BufferSp&                 expectedOutput = expectedOutputs[i];
+                               std::vector<deUint8>    expectedBytes;
+
+                               expectedOutput->getBytes(expectedBytes);
+
+                               if (deMemCmp(&expectedBytes.front(), values, expectedBytes.size()))
+                               {
+                                       log << tcu::TestLog::Message << "Wrong contents of buffer after atomic operation" << tcu::TestLog::EndMessage;
+                                       return false;
+                               }
+                       }
+               }
+               return true;
+       }
+
+       template <int OpAtomic>
+       static bool compareRetVals (const deUint32* inputValues, const size_t inputValuesCount, const deUint32 resultValue, const deUint32* returnValues)
+       {
+               // as the order of execution is undefined, validation of return values for atomic operations is tricky:
+               // each inputValue stands for one atomic operation. Iterate through all of
+               // done operations in time, each time finding one matching current result and un-doing it.
+
+               std::vector<bool>               operationsUndone (inputValuesCount, false);
+               deUint32                                currentResult    = resultValue;
+
+               for (size_t operationUndone = 0; operationUndone < inputValuesCount; ++operationUndone)
+               {
+                       // find which of operations was done at this moment
+                       size_t ndx;
+                       for (ndx = 0; ndx < inputValuesCount; ++ndx)
+                       {
+                               if (operationsUndone[ndx]) continue;
+
+                               deUint32 previousResult = currentResult;
+
+                               switch (OpAtomic)
+                               {
+                                       // operations are undone here, so the actual opeation is reversed
+                                       case OPATOMIC_IADD:             previousResult -= inputValues[ndx];                                             break;
+                                       case OPATOMIC_ISUB:             previousResult += inputValues[ndx];                                             break;
+                                       case OPATOMIC_IINC:             previousResult--;                                                                               break;
+                                       case OPATOMIC_IDEC:             previousResult++;                                                                               break;
+                                       default:                                DE_FATAL("Unsupported OpAtomic type for return value compare");
+                               }
+
+                               if (previousResult == returnValues[ndx])
+                               {
+                                       // found matching operation
+                                       currentResult                   = returnValues[ndx];
+                                       operationsUndone[ndx]   = true;
+                                       break;
+                               }
+                       }
+                       if (ndx == inputValuesCount)
+                       {
+                               // no operation matches the current result value
+                               return false;
+                       }
+               }
+               return true;
+       }
+
 private:
        const deUint32          m_numInputElements;
        const deUint32          m_numOutputElements;
index fa647dd..364a261 100644 (file)
@@ -530,22 +530,26 @@ struct OpAtomicCase
 {
        const char*             name;
        const char*             assembly;
+       const char*             retValAssembly;
        OpAtomicType    opAtomic;
        deInt32                 numOutputElements;
 
-                                       OpAtomicCase                    (const char* _name, const char* _assembly, OpAtomicType _opAtomic, deInt32 _numOutputElements)
+                                       OpAtomicCase(const char* _name, const char* _assembly, const char* _retValAssembly, OpAtomicType _opAtomic, deInt32 _numOutputElements)
                                                : name                          (_name)
                                                , assembly                      (_assembly)
+                                               , retValAssembly        (_retValAssembly)
                                                , opAtomic                      (_opAtomic)
                                                , numOutputElements     (_numOutputElements) {}
 };
 
-tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStorageBuffer)
+tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStorageBuffer, int numElements = 65535, bool verifyReturnValues = false)
 {
-       de::MovePtr<tcu::TestCaseGroup> group                           (new tcu::TestCaseGroup(testCtx,
-                                                                                                                                                               useStorageBuffer ? "opatomic_storage_buffer" : "opatomic",
-                                                                                                                                                               "Test the OpAtomic* opcodes"));
-       const int                                               numElements                     = 65535;
+       std::string                                             groupName                       ("opatomic");
+       if (useStorageBuffer)
+               groupName += "_storage_buffer";
+       if (verifyReturnValues)
+               groupName += "_return_values";
+       de::MovePtr<tcu::TestCaseGroup> group                           (new tcu::TestCaseGroup(testCtx, groupName.c_str(), "Test the OpAtomic* opcodes"));
        vector<OpAtomicCase>                    cases;
 
        const StringTemplate                    shaderTemplate  (
@@ -574,6 +578,8 @@ tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStor
                "OpMemberDecorate %sumbuf 0 Coherent\n"
                "OpMemberDecorate %sumbuf 0 Offset 0\n"
 
+               "${RETVAL_BUF_DECORATE}"
+
                + getComputeAsmCommonTypes("${BLOCK_POINTER_TYPE}") +
 
                "%buf       = OpTypeStruct %i32arr\n"
@@ -584,6 +590,8 @@ tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStor
                "%sumbufptr = OpTypePointer ${BLOCK_POINTER_TYPE} %sumbuf\n"
                "%sum       = OpVariable %sumbufptr ${BLOCK_POINTER_TYPE}\n"
 
+               "${RETVAL_BUF_DECL}"
+
                "%id        = OpVariable %uvec3ptr Input\n"
                "%minusone  = OpConstant %i32 -1\n"
                "%zero      = OpConstant %i32 0\n"
@@ -600,28 +608,39 @@ tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStor
 
                "%outloc    = OpAccessChain %i32ptr %sum %zero ${INDEX}\n"
                "${INSTRUCTION}"
+               "${RETVAL_ASSEMBLY}"
 
                "             OpReturn\n"
                "             OpFunctionEnd\n");
 
-       #define ADD_OPATOMIC_CASE(NAME, ASSEMBLY, OPATOMIC, NUM_OUTPUT_ELEMENTS) \
+       #define ADD_OPATOMIC_CASE(NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC, NUM_OUTPUT_ELEMENTS) \
        do { \
-               DE_STATIC_ASSERT((NUM_OUTPUT_ELEMENTS) == 1 || (NUM_OUTPUT_ELEMENTS) == numElements); \
-               cases.push_back(OpAtomicCase(#NAME, ASSEMBLY, OPATOMIC, NUM_OUTPUT_ELEMENTS)); \
+               DE_ASSERT((NUM_OUTPUT_ELEMENTS) == 1 || (NUM_OUTPUT_ELEMENTS) == numElements); \
+               cases.push_back(OpAtomicCase(#NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC, NUM_OUTPUT_ELEMENTS)); \
        } while (deGetFalse())
-       #define ADD_OPATOMIC_CASE_1(NAME, ASSEMBLY, OPATOMIC) ADD_OPATOMIC_CASE(NAME, ASSEMBLY, OPATOMIC, 1)
-       #define ADD_OPATOMIC_CASE_N(NAME, ASSEMBLY, OPATOMIC) ADD_OPATOMIC_CASE(NAME, ASSEMBLY, OPATOMIC, numElements)
-
-       ADD_OPATOMIC_CASE_1(iadd,       "%unused    = OpAtomicIAdd %i32 %outloc %one %zero %inval\n", OPATOMIC_IADD );
-       ADD_OPATOMIC_CASE_1(isub,       "%unused    = OpAtomicISub %i32 %outloc %one %zero %inval\n", OPATOMIC_ISUB );
-       ADD_OPATOMIC_CASE_1(iinc,       "%unused    = OpAtomicIIncrement %i32 %outloc %one %zero\n",  OPATOMIC_IINC );
-       ADD_OPATOMIC_CASE_1(idec,       "%unused    = OpAtomicIDecrement %i32 %outloc %one %zero\n",  OPATOMIC_IDEC );
-       ADD_OPATOMIC_CASE_N(load,       "%inval2    = OpAtomicLoad %i32 %inloc %one %zero\n"
-                                                               "             OpStore %outloc %inval2\n",  OPATOMIC_LOAD );
-       ADD_OPATOMIC_CASE_N(store,      "             OpAtomicStore %outloc %one %zero %inval\n",  OPATOMIC_STORE );
+       #define ADD_OPATOMIC_CASE_1(NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC) ADD_OPATOMIC_CASE(NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC, 1)
+       #define ADD_OPATOMIC_CASE_N(NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC) ADD_OPATOMIC_CASE(NAME, ASSEMBLY, RETVAL_ASSEMBLY, OPATOMIC, numElements)
+
+       ADD_OPATOMIC_CASE_1(iadd,       "%retv      = OpAtomicIAdd %i32 %outloc %one %zero %inval\n",
+                                                               "             OpStore %retloc %retv\n", OPATOMIC_IADD );
+       ADD_OPATOMIC_CASE_1(isub,       "%retv      = OpAtomicISub %i32 %outloc %one %zero %inval\n",
+                                                               "             OpStore %retloc %retv\n", OPATOMIC_ISUB );
+       ADD_OPATOMIC_CASE_1(iinc,       "%retv      = OpAtomicIIncrement %i32 %outloc %one %zero\n",
+                                                               "             OpStore %retloc %retv\n", OPATOMIC_IINC );
+       ADD_OPATOMIC_CASE_1(idec,       "%retv      = OpAtomicIDecrement %i32 %outloc %one %zero\n",
+                                                               "             OpStore %retloc %retv\n", OPATOMIC_IDEC );
+       if (!verifyReturnValues)
+       {
+               ADD_OPATOMIC_CASE_N(load,       "%inval2    = OpAtomicLoad %i32 %inloc %one %zero\n"
+                                                                       "             OpStore %outloc %inval2\n", "", OPATOMIC_LOAD );
+               ADD_OPATOMIC_CASE_N(store,      "             OpAtomicStore %outloc %one %zero %inval\n", "", OPATOMIC_STORE );
+       }
+
        ADD_OPATOMIC_CASE_N(compex, "%even      = OpSMod %i32 %inval %two\n"
                                                                "             OpStore %outloc %even\n"
-                                                               "%unused    = OpAtomicCompareExchange %i32 %outloc %one %zero %zero %minusone %zero\n",  OPATOMIC_COMPEX );
+                                                               "%retv      = OpAtomicCompareExchange %i32 %outloc %one %zero %zero %minusone %zero\n",
+                                                               "                         OpStore %retloc %retv\n", OPATOMIC_COMPEX );
+
 
        #undef ADD_OPATOMIC_CASE
        #undef ADD_OPATOMIC_CASE_1
@@ -638,6 +657,36 @@ tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStor
                specializations["INSTRUCTION"]                  = cases[caseNdx].assembly;
                specializations["BLOCK_DECORATION"]             = useStorageBuffer ? "Block" : "BufferBlock";
                specializations["BLOCK_POINTER_TYPE"]   = useStorageBuffer ? "StorageBuffer" : "Uniform";
+
+               if (verifyReturnValues)
+               {
+                       const StringTemplate blockDecoration    (
+                               "\n"
+                               "OpDecorate %retbuf ${BLOCK_DECORATION}\n"
+                               "OpDecorate %ret DescriptorSet 0\n"
+                               "OpDecorate %ret Binding 2\n"
+                               "OpMemberDecorate %retbuf 0 Offset 0\n\n");
+
+                       const StringTemplate blockDeclaration   (
+                               "\n"
+                               "%retbuf    = OpTypeStruct %i32arr\n"
+                               "%retbufptr = OpTypePointer ${BLOCK_POINTER_TYPE} %retbuf\n"
+                               "%ret       = OpVariable %retbufptr ${BLOCK_POINTER_TYPE}\n\n");
+
+                       specializations["RETVAL_ASSEMBLY"] =
+                               "%retloc    = OpAccessChain %i32ptr %ret %zero %x\n"
+                               + std::string(cases[caseNdx].retValAssembly);
+
+                       specializations["RETVAL_BUF_DECORATE"]  = blockDecoration.specialize(specializations);
+                       specializations["RETVAL_BUF_DECL"]              = blockDeclaration.specialize(specializations);
+               }
+               else
+               {
+                       specializations["RETVAL_ASSEMBLY"]              = "";
+                       specializations["RETVAL_BUF_DECORATE"]  = "";
+                       specializations["RETVAL_BUF_DECL"]              = "";
+               }
+
                spec.assembly                                                   = shaderTemplate.specialize(specializations);
 
                if (useStorageBuffer)
@@ -645,7 +694,33 @@ tcu::TestCaseGroup* createOpAtomicGroup (tcu::TestContext& testCtx, bool useStor
 
                spec.inputs.push_back(BufferSp(new OpAtomicBuffer(numElements, cases[caseNdx].numOutputElements, cases[caseNdx].opAtomic, BUFFERTYPE_INPUT)));
                spec.outputs.push_back(BufferSp(new OpAtomicBuffer(numElements, cases[caseNdx].numOutputElements, cases[caseNdx].opAtomic, BUFFERTYPE_EXPECTED)));
+               if (verifyReturnValues)
+                       spec.outputs.push_back(BufferSp(new OpAtomicBuffer(numElements, cases[caseNdx].numOutputElements, cases[caseNdx].opAtomic, BUFFERTYPE_ATOMIC_RET)));
                spec.numWorkGroups = IVec3(numElements, 1, 1);
+
+               if (verifyReturnValues)
+               {
+                       switch (cases[caseNdx].opAtomic)
+                       {
+                               case OPATOMIC_IADD:
+                                       spec.verifyIO = OpAtomicBuffer::compareWithRetvals<OPATOMIC_IADD>;
+                                       break;
+                               case OPATOMIC_ISUB:
+                                       spec.verifyIO = OpAtomicBuffer::compareWithRetvals<OPATOMIC_ISUB>;
+                                       break;
+                               case OPATOMIC_IINC:
+                                       spec.verifyIO = OpAtomicBuffer::compareWithRetvals<OPATOMIC_IINC>;
+                                       break;
+                               case OPATOMIC_IDEC:
+                                       spec.verifyIO = OpAtomicBuffer::compareWithRetvals<OPATOMIC_IDEC>;
+                                       break;
+                               case OPATOMIC_COMPEX:
+                                       spec.verifyIO = OpAtomicBuffer::compareWithRetvals<OPATOMIC_COMPEX>;
+                                       break;
+                               default:
+                                       DE_FATAL("Unsupported OpAtomic type for return value verification");
+                       }
+               }
                group->addChild(new SpvAsmComputeShaderCase(testCtx, cases[caseNdx].name, cases[caseNdx].name, spec));
        }
 
@@ -9313,7 +9388,8 @@ tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
        computeTests->addChild(createOpNopGroup(testCtx));
        computeTests->addChild(createOpFUnordGroup(testCtx));
        computeTests->addChild(createOpAtomicGroup(testCtx, false));
-       computeTests->addChild(createOpAtomicGroup(testCtx, true)); // Using new StorageBuffer decoration
+       computeTests->addChild(createOpAtomicGroup(testCtx, true));                                     // Using new StorageBuffer decoration
+       computeTests->addChild(createOpAtomicGroup(testCtx, false, 1024, true));        // Return value validation
        computeTests->addChild(createOpLineGroup(testCtx));
        computeTests->addChild(createOpModuleProcessedGroup(testCtx));
        computeTests->addChild(createOpNoLineGroup(testCtx));
index 6a454ff..4a04776 100644 (file)
@@ -208280,6 +208280,11 @@ dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.idec
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.load
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.store
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.compex
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iadd
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.isub
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iinc
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.idec
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.compex
 dEQP-VK.spirv_assembly.instruction.compute.opline.all
 dEQP-VK.spirv_assembly.instruction.compute.opmoduleprocessed.all
 dEQP-VK.spirv_assembly.instruction.compute.opnoline.all
index eb94aa1..43ddcc2 100644 (file)
@@ -208280,6 +208280,11 @@ dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.idec
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.load
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.store
 dEQP-VK.spirv_assembly.instruction.compute.opatomic_storage_buffer.compex
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iadd
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.isub
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.iinc
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.idec
+dEQP-VK.spirv_assembly.instruction.compute.opatomic_return_values.compex
 dEQP-VK.spirv_assembly.instruction.compute.opline.all
 dEQP-VK.spirv_assembly.instruction.compute.opmoduleprocessed.all
 dEQP-VK.spirv_assembly.instruction.compute.opnoline.all