зеркало из https://github.com/AvaloniaUI/angle.git
Refactor program pipeline handling.
In preparation for moving more code from gl::Program to gl::ProgramExecutable so it can be shared with ProgramPipeline. Bug: angleproject:6566 Change-Id: Icb7ecccb37ae8e0d7d5fef8968f0dd7ef6fe6150 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3226305 Reviewed-by: Tim Van Patten <timvp@google.com> Reviewed-by: Cody Northrop <cnorthrop@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Родитель
577cfeff92
Коммит
3a9f18f135
|
@ -281,7 +281,8 @@ enum SubjectIndexes : angle::SubjectIndex
|
|||
kSamplerMaxSubjectIndex = kSampler0SubjectIndex + IMPLEMENTATION_MAX_ACTIVE_TEXTURES,
|
||||
kVertexArraySubjectIndex = kSamplerMaxSubjectIndex,
|
||||
kReadFramebufferSubjectIndex,
|
||||
kDrawFramebufferSubjectIndex
|
||||
kDrawFramebufferSubjectIndex,
|
||||
kProgramPipelineSubjectIndex,
|
||||
};
|
||||
|
||||
bool IsClearBufferEnabled(const FramebufferState &fbState, GLenum buffer, GLint drawbuffer)
|
||||
|
@ -316,7 +317,6 @@ bool GetSaveAndRestoreState(const egl::AttributeMap &attribs)
|
|||
{
|
||||
return (attribs.get(EGL_EXTERNAL_CONTEXT_SAVE_STATE_ANGLE, EGL_FALSE) == EGL_TRUE);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#if defined(ANGLE_PLATFORM_APPLE)
|
||||
|
@ -400,6 +400,7 @@ Context::Context(egl::Display *display,
|
|||
mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
|
||||
mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
|
||||
mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
|
||||
mProgramPipelineObserverBinding(this, kProgramPipelineSubjectIndex),
|
||||
mThreadPool(nullptr),
|
||||
mFrameCapture(new angle::FrameCapture),
|
||||
mRefCount(0),
|
||||
|
@ -1293,8 +1294,7 @@ void Context::useProgramStages(ProgramPipelineID pipeline,
|
|||
pipeline);
|
||||
|
||||
ASSERT(programPipeline);
|
||||
ANGLE_CONTEXT_TRY(mState.useProgramStages(this, programPipeline, stages, shaderProgram));
|
||||
mStateCache.onProgramExecutableChange(this);
|
||||
ANGLE_CONTEXT_TRY(programPipeline->useProgramStages(this, stages, shaderProgram));
|
||||
}
|
||||
|
||||
void Context::bindTransformFeedback(GLenum target, TransformFeedbackID transformFeedbackHandle)
|
||||
|
@ -1310,7 +1310,12 @@ void Context::bindProgramPipeline(ProgramPipelineID pipelineHandle)
|
|||
ProgramPipeline *pipeline = mState.mProgramPipelineManager->checkProgramPipelineAllocation(
|
||||
mImplementation.get(), pipelineHandle);
|
||||
ANGLE_CONTEXT_TRY(mState.setProgramPipelineBinding(this, pipeline));
|
||||
if (pipeline && pipeline->isLinked())
|
||||
{
|
||||
ANGLE_CONTEXT_TRY(mState.onProgramPipelineExecutableChange(this));
|
||||
}
|
||||
mStateCache.onProgramExecutableChange(this);
|
||||
mProgramPipelineObserverBinding.bind(pipeline);
|
||||
}
|
||||
|
||||
void Context::beginQuery(QueryType target, QueryID query)
|
||||
|
@ -8924,6 +8929,22 @@ void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess
|
|||
}
|
||||
break;
|
||||
|
||||
case kProgramPipelineSubjectIndex:
|
||||
switch (message)
|
||||
{
|
||||
case angle::SubjectMessage::SubjectChanged:
|
||||
ANGLE_CONTEXT_TRY(mState.onProgramPipelineExecutableChange(this));
|
||||
mStateCache.onProgramExecutableChange(this);
|
||||
break;
|
||||
case angle::SubjectMessage::ProgramRelinked:
|
||||
ANGLE_CONTEXT_TRY(mState.mProgramPipeline->link(this));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (index < kTextureMaxSubjectIndex)
|
||||
{
|
||||
|
|
|
@ -775,6 +775,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
|
|||
angle::ObserverBinding mVertexArrayObserverBinding;
|
||||
angle::ObserverBinding mDrawFramebufferObserverBinding;
|
||||
angle::ObserverBinding mReadFramebufferObserverBinding;
|
||||
angle::ObserverBinding mProgramPipelineObserverBinding;
|
||||
std::vector<angle::ObserverBinding> mUniformBufferObserverBindings;
|
||||
std::vector<angle::ObserverBinding> mAtomicCounterBufferObserverBindings;
|
||||
std::vector<angle::ObserverBinding> mShaderStorageBufferObserverBindings;
|
||||
|
|
|
@ -1222,7 +1222,6 @@ ProgramState::ProgramState()
|
|||
mAttachedShaders{},
|
||||
mLocationsUsedForXfbExtension(0),
|
||||
mAtomicCounterUniformRange(0, 0),
|
||||
mYUVOutput(false),
|
||||
mBinaryRetrieveableHint(false),
|
||||
mSeparable(false),
|
||||
mNumViews(-1),
|
||||
|
@ -1631,13 +1630,6 @@ angle::Result Program::linkImpl(const Context *context)
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
if (!linkOutputVariables(context->getCaps(), context->getExtensions(),
|
||||
context->getClientVersion(), combinedImageUniforms,
|
||||
combinedShaderStorageBlocks))
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
gl::Shader *vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
|
||||
if (vertexShader)
|
||||
{
|
||||
|
@ -1648,6 +1640,15 @@ angle::Result Program::linkImpl(const Context *context)
|
|||
gl::Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
|
||||
if (fragmentShader)
|
||||
{
|
||||
if (!mState.mExecutable->linkValidateOutputVariables(
|
||||
context->getCaps(), context->getExtensions(), context->getClientVersion(),
|
||||
combinedImageUniforms, combinedShaderStorageBlocks,
|
||||
fragmentShader->getActiveOutputVariables(), fragmentShader->getShaderVersion(),
|
||||
mFragmentOutputLocations, mFragmentOutputIndexes))
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
mState.mEarlyFramentTestsOptimization =
|
||||
fragmentShader->hasEarlyFragmentTestsOptimization();
|
||||
mState.mSpecConstUsageBits |= fragmentShader->getSpecConstUsageBits();
|
||||
|
@ -1838,10 +1839,6 @@ void Program::unlink()
|
|||
|
||||
mState.mUniformLocations.clear();
|
||||
mState.mBufferVariables.clear();
|
||||
mState.mOutputVariableTypes.clear();
|
||||
mState.mDrawBufferTypeMask.reset();
|
||||
mState.mYUVOutput = false;
|
||||
mState.mActiveOutputVariables.reset();
|
||||
mState.mComputeShaderLocalSize.fill(1);
|
||||
mState.mNumViews = -1;
|
||||
mState.mDrawIDLocation = -1;
|
||||
|
@ -2326,12 +2323,6 @@ size_t Program::getOutputResourceCount() const
|
|||
return (mLinked ? mState.mExecutable->getOutputVariables().size() : 0);
|
||||
}
|
||||
|
||||
const std::vector<GLenum> &Program::getOutputVariableTypes() const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
return mState.mOutputVariableTypes;
|
||||
}
|
||||
|
||||
void Program::getResourceName(const std::string name,
|
||||
GLsizei bufSize,
|
||||
GLsizei *length,
|
||||
|
@ -2453,12 +2444,6 @@ const gl::ProgramAliasedBindings &Program::getFragmentOutputIndexes() const
|
|||
return mFragmentOutputIndexes;
|
||||
}
|
||||
|
||||
ComponentTypeMask Program::getDrawBufferTypeMask() const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
return mState.mDrawBufferTypeMask;
|
||||
}
|
||||
|
||||
const std::vector<GLsizei> &Program::getTransformFeedbackStrides() const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
|
@ -3999,367 +3984,6 @@ bool Program::linkInterfaceBlocks(const Caps &caps,
|
|||
return true;
|
||||
}
|
||||
|
||||
int Program::getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const
|
||||
{
|
||||
if (outputVariable.location != -1)
|
||||
{
|
||||
return outputVariable.location;
|
||||
}
|
||||
int apiLocation = mFragmentOutputLocations.getBinding(outputVariable);
|
||||
if (apiLocation != -1)
|
||||
{
|
||||
return apiLocation;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Program::isOutputSecondaryForLink(const sh::ShaderVariable &outputVariable) const
|
||||
{
|
||||
if (outputVariable.index != -1)
|
||||
{
|
||||
ASSERT(outputVariable.index == 0 || outputVariable.index == 1);
|
||||
return (outputVariable.index == 1);
|
||||
}
|
||||
int apiIndex = mFragmentOutputIndexes.getBinding(outputVariable);
|
||||
if (apiIndex != -1)
|
||||
{
|
||||
// Index layout qualifier from the shader takes precedence, so the index from the API is
|
||||
// checked only if the index was not set in the shader. This is not specified in the EXT
|
||||
// spec, but is specified in desktop OpenGL specs.
|
||||
return (apiIndex == 1);
|
||||
}
|
||||
// EXT_blend_func_extended: Outputs get index 0 by default.
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
|
||||
unsigned int baseLocation,
|
||||
unsigned int elementCount,
|
||||
const std::vector<VariableLocation> &reservedLocations,
|
||||
unsigned int variableIndex)
|
||||
{
|
||||
if (baseLocation + elementCount > outputLocations.size())
|
||||
{
|
||||
elementCount = baseLocation < outputLocations.size()
|
||||
? static_cast<unsigned int>(outputLocations.size() - baseLocation)
|
||||
: 0;
|
||||
}
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
if (outputLocations[location].used())
|
||||
{
|
||||
VariableLocation locationInfo(elementIndex, variableIndex);
|
||||
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
|
||||
reservedLocations.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
|
||||
unsigned int baseLocation,
|
||||
unsigned int elementCount,
|
||||
const std::vector<VariableLocation> &reservedLocations,
|
||||
unsigned int variableIndex,
|
||||
sh::ShaderVariable &outputVariable)
|
||||
{
|
||||
if (baseLocation + elementCount > outputLocations.size())
|
||||
{
|
||||
outputLocations.resize(baseLocation + elementCount);
|
||||
}
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
VariableLocation locationInfo(elementIndex, variableIndex);
|
||||
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
|
||||
reservedLocations.end())
|
||||
{
|
||||
outputVariable.location = baseLocation;
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
outputLocations[location] = locationInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool Program::linkOutputVariables(const Caps &caps,
|
||||
const Extensions &extensions,
|
||||
const Version &version,
|
||||
GLuint combinedImageUniformsCount,
|
||||
GLuint combinedShaderStorageBlocksCount)
|
||||
{
|
||||
InfoLog &infoLog = mState.mExecutable->getInfoLog();
|
||||
Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
|
||||
|
||||
ASSERT(mState.mOutputVariableTypes.empty());
|
||||
ASSERT(mState.mActiveOutputVariables.none());
|
||||
ASSERT(mState.mDrawBufferTypeMask.none());
|
||||
ASSERT(!mState.mYUVOutput);
|
||||
|
||||
if (!fragmentShader)
|
||||
{
|
||||
// No fragment shader, so nothing to link
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<sh::ShaderVariable> &outputVariables =
|
||||
fragmentShader->getActiveOutputVariables();
|
||||
|
||||
// Gather output variable types
|
||||
for (const sh::ShaderVariable &outputVariable : outputVariables)
|
||||
{
|
||||
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
|
||||
outputVariable.name != "gl_FragData")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int baseLocation =
|
||||
(outputVariable.location == -1 ? 0u
|
||||
: static_cast<unsigned int>(outputVariable.location));
|
||||
|
||||
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
||||
// structures, so we may use getBasicTypeElementCount().
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
if (location >= mState.mOutputVariableTypes.size())
|
||||
{
|
||||
mState.mOutputVariableTypes.resize(location + 1, GL_NONE);
|
||||
}
|
||||
ASSERT(location < mState.mActiveOutputVariables.size());
|
||||
mState.mActiveOutputVariables.set(location);
|
||||
mState.mOutputVariableTypes[location] = VariableComponentType(outputVariable.type);
|
||||
ComponentType componentType =
|
||||
GLenumToComponentType(mState.mOutputVariableTypes[location]);
|
||||
SetComponentTypeMask(componentType, location, &mState.mDrawBufferTypeMask);
|
||||
}
|
||||
|
||||
if (outputVariable.yuv)
|
||||
{
|
||||
ASSERT(outputVariables.size() == 1);
|
||||
mState.mYUVOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 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() >
|
||||
static_cast<GLuint>(caps.maxCombinedShaderOutputResources))
|
||||
{
|
||||
infoLog
|
||||
<< "The sum of the number of active image uniforms, active shader storage blocks "
|
||||
"and active fragment shader outputs exceeds "
|
||||
"MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
|
||||
<< caps.maxCombinedShaderOutputResources << ")";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this step for GLES2 shaders.
|
||||
if (fragmentShader && fragmentShader->getShaderVersion() == 100)
|
||||
return true;
|
||||
|
||||
mState.mExecutable->mOutputVariables = outputVariables;
|
||||
mState.mExecutable->mYUVOutput = mState.mYUVOutput;
|
||||
// TODO(jmadill): any caps validation here?
|
||||
|
||||
// EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
|
||||
// output array in explicit terms.
|
||||
//
|
||||
// Assuming fragData is an output array, you can defend the position that:
|
||||
// P1) you must support binding "fragData" because it's specified
|
||||
// P2) you must support querying "fragData[x]" because it's specified
|
||||
// P3) you must support binding "fragData[0]" because it's a frequently used pattern
|
||||
//
|
||||
// Then you can make the leap of faith:
|
||||
// P4) you must support binding "fragData[x]" because you support "fragData[0]"
|
||||
// P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
|
||||
//
|
||||
// The spec brings in the "world of arrays" when it mentions binding the arrays and the
|
||||
// automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
|
||||
// must infer the only possible interpretation (?). Note again: this need of interpretation
|
||||
// might be completely off of what GL spec logic is.
|
||||
//
|
||||
// The other complexity is that unless you implement this feature, it's hard to understand what
|
||||
// should happen when the client invokes the feature. You cannot add an additional error as it
|
||||
// is not specified. One can ignore it, but obviously it creates the discrepancies...
|
||||
|
||||
std::vector<VariableLocation> reservedLocations;
|
||||
|
||||
// Process any output API bindings for arrays that don't alias to the first element.
|
||||
for (const auto &binding : mFragmentOutputLocations)
|
||||
{
|
||||
size_t nameLengthWithoutArrayIndex;
|
||||
unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex);
|
||||
if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (unsigned int outputVariableIndex = 0;
|
||||
outputVariableIndex < mState.mExecutable->getOutputVariables().size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable =
|
||||
mState.mExecutable->getOutputVariables()[outputVariableIndex];
|
||||
// Check that the binding corresponds to an output array and its array index fits.
|
||||
if (outputVariable.isBuiltIn() || !outputVariable.isArray() ||
|
||||
!angle::BeginsWith(outputVariable.name, binding.first,
|
||||
nameLengthWithoutArrayIndex) ||
|
||||
arrayIndex >= outputVariable.getOutermostArraySize())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the API index that corresponds to this exact binding.
|
||||
// This index may differ from the index used for the array's base.
|
||||
auto &outputLocations = mFragmentOutputIndexes.getBindingByName(binding.first) == 1
|
||||
? mState.mExecutable->mSecondaryOutputLocations
|
||||
: mState.mExecutable->mOutputLocations;
|
||||
unsigned int location = binding.second.location;
|
||||
VariableLocation locationInfo(arrayIndex, outputVariableIndex);
|
||||
if (location >= outputLocations.size())
|
||||
{
|
||||
outputLocations.resize(location + 1);
|
||||
}
|
||||
if (outputLocations[location].used())
|
||||
{
|
||||
infoLog << "Location of variable " << outputVariable.name
|
||||
<< " conflicts with another variable.";
|
||||
return false;
|
||||
}
|
||||
outputLocations[location] = locationInfo;
|
||||
|
||||
// Note the array binding location so that it can be skipped later.
|
||||
reservedLocations.push_back(locationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve locations for output variables whose location is fixed in the shader or through the
|
||||
// API. Otherwise, the remaining unallocated outputs will be processed later.
|
||||
for (unsigned int outputVariableIndex = 0;
|
||||
outputVariableIndex < mState.mExecutable->getOutputVariables().size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable =
|
||||
mState.mExecutable->getOutputVariables()[outputVariableIndex];
|
||||
|
||||
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
|
||||
if (outputVariable.isBuiltIn())
|
||||
continue;
|
||||
|
||||
int fixedLocation = getOutputLocationForLink(outputVariable);
|
||||
if (fixedLocation == -1)
|
||||
{
|
||||
// Here we're only reserving locations for variables whose location is fixed.
|
||||
continue;
|
||||
}
|
||||
unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
|
||||
|
||||
auto &outputLocations = isOutputSecondaryForLink(outputVariable)
|
||||
? mState.mExecutable->mSecondaryOutputLocations
|
||||
: mState.mExecutable->mOutputLocations;
|
||||
|
||||
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
||||
// structures, so we may use getBasicTypeElementCount().
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex))
|
||||
{
|
||||
infoLog << "Location of variable " << outputVariable.name
|
||||
<< " conflicts with another variable.";
|
||||
return false;
|
||||
}
|
||||
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex,
|
||||
mState.mExecutable->mOutputVariables[outputVariableIndex]);
|
||||
}
|
||||
|
||||
// Here we assign locations for the output variables that don't yet have them. Note that we're
|
||||
// not necessarily able to fit the variables optimally, since then we might have to try
|
||||
// different arrangements of output arrays. Now we just assign the locations in the order that
|
||||
// we got the output variables. The spec isn't clear on what kind of algorithm is required for
|
||||
// finding locations for the output variables, so this should be acceptable at least for now.
|
||||
GLuint maxLocation = static_cast<GLuint>(caps.maxDrawBuffers);
|
||||
if (!mState.mExecutable->getSecondaryOutputLocations().empty())
|
||||
{
|
||||
// EXT_blend_func_extended: Program outputs will be validated against
|
||||
// MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one.
|
||||
maxLocation = caps.maxDualSourceDrawBuffers;
|
||||
}
|
||||
|
||||
for (unsigned int outputVariableIndex = 0;
|
||||
outputVariableIndex < mState.mExecutable->getOutputVariables().size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable =
|
||||
mState.mExecutable->getOutputVariables()[outputVariableIndex];
|
||||
|
||||
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
|
||||
if (outputVariable.isBuiltIn())
|
||||
continue;
|
||||
|
||||
int fixedLocation = getOutputLocationForLink(outputVariable);
|
||||
auto &outputLocations = isOutputSecondaryForLink(outputVariable)
|
||||
? mState.mExecutable->mSecondaryOutputLocations
|
||||
: mState.mExecutable->mOutputLocations;
|
||||
unsigned int baseLocation = 0;
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
if (fixedLocation != -1)
|
||||
{
|
||||
// Secondary inputs might have caused the max location to drop below what has already
|
||||
// been explicitly assigned locations. Check for any fixed locations above the max
|
||||
// that should cause linking to fail.
|
||||
baseLocation = static_cast<unsigned int>(fixedLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No fixed location, so try to fit the output in unassigned locations.
|
||||
// Try baseLocations starting from 0 one at a time and see if the variable fits.
|
||||
while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount,
|
||||
reservedLocations, outputVariableIndex))
|
||||
{
|
||||
baseLocation++;
|
||||
}
|
||||
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex,
|
||||
mState.mExecutable->mOutputVariables[outputVariableIndex]);
|
||||
}
|
||||
|
||||
// Check for any elements assigned above the max location that are actually used.
|
||||
if (baseLocation + elementCount > maxLocation &&
|
||||
(baseLocation >= maxLocation ||
|
||||
FindUsedOutputLocation(outputLocations, maxLocation,
|
||||
baseLocation + elementCount - maxLocation, reservedLocations,
|
||||
outputVariableIndex)))
|
||||
{
|
||||
// EXT_blend_func_extended: Linking can fail:
|
||||
// "if the explicit binding assignments do not leave enough space for the linker to
|
||||
// automatically assign a location for a varying out array, which requires multiple
|
||||
// contiguous locations."
|
||||
infoLog << "Could not fit output variable into available locations: "
|
||||
<< outputVariable.name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::setUniformValuesFromBindingQualifiers()
|
||||
{
|
||||
for (unsigned int samplerIndex : mState.mExecutable->getSamplerUniformRange())
|
||||
|
@ -4670,20 +4294,6 @@ angle::Result Program::serialize(const Context *context, angle::MemoryBuffer *bi
|
|||
"driver.";
|
||||
}
|
||||
|
||||
stream.writeInt(mState.mOutputVariableTypes.size());
|
||||
for (const auto &outputVariableType : mState.mOutputVariableTypes)
|
||||
{
|
||||
stream.writeInt(outputVariableType);
|
||||
}
|
||||
|
||||
static_assert(
|
||||
IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
|
||||
"All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
|
||||
stream.writeInt(static_cast<int>(mState.mDrawBufferTypeMask.to_ulong()));
|
||||
stream.writeInt(static_cast<int>(mState.mActiveOutputVariables.to_ulong()));
|
||||
|
||||
stream.writeBool(mState.isYUVOutput());
|
||||
|
||||
stream.writeInt(mState.getAtomicCounterUniformRange().low());
|
||||
stream.writeInt(mState.getAtomicCounterUniformRange().high());
|
||||
|
||||
|
@ -4778,21 +4388,6 @@ angle::Result Program::deserialize(const Context *context,
|
|||
mState.mBufferVariables.push_back(bufferVariable);
|
||||
}
|
||||
|
||||
size_t outputTypeCount = stream.readInt<size_t>();
|
||||
for (size_t outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
|
||||
{
|
||||
mState.mOutputVariableTypes.push_back(stream.readInt<GLenum>());
|
||||
}
|
||||
|
||||
static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
|
||||
"All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
|
||||
"into 32 bits each");
|
||||
mState.mDrawBufferTypeMask = gl::ComponentTypeMask(stream.readInt<uint32_t>());
|
||||
mState.mActiveOutputVariables =
|
||||
gl::DrawBufferMask(stream.readInt<gl::DrawBufferMask::value_type>());
|
||||
|
||||
stream.readBool(&mState.mYUVOutput);
|
||||
|
||||
unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
|
||||
unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
|
||||
mState.mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
|
||||
|
|
|
@ -252,7 +252,6 @@ class ProgramState final : angle::NonCopyable
|
|||
{
|
||||
return mExecutable->getProgramInputs();
|
||||
}
|
||||
DrawBufferMask getActiveOutputVariables() const { return mActiveOutputVariables; }
|
||||
const std::vector<sh::ShaderVariable> &getOutputVariables() const
|
||||
{
|
||||
return mExecutable->getOutputVariables();
|
||||
|
@ -352,12 +351,6 @@ class ProgramState final : angle::NonCopyable
|
|||
|
||||
uint32_t getLocationsUsedForXfbExtension() const { return mLocationsUsedForXfbExtension; }
|
||||
|
||||
const std::vector<GLenum> &getOutputVariableTypes() const { return mOutputVariableTypes; }
|
||||
|
||||
ComponentTypeMask getDrawBufferTypeMask() const { return mDrawBufferTypeMask; }
|
||||
|
||||
bool isYUVOutput() const { return mYUVOutput; }
|
||||
|
||||
bool hasBinaryRetrieveableHint() const { return mBinaryRetrieveableHint; }
|
||||
|
||||
bool isSeparable() const { return mSeparable; }
|
||||
|
@ -394,16 +387,6 @@ class ProgramState final : angle::NonCopyable
|
|||
std::vector<BufferVariable> mBufferVariables;
|
||||
RangeUI mAtomicCounterUniformRange;
|
||||
|
||||
DrawBufferMask mActiveOutputVariables;
|
||||
|
||||
// Fragment output variable base types: FLOAT, INT, or UINT. Ordered by location.
|
||||
std::vector<GLenum> mOutputVariableTypes;
|
||||
ComponentTypeMask mDrawBufferTypeMask;
|
||||
|
||||
// GL_EXT_YUV_target. YUV output shaders can only have one ouput and can only write to YUV
|
||||
// framebuffers.
|
||||
bool mYUVOutput;
|
||||
|
||||
bool mBinaryRetrieveableHint;
|
||||
bool mSeparable;
|
||||
bool mEarlyFramentTestsOptimization;
|
||||
|
@ -479,7 +462,7 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
void bindFragmentOutputIndex(GLuint index, const char *name);
|
||||
|
||||
// KHR_parallel_shader_compile
|
||||
// Try to link the program asynchrously. As a result, background threads may be launched to
|
||||
// Try to link the program asynchronously. As a result, background threads may be launched to
|
||||
// execute the linking tasks concurrently.
|
||||
angle::Result link(const Context *context);
|
||||
|
||||
|
@ -525,12 +508,6 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
|
||||
GLint getFragDataLocation(const std::string &name) const;
|
||||
size_t getOutputResourceCount() const;
|
||||
const std::vector<GLenum> &getOutputVariableTypes() const;
|
||||
DrawBufferMask getActiveOutputVariables() const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
return mState.mActiveOutputVariables;
|
||||
}
|
||||
|
||||
// EXT_blend_func_extended
|
||||
GLint getFragDataIndex(const std::string &name) const;
|
||||
|
@ -768,14 +745,6 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
|
||||
bool usesMultiview() const { return mState.usesMultiview(); }
|
||||
|
||||
ComponentTypeMask getDrawBufferTypeMask() const;
|
||||
|
||||
bool isYUVOutput() const
|
||||
{
|
||||
ASSERT(!mLinkingState);
|
||||
return mState.isYUVOutput();
|
||||
}
|
||||
|
||||
const std::vector<GLsizei> &getTransformFeedbackStrides() const;
|
||||
|
||||
// Program dirty bits.
|
||||
|
@ -844,14 +813,6 @@ class Program final : public LabeledObject, public angle::Subject
|
|||
|
||||
void updateLinkedShaderStages();
|
||||
|
||||
int getOutputLocationForLink(const sh::ShaderVariable &outputVariable) const;
|
||||
bool isOutputSecondaryForLink(const sh::ShaderVariable &outputVariable) const;
|
||||
bool linkOutputVariables(const Caps &caps,
|
||||
const Extensions &extensions,
|
||||
const Version &version,
|
||||
GLuint combinedImageUniformsCount,
|
||||
GLuint combinedShaderStorageBlocksCount);
|
||||
|
||||
void setUniformValuesFromBindingQualifiers();
|
||||
bool shouldIgnoreUniform(UniformLocation location) const;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "libANGLE/ProgramExecutable.h"
|
||||
|
||||
#include "common/string_utils.h"
|
||||
#include "libANGLE/Context.h"
|
||||
#include "libANGLE/Program.h"
|
||||
#include "libANGLE/Shader.h"
|
||||
|
@ -61,6 +62,94 @@ const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &
|
|||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
|
||||
unsigned int baseLocation,
|
||||
unsigned int elementCount,
|
||||
const std::vector<VariableLocation> &reservedLocations,
|
||||
unsigned int variableIndex)
|
||||
{
|
||||
if (baseLocation + elementCount > outputLocations.size())
|
||||
{
|
||||
elementCount = baseLocation < outputLocations.size()
|
||||
? static_cast<unsigned int>(outputLocations.size() - baseLocation)
|
||||
: 0;
|
||||
}
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
if (outputLocations[location].used())
|
||||
{
|
||||
VariableLocation locationInfo(elementIndex, variableIndex);
|
||||
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
|
||||
reservedLocations.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
|
||||
unsigned int baseLocation,
|
||||
unsigned int elementCount,
|
||||
const std::vector<VariableLocation> &reservedLocations,
|
||||
unsigned int variableIndex,
|
||||
sh::ShaderVariable &outputVariable)
|
||||
{
|
||||
if (baseLocation + elementCount > outputLocations.size())
|
||||
{
|
||||
outputLocations.resize(baseLocation + elementCount);
|
||||
}
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
VariableLocation locationInfo(elementIndex, variableIndex);
|
||||
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
|
||||
reservedLocations.end())
|
||||
{
|
||||
outputVariable.location = baseLocation;
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
outputLocations[location] = locationInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GetOutputLocationForLink(const ProgramAliasedBindings &fragmentOutputLocations,
|
||||
const sh::ShaderVariable &outputVariable)
|
||||
{
|
||||
if (outputVariable.location != -1)
|
||||
{
|
||||
return outputVariable.location;
|
||||
}
|
||||
int apiLocation = fragmentOutputLocations.getBinding(outputVariable);
|
||||
if (apiLocation != -1)
|
||||
{
|
||||
return apiLocation;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsOutputSecondaryForLink(const ProgramAliasedBindings &fragmentOutputIndexes,
|
||||
const sh::ShaderVariable &outputVariable)
|
||||
{
|
||||
if (outputVariable.index != -1)
|
||||
{
|
||||
ASSERT(outputVariable.index == 0 || outputVariable.index == 1);
|
||||
return (outputVariable.index == 1);
|
||||
}
|
||||
int apiIndex = fragmentOutputIndexes.getBinding(outputVariable);
|
||||
if (apiIndex != -1)
|
||||
{
|
||||
// Index layout qualifier from the shader takes precedence, so the index from the API is
|
||||
// checked only if the index was not set in the shader. This is not specified in the EXT
|
||||
// spec, but is specified in desktop OpenGL specs.
|
||||
return (apiIndex == 1);
|
||||
}
|
||||
// EXT_blend_func_extended: Outputs get index 0 by default.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ProgramExecutable::ProgramExecutable()
|
||||
|
@ -165,6 +254,7 @@ void ProgramExecutable::reset()
|
|||
mAtomicCounterBuffers.clear();
|
||||
mOutputVariables.clear();
|
||||
mOutputLocations.clear();
|
||||
mActiveOutputVariablesMask.reset();
|
||||
mSecondaryOutputLocations.clear();
|
||||
mYUVOutput = false;
|
||||
mSamplerBindings.clear();
|
||||
|
@ -186,6 +276,9 @@ void ProgramExecutable::reset()
|
|||
mTessGenSpacing = GL_NONE;
|
||||
mTessGenVertexOrder = GL_NONE;
|
||||
mTessGenPointMode = GL_NONE;
|
||||
|
||||
mOutputVariableTypes.clear();
|
||||
mDrawBufferTypeMask.reset();
|
||||
}
|
||||
|
||||
void ProgramExecutable::load(bool isSeparable, gl::BinaryInputStream *stream)
|
||||
|
@ -325,6 +418,22 @@ void ProgramExecutable::load(bool isSeparable, gl::BinaryInputStream *stream)
|
|||
mOutputLocations.push_back(locationData);
|
||||
}
|
||||
|
||||
mActiveOutputVariablesMask =
|
||||
gl::DrawBufferMask(stream->readInt<gl::DrawBufferMask::value_type>());
|
||||
|
||||
size_t outputTypeCount = stream->readInt<size_t>();
|
||||
for (size_t outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
|
||||
{
|
||||
mOutputVariableTypes.push_back(stream->readInt<GLenum>());
|
||||
}
|
||||
|
||||
static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
|
||||
"All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
|
||||
"into 32 bits each");
|
||||
mDrawBufferTypeMask = gl::ComponentTypeMask(stream->readInt<uint32_t>());
|
||||
|
||||
stream->readBool(&mYUVOutput);
|
||||
|
||||
size_t secondaryOutputVarCount = stream->readInt<size_t>();
|
||||
ASSERT(getSecondaryOutputLocations().empty());
|
||||
for (size_t outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex)
|
||||
|
@ -504,6 +613,21 @@ void ProgramExecutable::save(bool isSeparable, gl::BinaryOutputStream *stream) c
|
|||
stream->writeBool(outputVar.ignored);
|
||||
}
|
||||
|
||||
stream->writeInt(static_cast<int>(mActiveOutputVariablesMask.to_ulong()));
|
||||
|
||||
stream->writeInt(mOutputVariableTypes.size());
|
||||
for (const auto &outputVariableType : mOutputVariableTypes)
|
||||
{
|
||||
stream->writeInt(outputVariableType);
|
||||
}
|
||||
|
||||
static_assert(
|
||||
IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
|
||||
"All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
|
||||
stream->writeInt(static_cast<int>(mDrawBufferTypeMask.to_ulong()));
|
||||
|
||||
stream->writeBool(mYUVOutput);
|
||||
|
||||
stream->writeInt(getSecondaryOutputLocations().size());
|
||||
for (const auto &outputVar : getSecondaryOutputLocations())
|
||||
{
|
||||
|
@ -1049,4 +1173,260 @@ bool ProgramExecutable::validateSamplersImpl(InfoLog *infoLog, const Caps &caps)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ProgramExecutable::linkValidateOutputVariables(
|
||||
const Caps &caps,
|
||||
const Extensions &extensions,
|
||||
const Version &version,
|
||||
GLuint combinedImageUniformsCount,
|
||||
GLuint combinedShaderStorageBlocksCount,
|
||||
const std::vector<sh::ShaderVariable> &outputVariables,
|
||||
int fragmentShaderVersion,
|
||||
const ProgramAliasedBindings &fragmentOutputLocations,
|
||||
const ProgramAliasedBindings &fragmentOutputIndices)
|
||||
{
|
||||
ASSERT(mOutputVariableTypes.empty());
|
||||
ASSERT(mActiveOutputVariablesMask.none());
|
||||
ASSERT(mDrawBufferTypeMask.none());
|
||||
ASSERT(!mYUVOutput);
|
||||
|
||||
// Gather output variable types
|
||||
for (const sh::ShaderVariable &outputVariable : outputVariables)
|
||||
{
|
||||
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
|
||||
outputVariable.name != "gl_FragData")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int baseLocation =
|
||||
(outputVariable.location == -1 ? 0u
|
||||
: static_cast<unsigned int>(outputVariable.location));
|
||||
|
||||
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
||||
// structures, so we may use getBasicTypeElementCount().
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
|
||||
{
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
if (location >= mOutputVariableTypes.size())
|
||||
{
|
||||
mOutputVariableTypes.resize(location + 1, GL_NONE);
|
||||
}
|
||||
ASSERT(location < mActiveOutputVariablesMask.size());
|
||||
mActiveOutputVariablesMask.set(location);
|
||||
mOutputVariableTypes[location] = VariableComponentType(outputVariable.type);
|
||||
ComponentType componentType = GLenumToComponentType(mOutputVariableTypes[location]);
|
||||
SetComponentTypeMask(componentType, location, &mDrawBufferTypeMask);
|
||||
}
|
||||
|
||||
if (outputVariable.yuv)
|
||||
{
|
||||
ASSERT(outputVariables.size() == 1);
|
||||
mYUVOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 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 +
|
||||
mActiveOutputVariablesMask.count() >
|
||||
static_cast<GLuint>(caps.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 ("
|
||||
<< caps.maxCombinedShaderOutputResources << ")";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fragmentShaderVersion == 100)
|
||||
return true;
|
||||
|
||||
mOutputVariables = outputVariables;
|
||||
|
||||
// EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
|
||||
// output array in explicit terms.
|
||||
//
|
||||
// Assuming fragData is an output array, you can defend the position that:
|
||||
// P1) you must support binding "fragData" because it's specified
|
||||
// P2) you must support querying "fragData[x]" because it's specified
|
||||
// P3) you must support binding "fragData[0]" because it's a frequently used pattern
|
||||
//
|
||||
// Then you can make the leap of faith:
|
||||
// P4) you must support binding "fragData[x]" because you support "fragData[0]"
|
||||
// P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
|
||||
//
|
||||
// The spec brings in the "world of arrays" when it mentions binding the arrays and the
|
||||
// automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
|
||||
// must infer the only possible interpretation (?). Note again: this need of interpretation
|
||||
// might be completely off of what GL spec logic is.
|
||||
//
|
||||
// The other complexity is that unless you implement this feature, it's hard to understand what
|
||||
// should happen when the client invokes the feature. You cannot add an additional error as it
|
||||
// is not specified. One can ignore it, but obviously it creates the discrepancies...
|
||||
|
||||
std::vector<VariableLocation> reservedLocations;
|
||||
|
||||
// Process any output API bindings for arrays that don't alias to the first element.
|
||||
for (const auto &bindingPair : fragmentOutputLocations)
|
||||
{
|
||||
const std::string &name = bindingPair.first;
|
||||
const ProgramBinding &binding = bindingPair.second;
|
||||
|
||||
size_t nameLengthWithoutArrayIndex;
|
||||
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
|
||||
if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
|
||||
// Check that the binding corresponds to an output array and its array index fits.
|
||||
if (outputVariable.isBuiltIn() || !outputVariable.isArray() ||
|
||||
!angle::BeginsWith(outputVariable.name, name, nameLengthWithoutArrayIndex) ||
|
||||
arrayIndex >= outputVariable.getOutermostArraySize())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the API index that corresponds to this exact binding.
|
||||
// This index may differ from the index used for the array's base.
|
||||
std::vector<VariableLocation> &outputLocations =
|
||||
fragmentOutputIndices.getBindingByName(name) == 1 ? mSecondaryOutputLocations
|
||||
: mOutputLocations;
|
||||
unsigned int location = binding.location;
|
||||
VariableLocation locationInfo(arrayIndex, outputVariableIndex);
|
||||
if (location >= outputLocations.size())
|
||||
{
|
||||
outputLocations.resize(location + 1);
|
||||
}
|
||||
if (outputLocations[location].used())
|
||||
{
|
||||
mInfoLog << "Location of variable " << outputVariable.name
|
||||
<< " conflicts with another variable.";
|
||||
return false;
|
||||
}
|
||||
outputLocations[location] = locationInfo;
|
||||
|
||||
// Note the array binding location so that it can be skipped later.
|
||||
reservedLocations.push_back(locationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve locations for output variables whose location is fixed in the shader or through the
|
||||
// API. Otherwise, the remaining unallocated outputs will be processed later.
|
||||
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
|
||||
|
||||
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
|
||||
if (outputVariable.isBuiltIn())
|
||||
continue;
|
||||
|
||||
int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable);
|
||||
if (fixedLocation == -1)
|
||||
{
|
||||
// Here we're only reserving locations for variables whose location is fixed.
|
||||
continue;
|
||||
}
|
||||
unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
|
||||
|
||||
std::vector<VariableLocation> &outputLocations =
|
||||
IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable)
|
||||
? mSecondaryOutputLocations
|
||||
: mOutputLocations;
|
||||
|
||||
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
||||
// structures, so we may use getBasicTypeElementCount().
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex))
|
||||
{
|
||||
mInfoLog << "Location of variable " << outputVariable.name
|
||||
<< " conflicts with another variable.";
|
||||
return false;
|
||||
}
|
||||
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex, mOutputVariables[outputVariableIndex]);
|
||||
}
|
||||
|
||||
// Here we assign locations for the output variables that don't yet have them. Note that we're
|
||||
// not necessarily able to fit the variables optimally, since then we might have to try
|
||||
// different arrangements of output arrays. Now we just assign the locations in the order that
|
||||
// we got the output variables. The spec isn't clear on what kind of algorithm is required for
|
||||
// finding locations for the output variables, so this should be acceptable at least for now.
|
||||
GLuint maxLocation = static_cast<GLuint>(caps.maxDrawBuffers);
|
||||
if (!mSecondaryOutputLocations.empty())
|
||||
{
|
||||
// EXT_blend_func_extended: Program outputs will be validated against
|
||||
// MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one.
|
||||
maxLocation = caps.maxDualSourceDrawBuffers;
|
||||
}
|
||||
|
||||
for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size();
|
||||
outputVariableIndex++)
|
||||
{
|
||||
const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex];
|
||||
|
||||
// Don't store outputs for gl_FragDepth, gl_FragColor, etc.
|
||||
if (outputVariable.isBuiltIn())
|
||||
continue;
|
||||
|
||||
int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable);
|
||||
std::vector<VariableLocation> &outputLocations =
|
||||
IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable)
|
||||
? mSecondaryOutputLocations
|
||||
: mOutputLocations;
|
||||
unsigned int baseLocation = 0;
|
||||
unsigned int elementCount = outputVariable.getBasicTypeElementCount();
|
||||
if (fixedLocation != -1)
|
||||
{
|
||||
// Secondary inputs might have caused the max location to drop below what has already
|
||||
// been explicitly assigned locations. Check for any fixed locations above the max
|
||||
// that should cause linking to fail.
|
||||
baseLocation = static_cast<unsigned int>(fixedLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No fixed location, so try to fit the output in unassigned locations.
|
||||
// Try baseLocations starting from 0 one at a time and see if the variable fits.
|
||||
while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount,
|
||||
reservedLocations, outputVariableIndex))
|
||||
{
|
||||
baseLocation++;
|
||||
}
|
||||
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
|
||||
outputVariableIndex, mOutputVariables[outputVariableIndex]);
|
||||
}
|
||||
|
||||
// Check for any elements assigned above the max location that are actually used.
|
||||
if (baseLocation + elementCount > maxLocation &&
|
||||
(baseLocation >= maxLocation ||
|
||||
FindUsedOutputLocation(outputLocations, maxLocation,
|
||||
baseLocation + elementCount - maxLocation, reservedLocations,
|
||||
outputVariableIndex)))
|
||||
{
|
||||
// EXT_blend_func_extended: Linking can fail:
|
||||
// "if the explicit binding assignments do not leave enough space for the linker to
|
||||
// automatically assign a location for a varying out array, which requires multiple
|
||||
// contiguous locations."
|
||||
mInfoLog << "Could not fit output variable into available locations: "
|
||||
<< outputVariable.name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
|
|
@ -316,7 +316,7 @@ class ProgramExecutable final : public angle::Subject
|
|||
bool validateSamplers(InfoLog *infoLog, const Caps &caps) const
|
||||
{
|
||||
// Use the cache if:
|
||||
// - we aren't using an infolog (which gives the full error).
|
||||
// - we aren't using an info log (which gives the full error).
|
||||
// - The sample mapping hasn't changed and we've already validated.
|
||||
if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
|
||||
{
|
||||
|
@ -326,6 +326,9 @@ class ProgramExecutable final : public angle::Subject
|
|||
return validateSamplersImpl(infoLog, caps);
|
||||
}
|
||||
|
||||
ComponentTypeMask getFragmentOutputsTypeMask() const { return mDrawBufferTypeMask; }
|
||||
DrawBufferMask getActiveOutputVariablesMask() const { return mActiveOutputVariablesMask; }
|
||||
|
||||
private:
|
||||
// TODO(timvp): http://anglebug.com/3570: Investigate removing these friend
|
||||
// class declarations and accessing the necessary members with getters/setters.
|
||||
|
@ -361,6 +364,16 @@ class ProgramExecutable final : public angle::Subject
|
|||
|
||||
bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const;
|
||||
|
||||
bool linkValidateOutputVariables(const Caps &caps,
|
||||
const Extensions &extensions,
|
||||
const Version &version,
|
||||
GLuint combinedImageUniformsCount,
|
||||
GLuint combinedShaderStorageBlocksCount,
|
||||
const std::vector<sh::ShaderVariable> &outputVariables,
|
||||
int fragmentShaderVersion,
|
||||
const ProgramAliasedBindings &fragmentOutputLocations,
|
||||
const ProgramAliasedBindings &fragmentOutputIndices);
|
||||
|
||||
InfoLog mInfoLog;
|
||||
|
||||
ShaderBitSet mLinkedShaderStages;
|
||||
|
@ -389,6 +402,7 @@ class ProgramExecutable final : public angle::Subject
|
|||
// to uniforms.
|
||||
std::vector<sh::ShaderVariable> mOutputVariables;
|
||||
std::vector<VariableLocation> mOutputLocations;
|
||||
DrawBufferMask mActiveOutputVariablesMask;
|
||||
// EXT_blend_func_extended secondary outputs (ones with index 1)
|
||||
std::vector<VariableLocation> mSecondaryOutputLocations;
|
||||
bool mYUVOutput;
|
||||
|
@ -458,6 +472,10 @@ class ProgramExecutable final : public angle::Subject
|
|||
GLenum mTessGenVertexOrder;
|
||||
GLenum mTessGenPointMode;
|
||||
|
||||
// Fragment output variable base types: FLOAT, INT, or UINT. Ordered by location.
|
||||
std::vector<GLenum> mOutputVariableTypes;
|
||||
ComponentTypeMask mDrawBufferTypeMask;
|
||||
|
||||
// Cache for sampler validation
|
||||
mutable Optional<bool> mCachedValidateSamplersResult;
|
||||
};
|
||||
|
|
|
@ -1423,7 +1423,7 @@ LinkingVariables::LinkingVariables(const ProgramState &state)
|
|||
|
||||
LinkingVariables::LinkingVariables(const ProgramPipelineState &state)
|
||||
{
|
||||
for (ShaderType shaderType : state.getProgramExecutable().getLinkedShaderStages())
|
||||
for (ShaderType shaderType : state.getExecutable().getLinkedShaderStages())
|
||||
{
|
||||
const Program *program = state.getShaderProgram(shaderType);
|
||||
ASSERT(program);
|
||||
|
|
|
@ -124,7 +124,8 @@ void ProgramPipelineState::updateExecutableTextures()
|
|||
{
|
||||
const Program *program = getShaderProgram(shaderType);
|
||||
ASSERT(program);
|
||||
mExecutable->setActiveTextureMask(program->getExecutable().getActiveSamplersMask());
|
||||
mExecutable->setActiveTextureMask(mExecutable->getActiveSamplersMask() |
|
||||
program->getExecutable().getActiveSamplersMask());
|
||||
mExecutable->setActiveImagesMask(mExecutable->getActiveImagesMask() |
|
||||
program->getExecutable().getActiveImagesMask());
|
||||
// Updates mActiveSamplerRefCounts, mActiveSamplerTypes, and mActiveSamplerFormats
|
||||
|
@ -197,15 +198,16 @@ void ProgramPipeline::activeShaderProgram(Program *shaderProgram)
|
|||
mState.activeShaderProgram(shaderProgram);
|
||||
}
|
||||
|
||||
void ProgramPipeline::useProgramStages(const Context *context,
|
||||
GLbitfield stages,
|
||||
Program *shaderProgram)
|
||||
angle::Result ProgramPipeline::useProgramStages(const Context *context,
|
||||
GLbitfield stages,
|
||||
Program *shaderProgram)
|
||||
{
|
||||
mState.useProgramStages(context, stages, shaderProgram, &mProgramObserverBindings);
|
||||
updateLinkedShaderStages();
|
||||
updateExecutable();
|
||||
|
||||
mState.mIsLinked = false;
|
||||
|
||||
return link(context);
|
||||
}
|
||||
|
||||
void ProgramPipeline::updateLinkedShaderStages()
|
||||
|
@ -432,8 +434,6 @@ void ProgramPipeline::updateHasBooleans()
|
|||
|
||||
void ProgramPipeline::updateExecutable()
|
||||
{
|
||||
mState.mExecutable->reset();
|
||||
|
||||
// Vertex Shader ProgramExecutable properties
|
||||
updateExecutableAttributes();
|
||||
updateTransformFeedbackMembers();
|
||||
|
@ -471,6 +471,8 @@ angle::Result ProgramPipeline::link(const Context *context)
|
|||
ProgramVaryingPacking varyingPacking;
|
||||
LinkingVariables linkingVariables(mState);
|
||||
|
||||
mState.mExecutable->reset();
|
||||
|
||||
if (mState.mExecutable->hasLinkedShaderStage(gl::ShaderType::Vertex))
|
||||
{
|
||||
InfoLog &infoLog = mState.mExecutable->getInfoLog();
|
||||
|
@ -486,6 +488,23 @@ angle::Result ProgramPipeline::link(const Context *context)
|
|||
return angle::Result::Stop;
|
||||
}
|
||||
|
||||
Program *fragmentShaderProgram = getShaderProgram(ShaderType::Fragment);
|
||||
if (fragmentShaderProgram)
|
||||
{
|
||||
// We should also be validating image uniforms and SSBOs.
|
||||
const int combinedImageUniforms = 0;
|
||||
const int combinedShaderStorageBlocks = 0;
|
||||
const ProgramExecutable &fragmentExecutable = fragmentShaderProgram->getExecutable();
|
||||
if (!mState.mExecutable->linkValidateOutputVariables(
|
||||
context->getCaps(), context->getExtensions(), context->getClientVersion(),
|
||||
combinedImageUniforms, combinedShaderStorageBlocks,
|
||||
fragmentExecutable.getOutputVariables(),
|
||||
fragmentExecutable.getLinkedShaderVersion(ShaderType::Fragment),
|
||||
ProgramAliasedBindings(), ProgramAliasedBindings()))
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
}
|
||||
mergedVaryings = GetMergedVaryingsFromLinkingVariables(linkingVariables);
|
||||
// If separable program objects are in use, the set of attributes captured is taken
|
||||
// from the program object active on the last vertex processing stage.
|
||||
|
@ -524,7 +543,11 @@ angle::Result ProgramPipeline::link(const Context *context)
|
|||
ANGLE_TRY(getImplementation()->link(context, mergedVaryings, varyingPacking));
|
||||
}
|
||||
|
||||
mState.mExecutable->mActiveSamplerRefCounts.fill(0);
|
||||
updateExecutable();
|
||||
|
||||
mState.mIsLinked = true;
|
||||
onStateChange(angle::SubjectMessage::SubjectChanged);
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
@ -641,16 +664,16 @@ void ProgramPipeline::onSubjectStateChange(angle::SubjectIndex index, angle::Sub
|
|||
switch (message)
|
||||
{
|
||||
case angle::SubjectMessage::ProgramTextureOrImageBindingChanged:
|
||||
mState.mIsLinked = false;
|
||||
mState.mExecutable->mActiveSamplerRefCounts.fill(0);
|
||||
mState.updateExecutableTextures();
|
||||
break;
|
||||
|
||||
case angle::SubjectMessage::ProgramRelinked:
|
||||
mState.mIsLinked = false;
|
||||
updateExecutable();
|
||||
onStateChange(angle::SubjectMessage::ProgramRelinked);
|
||||
break;
|
||||
case angle::SubjectMessage::SamplerUniformsUpdated:
|
||||
getExecutable().resetCachedValidateSamplersResult();
|
||||
mState.mExecutable->resetCachedValidateSamplersResult();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
|
|
@ -38,12 +38,7 @@ class ProgramPipelineState final : angle::NonCopyable
|
|||
|
||||
const std::string &getLabel() const;
|
||||
|
||||
const ProgramExecutable &getProgramExecutable() const
|
||||
{
|
||||
ASSERT(mExecutable);
|
||||
return *mExecutable;
|
||||
}
|
||||
ProgramExecutable &getProgramExecutable()
|
||||
ProgramExecutable &getExecutable() const
|
||||
{
|
||||
ASSERT(mExecutable);
|
||||
return *mExecutable;
|
||||
|
@ -91,7 +86,8 @@ class ProgramPipelineState final : angle::NonCopyable
|
|||
|
||||
class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
||||
public LabeledObject,
|
||||
public angle::ObserverInterface
|
||||
public angle::ObserverInterface,
|
||||
public angle::Subject
|
||||
{
|
||||
public:
|
||||
ProgramPipeline(rx::GLImplFactory *factory, ProgramPipelineID handle);
|
||||
|
@ -105,8 +101,7 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
|||
const ProgramPipelineState &getState() const { return mState; }
|
||||
ProgramPipelineState &getState() { return mState; }
|
||||
|
||||
const ProgramExecutable &getExecutable() const { return mState.getProgramExecutable(); }
|
||||
ProgramExecutable &getExecutable() { return mState.getProgramExecutable(); }
|
||||
ProgramExecutable &getExecutable() const { return mState.getExecutable(); }
|
||||
|
||||
rx::ProgramPipelineImpl *getImplementation() const;
|
||||
|
||||
|
@ -122,7 +117,9 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
|||
return program;
|
||||
}
|
||||
|
||||
void useProgramStages(const Context *context, GLbitfield stages, Program *shaderProgram);
|
||||
angle::Result useProgramStages(const Context *context,
|
||||
GLbitfield stages,
|
||||
Program *shaderProgram);
|
||||
|
||||
Program *getShaderProgram(ShaderType shaderType) const { return mState.mPrograms[shaderType]; }
|
||||
|
||||
|
@ -131,6 +128,7 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
|
|||
bool linkVaryings(InfoLog &infoLog) const;
|
||||
void validate(const gl::Context *context);
|
||||
GLboolean isValid() const { return mState.isValid(); }
|
||||
bool isLinked() const { return mState.mIsLinked; }
|
||||
|
||||
// ObserverInterface implementation.
|
||||
void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
|
||||
|
|
|
@ -2008,17 +2008,6 @@ bool State::removeTransformFeedbackBinding(const Context *context,
|
|||
return false;
|
||||
}
|
||||
|
||||
angle::Result State::useProgramStages(const Context *context,
|
||||
ProgramPipeline *programPipeline,
|
||||
GLbitfield stages,
|
||||
Program *shaderProgram)
|
||||
{
|
||||
programPipeline->useProgramStages(context, stages, shaderProgram);
|
||||
ANGLE_TRY(onProgramPipelineExecutableChange(context, programPipeline));
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result State::setProgramPipelineBinding(const Context *context, ProgramPipeline *pipeline)
|
||||
{
|
||||
if (mProgramPipeline.get() == pipeline)
|
||||
|
@ -2048,11 +2037,6 @@ angle::Result State::setProgramPipelineBinding(const Context *context, ProgramPi
|
|||
}
|
||||
}
|
||||
|
||||
if (mProgramPipeline.get())
|
||||
{
|
||||
ANGLE_TRY(onProgramPipelineExecutableChange(context, mProgramPipeline.get()));
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
|
@ -3280,8 +3264,8 @@ void State::getBooleani_v(GLenum target, GLuint index, GLboolean *data) const
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(https://anglebug.com/3889): Remove this helper function after blink and chromium part
|
||||
// refactory done.
|
||||
// TODO(http://anglebug.com/3889): Remove this helper function after blink and chromium part
|
||||
// refactor done.
|
||||
Texture *State::getTextureForActiveSampler(TextureType type, size_t index)
|
||||
{
|
||||
if (type != TextureType::VideoImage)
|
||||
|
@ -3575,15 +3559,15 @@ angle::Result State::onProgramExecutableChange(const Context *context, Program *
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result State::onProgramPipelineExecutableChange(const Context *context,
|
||||
ProgramPipeline *programPipeline)
|
||||
angle::Result State::onProgramPipelineExecutableChange(const Context *context)
|
||||
{
|
||||
mDirtyBits.set(DIRTY_BIT_PROGRAM_EXECUTABLE);
|
||||
|
||||
// Set any bound textures.
|
||||
const ActiveTextureTypeArray &textureTypes =
|
||||
programPipeline->getExecutable().getActiveSamplerTypes();
|
||||
for (size_t textureIndex : programPipeline->getExecutable().getActiveSamplersMask())
|
||||
const ProgramExecutable &executable = mProgramPipeline->getExecutable();
|
||||
const ActiveTextureTypeArray &textureTypes = executable.getActiveSamplerTypes();
|
||||
|
||||
for (size_t textureIndex : executable.getActiveSamplersMask())
|
||||
{
|
||||
TextureType type = textureTypes[textureIndex];
|
||||
|
||||
|
@ -3595,7 +3579,7 @@ angle::Result State::onProgramPipelineExecutableChange(const Context *context,
|
|||
updateTextureBinding(context, textureIndex, texture);
|
||||
}
|
||||
|
||||
for (size_t imageUnitIndex : programPipeline->getExecutable().getActiveImagesMask())
|
||||
for (size_t imageUnitIndex : executable.getActiveImagesMask())
|
||||
{
|
||||
Texture *image = mImageUnits[imageUnitIndex].texture.get();
|
||||
if (!image)
|
||||
|
|
|
@ -411,10 +411,6 @@ class State : angle::NonCopyable
|
|||
Query *getActiveQuery(QueryType type) const;
|
||||
|
||||
// Program Pipeline binding manipulation
|
||||
angle::Result useProgramStages(const Context *context,
|
||||
ProgramPipeline *programPipeline,
|
||||
GLbitfield stages,
|
||||
Program *shaderProgram);
|
||||
angle::Result setProgramPipelineBinding(const Context *context, ProgramPipeline *pipeline);
|
||||
void detachProgramPipeline(const Context *context, ProgramPipelineID pipeline);
|
||||
|
||||
|
@ -605,8 +601,7 @@ class State : angle::NonCopyable
|
|||
// Sets the dirty bit for the program executable.
|
||||
angle::Result onProgramExecutableChange(const Context *context, Program *program);
|
||||
// Sets the dirty bit for the program pipeline executable.
|
||||
angle::Result onProgramPipelineExecutableChange(const Context *context,
|
||||
ProgramPipeline *program);
|
||||
angle::Result onProgramPipelineExecutableChange(const Context *context);
|
||||
|
||||
enum DirtyBitType
|
||||
{
|
||||
|
|
|
@ -1019,9 +1019,6 @@ void SerializeProgramState(JsonSerializer *json, const gl::ProgramState &program
|
|||
SerializeRange(json, programState.getAtomicCounterUniformRange());
|
||||
SerializeVariableLocationsVector(json, "SecondaryOutputLocations",
|
||||
programState.getSecondaryOutputLocations());
|
||||
json->addScalar("ActiveOutputVariables", programState.getActiveOutputVariables().to_ulong());
|
||||
json->addVector("OutputVariableTypes", programState.getOutputVariableTypes());
|
||||
json->addScalar("DrawBufferTypeMask", programState.getDrawBufferTypeMask().to_ulong());
|
||||
json->addScalar("BinaryRetrieveableHint", programState.hasBinaryRetrieveableHint());
|
||||
json->addScalar("Separable", programState.isSeparable());
|
||||
json->addScalar("EarlyFragmentTestsOptimization",
|
||||
|
|
|
@ -311,7 +311,7 @@ angle::Result FramebufferD3D::syncState(const gl::Context *context,
|
|||
const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl::Context *context)
|
||||
{
|
||||
gl::DrawBufferMask activeProgramOutputs =
|
||||
context->getState().getProgram()->getActiveOutputVariables();
|
||||
context->getState().getProgram()->getExecutable().getActiveOutputVariablesMask();
|
||||
|
||||
if (mColorAttachmentsForRender.valid() && mCurrentActiveProgramOutputs == activeProgramOutputs)
|
||||
{
|
||||
|
|
|
@ -2003,12 +2003,13 @@ angle::Result StateManager11::syncFramebuffer(const gl::Context *context)
|
|||
RTVArray framebufferRTVs = {{}};
|
||||
const auto &colorRTs = mFramebuffer11->getCachedColorRenderTargets();
|
||||
|
||||
size_t appliedRTIndex = 0;
|
||||
bool skipInactiveRTs = mRenderer->getFeatures().mrtPerfWorkaround.enabled;
|
||||
const auto &drawStates = mFramebuffer11->getState().getDrawBufferStates();
|
||||
gl::DrawBufferMask activeProgramOutputs = mProgramD3D->getState().getActiveOutputVariables();
|
||||
UINT maxExistingRT = 0;
|
||||
const auto &colorAttachments = mFramebuffer11->getState().getColorAttachments();
|
||||
size_t appliedRTIndex = 0;
|
||||
bool skipInactiveRTs = mRenderer->getFeatures().mrtPerfWorkaround.enabled;
|
||||
const auto &drawStates = mFramebuffer11->getState().getDrawBufferStates();
|
||||
gl::DrawBufferMask activeProgramOutputs =
|
||||
mProgramD3D->getState().getExecutable().getActiveOutputVariablesMask();
|
||||
UINT maxExistingRT = 0;
|
||||
const auto &colorAttachments = mFramebuffer11->getState().getColorAttachments();
|
||||
|
||||
for (size_t rtIndex = 0; rtIndex < colorRTs.size(); ++rtIndex)
|
||||
{
|
||||
|
|
|
@ -835,12 +835,12 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
|
|||
mGraphicsDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
|
||||
}
|
||||
|
||||
if (mProgram && mProgram->dirtyUniforms())
|
||||
if (mProgram && mProgram->hasDirtyUniforms())
|
||||
{
|
||||
ANGLE_TRY(mProgram->updateUniforms(this));
|
||||
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
|
||||
}
|
||||
else if (mProgramPipeline && mProgramPipeline->dirtyUniforms(getState()))
|
||||
else if (mProgramPipeline && mProgramPipeline->hasDirtyUniforms())
|
||||
{
|
||||
ANGLE_TRY(mProgramPipeline->updateUniforms(this));
|
||||
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
|
||||
|
@ -1076,12 +1076,12 @@ angle::Result ContextVk::setupDispatch(const gl::Context *context)
|
|||
// TODO: Remove this and fix tests. http://anglebug.com/5070
|
||||
ANGLE_TRY(flushOutsideRenderPassCommands());
|
||||
|
||||
if (mProgram && mProgram->dirtyUniforms())
|
||||
if (mProgram && mProgram->hasDirtyUniforms())
|
||||
{
|
||||
ANGLE_TRY(mProgram->updateUniforms(this));
|
||||
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
|
||||
}
|
||||
else if (mProgramPipeline && mProgramPipeline->dirtyUniforms(getState()))
|
||||
else if (mProgramPipeline && mProgramPipeline->hasDirtyUniforms())
|
||||
{
|
||||
ANGLE_TRY(mProgramPipeline->updateUniforms(this));
|
||||
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
|
||||
|
@ -3421,7 +3421,7 @@ void ContextVk::invalidateProgramBindingHelper(const gl::State &glState)
|
|||
}
|
||||
else if (mProgramPipeline)
|
||||
{
|
||||
mProgramPipeline->onProgramBind(this);
|
||||
mProgramPipeline->onProgramBind();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5123,8 +5123,10 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, gl::Co
|
|||
}
|
||||
}
|
||||
|
||||
recreatePipelineLayout =
|
||||
textureVk->getAndResetImmutableSamplerDirtyState() || recreatePipelineLayout;
|
||||
if (textureVk->getAndResetImmutableSamplerDirtyState())
|
||||
{
|
||||
recreatePipelineLayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mExecutable->isImmutableSamplerFormatCompatible(externalFormatIndexMap, vkFormatIndexMap))
|
||||
|
@ -5135,7 +5137,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, gl::Co
|
|||
// Recreate the pipeline layout, if necessary.
|
||||
if (recreatePipelineLayout)
|
||||
{
|
||||
ANGLE_TRY(mExecutable->createPipelineLayout(context, &mActiveTextures));
|
||||
ANGLE_TRY(mExecutable->createPipelineLayout(this, *executable, &mActiveTextures));
|
||||
|
||||
// The default uniforms descriptor set was reset during createPipelineLayout(), so mark them
|
||||
// dirty to get everything reallocated/rebound before the next draw.
|
||||
|
@ -5147,7 +5149,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, gl::Co
|
|||
}
|
||||
else if (mProgramPipeline)
|
||||
{
|
||||
mProgramPipeline->setAllDefaultUniformsDirty(context->getState());
|
||||
mProgramPipeline->setAllDefaultUniformsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1929,7 +1929,7 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
|
|||
// optimize that path which requires modifying the current render pass.
|
||||
// We're deferring the resolve check to FramebufferVk::blit(), since if the read buffer is
|
||||
// multisampled-render-to-texture, then srcFramebuffer->getSamples(context) gives > 1, but
|
||||
// there's no resolve happening as the read buffer's singlesampled image will be used as
|
||||
// there's no resolve happening as the read buffer's single sampled image will be used as
|
||||
// blit src. FramebufferVk::blit() will handle those details for us.
|
||||
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
|
||||
}
|
||||
|
|
|
@ -395,7 +395,7 @@ ProgramVk *ProgramExecutableVk::getShaderProgram(const gl::State &glState,
|
|||
}
|
||||
else if (mProgramPipeline)
|
||||
{
|
||||
return mProgramPipeline->getShaderProgram(glState, shaderType);
|
||||
return mProgramPipeline->getShaderProgram(shaderType);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -414,7 +414,7 @@ void ProgramExecutableVk::fillProgramStateMap(
|
|||
}
|
||||
else if (mProgramPipeline)
|
||||
{
|
||||
mProgramPipeline->fillProgramStateMap(contextVk, programStatesOut);
|
||||
mProgramPipeline->fillProgramStateMap(programStatesOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,7 +425,7 @@ const gl::ProgramExecutable &ProgramExecutableVk::getGlExecutable()
|
|||
{
|
||||
return mProgram->getState().getExecutable();
|
||||
}
|
||||
return mProgramPipeline->getState().getProgramExecutable();
|
||||
return mProgramPipeline->getState().getExecutable();
|
||||
}
|
||||
|
||||
uint32_t GetInterfaceBlockArraySize(const std::vector<gl::InterfaceBlock> &blocks,
|
||||
|
@ -954,13 +954,11 @@ angle::Result ProgramExecutableVk::initDynamicDescriptorPools(
|
|||
}
|
||||
|
||||
angle::Result ProgramExecutableVk::createPipelineLayout(
|
||||
const gl::Context *glContext,
|
||||
ContextVk *contextVk,
|
||||
const gl::ProgramExecutable &glExecutable,
|
||||
gl::ActiveTextureArray<vk::TextureUnit> *activeTextures)
|
||||
{
|
||||
const gl::State &glState = glContext->getState();
|
||||
ContextVk *contextVk = vk::GetImpl(glContext);
|
||||
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
|
||||
const gl::ProgramExecutable &glExecutable = getGlExecutable();
|
||||
gl::TransformFeedback *transformFeedback = contextVk->getState().getCurrentTransformFeedback();
|
||||
const gl::ShaderBitSet &linkedShaderStages = glExecutable.getLinkedShaderStages();
|
||||
gl::ShaderMap<const gl::ProgramState *> programStates;
|
||||
fillProgramStateMap(contextVk, &programStates);
|
||||
|
@ -1179,7 +1177,7 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
|
|||
VkWriteDescriptorSet &writeInfo = contextVk->allocWriteDescriptorSet();
|
||||
VkDescriptorBufferInfo &bufferInfo = contextVk->allocDescriptorBufferInfo();
|
||||
|
||||
// Size is set to the size of the empty buffer for shader statges with no uniform data,
|
||||
// Size is set to the size of the empty buffer for shader stages with no uniform data,
|
||||
// otherwise it is set to the total size of the uniform data in the current shader stage
|
||||
VkDeviceSize size = defaultUniformBlock.uniformData.size();
|
||||
vk::BufferHelper *bufferHelper = defaultUniformBuffer;
|
||||
|
|
|
@ -153,7 +153,8 @@ class ProgramExecutableVk
|
|||
angle::Result getComputePipeline(ContextVk *contextVk, vk::PipelineAndSerial **pipelineOut);
|
||||
|
||||
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
|
||||
angle::Result createPipelineLayout(const gl::Context *glContext,
|
||||
angle::Result createPipelineLayout(ContextVk *contextVk,
|
||||
const gl::ProgramExecutable &glExecutable,
|
||||
gl::ActiveTextureArray<vk::TextureUnit> *activeTextures);
|
||||
|
||||
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk,
|
||||
|
|
|
@ -37,14 +37,13 @@ void ProgramPipelineVk::reset(ContextVk *contextVk)
|
|||
// TODO: http://anglebug.com/3570: Move/Copy all of the necessary information into
|
||||
// the ProgramExecutable, so this function can be removed.
|
||||
void ProgramPipelineVk::fillProgramStateMap(
|
||||
const ContextVk *contextVk,
|
||||
gl::ShaderMap<const gl::ProgramState *> *programStatesOut)
|
||||
{
|
||||
for (gl::ShaderType shaderType : gl::AllShaderTypes())
|
||||
{
|
||||
(*programStatesOut)[shaderType] = nullptr;
|
||||
|
||||
ProgramVk *programVk = getShaderProgram(contextVk->getState(), shaderType);
|
||||
ProgramVk *programVk = getShaderProgram(shaderType);
|
||||
if (programVk)
|
||||
{
|
||||
(*programStatesOut)[shaderType] = &programVk->getState();
|
||||
|
@ -57,9 +56,7 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
|
|||
const gl::ProgramVaryingPacking &varyingPacking)
|
||||
{
|
||||
ContextVk *contextVk = vk::GetImpl(glContext);
|
||||
const gl::State &glState = glContext->getState();
|
||||
const gl::ProgramPipeline *glPipeline = glState.getProgramPipeline();
|
||||
const gl::ProgramExecutable &glExecutable = glPipeline->getExecutable();
|
||||
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
|
||||
GlslangSourceOptions options =
|
||||
GlslangWrapperVk::CreateSourceOptions(contextVk->getRenderer()->getFeatures());
|
||||
GlslangProgramInterfaceInfo glslangProgramInterfaceInfo;
|
||||
|
@ -72,20 +69,19 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
|
|||
const gl::ShaderType linkedTransformFeedbackStage =
|
||||
glExecutable.getLinkedTransformFeedbackStage();
|
||||
|
||||
// This should be done before assigning varying location. Otherwise, We can encounter shader
|
||||
// interface mismatching problem in case the transformFeedback stage is not Vertex stage.
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
// This should be done before assigning varying locations. Otherwise, we can encounter shader
|
||||
// interface mismatching problems when the transform feedback stage is not the vertex stage.
|
||||
if (options.supportsTransformFeedbackExtension)
|
||||
{
|
||||
gl::Program *glProgram =
|
||||
const_cast<gl::Program *>(glPipeline->getShaderProgram(shaderType));
|
||||
if (glProgram)
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
{
|
||||
const bool isTransformFeedbackStage =
|
||||
shaderType == linkedTransformFeedbackStage &&
|
||||
!glProgram->getState().getLinkedTransformFeedbackVaryings().empty();
|
||||
if (options.supportsTransformFeedbackExtension &&
|
||||
gl::ShaderTypeSupportsTransformFeedback(shaderType))
|
||||
const gl::Program *glProgram = mState.getShaderProgram(shaderType);
|
||||
if (glProgram && gl::ShaderTypeSupportsTransformFeedback(shaderType))
|
||||
{
|
||||
const bool isTransformFeedbackStage =
|
||||
shaderType == linkedTransformFeedbackStage &&
|
||||
!glProgram->getState().getLinkedTransformFeedbackVaryings().empty();
|
||||
|
||||
GlslangAssignTransformFeedbackLocations(
|
||||
shaderType, glProgram->getState(), isTransformFeedbackStage,
|
||||
&glslangProgramInterfaceInfo, &mExecutable.mVariableInfoMap);
|
||||
|
@ -97,8 +93,7 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
|
|||
UniformBindingIndexMap uniformBindingIndexMap;
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
{
|
||||
gl::Program *glProgram =
|
||||
const_cast<gl::Program *>(glPipeline->getShaderProgram(shaderType));
|
||||
const gl::Program *glProgram = mState.getShaderProgram(shaderType);
|
||||
if (glProgram)
|
||||
{
|
||||
const bool isTransformFeedbackStage =
|
||||
|
@ -118,19 +113,17 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
|
|||
mExecutable.resolvePrecisionMismatch(mergedVaryings);
|
||||
}
|
||||
|
||||
return mExecutable.createPipelineLayout(glContext, nullptr);
|
||||
return mExecutable.createPipelineLayout(contextVk, mState.getExecutable(), nullptr);
|
||||
}
|
||||
|
||||
size_t ProgramPipelineVk::calcUniformUpdateRequiredSpace(
|
||||
ContextVk *contextVk,
|
||||
const gl::ProgramExecutable &glExecutable,
|
||||
const gl::State &glState,
|
||||
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const
|
||||
{
|
||||
size_t requiredSpace = 0;
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
for (const gl::ShaderType shaderType : mState.getExecutable().getLinkedShaderStages())
|
||||
{
|
||||
ProgramVk *programVk = getShaderProgram(glState, shaderType);
|
||||
ProgramVk *programVk = getShaderProgram(shaderType);
|
||||
ASSERT(programVk);
|
||||
if (programVk->isShaderUniformDirty(shaderType))
|
||||
{
|
||||
|
@ -143,8 +136,7 @@ size_t ProgramPipelineVk::calcUniformUpdateRequiredSpace(
|
|||
|
||||
angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
||||
{
|
||||
const gl::State &glState = contextVk->getState();
|
||||
const gl::ProgramExecutable &glExecutable = *glState.getProgramExecutable();
|
||||
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
|
||||
vk::DynamicBuffer *defaultUniformStorage = contextVk->getDefaultUniformStorage();
|
||||
uint8_t *bufferData = nullptr;
|
||||
VkDeviceSize bufferOffset = 0;
|
||||
|
@ -155,9 +147,9 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
|||
|
||||
// We usually only update uniform data for shader stages that are actually dirty. But when the
|
||||
// buffer for uniform data have switched, because all shader stages are using the same buffer,
|
||||
// we then must update uniform data for all shader stages to keep all shader stages' unform data
|
||||
// in the same buffer.
|
||||
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, glState, &offsets);
|
||||
// we then must update uniform data for all shader stages to keep all shader stages' uniform
|
||||
// data in the same buffer.
|
||||
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, &offsets);
|
||||
ASSERT(requiredSpace > 0);
|
||||
|
||||
// Allocate space from dynamicBuffer. Always try to allocate from the current buffer first.
|
||||
|
@ -165,16 +157,16 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
|||
if (!defaultUniformStorage->allocateFromCurrentBuffer(requiredSpace, &bufferData,
|
||||
&bufferOffset))
|
||||
{
|
||||
setAllDefaultUniformsDirty(contextVk->getState());
|
||||
setAllDefaultUniformsDirty();
|
||||
|
||||
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, glState, &offsets);
|
||||
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, &offsets);
|
||||
ANGLE_TRY(defaultUniformStorage->allocate(contextVk, requiredSpace, &bufferData, nullptr,
|
||||
&bufferOffset, &anyNewBufferAllocated));
|
||||
}
|
||||
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
{
|
||||
ProgramVk *programVk = getShaderProgram(glState, shaderType);
|
||||
ProgramVk *programVk = getShaderProgram(shaderType);
|
||||
ASSERT(programVk);
|
||||
if (programVk->isShaderUniformDirty(shaderType))
|
||||
{
|
||||
|
@ -208,7 +200,7 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
|||
if (glExecutable.hasTransformFeedbackOutput())
|
||||
{
|
||||
TransformFeedbackVk *transformFeedbackVk =
|
||||
vk::GetImpl(glState.getCurrentTransformFeedback());
|
||||
vk::GetImpl(contextVk->getState().getCurrentTransformFeedback());
|
||||
uniformsAndXfbBufferDesc = &transformFeedbackVk->getTransformFeedbackDesc();
|
||||
uniformsAndXfbBufferDesc->updateDefaultUniformBuffer(
|
||||
defaultUniformBuffer->getBufferSerial());
|
||||
|
@ -227,7 +219,7 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
|||
{
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
{
|
||||
ProgramVk *programVk = getShaderProgram(glState, shaderType);
|
||||
ProgramVk *programVk = getShaderProgram(shaderType);
|
||||
mExecutable.updateDefaultUniformsDescriptorSet(
|
||||
shaderType, programVk->getDefaultUniformBlocks()[shaderType],
|
||||
defaultUniformBuffer, contextVk);
|
||||
|
@ -240,12 +232,12 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
bool ProgramPipelineVk::dirtyUniforms(const gl::State &glState)
|
||||
bool ProgramPipelineVk::hasDirtyUniforms() const
|
||||
{
|
||||
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
|
||||
{
|
||||
const ProgramVk *program = getShaderProgram(glState, shaderType);
|
||||
if (program && program->dirtyUniforms())
|
||||
const ProgramVk *program = getShaderProgram(shaderType);
|
||||
if (program && program->hasDirtyUniforms())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -254,21 +246,21 @@ bool ProgramPipelineVk::dirtyUniforms(const gl::State &glState)
|
|||
return false;
|
||||
}
|
||||
|
||||
void ProgramPipelineVk::setAllDefaultUniformsDirty(const gl::State &glState)
|
||||
void ProgramPipelineVk::setAllDefaultUniformsDirty()
|
||||
{
|
||||
const gl::ProgramExecutable &glExecutable = *glState.getProgramExecutable();
|
||||
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
|
||||
|
||||
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
|
||||
{
|
||||
ProgramVk *programVk = getShaderProgram(glState, shaderType);
|
||||
ProgramVk *programVk = getShaderProgram(shaderType);
|
||||
ASSERT(programVk);
|
||||
programVk->setShaderUniformDirtyBit(shaderType);
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramPipelineVk::onProgramBind(ContextVk *contextVk)
|
||||
void ProgramPipelineVk::onProgramBind()
|
||||
{
|
||||
setAllDefaultUniformsDirty(contextVk->getState());
|
||||
setAllDefaultUniformsDirty();
|
||||
}
|
||||
|
||||
} // namespace rx
|
||||
|
|
|
@ -31,19 +31,13 @@ class ProgramPipelineVk : public ProgramPipelineImpl
|
|||
const ProgramExecutableVk &getExecutable() const { return mExecutable; }
|
||||
ProgramExecutableVk &getExecutable() { return mExecutable; }
|
||||
|
||||
ProgramVk *getShaderProgram(const gl::State &glState, gl::ShaderType shaderType) const
|
||||
ProgramVk *getShaderProgram(gl::ShaderType shaderType) const
|
||||
{
|
||||
gl::ProgramPipeline *pipeline = glState.getProgramPipeline();
|
||||
const gl::Program *program = pipeline->getShaderProgram(shaderType);
|
||||
if (program)
|
||||
{
|
||||
return vk::GetImpl(program);
|
||||
}
|
||||
return nullptr;
|
||||
const gl::Program *program = mState.getShaderProgram(shaderType);
|
||||
return SafeGetImplAs<ProgramVk>(program);
|
||||
}
|
||||
|
||||
void fillProgramStateMap(const ContextVk *contextVk,
|
||||
gl::ShaderMap<const gl::ProgramState *> *programStatesOut);
|
||||
void fillProgramStateMap(gl::ShaderMap<const gl::ProgramState *> *programStatesOut);
|
||||
|
||||
angle::Result link(const gl::Context *glContext,
|
||||
const gl::ProgramMergedVaryings &mergedVaryings,
|
||||
|
@ -51,14 +45,12 @@ class ProgramPipelineVk : public ProgramPipelineImpl
|
|||
|
||||
angle::Result updateUniforms(ContextVk *contextVk);
|
||||
|
||||
void setAllDefaultUniformsDirty(const gl::State &glState);
|
||||
bool dirtyUniforms(const gl::State &glState);
|
||||
void onProgramBind(ContextVk *contextVk);
|
||||
void setAllDefaultUniformsDirty();
|
||||
bool hasDirtyUniforms() const;
|
||||
void onProgramBind();
|
||||
|
||||
private:
|
||||
size_t calcUniformUpdateRequiredSpace(ContextVk *contextVk,
|
||||
const gl::ProgramExecutable &glExecutable,
|
||||
const gl::State &glState,
|
||||
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const;
|
||||
|
||||
ProgramExecutableVk mExecutable;
|
||||
|
|
|
@ -199,7 +199,7 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
|
|||
return std::make_unique<LinkEventDone>(status);
|
||||
}
|
||||
|
||||
status = mExecutable.createPipelineLayout(context, nullptr);
|
||||
status = mExecutable.createPipelineLayout(contextVk, mState.getExecutable(), nullptr);
|
||||
return std::make_unique<LinkEventDone>(status);
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
|
|||
|
||||
// TODO(jie.a.chen@intel.com): Parallelize linking.
|
||||
// http://crbug.com/849576
|
||||
status = mExecutable.createPipelineLayout(context, nullptr);
|
||||
status = mExecutable.createPipelineLayout(contextVk, mState.getExecutable(), nullptr);
|
||||
return std::make_unique<LinkEventDone>(status);
|
||||
}
|
||||
|
||||
|
@ -715,7 +715,7 @@ size_t ProgramVk::calcUniformUpdateRequiredSpace(ContextVk *contextVk,
|
|||
|
||||
angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
|
||||
{
|
||||
ASSERT(dirtyUniforms());
|
||||
ASSERT(hasDirtyUniforms());
|
||||
|
||||
bool anyNewBufferAllocated = false;
|
||||
uint8_t *bufferData = nullptr;
|
||||
|
|
|
@ -106,7 +106,7 @@ class ProgramVk : public ProgramImpl
|
|||
angle::Result updateUniforms(ContextVk *contextVk);
|
||||
|
||||
void setAllDefaultUniformsDirty();
|
||||
bool dirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); }
|
||||
bool hasDirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); }
|
||||
bool isShaderUniformDirty(gl::ShaderType shaderType) const
|
||||
{
|
||||
return mDefaultUniformBlocksDirty[shaderType];
|
||||
|
|
|
@ -461,19 +461,19 @@ bool ValidateFragmentShaderColorBufferMaskMatch(const Context *context)
|
|||
const Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
|
||||
|
||||
auto drawBufferMask = framebuffer->getDrawBufferMask().to_ulong();
|
||||
auto fragmentOutputMask = program->getActiveOutputVariables().to_ulong();
|
||||
auto fragmentOutputMask = program->getExecutable().getActiveOutputVariablesMask().to_ulong();
|
||||
|
||||
return drawBufferMask == (drawBufferMask & fragmentOutputMask);
|
||||
}
|
||||
|
||||
bool ValidateFragmentShaderColorBufferTypeMatch(const Context *context)
|
||||
{
|
||||
const Program *program = context->getActiveLinkedProgram();
|
||||
const Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
|
||||
const ProgramExecutable *executable = context->getState().getProgramExecutable();
|
||||
const Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
|
||||
|
||||
return ValidateComponentTypeMasks(program->getDrawBufferTypeMask().to_ulong(),
|
||||
return ValidateComponentTypeMasks(executable->getFragmentOutputsTypeMask().to_ulong(),
|
||||
framebuffer->getDrawBufferTypeMask().to_ulong(),
|
||||
program->getActiveOutputVariables().to_ulong(),
|
||||
executable->getActiveOutputVariablesMask().to_ulong(),
|
||||
framebuffer->getDrawBufferMask().to_ulong());
|
||||
}
|
||||
|
||||
|
@ -4036,7 +4036,7 @@ const char *ValidateDrawStates(const Context *context)
|
|||
return errorMsg;
|
||||
}
|
||||
|
||||
programIsYUVOutput = program->isYUVOutput();
|
||||
programIsYUVOutput = executable->isYUVOutput();
|
||||
}
|
||||
else if (programPipeline)
|
||||
{
|
||||
|
@ -4052,10 +4052,7 @@ const char *ValidateDrawStates(const Context *context)
|
|||
return errorMsg;
|
||||
}
|
||||
|
||||
bool goodResult = programPipeline->link(context) == angle::Result::Continue;
|
||||
|
||||
ASSERT(executable);
|
||||
if (!goodResult)
|
||||
if (!programPipeline->isLinked())
|
||||
{
|
||||
return kProgramPipelineLinkFailed;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче