Commit a3106c58 by Jiajia Qin Committed by Commit Bot

ES31: Add atomic memory functions

BUG=angleproject:1442 TEST=angle_unittests, angle_end2end_tests dEQP-GLES31.functional.compute.shared_var.atomic* dEQP-GLES31.functional.compute.basic.shared_atomic_op* dEQP-GLES31.functional.ssbo.atomic.* Change-Id: I82b54fde3a852d3bd917b1e19680baa1c28fce4d Reviewed-on: https://chromium-review.googlesource.com/765061 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 39644fee
...@@ -635,6 +635,26 @@ void InsertBuiltInFunctions(sh::GLenum type, ...@@ -635,6 +635,26 @@ void InsertBuiltInFunctions(sh::GLenum type,
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterIncrement", atomicCounter); symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterIncrement", atomicCounter);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterDecrement", atomicCounter); symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterDecrement", atomicCounter);
// Insert all atomic memory functions
const TType *int1InOut = TCache::getType(EbtInt, EvqInOut);
const TType *uint1InOut = TCache::getType(EbtUInt, EvqInOut);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAdd", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAdd", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMin", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMin", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicMax", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicMax", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicAnd", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicAnd", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicOr", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicOr", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicXor", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicXor", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicExchange", uint1InOut, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicExchange", int1InOut, int1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCompSwap", uint1InOut, uint1, uint1);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int1, "atomicCompSwap", int1InOut, int1, int1);
const TType *gimage2D = TCache::getType(EbtGImage2D); const TType *gimage2D = TCache::getType(EbtGImage2D);
const TType *gimage3D = TCache::getType(EbtGImage3D); const TType *gimage3D = TCache::getType(EbtGImage3D);
const TType *gimage2DArray = TCache::getType(EbtGImage2DArray); const TType *gimage2DArray = TCache::getType(EbtGImage2DArray);
......
...@@ -32,6 +32,22 @@ namespace ...@@ -32,6 +32,22 @@ namespace
const int kWebGLMaxStructNesting = 4; const int kWebGLMaxStructNesting = 4;
const std::array<const char *, 8> kAtomicBuiltin = {{"atomicAdd", "atomicMin", "atomicMax",
"atomicAnd", "atomicOr", "atomicXor",
"atomicExchange", "atomicCompSwap"}};
bool IsAtomicBuiltin(const TString &name)
{
for (size_t i = 0; i < kAtomicBuiltin.size(); ++i)
{
if (name.compare(kAtomicBuiltin[i]) == 0)
{
return true;
}
}
return false;
}
bool ContainsSampler(const TStructure *structType); bool ContainsSampler(const TStructure *structType);
bool ContainsSampler(const TType &type) bool ContainsSampler(const TType &type)
...@@ -116,6 +132,16 @@ GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType) ...@@ -116,6 +132,16 @@ GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType)
} }
} }
bool IsBufferOrSharedVariable(TIntermTyped *var)
{
if (var->isInterfaceBlock() || var->getQualifier() == EvqBuffer ||
var->getQualifier() == EvqShared)
{
return true;
}
return false;
}
} // namespace } // namespace
// This tracks each binding point's current default offset for inheritance of subsequent // This tracks each binding point's current default offset for inheritance of subsequent
...@@ -5592,6 +5618,35 @@ void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall) ...@@ -5592,6 +5618,35 @@ void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
} }
} }
void TParseContext::checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall)
{
const TString &name = functionCall->getFunctionSymbolInfo()->getName();
if (IsAtomicBuiltin(name))
{
TIntermSequence *arguments = functionCall->getSequence();
TIntermTyped *memNode = (*arguments)[0]->getAsTyped();
if (IsBufferOrSharedVariable(memNode))
{
return;
}
while (memNode->getAsBinaryNode())
{
memNode = memNode->getAsBinaryNode()->getLeft();
if (IsBufferOrSharedVariable(memNode))
{
return;
}
}
error(memNode->getLine(),
"The value passed to the mem argument of an atomic memory function does not "
"correspond to a buffer or shared variable.",
functionCall->getFunctionSymbolInfo()->getName().c_str());
}
}
// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall) void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
{ {
...@@ -5828,6 +5883,7 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunction *fnCall, ...@@ -5828,6 +5883,7 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunction *fnCall,
checkTextureOffsetConst(callNode); checkTextureOffsetConst(callNode);
checkTextureGather(callNode); checkTextureGather(callNode);
checkImageMemoryAccessForBuiltinFunctions(callNode); checkImageMemoryAccessForBuiltinFunctions(callNode);
checkAtomicMemoryBuiltinFunctions(callNode);
} }
else else
{ {
......
...@@ -417,6 +417,7 @@ class TParseContext : angle::NonCopyable ...@@ -417,6 +417,7 @@ class TParseContext : angle::NonCopyable
void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall); void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition, void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
const TIntermAggregate *functionCall); const TIntermAggregate *functionCall);
void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
TIntermSequence *createEmptyArgumentsList(); TIntermSequence *createEmptyArgumentsList();
// fnCall is only storing the built-in op, and function name or constructor type. arguments // fnCall is only storing the built-in op, and function name or constructor type. arguments
......
...@@ -5325,3 +5325,232 @@ TEST_F(FragmentShaderValidationTest, UnsizedConstArray) ...@@ -5325,3 +5325,232 @@ TEST_F(FragmentShaderValidationTest, UnsizedConstArray)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
// Test that the value passed to the mem argument of an atomic memory function can be a shared
// variable.
TEST_F(ComputeShaderValidationTest, AtomicAddWithSharedVariable)
{
const std::string &shaderString =
R"(#version 310 es
layout(local_size_x = 5) in;
shared uint myShared;
void main() {
atomicAdd(myShared, 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass an element of an array to the mem argument of an atomic memory
// function, as long as the underlying array is a buffer or shared variable.
TEST_F(ComputeShaderValidationTest, AtomicAddWithSharedVariableArray)
{
const std::string &shaderString =
R"(#version 310 es
layout(local_size_x = 5) in;
shared uint myShared[2];
void main() {
atomicAdd(myShared[0], 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass a single component of a vector to the mem argument of an
// atomic memory function, as long as the underlying vector is a buffer or shared variable.
TEST_F(ComputeShaderValidationTest, AtomicAddWithSharedVariableVector)
{
const std::string &shaderString =
R"(#version 310 es
layout(local_size_x = 5) in;
shared uvec4 myShared;
void main() {
atomicAdd(myShared[0], 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that the value passed to the mem argument of an atomic memory function can be a buffer
// variable.
TEST_F(FragmentShaderValidationTest, AtomicAddWithBufferVariable)
{
const std::string &shaderString =
R"(#version 310 es
layout(std140) buffer bufferName1{
uint u1;
};
void main()
{
atomicAdd(u1, 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass an element of an array to the mem argument of an atomic memory
// function, as long as the underlying array is a buffer or shared variable.
TEST_F(FragmentShaderValidationTest, AtomicAddWithBufferVariableArrayElement)
{
const std::string &shaderString =
R"(#version 310 es
layout(std140) buffer bufferName1{
uint u1[2];
};
void main()
{
atomicAdd(u1[0], 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass a member of a shader storage block instance to the mem
// argument of an atomic memory function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithBufferVariableInBlockInstance)
{
const std::string &shaderString =
R"(#version 310 es
layout(std140) buffer bufferName{
uint u1;
} instanceName;
void main()
{
atomicAdd(instanceName.u1, 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass a member of a shader storage block instance array to the mem
// argument of an atomic memory function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithBufferVariableInBlockInstanceArray)
{
const std::string &shaderString =
R"(#version 310 es
layout(std140) buffer bufferName{
uint u1;
} instanceName[1];
void main()
{
atomicAdd(instanceName[0].u1, 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is acceptable to pass an element of an array of a shader storage block instance to
// the mem argument of an atomic memory function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithElementOfArrayInBlockInstance)
{
const std::string &shaderString =
R"(#version 310 es
layout(std140) buffer blockName {
uint data[2];
} instanceName;
void main()
{
atomicAdd(instanceName.data[0], 2u);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that it is not allowed to pass an atomic counter variable to the mem argument of an atomic
// memory function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithAtomicCounter)
{
const std::string &shaderString =
R"(#version 310 es
layout(binding = 0, offset = 4) uniform atomic_uint ac;
void main()
{
atomicAdd(ac, 2u);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that it is not allowed to pass an element of an atomic counter array to the mem argument of
// an atomic memory function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithAtomicCounterArray)
{
const std::string &shaderString =
R"(#version 310 es
layout(binding = 0, offset = 4) uniform atomic_uint ac[2];
void main()
{
atomicAdd(ac[0], 2u);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that it is not allowed to pass a local uint value to the mem argument of an atomic memory
// function.
TEST_F(FragmentShaderValidationTest, AtomicAddWithNonStorageVariable)
{
const std::string &shaderString =
R"(#version 310 es
void main()
{
uint test = 1u;
atomicAdd(test, 2u);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
...@@ -80,6 +80,11 @@ ...@@ -80,6 +80,11 @@
1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_single_group = FAIL 1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_single_group = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_multiple_invocations = FAIL 1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_multiple_invocations = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_multiple_groups = FAIL 1442 D3D11 : dEQP-GLES31.functional.compute.basic.ubo_to_ssbo_multiple_groups = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_single_invocation = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_single_group = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_multiple_invocations = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_multiple_groups = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.shared_var.atomic.* = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.shared_var.basic_type.* = FAIL 1442 D3D11 : dEQP-GLES31.functional.compute.shared_var.basic_type.* = FAIL
1442 D3D11 : dEQP-GLES31.functional.compute.shared_var.work_group_size.* = FAIL 1442 D3D11 : dEQP-GLES31.functional.compute.shared_var.work_group_size.* = FAIL
1442 D3D11 : dEQP-GLES31.functional.atomic_counter.* = FAIL 1442 D3D11 : dEQP-GLES31.functional.atomic_counter.* = FAIL
...@@ -1176,10 +1181,6 @@ ...@@ -1176,10 +1181,6 @@
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.write_multiple_unsized_arr_multiple_groups = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.write_multiple_unsized_arr_multiple_groups = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.ssbo_cmd_barrier_single = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.ssbo_cmd_barrier_single = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.ssbo_cmd_barrier_multiple = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.ssbo_cmd_barrier_multiple = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_single_invocation = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_single_group = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_multiple_invocations = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.shared_atomic_op_multiple_groups = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.copy_image_to_ssbo_large = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.copy_image_to_ssbo_large = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.copy_ssbo_to_image_large = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.copy_ssbo_to_image_large = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.image_barrier_single = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.image_barrier_single = FAIL
...@@ -1188,7 +1189,6 @@ ...@@ -1188,7 +1189,6 @@
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_single_group = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_single_group = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_multiple_invocations = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_multiple_invocations = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_multiple_groups = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.basic.atomic_counter_multiple_groups = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.shared_var.atomic.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.indirect_dispatch.* = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.compute.indirect_dispatch.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.ssbo.* = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.ssbo.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.ubo.2_level_array.* = FAIL 1442 OPENGL D3D11 : dEQP-GLES31.functional.ubo.2_level_array.* = FAIL
......
...@@ -134,6 +134,59 @@ TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWrite) ...@@ -134,6 +134,59 @@ TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWrite)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Test atomic memory functions.
TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions)
{
ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL() && IsWindows());
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL());
const std::string &csSource =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 1) buffer blockName {
uint data[2];
} instanceName;
void main()
{
instanceName.data[0] = 0u;
instanceName.data[1] = 0u;
atomicAdd(instanceName.data[0], 5u);
atomicMax(instanceName.data[1], 7u);
})";
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
glUseProgram(program.get());
unsigned int bufferData[2] = {0u};
// Create shader storage buffer
GLBuffer shaderStorageBuffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(bufferData), nullptr, GL_STATIC_DRAW);
// Bind shader storage buffer
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer);
// Dispath compute
glDispatchCompute(1, 1, 1);
glFinish();
// Read back shader storage buffer
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(bufferData), GL_MAP_READ_BIT);
memcpy(bufferData, ptr, sizeof(bufferData));
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
EXPECT_EQ(5u, bufferData[0]);
EXPECT_EQ(7u, bufferData[1]);
EXPECT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(ShaderStorageBufferTest31, ES31_OPENGL(), ES31_OPENGLES()); ANGLE_INSTANTIATE_TEST(ShaderStorageBufferTest31, ES31_OPENGL(), ES31_OPENGLES());
} // namespace } // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment