Commit 4a22f4b0 by Brandon Jones Committed by Commit Bot

ES31: Add atomic_uint support to HLSL translator

This is the first commit in a series to enable atomic counter buffers. Adds support for atomic counters to the GLSL->HLSL translator using RWByteAddressBuffer. Bug: angleproject:1729 Test: angle_end2end_tests Change-Id: I3b7e08f9256dc9bdbcc02ad8910040f2bc14aeac Reviewed-on: https://chromium-review.googlesource.com/c/1291329 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent df73a8e5
......@@ -223,6 +223,8 @@ angle_translator_glsl_sources = [
angle_translator_hlsl_sources = [
"src/compiler/translator/ASTMetadataHLSL.cpp",
"src/compiler/translator/ASTMetadataHLSL.h",
"src/compiler/translator/AtomicCounterFunctionHLSL.cpp",
"src/compiler/translator/AtomicCounterFunctionHLSL.h",
"src/compiler/translator/blocklayoutHLSL.cpp",
"src/compiler/translator/blocklayoutHLSL.h",
"src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp",
......
//
// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// AtomicCounterFunctionHLSL: Class for writing implementation of atomic counter functions into HLSL
// output.
//
#include "compiler/translator/AtomicCounterFunctionHLSL.h"
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
namespace sh
{
namespace
{
constexpr ImmutableString kAtomicCounter("atomicCounter");
constexpr ImmutableString kAtomicCounterIncrement("atomicCounterIncrement");
constexpr ImmutableString kAtomicCounterDecrement("atomicCounterDecrement");
constexpr ImmutableString kAtomicCounterBaseName("_acbase_");
} // namespace
ImmutableString AtomicCounterFunctionHLSL::useAtomicCounterFunction(const ImmutableString &name)
{
// The largest string that will be create created is "_acbase_increment" or "_acbase_decrement"
ImmutableStringBuilder hlslFunctionNameSB(kAtomicCounterBaseName.length() +
strlen("increment"));
hlslFunctionNameSB << kAtomicCounterBaseName;
AtomicCounterFunction atomicMethod;
if (kAtomicCounter == name)
{
atomicMethod = AtomicCounterFunction::LOAD;
hlslFunctionNameSB << "load";
}
else if (kAtomicCounterIncrement == name)
{
atomicMethod = AtomicCounterFunction::INCREMENT;
hlslFunctionNameSB << "increment";
}
else if (kAtomicCounterDecrement == name)
{
atomicMethod = AtomicCounterFunction::DECREMENT;
hlslFunctionNameSB << "decrement";
}
else
{
atomicMethod = AtomicCounterFunction::INVALID;
UNREACHABLE();
}
ImmutableString hlslFunctionName(hlslFunctionNameSB);
mAtomicCounterFunctions[hlslFunctionName] = atomicMethod;
return hlslFunctionName;
}
void AtomicCounterFunctionHLSL::atomicCounterFunctionHeader(TInfoSinkBase &out)
{
for (auto &atomicFunction : mAtomicCounterFunctions)
{
out << "uint " << atomicFunction.first
<< "(in RWByteAddressBuffer counter, int address)\n"
"{\n";
switch (atomicFunction.second)
{
case AtomicCounterFunction::INCREMENT:
case AtomicCounterFunction::DECREMENT:
out << " uint ret;\n"
" counter.InterlockedAdd(address, ";
if (atomicFunction.second == AtomicCounterFunction::DECREMENT)
{
out << "0u - ";
}
out << "1u, ret);\n"
<< " return ret;\n";
break;
case AtomicCounterFunction::LOAD:
out << " return counter.Load(address);\n";
break;
default:
UNREACHABLE();
break;
}
out << "}\n\n";
}
}
ImmutableString getAtomicCounterNameForBinding(int binding)
{
std::stringstream counterName;
counterName << kAtomicCounterBaseName << binding;
return ImmutableString(counterName.str());
}
} // namespace sh
//
// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// AtomicCounterFunctionHLSL: Class for writing implementation of atomic counter functions into HLSL
// output.
//
#ifndef COMPILER_TRANSLATOR_ATOMICCOUNTERFUNCTIONHLSL_H_
#define COMPILER_TRANSLATOR_ATOMICCOUNTERFUNCTIONHLSL_H_
#include <map>
#include "compiler/translator/Common.h"
#include "compiler/translator/ImmutableString.h"
namespace sh
{
class TInfoSinkBase;
struct TLayoutQualifier;
class AtomicCounterFunctionHLSL final : angle::NonCopyable
{
public:
ImmutableString useAtomicCounterFunction(const ImmutableString &name);
void atomicCounterFunctionHeader(TInfoSinkBase &out);
private:
enum class AtomicCounterFunction
{
LOAD,
INCREMENT,
DECREMENT,
INVALID
};
std::map<ImmutableString, AtomicCounterFunction> mAtomicCounterFunctions;
};
ImmutableString getAtomicCounterNameForBinding(int binding);
} // namespace sh
#endif // COMPILER_TRANSLATOR_ATOMICCOUNTERFUNCTIONHLSL_H_
......@@ -13,6 +13,7 @@
#include "common/angleutils.h"
#include "common/debug.h"
#include "common/utilities.h"
#include "compiler/translator/AtomicCounterFunctionHLSL.h"
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
#include "compiler/translator/ImageFunctionHLSL.h"
......@@ -233,9 +234,10 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType,
mExcessiveLoopIndex = nullptr;
mStructureHLSL = new StructureHLSL;
mTextureFunctionHLSL = new TextureFunctionHLSL;
mImageFunctionHLSL = new ImageFunctionHLSL;
mStructureHLSL = new StructureHLSL;
mTextureFunctionHLSL = new TextureFunctionHLSL;
mImageFunctionHLSL = new ImageFunctionHLSL;
mAtomicCounterFunctionHLSL = new AtomicCounterFunctionHLSL;
unsigned int firstUniformRegister =
((compileOptions & SH_SKIP_D3D_CONSTANT_REGISTER_ZERO) != 0) ? 1u : 0u;
......@@ -263,6 +265,7 @@ OutputHLSL::~OutputHLSL()
SafeDelete(mResourcesHLSL);
SafeDelete(mTextureFunctionHLSL);
SafeDelete(mImageFunctionHLSL);
SafeDelete(mAtomicCounterFunctionHLSL);
for (auto &eqFunction : mStructEqualityFunctions)
{
SafeDelete(eqFunction);
......@@ -545,6 +548,11 @@ void OutputHLSL::header(TInfoSinkBase &out,
"#define FLATTEN\n"
"#endif\n";
// array stride for atomic counter buffers is always 4 per original extension
// ARB_shader_atomic_counters and discussion on
// https://github.com/KhronosGroup/OpenGL-API/issues/5
out << "\n#define ATOMIC_COUNTER_ARRAY_STRIDE 4\n\n";
if (mShaderType == GL_FRAGMENT_SHADER)
{
const bool usingMRTExtension =
......@@ -864,6 +872,7 @@ void OutputHLSL::header(TInfoSinkBase &out,
(mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0;
mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
mImageFunctionHLSL->imageFunctionHeader(out);
mAtomicCounterFunctionHLSL->atomicCounterFunctionHeader(out);
if (mUsesFragCoord)
{
......@@ -941,6 +950,21 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
mUsesDepthRange = true;
out << name;
}
else if (IsAtomicCounter(variable.getType().getBasicType()))
{
const TType &variableType = variable.getType();
if (variableType.getQualifier() == EvqUniform)
{
TLayoutQualifier layout = variableType.getLayoutQualifier();
mReferencedUniforms[uniqueId.get()] = &variable;
out << getAtomicCounterNameForBinding(layout.binding) << ", " << layout.offset;
}
else
{
TString varName = DecorateVariableIfNeeded(variable);
out << varName << ", " << varName << "_offset";
}
}
else
{
const TType &variableType = variable.getType();
......@@ -1377,6 +1401,10 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
// separator to access the sampler variable that has been moved out of the struct.
outputTriplet(out, visit, "", "_", "");
}
else if (IsAtomicCounter(leftType.getBasicType()))
{
outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE");
}
else
{
outputTriplet(out, visit, "", "[", "]");
......@@ -1384,10 +1412,21 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
}
break;
case EOpIndexIndirect:
{
// We do not currently support indirect references to interface blocks
ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
outputTriplet(out, visit, "", "[", "]");
const TType &leftType = node->getLeft()->getType();
if (IsAtomicCounter(leftType.getBasicType()))
{
outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE");
}
else
{
outputTriplet(out, visit, "", "[", "]");
}
break;
}
case EOpIndexDirectStruct:
{
const TStructure *structure = node->getLeft()->getType().getStruct();
......@@ -2111,6 +2150,13 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
type.getMemoryQualifier().readonly);
out << imageFunctionName << "(";
}
else if (node->getFunction()->isAtomicCounterFunction())
{
const ImmutableString &name = node->getFunction()->name();
ImmutableString atomicFunctionName =
mAtomicCounterFunctionHLSL->useAtomicCounterFunction(name);
out << atomicFunctionName << "(";
}
else
{
const ImmutableString &name = node->getFunction()->name();
......@@ -2871,8 +2917,18 @@ void OutputHLSL::writeParameter(const TVariable *param, TInfoSinkBase &out)
}
}
out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr
<< ArrayString(type);
// If the parameter is an atomic counter, we need to add an extra parameter to keep track of the
// buffer offset.
if (IsAtomicCounter(type.getBasicType()))
{
out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr << ", int "
<< nameStr << "_offset";
}
else
{
out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr
<< ArrayString(type);
}
// If the structure parameter contains samplers, they need to be passed into the function as
// separate parameters. HLSL doesn't natively support samplers in structs.
......
......@@ -23,6 +23,7 @@ class BuiltInFunctionEmulator;
namespace sh
{
class AtomicCounterFunctionHLSL;
class ImageFunctionHLSL;
class ResourcesHLSL;
class StructureHLSL;
......@@ -177,6 +178,7 @@ class OutputHLSL : public TIntermTraverser
ResourcesHLSL *mResourcesHLSL;
TextureFunctionHLSL *mTextureFunctionHLSL;
ImageFunctionHLSL *mImageFunctionHLSL;
AtomicCounterFunctionHLSL *mAtomicCounterFunctionHLSL;
// Parameters determining what goes in the header output
bool mUsesFragColor;
......
......@@ -10,6 +10,7 @@
#include "compiler/translator/ResourcesHLSL.h"
#include "common/utilities.h"
#include "compiler/translator/AtomicCounterFunctionHLSL.h"
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/StructureHLSL.h"
#include "compiler/translator/UtilsHLSL.h"
......@@ -378,6 +379,14 @@ void ResourcesHLSL::outputUniform(TInfoSinkBase &out,
out << ArrayString(type) << " : " << registerString << ";\n";
}
void ResourcesHLSL::outputAtomicCounterBuffer(TInfoSinkBase &out,
const int binding,
const unsigned int registerIndex)
{
out << "uniform RWByteAddressBuffer " << getAtomicCounterNameForBinding(binding)
<< " : register(u" << registerIndex << ");\n";
}
void ResourcesHLSL::uniformsHeader(TInfoSinkBase &out,
ShShaderOutput outputType,
const ReferencedVariables &referencedUniforms,
......@@ -394,6 +403,8 @@ void ResourcesHLSL::uniformsHeader(TInfoSinkBase &out,
TMap<const TVariable *, TString> samplerInStructSymbolsToAPINames;
TVector<TVector<const TVariable *>> groupedReadonlyImageUniforms(HLSL_TEXTURE_MAX + 1);
TVector<TVector<const TVariable *>> groupedImageUniforms(HLSL_RWTEXTURE_MAX + 1);
TUnorderedMap<int, unsigned int> assignedAtomicCounterBindings;
for (auto &uniformIt : referencedUniforms)
{
// Output regular uniforms. Group sampler uniforms by type.
......@@ -425,6 +436,24 @@ void ResourcesHLSL::uniformsHeader(TInfoSinkBase &out,
groupedImageUniforms[group].push_back(&variable);
}
}
else if (outputType == SH_HLSL_4_1_OUTPUT && IsAtomicCounter(type.getBasicType()))
{
TLayoutQualifier layout = type.getLayoutQualifier();
int binding = layout.binding;
unsigned int registerIndex;
if (assignedAtomicCounterBindings.find(binding) == assignedAtomicCounterBindings.end())
{
registerIndex = mUAVRegister++;
assignedAtomicCounterBindings[binding] = registerIndex;
outputAtomicCounterBuffer(out, binding, registerIndex);
}
else
{
registerIndex = assignedAtomicCounterBindings[binding];
}
const Uniform *uniform = findUniformByName(variable.name());
mUniformRegisterMap[uniform->name] = registerIndex;
}
else
{
if (type.isStructureContainingSamplers())
......@@ -469,8 +498,10 @@ void ResourcesHLSL::uniformsHeader(TInfoSinkBase &out,
if (outputType == SH_HLSL_4_1_OUTPUT)
{
unsigned int groupTextureRegisterIndex = 0;
unsigned int groupRWTextureRegisterIndex = 0;
unsigned int groupTextureRegisterIndex = 0;
// Atomic counters and RW texture share the same resources. Therefore, RW texture need to
// start counting after the last atomic counter.
unsigned int groupRWTextureRegisterIndex = mUAVRegister;
unsigned int imageUniformGroupIndex = 0;
// TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case.
ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D);
......@@ -693,4 +724,4 @@ TString ResourcesHLSL::uniformBlockStructString(const TInterfaceBlock &interface
"{\n" +
uniformBlockMembersString(interfaceBlock, blockStorage) + "};\n\n";
}
}
} // namespace sh
......@@ -81,6 +81,9 @@ class ResourcesHLSL : angle::NonCopyable
const TType &type,
const TVariable &variable,
const unsigned int registerIndex);
void outputAtomicCounterBuffer(TInfoSinkBase &out,
const int binding,
const unsigned int registerIndex);
// Returns the uniform's register index
unsigned int assignUniformRegister(const TType &type,
......
......@@ -25,6 +25,7 @@ constexpr const ImmutableString kMainName("main");
constexpr const ImmutableString kImageLoadName("imageLoad");
constexpr const ImmutableString kImageStoreName("imageStore");
constexpr const ImmutableString kImageSizeName("imageSize");
constexpr const ImmutableString kAtomicCounterName("atomicCounter");
static const char kFunctionMangledNameSeparator = '(';
......@@ -219,6 +220,11 @@ bool TFunction::isImageFunction() const
(name() == kImageSizeName || name() == kImageLoadName || name() == kImageStoreName);
}
bool TFunction::isAtomicCounterFunction() const
{
return SymbolType() == SymbolType::BuiltIn && name().beginsWith(kAtomicCounterName);
}
bool TFunction::hasSamplerInStructParams() const
{
for (size_t paramIndex = 0; paramIndex < mParamCount; ++paramIndex)
......
......@@ -235,6 +235,7 @@ class TFunction : public TSymbol
bool isMain() const;
bool isImageFunction() const;
bool isAtomicCounterFunction() const;
bool hasSamplerInStructParams() const;
// Note: Only to be used for static built-in functions!
......
......@@ -967,7 +967,8 @@ TString TypeString(const TType &type)
case EbtSamplerExternalOES:
return "sampler2D";
case EbtAtomicCounter:
return "atomic_uint";
// Multiple atomic_uints will be implemented as a single RWByteAddressBuffer
return "RWByteAddressBuffer";
default:
break;
}
......
......@@ -966,6 +966,12 @@ void SetUAVRelatedResourceLimits(D3D_FEATURE_LEVEL featureLevel, gl::Caps *caps)
reservedUAVsForAtomicCounterBuffers;
caps->maxAtomicCounterBufferBindings = reservedUAVsForAtomicCounterBuffers;
// Setting MAX_COMPUTE_ATOMIC_COUNTERS to a conservative number of 1024 * the number of UAV
// reserved for atomic counters. It could theoretically be set to max buffer size / 4 but that
// number could cause problems.
caps->maxCombinedAtomicCounters = reservedUAVsForAtomicCounterBuffers * 1024;
caps->maxShaderAtomicCounters[gl::ShaderType::Compute] = caps->maxCombinedAtomicCounters;
// Allocate the remaining slots for images and shader storage blocks.
// The maximum number of fragment shader outputs depends on the current context version, so we
// will not set it here. See comments in Context11::initialize().
......
......@@ -34,7 +34,7 @@ TEST_P(AtomicCounterBufferTest, AtomicCounterBufferBindings)
{
ASSERT_EQ(3, getClientMajorVersion());
GLBuffer atomicCounterBuffer;
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, atomicCounterBuffer.get());
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer.get());
if (getClientMinorVersion() < 1)
{
EXPECT_GL_ERROR(GL_INVALID_ENUM);
......@@ -54,7 +54,7 @@ TEST_P(AtomicCounterBufferTest31, ExceedMaxVertexAtomicCounters)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"layout(binding = 2) uniform atomic_uint foo[gl_MaxVertexAtomicCounters + 1];\n"
"layout(binding = 0) uniform atomic_uint foo[gl_MaxVertexAtomicCounters + 1];\n"
"void main()\n"
"{\n"
" atomicCounterIncrement(foo[0]);\n"
......@@ -75,14 +75,14 @@ TEST_P(AtomicCounterBufferTest31, OffsetNotAllSpecified)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint foo;\n"
"layout(binding = 0, offset = 4) uniform atomic_uint foo;\n"
"void main()\n"
"{\n"
" atomicCounterIncrement(foo);\n"
"}\n";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"layout(binding = 2) uniform atomic_uint foo;\n"
"layout(binding = 0) uniform atomic_uint foo;\n"
"void main()\n"
"{\n"
"}\n";
......@@ -97,14 +97,14 @@ TEST_P(AtomicCounterBufferTest31, OffsetNotAllSpecifiedWithSameValue)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint foo;\n"
"layout(binding = 0, offset = 4) uniform atomic_uint foo;\n"
"void main()\n"
"{\n"
" atomicCounterIncrement(foo);\n"
"}\n";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"layout(binding = 2, offset = 8) uniform atomic_uint foo;\n"
"layout(binding = 0, offset = 8) uniform atomic_uint foo;\n"
"void main()\n"
"{\n"
"}\n";
......@@ -113,13 +113,43 @@ TEST_P(AtomicCounterBufferTest31, OffsetNotAllSpecifiedWithSameValue)
EXPECT_EQ(0u, program);
}
// Tests atomic counter reads using compute shaders. Used as a sanity check for the translator.
TEST_P(AtomicCounterBufferTest31, AtomicCounterReadCompute)
{
// Skipping due to a bug on the Adreno OpenGLES Android driver.
// http://anglebug.com/2925
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
constexpr char kComputeShaderSource[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 0, offset = 8) uniform atomic_uint ac[3];
void atomicCounterInFunction(in atomic_uint counter[3])
{
atomicCounter(counter[0]);
}
void main()
{
atomicCounterInFunction(ac);
atomicCounter(ac[gl_LocalInvocationIndex + 1u]);
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
EXPECT_GL_NO_ERROR();
}
// Test atomic counter read.
TEST_P(AtomicCounterBufferTest31, AtomicCounterRead)
{
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
ANGLE_SKIP_TEST_IF(IsD3D11());
const std::string &fragShader =
"#version 310 es\n"
"precision highp float;\n"
"layout(binding = 2, offset = 4) uniform atomic_uint ac;\n"
"layout(binding = 0, offset = 4) uniform atomic_uint ac;\n"
"out highp vec4 my_color;\n"
"void main()\n"
"{\n"
......@@ -138,7 +168,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterRead)
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 2, atomicCounterBuffer);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer);
drawQuad(program.get(), essl31_shaders::PositionAttrib(), 0.0f);
ASSERT_GL_NO_ERROR();
......@@ -148,10 +178,14 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterRead)
// Test atomic counter increment and decrement.
TEST_P(AtomicCounterBufferTest31, AtomicCounterIncrementAndDecrement)
{
// Skipping test while we work on enabling atomic counter buffer support in th D3D renderer.
// http://anglebug.com/1729
ANGLE_SKIP_TEST_IF(IsD3D11());
const std::string &csSource =
"#version 310 es\n"
"layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
"layout(binding = 2, offset = 4) uniform atomic_uint ac[2];\n"
"layout(binding = 0, offset = 4) uniform atomic_uint ac[2];\n"
"void main()\n"
"{\n"
" atomicCounterIncrement(ac[0]);\n"
......@@ -168,7 +202,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterIncrementAndDecrement)
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 2, atomicCounterBuffer);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer);
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
......@@ -188,7 +222,8 @@ ANGLE_INSTANTIATE_TEST(AtomicCounterBufferTest,
ES3_OPENGL(),
ES3_OPENGLES(),
ES31_OPENGL(),
ES31_OPENGLES());
ANGLE_INSTANTIATE_TEST(AtomicCounterBufferTest31, ES31_OPENGL(), ES31_OPENGLES());
ES31_OPENGLES(),
ES31_D3D11());
ANGLE_INSTANTIATE_TEST(AtomicCounterBufferTest31, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
} // 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