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: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index bf1d3e1..4a8fd22 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -1089,16 +1089,34 @@
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 @@
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 @@
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::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 @@
linker.getResults(&mState.mUniforms, &mState.mUniformLocations);
- linkSamplerAndImageBindings();
+ linkSamplerAndImageBindings(combinedImageUniformsCount);
if (!linkAtomicCounterBuffers())
{
@@ -2524,8 +2548,10 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
}
}
}
+
+ return true;
}
void Program::setUniformValuesFromBindingQualifiers()