Commit 7e4eff11 by Jamie Madill Committed by Commit Bot

Program: Add cache samplers and type masks.

This mask cleans up some of the iteration logic in State::syncProgramTextures. It will make it easier to optimize this function in the future. This will also make it easier to recompute the sampler type validation. Leads to a 5% improvement in State::syncProgramTextures. Bug: angleproject:2747 Bug: angleproject:2763 Change-Id: Ic9a555df843ad23b4c562e6e4a2d425bee58a856 Reviewed-on: https://chromium-review.googlesource.com/1164306 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 46848420
...@@ -5612,22 +5612,22 @@ void Context::uniform1fv(GLint location, GLsizei count, const GLfloat *v) ...@@ -5612,22 +5612,22 @@ void Context::uniform1fv(GLint location, GLsizei count, const GLfloat *v)
program->setUniform1fv(location, count, v); program->setUniform1fv(location, count, v);
} }
void Context::uniform1i(GLint location, GLint x) void Context::setUniform1iImpl(Program *program, GLint location, GLsizei count, const GLint *v)
{ {
Program *program = mGLState.getProgram(); if (program->setUniform1iv(location, count, v) == Program::SetUniformResult::SamplerChanged)
if (program->setUniform1iv(location, 1, &x) == Program::SetUniformResult::SamplerChanged)
{ {
mGLState.setObjectDirty(GL_PROGRAM); mGLState.setObjectDirty(GL_PROGRAM);
} }
} }
void Context::uniform1i(GLint location, GLint x)
{
setUniform1iImpl(mGLState.getProgram(), location, 1, &x);
}
void Context::uniform1iv(GLint location, GLsizei count, const GLint *v) void Context::uniform1iv(GLint location, GLsizei count, const GLint *v)
{ {
Program *program = mGLState.getProgram(); setUniform1iImpl(mGLState.getProgram(), location, count, v);
if (program->setUniform1iv(location, count, v) == Program::SetUniformResult::SamplerChanged)
{
mGLState.setObjectDirty(GL_PROGRAM);
}
} }
void Context::uniform2f(GLint location, GLfloat x, GLfloat y) void Context::uniform2f(GLint location, GLfloat x, GLfloat y)
...@@ -6341,11 +6341,7 @@ void Context::programUniform1iv(GLuint program, GLint location, GLsizei count, c ...@@ -6341,11 +6341,7 @@ void Context::programUniform1iv(GLuint program, GLint location, GLsizei count, c
{ {
Program *programObject = getProgram(program); Program *programObject = getProgram(program);
ASSERT(programObject); ASSERT(programObject);
if (programObject->setUniform1iv(location, count, value) == setUniform1iImpl(programObject, location, count, value);
Program::SetUniformResult::SamplerChanged)
{
mGLState.setObjectDirty(GL_PROGRAM);
}
} }
void Context::programUniform2iv(GLuint program, GLint location, GLsizei count, const GLint *value) void Context::programUniform2iv(GLuint program, GLint location, GLsizei count, const GLint *value)
......
...@@ -1587,6 +1587,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1587,6 +1587,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
gl::LabeledObject *getLabeledObject(GLenum identifier, GLuint name) const; gl::LabeledObject *getLabeledObject(GLenum identifier, GLuint name) const;
gl::LabeledObject *getLabeledObjectFromPtr(const void *ptr) const; gl::LabeledObject *getLabeledObjectFromPtr(const void *ptr) const;
void setUniform1iImpl(Program *program, GLint location, GLsizei count, const GLint *v);
ContextState mState; ContextState mState;
bool mSkipValidation; bool mSkipValidation;
bool mDisplayTextureShareGroup; bool mDisplayTextureShareGroup;
......
...@@ -434,6 +434,7 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context, ...@@ -434,6 +434,7 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
state->mLinkedShaderStages = ShaderBitSet(stream.readInt<uint8_t>()); state->mLinkedShaderStages = ShaderBitSet(stream.readInt<uint8_t>());
state->updateTransformFeedbackStrides(); state->updateTransformFeedbackStrides();
state->updateActiveSamplers();
return program->getImplementation()->load(context, infoLog, &stream); return program->getImplementation()->load(context, infoLog, &stream);
} }
......
...@@ -794,9 +794,11 @@ ProgramState::ProgramState() ...@@ -794,9 +794,11 @@ ProgramState::ProgramState()
mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles), mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles),
mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip), mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip),
mGeometryShaderInvocations(1), mGeometryShaderInvocations(1),
mGeometryShaderMaxVertices(0) mGeometryShaderMaxVertices(0),
mActiveSamplerRefCounts{}
{ {
mComputeShaderLocalSize.fill(1); mComputeShaderLocalSize.fill(1);
mActiveSamplerTypes.fill(TextureType::InvalidEnum);
} }
ProgramState::~ProgramState() ProgramState::~ProgramState()
...@@ -1226,8 +1228,6 @@ Error Program::link(const gl::Context *context) ...@@ -1226,8 +1228,6 @@ Error Program::link(const gl::Context *context)
initInterfaceBlockBindings(); initInterfaceBlockBindings();
setUniformValuesFromBindingQualifiers();
// According to GLES 3.0/3.1 spec for LinkProgram and UseProgram, // According to GLES 3.0/3.1 spec for LinkProgram and UseProgram,
// Only successfully linked program can replace the executables. // Only successfully linked program can replace the executables.
ASSERT(mLinked); ASSERT(mLinked);
...@@ -1236,6 +1236,11 @@ Error Program::link(const gl::Context *context) ...@@ -1236,6 +1236,11 @@ Error Program::link(const gl::Context *context)
// Mark implementation-specific unreferenced uniforms as ignored. // Mark implementation-specific unreferenced uniforms as ignored.
mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings); mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings);
// Must be called after markUnusedUniformLocations.
mState.updateActiveSamplers();
setUniformValuesFromBindingQualifiers();
// Save to the program cache. // Save to the program cache.
if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() || if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() ||
!context->getWorkarounds().disableProgramCachingForTransformFeedback)) !context->getWorkarounds().disableProgramCachingForTransformFeedback))
...@@ -1287,6 +1292,22 @@ void ProgramState::updateTransformFeedbackStrides() ...@@ -1287,6 +1292,22 @@ void ProgramState::updateTransformFeedbackStrides()
} }
} }
void ProgramState::updateActiveSamplers()
{
for (SamplerBinding &samplerBinding : mSamplerBindings)
{
if (samplerBinding.unreferenced)
continue;
for (GLint textureUnit : samplerBinding.boundTextureUnits)
{
mActiveSamplerRefCounts[textureUnit]++;
mActiveSamplerTypes[textureUnit] = getSamplerUniformTextureType(textureUnit);
mActiveSamplersMask.set(textureUnit);
}
}
}
// Returns the program object to an unlinked state, before re-linking, or at destruction // Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink() void Program::unlink()
{ {
...@@ -2046,60 +2067,22 @@ void Program::validate(const Caps &caps) ...@@ -2046,60 +2067,22 @@ void Program::validate(const Caps &caps)
bool Program::validateSamplersImpl(InfoLog *infoLog, const Caps &caps) bool Program::validateSamplersImpl(InfoLog *infoLog, const Caps &caps)
{ {
if (mTextureUnitTypesCache.empty())
{
mTextureUnitTypesCache.resize(caps.maxCombinedTextureImageUnits, TextureType::InvalidEnum);
}
else
{
std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(),
TextureType::InvalidEnum);
}
// if any two active samplers in a program are of different types, but refer to the same // 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 // texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error. // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
for (const auto &samplerBinding : mState.mSamplerBindings) for (size_t textureUnit : mState.mActiveSamplersMask)
{ {
if (samplerBinding.unreferenced) if (mState.mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
continue;
TextureType textureType = samplerBinding.textureType;
for (GLuint textureUnit : samplerBinding.boundTextureUnits)
{ {
if (textureUnit >= caps.maxCombinedTextureImageUnits) if (infoLog)
{ {
if (infoLog) (*infoLog) << "Samplers of conflicting types refer to the same texture "
{ "image unit ("
(*infoLog) << "Sampler uniform (" << textureUnit << textureUnit << ").";
<< ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
<< caps.maxCombinedTextureImageUnits << ")";
}
mCachedValidateSamplersResult = false;
return false;
} }
if (mTextureUnitTypesCache[textureUnit] != TextureType::InvalidEnum) mCachedValidateSamplersResult = false;
{ return false;
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;
}
} }
} }
...@@ -2651,8 +2634,8 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms) ...@@ -2651,8 +2634,8 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
{ {
const auto &samplerUniform = mState.mUniforms[samplerIndex]; const auto &samplerUniform = mState.mUniforms[samplerIndex];
TextureType textureType = SamplerTypeToTextureType(samplerUniform.type); TextureType textureType = SamplerTypeToTextureType(samplerUniform.type);
mState.mSamplerBindings.emplace_back( unsigned int elementCount = samplerUniform.getBasicTypeElementCount();
SamplerBinding(textureType, samplerUniform.getBasicTypeElementCount(), false)); mState.mSamplerBindings.emplace_back(textureType, elementCount, false);
} }
} }
...@@ -3469,15 +3452,93 @@ void Program::updateSamplerUniform(const VariableLocation &locationInfo, ...@@ -3469,15 +3452,93 @@ void Program::updateSamplerUniform(const VariableLocation &locationInfo,
{ {
ASSERT(mState.isSamplerUniformIndex(locationInfo.index)); ASSERT(mState.isSamplerUniformIndex(locationInfo.index));
GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationInfo.index); GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationInfo.index);
std::vector<GLuint> *boundTextureUnits = SamplerBinding &samplerBinding = mState.mSamplerBindings[samplerIndex];
&mState.mSamplerBindings[samplerIndex].boundTextureUnits; std::vector<GLuint> &boundTextureUnits = samplerBinding.boundTextureUnits;
if (samplerBinding.unreferenced)
return;
// Update the sampler uniforms.
for (GLsizei arrayIndex = 0; arrayIndex < clampedCount; ++arrayIndex)
{
GLint oldSamplerIndex = boundTextureUnits[arrayIndex + locationInfo.arrayIndex];
GLint newSamplerIndex = v[arrayIndex];
if (oldSamplerIndex == newSamplerIndex)
continue;
boundTextureUnits[arrayIndex + locationInfo.arrayIndex] = newSamplerIndex;
// Update the reference counts.
uint32_t &oldRefCount = mState.mActiveSamplerRefCounts[oldSamplerIndex];
uint32_t &newRefCount = mState.mActiveSamplerRefCounts[newSamplerIndex];
ASSERT(oldRefCount > 0);
ASSERT(newRefCount < std::numeric_limits<uint32_t>::max());
oldRefCount--;
newRefCount++;
std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.arrayIndex); // Check for binding type change.
TextureType &newSamplerType = mState.mActiveSamplerTypes[newSamplerIndex];
TextureType &oldSamplerType = mState.mActiveSamplerTypes[oldSamplerIndex];
if (newRefCount == 1)
{
newSamplerType = samplerBinding.textureType;
mState.mActiveSamplersMask.set(newSamplerIndex);
}
else if (newSamplerType != samplerBinding.textureType)
{
// Conflict detected. Ensure we reset it properly.
newSamplerType = TextureType::InvalidEnum;
}
// Unset previously active sampler.
if (oldRefCount == 0)
{
oldSamplerType = TextureType::InvalidEnum;
mState.mActiveSamplersMask.reset(oldSamplerIndex);
}
else if (oldSamplerType == TextureType::InvalidEnum)
{
// Previous conflict. Check if this new change fixed the conflict.
oldSamplerType = mState.getSamplerUniformTextureType(oldSamplerIndex);
}
}
// Invalidate the validation cache. // Invalidate the validation cache.
mCachedValidateSamplersResult.reset(); mCachedValidateSamplersResult.reset();
} }
TextureType ProgramState::getSamplerUniformTextureType(size_t textureUnitIndex) const
{
TextureType foundType = TextureType::InvalidEnum;
for (const SamplerBinding &binding : mSamplerBindings)
{
if (binding.unreferenced)
continue;
// A conflict exists if samplers of different types are sourced by the same texture unit.
// We need to check all bound textures to detect this error case.
for (GLuint textureUnit : binding.boundTextureUnits)
{
if (textureUnit == textureUnitIndex)
{
if (foundType == TextureType::InvalidEnum)
{
foundType = binding.textureType;
}
else if (foundType != binding.textureType)
{
return TextureType::InvalidEnum;
}
}
}
}
return foundType;
}
template <typename T> template <typename T>
GLsizei Program::clampUniformCount(const VariableLocation &locationInfo, GLsizei Program::clampUniformCount(const VariableLocation &locationInfo,
GLsizei count, GLsizei count,
......
...@@ -353,6 +353,10 @@ class ProgramState final : angle::NonCopyable ...@@ -353,6 +353,10 @@ class ProgramState final : angle::NonCopyable
friend class Program; friend class Program;
void updateTransformFeedbackStrides(); void updateTransformFeedbackStrides();
void updateActiveSamplers();
// Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'.
TextureType getSamplerUniformTextureType(size_t textureUnitIndex) const;
std::string mLabel; std::string mLabel;
...@@ -427,6 +431,11 @@ class ProgramState final : angle::NonCopyable ...@@ -427,6 +431,11 @@ class ProgramState final : angle::NonCopyable
// The size of the data written to each transform feedback buffer per vertex. // The size of the data written to each transform feedback buffer per vertex.
std::vector<GLsizei> mTransformFeedbackStrides; std::vector<GLsizei> mTransformFeedbackStrides;
// Cached mask of active samplers and sampler types.
ActiveTextureMask mActiveSamplersMask;
ActiveTextureArray<uint32_t> mActiveSamplerRefCounts;
ActiveTextureArray<TextureType> mActiveSamplerTypes;
}; };
class ProgramBindings final : angle::NonCopyable class ProgramBindings final : angle::NonCopyable
...@@ -752,6 +761,13 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -752,6 +761,13 @@ class Program final : angle::NonCopyable, public LabeledObject
return mState.mTransformFeedbackStrides; return mState.mTransformFeedbackStrides;
} }
const ActiveTextureMask &getActiveSamplersMask() const { return mState.mActiveSamplersMask; }
const ActiveTextureArray<TextureType> &getActiveSamplerTypes() const
{
return mState.mActiveSamplerTypes;
}
private: private:
~Program() override; ~Program() override;
...@@ -868,7 +884,6 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -868,7 +884,6 @@ class Program final : angle::NonCopyable, public LabeledObject
// Cache for sampler validation // Cache for sampler validation
Optional<bool> mCachedValidateSamplersResult; Optional<bool> mCachedValidateSamplersResult;
std::vector<TextureType> mTextureUnitTypesCache;
}; };
} // namespace gl } // namespace gl
......
...@@ -2653,60 +2653,56 @@ Error State::syncProgramTextures(const Context *context) ...@@ -2653,60 +2653,56 @@ Error State::syncProgramTextures(const Context *context)
// initialized. // initialized.
mCachedTexturesInitState = InitState::Initialized; mCachedTexturesInitState = InitState::Initialized;
for (const SamplerBinding &samplerBinding : mProgram->getSamplerBindings()) const ActiveTextureMask &activeTextures = mProgram->getActiveSamplersMask();
const ActiveTextureArray<TextureType> &textureTypes = mProgram->getActiveSamplerTypes();
for (size_t textureUnitIndex : activeTextures)
{ {
if (samplerBinding.unreferenced) TextureType textureType = textureTypes[textureUnitIndex];
continue;
TextureType textureType = samplerBinding.textureType; Texture *texture = getSamplerTexture(textureUnitIndex, textureType);
for (GLuint textureUnitIndex : samplerBinding.boundTextureUnits) Sampler *sampler = getSampler(textureUnitIndex);
{ ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size());
Texture *texture = getSamplerTexture(textureUnitIndex, textureType); ASSERT(static_cast<size_t>(textureUnitIndex) < newActiveTextures.size());
Sampler *sampler = getSampler(textureUnitIndex);
ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size());
ASSERT(static_cast<size_t>(textureUnitIndex) < newActiveTextures.size());
ASSERT(texture); ASSERT(texture);
// Mark the texture binding bit as dirty if the texture completeness changes. // Mark the texture binding bit as dirty if the texture completeness changes.
// TODO(jmadill): Use specific dirty bit for completeness change. // TODO(jmadill): Use specific dirty bit for completeness change.
if (texture->isSamplerComplete(context, sampler) && if (texture->isSamplerComplete(context, sampler) &&
!mDrawFramebuffer->hasTextureAttachment(texture)) !mDrawFramebuffer->hasTextureAttachment(texture))
{ {
ANGLE_TRY(texture->syncState(context)); ANGLE_TRY(texture->syncState(context));
mCompleteTextureCache[textureUnitIndex] = texture; mCompleteTextureCache[textureUnitIndex] = texture;
} }
else else
{ {
mCompleteTextureCache[textureUnitIndex] = nullptr; mCompleteTextureCache[textureUnitIndex] = nullptr;
} }
// Bind the texture unconditionally, to recieve completeness change notifications. // Bind the texture unconditionally, to recieve completeness change notifications.
mCompleteTextureBindings[textureUnitIndex].bind(texture->getSubject()); mCompleteTextureBindings[textureUnitIndex].bind(texture->getSubject());
mActiveTexturesMask.set(textureUnitIndex); newActiveTextures.set(textureUnitIndex);
newActiveTextures.set(textureUnitIndex);
if (sampler != nullptr) if (sampler != nullptr)
{ {
sampler->syncState(context); sampler->syncState(context);
} }
if (texture->initState() == InitState::MayNeedInit) if (texture->initState() == InitState::MayNeedInit)
{ {
mCachedTexturesInitState = InitState::MayNeedInit; mCachedTexturesInitState = InitState::MayNeedInit;
}
} }
} }
// Unset now missing textures. // Unset now missing textures.
ActiveTextureMask negativeMask = mActiveTexturesMask & ~newActiveTextures; ActiveTextureMask negativeMask = activeTextures & ~newActiveTextures;
if (negativeMask.any()) if (negativeMask.any())
{ {
for (auto textureIndex : negativeMask) for (auto textureIndex : negativeMask)
{ {
mCompleteTextureBindings[textureIndex].reset(); mCompleteTextureBindings[textureIndex].reset();
mCompleteTextureCache[textureIndex] = nullptr; mCompleteTextureCache[textureIndex] = nullptr;
mActiveTexturesMask.reset(textureIndex);
} }
} }
...@@ -2830,7 +2826,10 @@ Error State::clearUnclearedActiveTextures(const Context *context) ...@@ -2830,7 +2826,10 @@ Error State::clearUnclearedActiveTextures(const Context *context)
ASSERT(!mDirtyObjects[DIRTY_OBJECT_PROGRAM_TEXTURES]); ASSERT(!mDirtyObjects[DIRTY_OBJECT_PROGRAM_TEXTURES]);
for (auto textureIndex : mActiveTexturesMask) if (!mProgram)
return NoError();
for (auto textureIndex : mProgram->getActiveSamplersMask())
{ {
Texture *texture = mCompleteTextureCache[textureIndex]; Texture *texture = mCompleteTextureCache[textureIndex];
if (texture) if (texture)
......
...@@ -467,7 +467,6 @@ class State : public angle::ObserverInterface, angle::NonCopyable ...@@ -467,7 +467,6 @@ class State : public angle::ObserverInterface, angle::NonCopyable
GLenum format); GLenum format);
const ImageUnit &getImageUnit(GLuint unit) const; const ImageUnit &getImageUnit(GLuint unit) const;
const ActiveTextureMask &getActiveTexturesMask() const { return mActiveTexturesMask; }
const std::vector<Texture *> &getCompleteTextureCache() const { return mCompleteTextureCache; } const std::vector<Texture *> &getCompleteTextureCache() const { return mCompleteTextureCache; }
ComponentTypeMask getCurrentValuesTypeMask() const { return mCurrentValuesTypeMask; } ComponentTypeMask getCurrentValuesTypeMask() const { return mCurrentValuesTypeMask; }
...@@ -559,7 +558,6 @@ class State : public angle::ObserverInterface, angle::NonCopyable ...@@ -559,7 +558,6 @@ class State : public angle::ObserverInterface, angle::NonCopyable
std::vector<Texture *> mCompleteTextureCache; std::vector<Texture *> mCompleteTextureCache;
std::vector<angle::ObserverBinding> mCompleteTextureBindings; std::vector<angle::ObserverBinding> mCompleteTextureBindings;
InitState mCachedTexturesInitState; InitState mCachedTexturesInitState;
ActiveTextureMask mActiveTexturesMask;
using SamplerBindingVector = std::vector<BindingPointer<Sampler>>; using SamplerBindingVector = std::vector<BindingPointer<Sampler>>;
SamplerBindingVector mSamplers; SamplerBindingVector mSamplers;
......
...@@ -251,7 +251,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, ...@@ -251,7 +251,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
mTexturesDirty = false; mTexturesDirty = false;
// TODO(jmadill): Should probably merge this for loop with programVk's descriptor update. // TODO(jmadill): Should probably merge this for loop with programVk's descriptor update.
for (size_t textureIndex : state.getActiveTexturesMask()) for (size_t textureIndex : programGL->getActiveSamplersMask())
{ {
TextureVk *textureVk = mActiveTextures[textureIndex]; TextureVk *textureVk = mActiveTextures[textureIndex];
ANGLE_TRY(textureVk->ensureImageInitialized(this)); ANGLE_TRY(textureVk->ensureImageInitialized(this));
......
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