D3D11: fix for atomic assigned to an SSBO.

When assigning the previous value of an atomic op to an SSBO, we do not
want to use the "direct assignment" path, since we can't pass the SSBO
expression as an argument to Interlocked*().

Instead, we change the RewriteAtomicFunctionExpressions transform not to
defer assignments until HLSL output if the LHS is an SSBO expression,
and to do its usual creation of a temporary for the previous value of
the atomic op.

In OutputHLSL, we skip the direct assignment path if the LHS is an SSBO
expression.

Bug: angleproject:8182
Change-Id: I0707f4f69757119fe5c8f8e7a12bd26025ec74e6
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4573827
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Stephen White 2023-05-31 17:02:22 -04:00 коммит произвёл Angle LUCI CQ
Родитель 721c15efaa
Коммит e60f64ddb9
3 изменённых файлов: 59 добавлений и 2 удалений

Просмотреть файл

@ -150,7 +150,8 @@ bool IsAtomicFunctionForSharedVariableDirectAssign(const TIntermBinary &node)
if (node.getOp() == EOpAssign && BuiltInGroup::IsAtomicMemory(aggregateNode->getOp()))
{
return !IsInShaderStorageBlock((*aggregateNode->getSequence())[0]->getAsTyped());
return !IsInShaderStorageBlock((*aggregateNode->getSequence())[0]->getAsTyped()) &&
!IsInShaderStorageBlock(node.getLeft());
}
return false;

Просмотреть файл

@ -133,7 +133,8 @@ bool RewriteAtomicFunctionExpressionsTraverser::IsAtomicFunctionInsideExpression
auto *parentAsBinary = parentNode->getAsBinaryNode();
// Assignments are handled in OutputHLSL
return !parentAsBinary || parentAsBinary->getOp() != EOpAssign;
return !parentAsBinary || (parentAsBinary->getOp() != EOpAssign ||
IsInShaderStorageBlock(parentAsBinary->getLeft()));
}
bool RewriteAtomicFunctionExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)

Просмотреть файл

@ -5335,6 +5335,61 @@ TEST_P(ComputeShaderTest, SSBOAliasOverWrite)
EXPECT_EQ(false, error);
}
// Performs an atomic operation and assigns the previous value to an SSBO.
TEST_P(ComputeShaderTest, AtomicOpPreviousValueAssignedToSSBO)
{
constexpr char kCSSource[] = R"(#version 310 es
shared int wg;
layout(binding = 0, std430) buffer Storage0 {
int inner[16];
} buf;
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
wg = 0;
atomicExchange(wg, 0);
barrier();
buf.inner[gl_WorkGroupID.x] = atomicOr(wg, 1);
})";
const int dispatchSize = 16;
// define compute shader output buffer
const int outputBufferSizeInBytes = dispatchSize * sizeof(int32_t);
const int outputBufferElementsCount = dispatchSize;
std::vector<int32_t> minusOnes(outputBufferElementsCount, -1);
GLBuffer resultBuffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, resultBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, outputBufferSizeInBytes, &minusOnes[0], GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource);
glUseProgram(csProgram);
ASSERT_GL_NO_ERROR();
// Bind storage buffer to compute shader binding locations
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, resultBuffer);
glDispatchCompute(dispatchSize, 1, 1);
ASSERT_GL_NO_ERROR();
// verify the result
glBindBuffer(GL_SHADER_STORAGE_BUFFER, resultBuffer);
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
void *mappedResults =
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, outputBufferSizeInBytes, GL_MAP_READ_BIT);
std::vector<int32_t> results(outputBufferElementsCount);
memcpy(results.data(), mappedResults, outputBufferSizeInBytes);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
ASSERT_GL_NO_ERROR();
for (int index = 0; index < static_cast<int>(results.size()); ++index)
{
EXPECT_EQ(results[index], 0);
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ComputeShaderTest);
ANGLE_INSTANTIATE_TEST_ES31(ComputeShaderTest);