Re-land "Move sampler validation to the GL layer."
This previously was D3D-only, but is required for every draw call.
This completes the work of removing the D3D-specific Impl methods
from ProgramImpl.
Also add several regression tests to cover texture and sampler
validation.
Re-land with a fix for duplicate sampler active uniforms.
BUG=angleproject:1123
Change-Id: Iefef06e7901873c98bf2ba7864efd16a4c6435d3
Reviewed-on: https://chromium-review.googlesource.com/301581
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 6922adf..14a50ba 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -174,6 +174,17 @@
}
}
+bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &name)
+{
+ for (const LinkedUniform &uniform : list)
+ {
+ if (uniform.name == name)
+ return true;
+ }
+
+ return false;
+}
+
} // anonymous namespace
AttributeBindings::AttributeBindings()
@@ -359,7 +370,8 @@
mDeleteStatus(false),
mRefCount(0),
mResourceManager(manager),
- mHandle(handle)
+ mHandle(handle),
+ mSamplerUniformRange(0, 0)
{
ASSERT(mProgram);
@@ -674,12 +686,15 @@
{
int locationIndex = stream.readInt<int>();
VariableLocation locationData;
- locationData.element = stream.readInt<unsigned int>();
- locationData.index = stream.readInt<unsigned int>();
- locationData.name = stream.readString();
+ stream.readInt(&locationData.element);
+ stream.readInt(&locationData.index);
+ stream.readString(&locationData.name);
mData.mOutputVariables[locationIndex] = locationData;
}
+ stream.readInt(&mSamplerUniformRange.start);
+ stream.readInt(&mSamplerUniformRange.end);
+
rx::LinkResult result = mProgram->load(mInfoLog, &stream);
if (result.error.isError() || !result.linkSuccess)
{
@@ -769,6 +784,9 @@
stream.writeString(outputPair.second.name);
}
+ stream.writeInt(mSamplerUniformRange.start);
+ stream.writeInt(mSamplerUniformRange.end);
+
gl::Error error = mProgram->save(&stream);
if (error.isError())
{
@@ -1297,7 +1315,78 @@
bool Program::validateSamplers(InfoLog *infoLog, const Caps &caps)
{
- return mProgram->validateSamplers(infoLog, caps);
+ // Skip cache if we're using an infolog, so we get the full error.
+ // Also skip the cache if the sample mapping has changed, or if we haven't ever validated.
+ if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
+ {
+ return mCachedValidateSamplersResult.value();
+ }
+
+ if (mTextureUnitTypesCache.empty())
+ {
+ mTextureUnitTypesCache.resize(caps.maxCombinedTextureImageUnits, GL_NONE);
+ }
+ else
+ {
+ std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(), GL_NONE);
+ }
+
+ // if any two active samplers in a program are of different types, but refer to the same
+ // texture image unit, and this is the current program, then ValidateProgram will fail, and
+ // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
+ for (unsigned int samplerIndex = mSamplerUniformRange.start;
+ samplerIndex < mSamplerUniformRange.end; ++samplerIndex)
+ {
+ const LinkedUniform &uniform = mData.mUniforms[samplerIndex];
+ ASSERT(uniform.isSampler());
+
+ if (!uniform.staticUse)
+ continue;
+
+ const GLuint *dataPtr = reinterpret_cast<const GLuint *>(uniform.getDataPtrToElement(0));
+ GLenum textureType = SamplerTypeToTextureType(uniform.type);
+
+ for (unsigned int arrayElement = 0; arrayElement < uniform.elementCount(); ++arrayElement)
+ {
+ GLuint textureUnit = dataPtr[arrayElement];
+
+ if (textureUnit >= caps.maxCombinedTextureImageUnits)
+ {
+ if (infoLog)
+ {
+ (*infoLog) << "Sampler uniform (" << textureUnit
+ << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
+ << caps.maxCombinedTextureImageUnits << ")";
+ }
+
+ mCachedValidateSamplersResult = false;
+ return false;
+ }
+
+ if (mTextureUnitTypesCache[textureUnit] != GL_NONE)
+ {
+ if (textureType != mTextureUnitTypesCache[textureUnit])
+ {
+ if (infoLog)
+ {
+ (*infoLog) << "Samplers of conflicting types refer to the same texture "
+ "image unit ("
+ << textureUnit << ").";
+ }
+
+ mCachedValidateSamplersResult = false;
+ return false;
+ }
+ }
+ else
+ {
+ mTextureUnitTypesCache[textureUnit] = textureType;
+ }
+ }
+ }
+
+ mCachedValidateSamplersResult = true;
+ return true;
}
bool Program::isValidated() const
@@ -2086,11 +2175,13 @@
const gl::Shader *vertexShader = mData.getAttachedVertexShader();
VectorAndSamplerCount vsCounts;
+ std::vector<LinkedUniform> samplerUniforms;
+
for (const sh::Uniform &uniform : vertexShader->getUniforms())
{
if (uniform.staticUse)
{
- vsCounts += flattenUniform(uniform, uniform.name);
+ vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
}
}
@@ -2115,7 +2206,7 @@
{
if (uniform.staticUse)
{
- fsCounts += flattenUniform(uniform, uniform.name);
+ fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
}
}
@@ -2133,11 +2224,18 @@
return false;
}
+ mSamplerUniformRange.start = static_cast<unsigned int>(mData.mUniforms.size());
+ mSamplerUniformRange.end =
+ mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size());
+
+ mData.mUniforms.insert(mData.mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
+
return true;
}
Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable &uniform,
- const std::string &fullName)
+ const std::string &fullName,
+ std::vector<LinkedUniform> *samplerUniforms)
{
VectorAndSamplerCount vectorAndSamplerCount;
@@ -2152,7 +2250,7 @@
const sh::ShaderVariable &field = uniform.fields[fieldIndex];
const std::string &fieldFullName = (fullName + elementString + "." + field.name);
- vectorAndSamplerCount += flattenUniform(field, fieldFullName);
+ vectorAndSamplerCount += flattenUniform(field, fieldFullName, samplerUniforms);
}
}
@@ -2160,18 +2258,28 @@
}
// Not a struct
- if (mData.getUniformByName(fullName) == nullptr)
+ bool isSampler = IsSamplerType(uniform.type);
+ if (!UniformInList(mData.getUniforms(), fullName) && !UniformInList(*samplerUniforms, fullName))
{
gl::LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName,
uniform.arraySize, -1,
sh::BlockMemberInfo::getDefaultBlockInfo());
linkedUniform.staticUse = true;
- mData.mUniforms.push_back(linkedUniform);
+
+ // Store sampler uniforms separately, so we'll append them to the end of the list.
+ if (isSampler)
+ {
+ samplerUniforms->push_back(linkedUniform);
+ }
+ else
+ {
+ mData.mUniforms.push_back(linkedUniform);
+ }
}
- vectorAndSamplerCount.vectorCount =
- (VariableRegisterCount(uniform.type) * uniform.elementCount());
- vectorAndSamplerCount.samplerCount = (IsSamplerType(uniform.type) ? uniform.elementCount() : 0);
+ unsigned int elementCount = uniform.elementCount();
+ vectorAndSamplerCount.vectorCount = (VariableRegisterCount(uniform.type) * elementCount);
+ vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
return vectorAndSamplerCount;
}
@@ -2294,6 +2402,12 @@
}
else
{
+ // Invalide the validation cache if we modify the sampler data.
+ if (linkedUniform->isSampler() && memcmp(destPointer, v, sizeof(T) * count) != 0)
+ {
+ mCachedValidateSamplersResult.reset();
+ }
+
memcpy(destPointer, v, sizeof(T) * count);
}
}
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 3dd7c46..6e9d682 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -10,13 +10,6 @@
#ifndef LIBANGLE_PROGRAM_H_
#define LIBANGLE_PROGRAM_H_
-#include "libANGLE/angletypes.h"
-#include "libANGLE/Constants.h"
-#include "libANGLE/Error.h"
-#include "libANGLE/RefCountObject.h"
-
-#include "common/angleutils.h"
-
#include <GLES2/gl2.h>
#include <GLSLANG/ShaderLang.h>
@@ -25,6 +18,15 @@
#include <string>
#include <vector>
+#include "common/angleutils.h"
+#include "common/mathutil.h"
+#include "common/Optional.h"
+
+#include "libANGLE/angletypes.h"
+#include "libANGLE/Constants.h"
+#include "libANGLE/Error.h"
+#include "libANGLE/RefCountObject.h"
+
namespace rx
{
class ImplFactory;
@@ -215,6 +217,11 @@
std::vector<sh::Attribute> mAttributes;
std::bitset<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
+ // Uniforms are sorted in order:
+ // 1. Non-sampler uniforms
+ // 2. Sampler uniforms
+ // 3. Uniform block uniforms
+ // This makes sampler validation easier, since we don't need a separate list.
std::vector<LinkedUniform> mUniforms;
std::vector<VariableLocation> mUniformLocations;
std::vector<UniformBlock> mUniformBlocks;
@@ -384,7 +391,8 @@
};
VectorAndSamplerCount flattenUniform(const sh::ShaderVariable &uniform,
- const std::string &fullName);
+ const std::string &fullName,
+ std::vector<LinkedUniform> *samplerUniforms);
void gatherInterfaceBlockInfo();
void defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType);
@@ -414,6 +422,11 @@
const GLuint mHandle;
InfoLog mInfoLog;
+
+ // Cache for sampler validation
+ Optional<bool> mCachedValidateSamplersResult;
+ std::vector<GLenum> mTextureUnitTypesCache;
+ RangeUI mSamplerUniformRange;
};
}
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 752f995..d51015a 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -633,6 +633,7 @@
{
const auto it = mSamplerTextures.find(type);
ASSERT(it != mSamplerTextures.end());
+ ASSERT(sampler < it->second.size());
return it->second[sampler].get();
}
@@ -640,6 +641,7 @@
{
const auto it = mSamplerTextures.find(type);
ASSERT(it != mSamplerTextures.end());
+ ASSERT(sampler < it->second.size());
return it->second[sampler].id();
}
diff --git a/src/libANGLE/Uniform.h b/src/libANGLE/Uniform.h
index 040c978..1b21825 100644
--- a/src/libANGLE/Uniform.h
+++ b/src/libANGLE/Uniform.h
@@ -64,6 +64,7 @@
std::vector<unsigned int> memberUniformIndexes;
+ // TODO(jmadill): Make D3D-only.
unsigned int psRegisterIndex;
unsigned int vsRegisterIndex;
};
diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h
index 47fb9be..a47b18e 100644
--- a/src/libANGLE/renderer/ProgramImpl.h
+++ b/src/libANGLE/renderer/ProgramImpl.h
@@ -66,10 +66,6 @@
virtual void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
virtual void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = 0;
- // TODO: The following functions are possibly only applicable to D3D backends. The should be carefully evaluated to
- // determine if they can be removed from this interface.
- virtual bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps) = 0;
-
// Gather uniform block active uniform indices, and uniform block offset info.
virtual void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
std::vector<gl::LinkedUniform> *uniforms) = 0;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 6cb5c14..4819ca0 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -28,36 +28,6 @@
namespace
{
-GLenum GetTextureType(GLenum samplerType)
-{
- switch (samplerType)
- {
- case GL_SAMPLER_2D:
- case GL_INT_SAMPLER_2D:
- case GL_UNSIGNED_INT_SAMPLER_2D:
- case GL_SAMPLER_2D_SHADOW:
- return GL_TEXTURE_2D;
- case GL_SAMPLER_3D:
- case GL_INT_SAMPLER_3D:
- case GL_UNSIGNED_INT_SAMPLER_3D:
- return GL_TEXTURE_3D;
- case GL_SAMPLER_CUBE:
- case GL_SAMPLER_CUBE_SHADOW:
- return GL_TEXTURE_CUBE_MAP;
- case GL_INT_SAMPLER_CUBE:
- case GL_UNSIGNED_INT_SAMPLER_CUBE:
- return GL_TEXTURE_CUBE_MAP;
- case GL_SAMPLER_2D_ARRAY:
- case GL_INT_SAMPLER_2D_ARRAY:
- case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
- case GL_SAMPLER_2D_ARRAY_SHADOW:
- return GL_TEXTURE_2D_ARRAY;
- default: UNREACHABLE();
- }
-
- return GL_TEXTURE_2D;
-}
-
gl::InputLayout GetDefaultInputLayoutFromShader(const gl::Shader *vertexShader)
{
gl::InputLayout defaultLayout;
@@ -338,7 +308,6 @@
mUsedVertexSamplerRange(0),
mUsedPixelSamplerRange(0),
mDirtySamplerMapping(true),
- mTextureUnitTypesCache(renderer->getRendererCaps().maxCombinedTextureImageUnits),
mShaderVersion(100),
mSerial(issueSerial())
{
@@ -486,106 +455,6 @@
}
}
-bool ProgramD3D::validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps)
-{
- // Skip cache if we're using an infolog, so we get the full error.
- // Also skip the cache if the sample mapping has changed, or if we haven't ever validated.
- if (!mDirtySamplerMapping && infoLog == nullptr && mCachedValidateSamplersResult.valid())
- {
- return mCachedValidateSamplersResult.value();
- }
-
- // if any two active samplers in a program are of different types, but refer to the same
- // texture image unit, and this is the current program, then ValidateProgram will fail, and
- // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
- updateSamplerMapping();
-
- std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(), GL_NONE);
-
- for (unsigned int i = 0; i < mUsedPixelSamplerRange; ++i)
- {
- if (mSamplersPS[i].active)
- {
- unsigned int unit = mSamplersPS[i].logicalTextureUnit;
-
- if (unit >= caps.maxCombinedTextureImageUnits)
- {
- if (infoLog)
- {
- (*infoLog) << "Sampler uniform (" << unit
- << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
- << caps.maxCombinedTextureImageUnits << ")";
- }
-
- mCachedValidateSamplersResult = false;
- return false;
- }
-
- if (mTextureUnitTypesCache[unit] != GL_NONE)
- {
- if (mSamplersPS[i].textureType != mTextureUnitTypesCache[unit])
- {
- if (infoLog)
- {
- (*infoLog) << "Samplers of conflicting types refer to the same texture image unit ("
- << unit << ").";
- }
-
- mCachedValidateSamplersResult = false;
- return false;
- }
- }
- else
- {
- mTextureUnitTypesCache[unit] = mSamplersPS[i].textureType;
- }
- }
- }
-
- for (unsigned int i = 0; i < mUsedVertexSamplerRange; ++i)
- {
- if (mSamplersVS[i].active)
- {
- unsigned int unit = mSamplersVS[i].logicalTextureUnit;
-
- if (unit >= caps.maxCombinedTextureImageUnits)
- {
- if (infoLog)
- {
- (*infoLog) << "Sampler uniform (" << unit
- << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
- << caps.maxCombinedTextureImageUnits << ")";
- }
-
- mCachedValidateSamplersResult = false;
- return false;
- }
-
- if (mTextureUnitTypesCache[unit] != GL_NONE)
- {
- if (mSamplersVS[i].textureType != mTextureUnitTypesCache[unit])
- {
- if (infoLog)
- {
- (*infoLog) << "Samplers of conflicting types refer to the same texture image unit ("
- << unit << ").";
- }
-
- mCachedValidateSamplersResult = false;
- return false;
- }
- }
- else
- {
- mTextureUnitTypesCache[unit] = mSamplersVS[i].textureType;
- }
- }
- }
-
- mCachedValidateSamplersResult = true;
- return true;
-}
-
LinkResult ProgramD3D::load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream)
{
reset();
@@ -1173,7 +1042,7 @@
initSemanticIndex();
- assignUniformRegisters();
+ defineUniformsAndAssignRegisters();
gatherTransformFeedbackVaryings(linkedVaryings);
@@ -1187,10 +1056,10 @@
return LinkResult(true, gl::Error(GL_NO_ERROR));
}
-GLboolean ProgramD3D::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
+GLboolean ProgramD3D::validate(const gl::Caps & /*caps*/, gl::InfoLog * /*infoLog*/)
{
- applyUniforms();
- return validateSamplers(infoLog, caps);
+ // TODO(jmadill): Do something useful here?
+ return GL_TRUE;
}
void ProgramD3D::gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
@@ -1475,17 +1344,19 @@
setUniform(location, count, v, GL_UNSIGNED_INT_VEC4);
}
-void ProgramD3D::assignUniformRegisters()
+void ProgramD3D::defineUniformsAndAssignRegisters()
{
const gl::Shader *vertexShader = mData.getAttachedVertexShader();
const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(vertexShader);
+ D3DUniformMap uniformMap;
+
for (const sh::Uniform &vertexUniform : vertexShader->getUniforms())
{
if (vertexUniform.staticUse)
{
- assignUniformRegistersBase(vertexShaderD3D, vertexUniform);
+ defineUniformBase(vertexShaderD3D, vertexUniform, &uniformMap);
}
}
@@ -1496,19 +1367,32 @@
{
if (fragmentUniform.staticUse)
{
- assignUniformRegistersBase(fragmentShaderD3D, fragmentUniform);
+ defineUniformBase(fragmentShaderD3D, fragmentUniform, &uniformMap);
}
}
+ // Initialize the D3DUniform list to mirror the indexing of the GL layer.
+ for (const gl::LinkedUniform &glUniform : mData.getUniforms())
+ {
+ if (!glUniform.isInDefaultBlock())
+ continue;
+
+ auto mapEntry = uniformMap.find(glUniform.name);
+ ASSERT(mapEntry != uniformMap.end());
+ mD3DUniforms.push_back(mapEntry->second);
+ }
+
assignAllSamplerRegisters();
initializeUniformStorage();
}
-void ProgramD3D::assignUniformRegistersBase(const ShaderD3D *shader, const sh::Uniform &uniform)
+void ProgramD3D::defineUniformBase(const ShaderD3D *shader,
+ const sh::Uniform &uniform,
+ D3DUniformMap *uniformMap)
{
if (uniform.isBuiltIn())
{
- assignUniformRegisters(shader, uniform, uniform.name, nullptr);
+ defineUniform(shader, uniform, uniform.name, nullptr, uniformMap);
return;
}
@@ -1517,7 +1401,7 @@
sh::HLSLBlockEncoder encoder(sh::HLSLBlockEncoder::GetStrategyFor(outputType));
encoder.skipRegisters(startRegister);
- assignUniformRegisters(shader, uniform, uniform.name, &encoder);
+ defineUniform(shader, uniform, uniform.name, &encoder, uniformMap);
}
D3DUniform *ProgramD3D::getD3DUniformByName(const std::string &name)
@@ -1533,10 +1417,11 @@
return nullptr;
}
-void ProgramD3D::assignUniformRegisters(const ShaderD3D *shader,
- const sh::ShaderVariable &uniform,
- const std::string &fullName,
- sh::HLSLBlockEncoder *encoder)
+void ProgramD3D::defineUniform(const ShaderD3D *shader,
+ const sh::ShaderVariable &uniform,
+ const std::string &fullName,
+ sh::HLSLBlockEncoder *encoder,
+ D3DUniformMap *uniformMap)
{
if (uniform.isStruct())
{
@@ -1552,7 +1437,7 @@
const sh::ShaderVariable &field = uniform.fields[fieldIndex];
const std::string &fieldFullName = (fullName + elementString + "." + field.name);
- assignUniformRegisters(shader, field, fieldFullName, encoder);
+ defineUniform(shader, field, fieldFullName, encoder, uniformMap);
}
if (encoder)
@@ -1572,27 +1457,23 @@
encoder ? encoder->encodeType(uniform.type, uniform.arraySize, false)
: sh::BlockMemberInfo::getDefaultBlockInfo();
- D3DUniform *d3dUniform = getD3DUniformByName(fullName);
+ auto uniformMapEntry = uniformMap->find(fullName);
+ D3DUniform *d3dUniform = nullptr;
- if (!d3dUniform)
+ if (uniformMapEntry != uniformMap->end())
{
- // We're building the list twice, make sure we use the same indexing. Special case
- // built-ins.
- ASSERT(fullName.compare(0, 3, "gl_") == 0 ||
- mData.getUniformIndex(fullName) == static_cast<GLint>(mD3DUniforms.size()));
-
+ d3dUniform = uniformMapEntry->second;
+ }
+ else
+ {
d3dUniform = new D3DUniform(uniform.type, fullName, uniform.arraySize, true);
- mD3DUniforms.push_back(d3dUniform);
-
- if (encoder)
- {
- d3dUniform->registerElement =
- static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegisterElement(blockInfo));
- }
+ (*uniformMap)[fullName] = d3dUniform;
}
if (encoder)
{
+ d3dUniform->registerElement =
+ static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegisterElement(blockInfo));
unsigned int reg =
static_cast<unsigned int>(sh::HLSLBlockEncoder::getBlockRegister(blockInfo));
if (shader->getShaderType() == GL_FRAGMENT_SHADER)
@@ -1871,7 +1752,7 @@
ASSERT(samplerIndex < outSamplers.size());
Sampler *sampler = &outSamplers[samplerIndex];
sampler->active = true;
- sampler->textureType = GetTextureType(samplerType);
+ sampler->textureType = gl::SamplerTypeToTextureType(samplerType);
sampler->logicalTextureUnit = 0;
*outUsedRange = std::max(samplerIndex + 1, *outUsedRange);
samplerIndex++;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index 5e86516..2183961 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -12,7 +12,6 @@
#include <string>
#include <vector>
-#include "common/Optional.h"
#include "compiler/translator/blocklayoutHLSL.h"
#include "libANGLE/Constants.h"
#include "libANGLE/formatutils.h"
@@ -33,7 +32,7 @@
#endif
// Helper struct representing a single shader uniform
-struct D3DUniform
+struct D3DUniform : angle::NonCopyable
{
D3DUniform(GLenum typeIn,
const std::string &nameIn,
@@ -84,7 +83,6 @@
GLenum getSamplerTextureType(gl::SamplerType type, unsigned int samplerIndex) const;
GLint getUsedSamplerRange(gl::SamplerType type) const;
void updateSamplerMapping();
- bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps);
bool usesPointSize() const { return mUsesPointSize; }
bool usesPointSpriteEmulation() const;
@@ -198,14 +196,18 @@
GLenum textureType;
};
+ typedef std::map<std::string, D3DUniform *> D3DUniformMap;
typedef std::map<std::string, sh::BlockMemberInfo> BlockInfoMap;
- void assignUniformRegisters();
- void assignUniformRegistersBase(const ShaderD3D *shader, const sh::Uniform &uniform);
- void assignUniformRegisters(const ShaderD3D *shader,
- const sh::ShaderVariable &uniform,
- const std::string &fullName,
- sh::HLSLBlockEncoder *encoder);
+ void defineUniformsAndAssignRegisters();
+ void defineUniformBase(const ShaderD3D *shader,
+ const sh::Uniform &uniform,
+ D3DUniformMap *uniformMap);
+ void defineUniform(const ShaderD3D *shader,
+ const sh::ShaderVariable &uniform,
+ const std::string &fullName,
+ sh::HLSLBlockEncoder *encoder,
+ D3DUniformMap *uniformMap);
void assignAllSamplerRegisters();
void assignSamplerRegisters(const D3DUniform *d3dUniform);
@@ -262,9 +264,6 @@
GLuint mUsedPixelSamplerRange;
bool mDirtySamplerMapping;
- // Cache for validateSamplers
- std::vector<GLenum> mTextureUnitTypesCache;
-
// Cache for getPixelExecutableForFramebuffer
std::vector<GLenum> mPixelShaderOutputFormatCache;
@@ -275,8 +274,6 @@
unsigned int mSerial;
- Optional<bool> mCachedValidateSamplersResult;
-
std::vector<GLint> mVertexUBOCache;
std::vector<GLint> mFragmentUBOCache;
VertexExecutable::Signature mCachedVertexSignature;
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index bae8389..bbb4540 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -299,12 +299,6 @@
mFunctions->uniformMatrix4x3fv(uniLoc(location), count, transpose, value);
}
-bool ProgramGL::validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps)
-{
- //UNIMPLEMENTED();
- return true;
-}
-
void ProgramGL::reset()
{
mUniformRealLocationMap.clear();
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index 9cc0a14..982d5bb 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -62,8 +62,6 @@
void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override;
- bool validateSamplers(gl::InfoLog *infoLog, const gl::Caps &caps) override;
-
void gatherUniformBlockInfo(std::vector<gl::UniformBlock> *uniformBlocks,
std::vector<gl::LinkedUniform> *uniforms) override;
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index e9f871d..acb511f 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -703,8 +703,366 @@
ASSERT_GL_NO_ERROR();
}
+class TextureLimitsTest : public ANGLETest
+{
+ protected:
+ struct RGBA8
+ {
+ uint8_t R, G, B, A;
+ };
+
+ TextureLimitsTest()
+ : mProgram(0), mMaxVertexTextures(0), mMaxFragmentTextures(0), mMaxCombinedTextures(0)
+ {
+ setWindowWidth(128);
+ setWindowHeight(128);
+ setConfigRedBits(8);
+ setConfigGreenBits(8);
+ setConfigBlueBits(8);
+ setConfigAlphaBits(8);
+ }
+
+ ~TextureLimitsTest()
+ {
+ if (mProgram != 0)
+ {
+ glDeleteProgram(mProgram);
+ mProgram = 0;
+
+ if (!mTextures.empty())
+ {
+ glDeleteTextures(static_cast<GLsizei>(mTextures.size()), &mTextures[0]);
+ }
+ }
+ }
+
+ void SetUp() override
+ {
+ ANGLETest::SetUp();
+
+ glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mMaxVertexTextures);
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mMaxFragmentTextures);
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mMaxCombinedTextures);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ void compileProgramWithTextureCounts(const std::string &vertexPrefix,
+ GLint vertexTextureCount,
+ GLint vertexActiveTextureCount,
+ const std::string &fragPrefix,
+ GLint fragmentTextureCount,
+ GLint fragmentActiveTextureCount)
+ {
+ std::stringstream vertexShaderStr;
+ vertexShaderStr << "attribute vec2 position;\n"
+ << "varying vec4 color;\n"
+ << "varying vec2 texCoord;\n";
+
+ for (GLint textureIndex = 0; textureIndex < vertexTextureCount; ++textureIndex)
+ {
+ vertexShaderStr << "uniform sampler2D " << vertexPrefix << textureIndex << ";\n";
+ }
+
+ vertexShaderStr << "void main() {\n"
+ << " gl_Position = vec4(position, 0, 1);\n"
+ << " texCoord = (position * 0.5) + 0.5;\n"
+ << " color = vec4(0);\n";
+
+ for (GLint textureIndex = 0; textureIndex < vertexActiveTextureCount; ++textureIndex)
+ {
+ vertexShaderStr << " color += texture2D(" << vertexPrefix << textureIndex
+ << ", texCoord);\n";
+ }
+
+ vertexShaderStr << "}";
+
+ std::stringstream fragmentShaderStr;
+ fragmentShaderStr << "varying mediump vec4 color;\n"
+ << "varying mediump vec2 texCoord;\n";
+
+ for (GLint textureIndex = 0; textureIndex < fragmentTextureCount; ++textureIndex)
+ {
+ fragmentShaderStr << "uniform sampler2D " << fragPrefix << textureIndex << ";\n";
+ }
+
+ fragmentShaderStr << "void main() {\n"
+ << " gl_FragColor = color;\n";
+
+ for (GLint textureIndex = 0; textureIndex < fragmentActiveTextureCount; ++textureIndex)
+ {
+ fragmentShaderStr << " gl_FragColor += texture2D(" << fragPrefix << textureIndex
+ << ", texCoord);\n";
+ }
+
+ fragmentShaderStr << "}";
+
+ const std::string &vertexShaderSource = vertexShaderStr.str();
+ const std::string &fragmentShaderSource = fragmentShaderStr.str();
+
+ mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
+ }
+
+ RGBA8 getPixel(GLint texIndex)
+ {
+ RGBA8 pixel = {static_cast<uint8_t>(texIndex & 0x7u), static_cast<uint8_t>(texIndex >> 3),
+ 0, 255u};
+ return pixel;
+ }
+
+ void initTextures(GLint tex2DCount, GLint texCubeCount)
+ {
+ GLint totalCount = tex2DCount + texCubeCount;
+ mTextures.assign(totalCount, 0);
+ glGenTextures(totalCount, &mTextures[0]);
+ ASSERT_GL_NO_ERROR();
+
+ std::vector<RGBA8> texData(16 * 16);
+
+ GLint texIndex = 0;
+ for (; texIndex < tex2DCount; ++texIndex)
+ {
+ texData.assign(texData.size(), getPixel(texIndex));
+ glActiveTexture(GL_TEXTURE0 + texIndex);
+ glBindTexture(GL_TEXTURE_2D, mTextures[texIndex]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ &texData[0]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ ASSERT_GL_NO_ERROR();
+
+ for (; texIndex < texCubeCount; ++texIndex)
+ {
+ texData.assign(texData.size(), getPixel(texIndex));
+ glActiveTexture(GL_TEXTURE0 + texIndex);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[texIndex]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, &texData[0]);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ void testWithTextures(GLint vertexTextureCount,
+ const std::string &vertexTexturePrefix,
+ GLint fragmentTextureCount,
+ const std::string &fragmentTexturePrefix)
+ {
+ // Generate textures
+ initTextures(vertexTextureCount + fragmentTextureCount, 0);
+
+ glUseProgram(mProgram);
+ RGBA8 expectedSum = {0};
+ for (GLint texIndex = 0; texIndex < vertexTextureCount; ++texIndex)
+ {
+ std::stringstream uniformNameStr;
+ uniformNameStr << vertexTexturePrefix << texIndex;
+ const std::string &uniformName = uniformNameStr.str();
+ GLint location = glGetUniformLocation(mProgram, uniformName.c_str());
+ ASSERT_NE(-1, location);
+
+ glUniform1i(location, texIndex);
+ RGBA8 contribution = getPixel(texIndex);
+ expectedSum.R += contribution.R;
+ expectedSum.G += contribution.G;
+ }
+
+ for (GLint texIndex = 0; texIndex < fragmentTextureCount; ++texIndex)
+ {
+ std::stringstream uniformNameStr;
+ uniformNameStr << fragmentTexturePrefix << texIndex;
+ const std::string &uniformName = uniformNameStr.str();
+ GLint location = glGetUniformLocation(mProgram, uniformName.c_str());
+ ASSERT_NE(-1, location);
+
+ glUniform1i(location, texIndex + vertexTextureCount);
+ RGBA8 contribution = getPixel(texIndex + vertexTextureCount);
+ expectedSum.R += contribution.R;
+ expectedSum.G += contribution.G;
+ }
+
+ ASSERT_GE(256u, expectedSum.G);
+
+ drawQuad(mProgram, "position", 0.5f);
+ ASSERT_GL_NO_ERROR();
+ EXPECT_PIXEL_EQ(0, 0, expectedSum.R, expectedSum.G, 0, 255);
+ }
+
+ GLuint mProgram;
+ std::vector<GLuint> mTextures;
+ GLint mMaxVertexTextures;
+ GLint mMaxFragmentTextures;
+ GLint mMaxCombinedTextures;
+};
+
+// Test rendering with the maximum vertex texture units.
+TEST_P(TextureLimitsTest, MaxVertexTextures)
+{
+ compileProgramWithTextureCounts("tex", mMaxVertexTextures, mMaxVertexTextures, "tex", 0, 0);
+ ASSERT_NE(0u, mProgram);
+ ASSERT_GL_NO_ERROR();
+
+ testWithTextures(mMaxVertexTextures, "tex", 0, "tex");
+}
+
+// Test rendering with the maximum fragment texture units.
+TEST_P(TextureLimitsTest, MaxFragmentTextures)
+{
+ compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures, mMaxFragmentTextures);
+ ASSERT_NE(0u, mProgram);
+ ASSERT_GL_NO_ERROR();
+
+ testWithTextures(mMaxFragmentTextures, "tex", 0, "tex");
+}
+
+// Test rendering with maximum combined texture units.
+TEST_P(TextureLimitsTest, MaxCombinedTextures)
+{
+ GLint vertexTextures = mMaxVertexTextures;
+
+ if (vertexTextures + mMaxFragmentTextures > mMaxCombinedTextures)
+ {
+ vertexTextures = mMaxCombinedTextures - mMaxFragmentTextures;
+ }
+
+ compileProgramWithTextureCounts("vtex", vertexTextures, vertexTextures, "ftex",
+ mMaxFragmentTextures, mMaxFragmentTextures);
+ ASSERT_NE(0u, mProgram);
+ ASSERT_GL_NO_ERROR();
+
+ testWithTextures(vertexTextures, "vtex", mMaxFragmentTextures, "ftex");
+}
+
+// Negative test for exceeding the number of vertex textures
+TEST_P(TextureLimitsTest, ExcessiveVertexTextures)
+{
+ compileProgramWithTextureCounts("tex", mMaxVertexTextures + 1, mMaxVertexTextures + 1, "tex", 0,
+ 0);
+ ASSERT_EQ(0u, mProgram);
+}
+
+// Negative test for exceeding the number of fragment textures
+TEST_P(TextureLimitsTest, ExcessiveFragmentTextures)
+{
+ compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures + 1,
+ mMaxFragmentTextures + 1);
+ ASSERT_EQ(0u, mProgram);
+}
+
+// Test active vertex textures under the limit, but excessive textures specified.
+TEST_P(TextureLimitsTest, MaxActiveVertexTextures)
+{
+ compileProgramWithTextureCounts("tex", mMaxVertexTextures + 4, mMaxVertexTextures, "tex", 0, 0);
+ ASSERT_NE(0u, mProgram);
+ ASSERT_GL_NO_ERROR();
+
+ testWithTextures(mMaxVertexTextures, "tex", 0, "tex");
+}
+
+// Test active fragment textures under the limit, but excessive textures specified.
+TEST_P(TextureLimitsTest, MaxActiveFragmentTextures)
+{
+ compileProgramWithTextureCounts("tex", 0, 0, "tex", mMaxFragmentTextures + 4,
+ mMaxFragmentTextures);
+ ASSERT_NE(0u, mProgram);
+ ASSERT_GL_NO_ERROR();
+
+ testWithTextures(0, "tex", mMaxFragmentTextures, "tex");
+}
+
+// Negative test for pointing two sampler uniforms of different types to the same texture.
+TEST_P(TextureLimitsTest, TextureTypeConflict)
+{
+ const std::string &vertexShader =
+ "attribute vec2 position;\n"
+ "varying float color;\n"
+ "uniform sampler2D tex2D;\n"
+ "uniform samplerCube texCube;\n"
+ "void main() {\n"
+ " gl_Position = vec4(position, 0, 1);\n"
+ " vec2 texCoord = (position * 0.5) + 0.5;\n"
+ " color = texture2D(tex2D, texCoord).x;\n"
+ " color += textureCube(texCube, vec3(texCoord, 0)).x;\n"
+ "}";
+ const std::string &fragmentShader =
+ "varying mediump float color;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(color, 0, 0, 1);\n"
+ "}";
+
+ mProgram = CompileProgram(vertexShader, fragmentShader);
+ ASSERT_NE(0u, mProgram);
+
+ initTextures(1, 0);
+
+ glUseProgram(mProgram);
+ GLint tex2DLocation = glGetUniformLocation(mProgram, "tex2D");
+ ASSERT_NE(-1, tex2DLocation);
+ GLint texCubeLocation = glGetUniformLocation(mProgram, "texCube");
+ ASSERT_NE(-1, texCubeLocation);
+
+ glUniform1i(tex2DLocation, 0);
+ glUniform1i(texCubeLocation, 0);
+ ASSERT_GL_NO_ERROR();
+
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+// Negative test for rendering with texture outside the valid range.
+// TODO(jmadill): Research if this is correct.
+TEST_P(TextureLimitsTest, DrawWithTexturePastMaximum)
+{
+ const std::string &vertexShader =
+ "attribute vec2 position;\n"
+ "varying float color;\n"
+ "uniform sampler2D tex2D;\n"
+ "void main() {\n"
+ " gl_Position = vec4(position, 0, 1);\n"
+ " vec2 texCoord = (position * 0.5) + 0.5;\n"
+ " color = texture2D(tex2D, texCoord).x;\n"
+ "}";
+ const std::string &fragmentShader =
+ "varying mediump float color;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(color, 0, 0, 1);\n"
+ "}";
+
+ mProgram = CompileProgram(vertexShader, fragmentShader);
+ ASSERT_NE(0u, mProgram);
+
+ glUseProgram(mProgram);
+ GLint tex2DLocation = glGetUniformLocation(mProgram, "tex2D");
+ ASSERT_NE(-1, tex2DLocation);
+
+ glUniform1i(tex2DLocation, mMaxCombinedTextures);
+ ASSERT_GL_NO_ERROR();
+
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(TextureTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3()); // TODO(geofflang): Figure out why this test fails on Intel OpenGL
ANGLE_INSTANTIATE_TEST(TextureTestES3, ES3_D3D11(), ES3_OPENGL());
+ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL());
} // namespace
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index e1db43f..0cd3684 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -452,6 +452,46 @@
}
}
+// Check that sampler uniforms only show up one time in the list
+TEST_P(UniformTest, SamplerUniformsAppearOnce)
+{
+ const std::string &vertShader =
+ "attribute vec2 position;\n"
+ "uniform sampler2D tex2D;\n"
+ "varying vec4 color;\n"
+ "void main() {\n"
+ " gl_Position = vec4(position, 0, 1);\n"
+ " color = texture2D(tex2D, vec2(0));\n"
+ "}";
+
+ const std::string &fragShader =
+ "precision mediump float;\n"
+ "varying vec4 color;\n"
+ "uniform sampler2D tex2D;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(tex2D, vec2(0)) + color;\n"
+ "}";
+
+ GLuint program = CompileProgram(vertShader, fragShader);
+ ASSERT_NE(0u, program);
+
+ GLint activeUniformsCount = 0;
+ glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniformsCount);
+ ASSERT_EQ(1, activeUniformsCount);
+
+ GLint size = 0;
+ GLenum type = GL_NONE;
+ GLchar name[120] = {0};
+ glGetActiveUniform(program, 0, 100, nullptr, &size, &type, name);
+ EXPECT_EQ(1, size);
+ EXPECT_EQ(GL_SAMPLER_2D, type);
+ EXPECT_STREQ("tex2D", name);
+
+ EXPECT_GL_NO_ERROR();
+
+ glDeleteProgram(program);
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(UniformTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL());