Commit 7a8fe156 by Jiawei Shao Committed by Commit Bot

ES31: Add link validation on MAX_COMBINED_SHADER_OUTPUT_RESOURCES

This patch adds the link validation on the maximum combined shader output resources required in OpenGL ES 3.1 SPEC. OpenGL ES 3.1 SPEC has restrictions on the sum of the number of all active images, shader storage blocks and fragment shader outputs. A link error will be generated if this sum exceeds the implementation- dependent value of MAX_COMBINED_SHADER_OUTPUT_RESOURCES. In order not to affect the existing image tests, this patch also sets a temporary value for maxCombinedShaderOutputResources on D3D11 back-ends. We will set more accurate values for all the UAV related resource limits in the next patch. BUG=angleproject:2345 TEST=dEQP-GLES31.functional.state_query.integer.max_combined_shader_output_resources_* Change-Id: Ib83a19ef0ae0b9af3422b5c970c7c07d96b2359d Reviewed-on: https://chromium-review.googlesource.com/1039155 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 41e59f55
......@@ -1089,16 +1089,34 @@ Error Program::link(const gl::Context *context)
if (mState.mAttachedShaders[ShaderType::Compute])
{
if (!linkUniforms(context, mInfoLog, mUniformLocationBindings))
GLuint combinedImageUniforms = 0u;
if (!linkUniforms(context, mInfoLog, mUniformLocationBindings, &combinedImageUniforms))
{
return NoError();
}
if (!linkInterfaceBlocks(context, mInfoLog))
GLuint combinedShaderStorageBlocks = 0u;
if (!linkInterfaceBlocks(context, mInfoLog, &combinedShaderStorageBlocks))
{
return NoError();
}
// [OpenGL ES 3.1] Chapter 8.22 Page 203:
// A link error will be generated if the sum of the number of active image uniforms used in
// all shaders, the number of active shader storage blocks, and the number of active
// fragment shader outputs exceeds the implementation-dependent value of
// MAX_COMBINED_SHADER_OUTPUT_RESOURCES.
if (combinedImageUniforms + combinedShaderStorageBlocks >
context->getCaps().maxCombinedShaderOutputResources)
{
mInfoLog
<< "The sum of the number of active image uniforms, active shader storage blocks "
"and active fragment shader outputs exceeds "
"MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
<< context->getCaps().maxCombinedShaderOutputResources << ")";
return NoError();
}
ProgramLinkedResources resources = {
{0, PackMode::ANGLE_RELAXED},
{&mState.mUniformBlocks, &mState.mUniforms},
......@@ -1126,12 +1144,14 @@ Error Program::link(const gl::Context *context)
return NoError();
}
if (!linkUniforms(context, mInfoLog, mUniformLocationBindings))
GLuint combinedImageUniforms = 0u;
if (!linkUniforms(context, mInfoLog, mUniformLocationBindings, &combinedImageUniforms))
{
return NoError();
}
if (!linkInterfaceBlocks(context, mInfoLog))
GLuint combinedShaderStorageBlocks = 0u;
if (!linkInterfaceBlocks(context, mInfoLog, &combinedShaderStorageBlocks))
{
return NoError();
}
......@@ -1141,13 +1161,16 @@ Error Program::link(const gl::Context *context)
return NoError();
}
if (!linkOutputVariables(context, combinedImageUniforms, combinedShaderStorageBlocks))
{
return NoError();
}
const auto &mergedVaryings = getMergedVaryings(context);
ASSERT(mState.mAttachedShaders[ShaderType::Vertex]);
mState.mNumViews = mState.mAttachedShaders[ShaderType::Vertex]->getNumViews(context);
linkOutputVariables(context);
// Map the varyings to the register file
// In WebGL, we use a slightly different handling for packing variables.
gl::PackMode packMode = PackMode::ANGLE_RELAXED;
......@@ -2504,7 +2527,8 @@ bool Program::linkValidateFragmentInputBindings(const Context *context, gl::Info
bool Program::linkUniforms(const Context *context,
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings)
const ProgramBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount)
{
UniformLinker linker(mState);
if (!linker.link(context, infoLog, uniformLocationBindings))
......@@ -2514,7 +2538,7 @@ bool Program::linkUniforms(const Context *context,
linker.getResults(&mState.mUniforms, &mState.mUniformLocations);
linkSamplerAndImageBindings();
linkSamplerAndImageBindings(combinedImageUniformsCount);
if (!linkAtomicCounterBuffers())
{
......@@ -2524,8 +2548,10 @@ bool Program::linkUniforms(const Context *context,
return true;
}
void Program::linkSamplerAndImageBindings()
void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
{
ASSERT(combinedImageUniforms);
unsigned int high = static_cast<unsigned int>(mState.mUniforms.size());
unsigned int low = high;
......@@ -2546,7 +2572,7 @@ void Program::linkSamplerAndImageBindings()
}
mState.mImageUniformRange = RangeUI(low, high);
*combinedImageUniforms = 0u;
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mState.mImageUniformRange)
{
......@@ -2565,6 +2591,9 @@ void Program::linkSamplerAndImageBindings()
mState.mImageBindings.emplace_back(
ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount()));
}
GLuint arraySize = imageUniform.isArray() ? imageUniform.arraySizes[0] : 1u;
*combinedImageUniforms += imageUniform.activeShaderCount() * arraySize;
}
high = low;
......@@ -2779,8 +2808,12 @@ bool Program::linkAttributes(const Context *context, InfoLog &infoLog)
return true;
}
bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
bool Program::linkInterfaceBlocks(const Context *context,
InfoLog &infoLog,
GLuint *combinedShaderStorageBlocksCount)
{
ASSERT(combinedShaderStorageBlocksCount);
const auto &caps = context->getCaps();
if (mState.mAttachedShaders[ShaderType::Compute])
......@@ -2796,9 +2829,9 @@ bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
}
const auto &computeShaderStorageBlocks = computeShader.getShaderStorageBlocks(context);
if (!ValidateInterfaceBlocksCount(caps.maxComputeShaderStorageBlocks,
computeShaderStorageBlocks, ShaderType::Compute,
sh::BlockType::BLOCK_BUFFER, nullptr, infoLog))
if (!ValidateInterfaceBlocksCount(
caps.maxComputeShaderStorageBlocks, computeShaderStorageBlocks, ShaderType::Compute,
sh::BlockType::BLOCK_BUFFER, combinedShaderStorageBlocksCount, infoLog))
{
return false;
}
......@@ -2852,7 +2885,7 @@ bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
maxShaderStorageBlocks[ShaderType::Fragment] = caps.maxFragmentShaderStorageBlocks;
maxShaderStorageBlocks[ShaderType::Geometry] = caps.maxGeometryShaderStorageBlocks;
GLuint combinedShaderStorageBlocksCount = 0u;
*combinedShaderStorageBlocksCount = 0u;
ShaderMap<const std::vector<sh::InterfaceBlock> *> graphicsShaderStorageBlocks = {};
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
......@@ -2865,7 +2898,7 @@ bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
const auto &shaderStorageBlocks = shader->getShaderStorageBlocks(context);
if (!ValidateInterfaceBlocksCount(
maxShaderStorageBlocks[shaderType], shaderStorageBlocks, shaderType,
sh::BlockType::BLOCK_BUFFER, &combinedShaderStorageBlocksCount, infoLog))
sh::BlockType::BLOCK_BUFFER, combinedShaderStorageBlocksCount, infoLog))
{
return false;
}
......@@ -2873,7 +2906,7 @@ bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
graphicsShaderStorageBlocks[shaderType] = &shaderStorageBlocks;
}
if (combinedShaderStorageBlocksCount > caps.maxCombinedShaderStorageBlocks)
if (*combinedShaderStorageBlocksCount > caps.maxCombinedShaderStorageBlocks)
{
infoLog << "The sum of the number of active shader storage blocks exceeds "
"MAX_COMBINED_SHADER_STORAGE_BLOCKS ("
......@@ -3260,7 +3293,9 @@ ProgramMergedVaryings Program::getMergedVaryings(const Context *context) const
return merged;
}
void Program::linkOutputVariables(const Context *context)
bool Program::linkOutputVariables(const Context *context,
GLuint combinedImageUniformsCount,
GLuint combinedShaderStorageBlocksCount)
{
Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
ASSERT(fragmentShader != nullptr);
......@@ -3269,8 +3304,9 @@ void Program::linkOutputVariables(const Context *context)
ASSERT(mState.mActiveOutputVariables.none());
ASSERT(mState.mDrawBufferTypeMask.none());
const auto &outputVariables = fragmentShader->getActiveOutputVariables(context);
// Gather output variable types
for (const auto &outputVariable : fragmentShader->getActiveOutputVariables(context))
for (const auto &outputVariable : outputVariables)
{
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
outputVariable.name != "gl_FragData")
......@@ -3299,11 +3335,31 @@ void Program::linkOutputVariables(const Context *context)
}
}
if (context->getClientVersion() >= ES_3_1)
{
// [OpenGL ES 3.1] Chapter 8.22 Page 203:
// A link error will be generated if the sum of the number of active image uniforms used in
// all shaders, the number of active shader storage blocks, and the number of active
// fragment shader outputs exceeds the implementation-dependent value of
// MAX_COMBINED_SHADER_OUTPUT_RESOURCES.
if (combinedImageUniformsCount + combinedShaderStorageBlocksCount +
mState.mActiveOutputVariables.count() >
context->getCaps().maxCombinedShaderOutputResources)
{
mInfoLog
<< "The sum of the number of active image uniforms, active shader storage blocks "
"and active fragment shader outputs exceeds "
"MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
<< context->getCaps().maxCombinedShaderOutputResources << ")";
return false;
}
}
// Skip this step for GLES2 shaders.
if (fragmentShader->getShaderVersion(context) == 100)
return;
return true;
mState.mOutputVariables = fragmentShader->getActiveOutputVariables(context);
mState.mOutputVariables = outputVariables;
// TODO(jmadill): any caps validation here?
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
......@@ -3352,6 +3408,8 @@ void Program::linkOutputVariables(const Context *context)
}
}
}
return true;
}
void Program::setUniformValuesFromBindingQualifiers()
......
......@@ -705,13 +705,16 @@ class Program final : angle::NonCopyable, public LabeledObject
bool linkValidateShaders(const Context *context, InfoLog &infoLog);
bool linkAttributes(const Context *context, InfoLog &infoLog);
bool linkInterfaceBlocks(const Context *context, InfoLog &infoLog);
bool linkInterfaceBlocks(const Context *context,
InfoLog &infoLog,
GLuint *combinedShaderStorageBlocksCount);
bool linkVaryings(const Context *context, InfoLog &infoLog) const;
bool linkUniforms(const Context *context,
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings);
void linkSamplerAndImageBindings();
const ProgramBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount);
void linkSamplerAndImageBindings(GLuint *combinedImageUniformsCount);
bool linkAtomicCounterBuffers();
void updateLinkedShaderStages();
......@@ -741,7 +744,9 @@ class Program final : angle::NonCopyable, public LabeledObject
void gatherTransformFeedbackVaryings(const ProgramMergedVaryings &varyings);
ProgramMergedVaryings getMergedVaryings(const Context *context) const;
void linkOutputVariables(const Context *context);
bool linkOutputVariables(const Context *context,
GLuint combinedImageUniformsCount,
GLuint combinedShaderStorageBlocksCount);
void setUniformValuesFromBindingQualifiers();
......
......@@ -46,6 +46,11 @@ ShaderType ActiveVariable::getFirstShaderTypeWhereActive() const
return static_cast<ShaderType>(gl::ScanForward(mActiveUseBits.bits()));
}
GLuint ActiveVariable::activeShaderCount() const
{
return static_cast<GLuint>(mActiveUseBits.count());
}
LinkedUniform::LinkedUniform()
: typeInfo(nullptr), bufferIndex(-1), blockInfo(sh::BlockMemberInfo::getDefaultBlockInfo())
{
......
......@@ -32,6 +32,7 @@ struct ActiveVariable
void setActive(ShaderType shaderType, bool used);
void unionReferencesWith(const ActiveVariable &other);
bool isActive(ShaderType shaderType) const;
GLuint activeShaderCount() const;
private:
ShaderBitSet mActiveUseBits;
......
......@@ -957,6 +957,20 @@ size_t GetMaximumComputeImageUniforms(D3D_FEATURE_LEVEL featureLevel)
}
}
size_t GetMaximumCombinedShaderOutputResources(D3D_FEATURE_LEVEL featureLevel)
{
switch (featureLevel)
{
// TODO(jiawei.shao@intel.com): Get a more accurate limit. For now using the minimum
// requirement for GLES 3.1.
case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
return 4;
default:
return 0;
}
}
int GetMinimumTexelOffset(D3D_FEATURE_LEVEL featureLevel)
{
switch (featureLevel)
......@@ -1396,6 +1410,8 @@ void GenerateCaps(ID3D11Device *device,
caps->maxImageUnits = static_cast<GLuint>(GetMaximumImageUnits(featureLevel));
caps->maxComputeImageUniforms =
static_cast<GLuint>(GetMaximumComputeImageUniforms(featureLevel));
caps->maxCombinedShaderOutputResources =
static_cast<GLuint>(GetMaximumCombinedShaderOutputResources(featureLevel));
// Aggregate shader limits
caps->maxUniformBufferBindings = caps->maxVertexUniformBlocks + caps->maxFragmentUniformBlocks;
......
......@@ -70,7 +70,6 @@
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_shader_storage_buffer_bindings_* = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_shader_storage_block_size_* = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_combined_shader_storage_blocks_* = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_combined_shader_output_resources_* = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_uniform_buffer_bindings_* = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_combined_texture_image_units_* = FAIL
1729 D3D11 : dEQP-GLES31.functional.state_query.indexed.atomic_counter_buffer_* = FAIL
......
......@@ -1209,6 +1209,75 @@ TEST_P(ComputeShaderTest, groupMemoryBarrierAndBarrierTest)
}
}
// Verify that a link error is generated when the sum of the number of active image uniforms and
// active shader storage blocks in a compute shader exceeds GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES.
TEST_P(ComputeShaderTest, ExceedCombinedShaderOutputResourcesInCS)
{
// TODO(jiawei.shao@intel.com): enable this test when shader storage buffer is supported on
// D3D11 back-ends.
ANGLE_SKIP_TEST_IF(IsD3D11());
GLint maxCombinedShaderOutputResources;
GLint maxComputeShaderStorageBlocks;
GLint maxComputeImageUniforms;
glGetIntegerv(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, &maxCombinedShaderOutputResources);
glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeShaderStorageBlocks);
glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms);
ANGLE_SKIP_TEST_IF(maxCombinedShaderOutputResources >=
maxComputeShaderStorageBlocks + maxComputeImageUniforms);
std::ostringstream computeShaderStream;
computeShaderStream << "#version 310 es\n"
"layout(local_size_x = 3, local_size_y = 1, local_size_z = 1) in;\n"
"layout(shared, binding = 0) buffer blockName"
"{\n"
" uint data;\n"
"} instance["
<< maxComputeShaderStorageBlocks << "];\n";
ASSERT_GE(maxComputeImageUniforms, 4);
int numImagesInArray = maxComputeImageUniforms / 2;
int numImagesNonArray = maxComputeImageUniforms - numImagesInArray;
for (int i = 0; i < numImagesNonArray; ++i)
{
computeShaderStream << "layout(r32f, binding = " << i << ") uniform highp image2D image"
<< i << ";\n";
}
computeShaderStream << "layout(r32f, binding = " << numImagesNonArray
<< ") uniform highp image2D imageArray[" << numImagesInArray << "];\n";
computeShaderStream << "void main()\n"
"{\n"
" uint val = 0u;\n"
" vec4 val2 = vec4(0.0);\n";
for (int i = 0; i < maxComputeShaderStorageBlocks; ++i)
{
computeShaderStream << " val += instance[" << i << "].data; \n";
}
for (int i = 0; i < numImagesNonArray; ++i)
{
computeShaderStream << " val2 += imageLoad(image" << i
<< ", ivec2(gl_LocalInvocationID.xy)); \n";
}
for (int i = 0; i < numImagesInArray; ++i)
{
computeShaderStream << " val2 += imageLoad(imageArray[" << i << "]"
<< ", ivec2(gl_LocalInvocationID.xy)); \n";
}
computeShaderStream << " instance[0].data = val + uint(val2.x);\n"
"}\n";
GLuint computeProgram = CompileComputeProgram(computeShaderStream.str());
EXPECT_EQ(0u, computeProgram);
}
// Check that it is not possible to create a compute shader when the context does not support ES
// 3.10
TEST_P(ComputeShaderTestES3, NotSupported)
......
......@@ -4133,6 +4133,126 @@ TEST_P(GLSLTest_ES3, NestedFloorWithLargeMultiplierInside)
EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() - 1, GLColor::green);
}
// Verify that a link error is generated when the sum of the number of active image uniforms and
// active shader storage blocks in a rendering pipeline exceeds
// GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES.
TEST_P(GLSLTest_ES31, ExceedCombinedShaderOutputResourcesInVSAndFS)
{
// TODO(jiawei.shao@intel.com): enable this test when shader storage buffer is supported on
// D3D11 back-ends.
ANGLE_SKIP_TEST_IF(IsD3D11());
GLint maxVertexShaderStorageBlocks;
GLint maxVertexImageUniforms;
GLint maxFragmentShaderStorageBlocks;
GLint maxFragmentImageUniforms;
GLint maxCombinedShaderStorageBlocks;
GLint maxCombinedImageUniforms;
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks);
glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &maxVertexImageUniforms);
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &maxFragmentImageUniforms);
glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks);
glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &maxCombinedImageUniforms);
ASSERT_GE(maxCombinedShaderStorageBlocks, maxVertexShaderStorageBlocks);
ASSERT_GE(maxCombinedShaderStorageBlocks, maxFragmentShaderStorageBlocks);
ASSERT_GE(maxCombinedImageUniforms, maxVertexImageUniforms);
ASSERT_GE(maxCombinedImageUniforms, maxFragmentImageUniforms);
GLint vertexSSBOs = maxVertexShaderStorageBlocks;
GLint fragmentSSBOs = maxFragmentShaderStorageBlocks;
// Limit the sum of ssbos in vertex and fragment shaders to maxCombinedShaderStorageBlocks.
if (vertexSSBOs + fragmentSSBOs > maxCombinedShaderStorageBlocks)
{
fragmentSSBOs = maxCombinedShaderStorageBlocks - vertexSSBOs;
}
GLint vertexImages = maxVertexImageUniforms;
GLint fragmentImages = maxFragmentImageUniforms;
// Limit the sum of images in vertex and fragment shaders to maxCombinedImageUniforms.
if (vertexImages + fragmentImages > maxCombinedImageUniforms)
{
vertexImages = maxCombinedImageUniforms - fragmentImages;
}
GLint maxDrawBuffers;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
GLint maxCombinedShaderOutputResources;
glGetIntegerv(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, &maxCombinedShaderOutputResources);
ASSERT_GL_NO_ERROR();
ANGLE_SKIP_TEST_IF(vertexSSBOs + fragmentSSBOs + vertexImages + fragmentImages +
maxDrawBuffers <=
maxCombinedShaderOutputResources);
std::ostringstream vertexStream;
vertexStream << "#version 310 es\n";
for (int i = 0; i < vertexSSBOs; ++i)
{
vertexStream << "layout(shared, binding = " << i << ") buffer blockName" << i
<< "{\n"
" float data;\n"
"} ssbo"
<< i << ";\n";
}
vertexStream << "layout(r32f, binding = 0) uniform highp image2D imageArray[" << vertexImages
<< "];\n";
vertexStream << "void main()\n"
"{\n"
" float val = 0.1;\n"
" vec4 val2 = vec4(0.0);\n";
for (int i = 0; i < vertexSSBOs; ++i)
{
vertexStream << " val += ssbo" << i << ".data; \n";
}
for (int i = 0; i < vertexImages; ++i)
{
vertexStream << " val2 += imageLoad(imageArray[" << i << "], ivec2(0, 0)); \n";
}
vertexStream << " gl_Position = vec4(val, val2);\n"
"}\n";
std::ostringstream fragmentStream;
fragmentStream << "#version 310 es\n"
<< "precision highp float;\n";
for (int i = 0; i < fragmentSSBOs; ++i)
{
fragmentStream << "layout(shared, binding = " << i << ") buffer blockName" << i
<< "{\n"
" float data;\n"
"} ssbo"
<< i << ";\n";
}
fragmentStream << "layout(r32f, binding = 0) uniform highp image2D imageArray["
<< fragmentImages << "];\n";
fragmentStream << "layout (location = 0) out vec4 foutput[" << maxDrawBuffers << "];\n";
fragmentStream << "void main()\n"
"{\n"
" float val = 0.1;\n"
" vec4 val2 = vec4(0.0);\n";
for (int i = 0; i < fragmentSSBOs; ++i)
{
fragmentStream << " val += ssbo" << i << ".data; \n";
}
for (int i = 0; i < fragmentImages; ++i)
{
fragmentStream << " val2 += imageLoad(imageArray[" << i << "], ivec2(0, 0)); \n";
}
for (int i = 0; i < maxDrawBuffers; ++i)
{
fragmentStream << " foutput[" << i << "] = vec4(val, val2);\n";
}
fragmentStream << "}\n";
GLuint program = CompileProgram(vertexStream.str(), fragmentStream.str());
EXPECT_EQ(0u, program);
ASSERT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest,
......
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