ES31: Add atomic memory functions for SSBO
Due to SSBO is translated to RWByteAddressBuffer in HLSL, the corresponding
atomic memory functions atomic* will be translated to
RWByteAddressBuffer.Interlocked*. The translation is like below:
atomicAdd(instanceName.data[0], 5u);
// becomes
uint _ssbo_atomicAdd_uint(RWByteAddressBuffer buffer, uint loc, uint value)
{
uint original_value;
buffer.InterlockedAdd(loc, value, original_value);
return original_value;
}
_ssbo_atomicAdd_uint(_instanceName, 0 + 16 * 0, 5);
Bug: angleproject:1951
Change-Id: If2af8bedb67a4135b443d2512d43c6058a78888d
Reviewed-on: https://chromium-review.googlesource.com/c/1370676
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jiajia Qin <jiajia.qin@intel.com>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index aa81c11..d660b9a 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -110,10 +110,20 @@
}
}
-bool IsAtomicFunctionDirectAssign(const TIntermBinary &node)
+bool IsAtomicFunctionForSharedVariableDirectAssign(const TIntermBinary &node)
{
- return node.getOp() == EOpAssign && node.getRight()->getAsAggregate() &&
- IsAtomicFunction(node.getRight()->getAsAggregate()->getOp());
+ TIntermAggregate *aggregateNode = node.getRight()->getAsAggregate();
+ if (aggregateNode == nullptr)
+ {
+ return false;
+ }
+
+ if (node.getOp() == EOpAssign && IsAtomicFunction(aggregateNode->getOp()))
+ {
+ return !IsInShaderStorageBlock((*aggregateNode->getSequence())[0]->getAsTyped());
+ }
+
+ return false;
}
const char *kZeros = "_ANGLE_ZEROS_";
@@ -1271,7 +1281,7 @@
// function calls in HLSL.
// e.g. original_value = atomicAdd(dest, value) should be translated into
// InterlockedAdd(dest, value, original_value);
- else if (IsAtomicFunctionDirectAssign(*node))
+ else if (IsAtomicFunctionForSharedVariableDirectAssign(*node))
{
TIntermAggregate *atomicFunctionNode = node->getRight()->getAsAggregate();
TOperator atomicFunctionOp = atomicFunctionNode->getOp();
@@ -2400,20 +2410,60 @@
case EOpAtomicAnd:
case EOpAtomicOr:
case EOpAtomicXor:
- outputTriplet(out, visit, GetHLSLAtomicFunctionStringAndLeftParenthesis(node->getOp()),
- ",", ")");
- break;
-
- // The parameter 'original_value' of InterlockedExchange(dest, value, original_value) and
- // InterlockedCompareExchange(dest, compare_value, value, original_value) is not optional.
+ // The parameter 'original_value' of InterlockedExchange(dest, value, original_value)
+ // and InterlockedCompareExchange(dest, compare_value, value, original_value) is not
+ // optional.
// https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedexchange
// https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedcompareexchange
- // So all the call of atomicExchange(dest, value) and atomicCompSwap(dest, compare_value,
- // value) should all be modified into the form of "int temp; temp = atomicExchange(dest,
- // value);" and "int temp; temp = atomicCompSwap(dest, compare_value, value);" in the
- // intermediate tree before traversing outputHLSL.
+ // So all the call of atomicExchange(dest, value) and atomicCompSwap(dest,
+ // compare_value, value) should all be modified into the form of "int temp; temp =
+ // atomicExchange(dest, value);" and "int temp; temp = atomicCompSwap(dest,
+ // compare_value, value);" in the intermediate tree before traversing outputHLSL.
case EOpAtomicExchange:
case EOpAtomicCompSwap:
+ {
+ ASSERT(node->getChildCount() > 1);
+ TIntermTyped *memNode = (*node->getSequence())[0]->getAsTyped();
+ if (IsInShaderStorageBlock(memNode))
+ {
+ // Atomic memory functions for SSBO.
+ // "_ssbo_atomicXXX_TYPE(RWByteAddressBuffer buffer, uint loc" is written to |out|.
+ mSSBOOutputHLSL->outputAtomicMemoryFunctionCallPrefix(memNode, node->getOp());
+ // Write the rest argument list to |out|.
+ for (size_t i = 1; i < node->getChildCount(); i++)
+ {
+ out << ", ";
+ TIntermTyped *argument = (*node->getSequence())[i]->getAsTyped();
+ if (IsInShaderStorageBlock(argument))
+ {
+ mSSBOOutputHLSL->outputLoadFunctionCall(argument);
+ }
+ else
+ {
+ argument->traverse(this);
+ }
+ }
+
+ out << ")";
+ return false;
+ }
+ else
+ {
+ // Atomic memory functions for shared variable.
+ if (node->getOp() != EOpAtomicExchange && node->getOp() != EOpAtomicCompSwap)
+ {
+ outputTriplet(out, visit,
+ GetHLSLAtomicFunctionStringAndLeftParenthesis(node->getOp()), ",",
+ ")");
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+ }
+
+ break;
+ }
default:
UNREACHABLE();
}