Reland: Vulkan: Move default uniform init to link job

This is a reland of d8cd4dcdc9

Bug: angleproject:8297
Change-Id: Ib4f8e9dd258da71d44983bbb619b6b4abda0b109
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4793218
Reviewed-by: Charlie Lao <cclao@google.com>
Reviewed-by: Yuxin Hu <yuxinhu@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Shahbaz Youssefi
2023-08-09 16:51:13 -04:00
committed by Angle LUCI CQ
parent 865eceaa2a
commit 8f64b51d22
8 changed files with 165 additions and 168 deletions

View File

@@ -1125,7 +1125,7 @@ GLuint Context::createShaderProgramv(ShaderType type, GLsizei count, const GLcha
// We must wait to mark the program separable until it's successfully compiled.
programObject->setSeparable(true);
programObject->attachShader(shaderObject);
programObject->attachShader(this, shaderObject);
if (programObject->link(this) != angle::Result::Continue)
{
@@ -6155,7 +6155,7 @@ void Context::attachShader(ShaderProgramID program, ShaderProgramID shader)
Program *programObject = mState.mShaderProgramManager->getProgram(program);
Shader *shaderObject = mState.mShaderProgramManager->getShader(shader);
ASSERT(programObject && shaderObject);
programObject->attachShader(shaderObject);
programObject->attachShader(this, shaderObject);
}
void Context::copyBufferSubData(BufferBinding readTarget,

View File

@@ -672,8 +672,8 @@ angle::Result GLES1Renderer::linkProgram(Context *context,
*programOut = program;
programObject->attachShader(getShader(vertexShader));
programObject->attachShader(getShader(fragmentShader));
programObject->attachShader(context, getShader(vertexShader));
programObject->attachShader(context, getShader(fragmentShader));
for (auto it : attribLocs)
{

View File

@@ -1144,8 +1144,9 @@ const std::string &Program::getLabel() const
return mState.mLabel;
}
void Program::attachShader(Shader *shader)
void Program::attachShader(const Context *context, Shader *shader)
{
resolveLink(context);
ShaderType shaderType = shader->getType();
ASSERT(shaderType != ShaderType::InvalidEnum);

View File

@@ -456,7 +456,7 @@ class Program final : public LabeledObject, public angle::Subject
return mProgram;
}
void attachShader(Shader *shader);
void attachShader(const Context *context, Shader *shader);
void detachShader(const Context *context, Shader *shader);
int getAttachedShadersCount() const;

View File

@@ -1867,7 +1867,7 @@ void ProgramExecutableVk::onProgramBind(const gl::ProgramExecutable &glExecutabl
}
angle::Result ProgramExecutableVk::resizeUniformBlockMemory(
ContextVk *contextVk,
vk::Context *context,
const gl::ProgramExecutable &glExecutable,
const gl::ShaderMap<size_t> &requiredBufferSize)
{
@@ -1878,7 +1878,7 @@ angle::Result ProgramExecutableVk::resizeUniformBlockMemory(
if (!mDefaultUniformBlocks[shaderType]->uniformData.resize(
requiredBufferSize[shaderType]))
{
ANGLE_VK_CHECK(contextVk, false, VK_ERROR_OUT_OF_HOST_MEMORY);
ANGLE_VK_CHECK(context, false, VK_ERROR_OUT_OF_HOST_MEMORY);
}
// Initialize uniform buffer memory to zero by default.

View File

@@ -277,6 +277,11 @@ class ProgramExecutableVk
const gl::Program::DirtyBits &getDirtyBits() const { return mDirtyBits; }
void resetUniformBufferDirtyBits() { mDirtyBits.reset(); }
// The following functions are for internal use of programs, including from a threaded link job:
angle::Result resizeUniformBlockMemory(vk::Context *context,
const gl::ProgramExecutable &glExecutable,
const gl::ShaderMap<size_t> &requiredBufferSize);
private:
friend class ProgramVk;
friend class ProgramPipelineVk;
@@ -368,10 +373,6 @@ class ProgramExecutableVk
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result resizeUniformBlockMemory(ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable,
const gl::ShaderMap<size_t> &requiredBufferSize);
angle::Result getOrAllocateDescriptorSet(vk::Context *context,
UpdateDescriptorSetsBuilder *updateBuilder,
vk::CommandBufferHelperCommon *commandBufferHelper,

View File

@@ -22,16 +22,36 @@ namespace rx
namespace
{
class LinkTask final : public vk::Context, public angle::Closure
// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types.
class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder
{
public:
LinkTask(RendererVk *renderer,
const gl::ProgramState &state,
const gl::ProgramExecutable &glExecutable,
ProgramExecutableVk *executable,
bool isGLES1,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess)
void advanceOffset(GLenum type,
const std::vector<unsigned int> &arraySizes,
bool isRowMajorMatrix,
int arrayStride,
int matrixStride) override
{
if (gl::IsOpaqueType(type))
{
return;
}
sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride,
matrixStride);
}
};
class LinkTaskVk final : public vk::Context, public angle::Closure
{
public:
LinkTaskVk(RendererVk *renderer,
const gl::ProgramState &state,
const gl::ProgramExecutable &glExecutable,
ProgramExecutableVk *executable,
bool isGLES1,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess)
: vk::Context(renderer),
mState(state),
mGlExecutable(glExecutable),
@@ -41,7 +61,11 @@ class LinkTask final : public vk::Context, public angle::Closure
mPipelineProtectedAccess(pipelineProtectedAccess)
{}
void operator()() override;
void operator()() override
{
angle::Result result = linkImpl();
ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS));
}
void handleError(VkResult result,
const char *file,
@@ -78,6 +102,13 @@ class LinkTask final : public vk::Context, public angle::Closure
}
private:
angle::Result linkImpl();
angle::Result initDefaultUniformBlocks();
void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
gl::ShaderMap<size_t> *requiredBufferSizeOut);
void initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut);
// The front-end ensures that the program is not accessed while linking, so it is safe to
// direclty access the state from a potentially parallel job.
const gl::ProgramState &mState;
@@ -97,9 +128,11 @@ class LinkTask final : public vk::Context, public angle::Closure
unsigned int mErrorLine = 0;
};
void LinkTask::operator()()
angle::Result LinkTaskVk::linkImpl()
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTask::run");
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTaskVk::run");
ANGLE_TRY(initDefaultUniformBlocks());
// Warm up the pipeline cache by creating a few placeholder pipelines. This is not done for
// separable programs, and is deferred to when the program pipeline is finalized.
@@ -113,60 +146,27 @@ void LinkTask::operator()()
// - Individual GLES1 tests are long, and this adds a considerable overhead to those tests
if (!mState.isSeparable() && !mIsGLES1)
{
angle::Result result =
mExecutable->warmUpPipelineCache(this, mGlExecutable, mPipelineRobustness,
mPipelineProtectedAccess, &mCompatibleRenderPass);
ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS));
ANGLE_TRY(mExecutable->warmUpPipelineCache(this, mGlExecutable, mPipelineRobustness,
mPipelineProtectedAccess,
&mCompatibleRenderPass));
}
return angle::Result::Continue;
}
// The event for parallelized/lockless link.
class LinkEventVulkan final : public LinkEvent
angle::Result LinkTaskVk::initDefaultUniformBlocks()
{
public:
LinkEventVulkan(std::shared_ptr<angle::WorkerThreadPool> workerPool,
std::shared_ptr<LinkTask> linkTask)
: mLinkTask(linkTask),
mWaitableEvent(
std::shared_ptr<angle::WaitableEvent>(workerPool->postWorkerTask(mLinkTask)))
{}
// Process vertex and fragment uniforms into std140 packing.
gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVK::LinkEvent::wait");
generateUniformLayoutMapping(&layoutMap, &requiredBufferSize);
initDefaultUniformLayoutMapping(&layoutMap);
mWaitableEvent->wait();
return mLinkTask->getResult(vk::GetImpl(context));
}
bool isLinking() override { return !mWaitableEvent->isReady(); }
private:
std::shared_ptr<LinkTask> mLinkTask;
std::shared_ptr<angle::WaitableEvent> mWaitableEvent;
};
// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types.
class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder
{
public:
void advanceOffset(GLenum type,
const std::vector<unsigned int> &arraySizes,
bool isRowMajorMatrix,
int arrayStride,
int matrixStride) override
{
if (gl::IsOpaqueType(type))
{
return;
}
sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride,
matrixStride);
}
};
// All uniform initializations are complete, now resize the buffers accordingly and return
return mExecutable->resizeUniformBlockMemory(this, mGlExecutable, requiredBufferSize);
}
void InitDefaultUniformBlock(const std::vector<sh::ShaderVariable> &uniforms,
sh::BlockLayoutMap *blockLayoutMapOut,
@@ -194,6 +194,97 @@ void InitDefaultUniformBlock(const std::vector<sh::ShaderVariable> &uniforms,
return;
}
void LinkTaskVk::generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
gl::ShaderMap<size_t> *requiredBufferSizeOut)
{
for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages())
{
const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType);
if (shader)
{
const std::vector<sh::ShaderVariable> &uniforms = shader->uniforms;
InitDefaultUniformBlock(uniforms, &(*layoutMapOut)[shaderType],
&(*requiredBufferSizeOut)[shaderType]);
}
}
}
void LinkTaskVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut)
{
// Init the default block layout info.
const auto &uniforms = mGlExecutable.getUniforms();
for (const gl::VariableLocation &location : mState.getUniformLocations())
{
gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
if (location.used() && !location.ignored)
{
const auto &uniform = uniforms[location.index];
if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() &&
!uniform.isFragmentInOut())
{
std::string uniformName = mGlExecutable.getUniformNameByIndex(location.index);
if (uniform.isArray())
{
// Gets the uniform name without the [0] at the end.
uniformName = gl::StripLastArrayIndex(uniformName);
ASSERT(uniformName.size() !=
mGlExecutable.getUniformNameByIndex(location.index).size());
}
bool found = false;
for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages())
{
auto it = (*layoutMapOut)[shaderType].find(uniformName);
if (it != (*layoutMapOut)[shaderType].end())
{
found = true;
layoutInfo[shaderType] = it->second;
}
}
ASSERT(found);
}
}
for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages())
{
mExecutable->getSharedDefaultUniformBlock(shaderType)
->uniformLayout.push_back(layoutInfo[shaderType]);
}
}
}
// The event for parallelized/lockless link.
class LinkEventVulkan final : public LinkEvent
{
public:
LinkEventVulkan(std::shared_ptr<angle::WorkerThreadPool> workerPool,
std::shared_ptr<LinkTaskVk> linkTask)
: mLinkTask(linkTask),
mWaitableEvent(
std::shared_ptr<angle::WaitableEvent>(workerPool->postWorkerTask(mLinkTask)))
{}
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVK::LinkEvent::wait");
mWaitableEvent->wait();
return mLinkTask->getResult(vk::GetImpl(context));
}
bool isLinking() override { return !mWaitableEvent->isReady(); }
private:
std::shared_ptr<LinkTaskVk> mLinkTask;
std::shared_ptr<angle::WaitableEvent> mWaitableEvent;
};
template <typename T>
void UpdateDefaultUniformBlock(GLsizei count,
uint32_t arrayIndex,
@@ -343,21 +434,13 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
return std::make_unique<LinkEventDone>(status);
}
status = initDefaultUniformBlocks(context);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
// TODO(jie.a.chen@intel.com): Parallelize linking.
// http://crbug.com/849576
status = mExecutable.createPipelineLayout(contextVk, programExecutable, nullptr);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
std::shared_ptr<LinkTask> linkTask = std::make_shared<LinkTask>(
std::shared_ptr<LinkTaskVk> linkTask = std::make_shared<LinkTaskVk>(
contextVk->getRenderer(), mState, programExecutable, &mExecutable,
context->getState().isGLES1(), contextVk->pipelineRobustness(),
contextVk->pipelineProtectedAccess());
@@ -372,90 +455,6 @@ void ProgramVk::linkResources(const gl::ProgramLinkedResources &resources)
linker.linkResources(mState, resources);
}
angle::Result ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
{
ContextVk *contextVk = vk::GetImpl(glContext);
// Process vertex and fragment uniforms into std140 packing.
gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
generateUniformLayoutMapping(layoutMap, requiredBufferSize);
initDefaultUniformLayoutMapping(layoutMap);
// All uniform initializations are complete, now resize the buffers accordingly and return
return mExecutable.resizeUniformBlockMemory(contextVk, mState.getExecutable(),
requiredBufferSize);
}
void ProgramVk::generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> &layoutMap,
gl::ShaderMap<size_t> &requiredBufferSize)
{
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType);
if (shader)
{
const std::vector<sh::ShaderVariable> &uniforms = shader->uniforms;
InitDefaultUniformBlock(uniforms, &layoutMap[shaderType],
&requiredBufferSize[shaderType]);
}
}
}
void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> &layoutMap)
{
// Init the default block layout info.
const auto &uniforms = mState.getUniforms();
const gl::ProgramExecutable &glExecutable = mState.getExecutable();
for (const gl::VariableLocation &location : mState.getUniformLocations())
{
gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
if (location.used() && !location.ignored)
{
const auto &uniform = uniforms[location.index];
if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() &&
!uniform.isFragmentInOut())
{
std::string uniformName = glExecutable.getUniformNameByIndex(location.index);
if (uniform.isArray())
{
// Gets the uniform name without the [0] at the end.
uniformName = gl::StripLastArrayIndex(uniformName);
ASSERT(uniformName.size() !=
glExecutable.getUniformNameByIndex(location.index).size());
}
bool found = false;
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
auto it = layoutMap[shaderType].find(uniformName);
if (it != layoutMap[shaderType].end())
{
found = true;
layoutInfo[shaderType] = it->second;
}
}
ASSERT(found);
}
}
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
mExecutable.mDefaultUniformBlocks[shaderType]->uniformLayout.push_back(
layoutInfo[shaderType]);
}
}
}
GLboolean ProgramVk::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
{
// No-op. The spec is very vague about the behavior of validation.

View File

@@ -127,10 +127,6 @@ class ProgramVk : public ProgramImpl
const GLfloat *value);
void reset(ContextVk *contextVk);
angle::Result initDefaultUniformBlocks(const gl::Context *glContext);
void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> &layoutMap,
gl::ShaderMap<size_t> &requiredBufferSize);
void initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> &layoutMap);
template <class T>
void getUniformImpl(GLint location, T *v, GLenum entryPointType) const;