Support for link to be entirely parallelized

The link job is split as such:

- Front-end link
- Back-end link
- Independent back-end link subtasks (typically native driver compile
  jobs)
- Post-link finalization

Each step depends on the previous.  These steps are executed as such:

1. Program::link calls into ProgramImpl::link
  - ProgramImpl::link runs whatever needs the Context, such as releasing
    resources
  - ProgramImpl::link returns a LinkTask
2. Program::link implements a closure that calls the front-end link and
   passes the results to the backend's LinkTask.
3. The LinkTask potentially returns a set of LinkSubTasks to be
   scheduled by the worker pool
4. Once the link is resolved, the post-link finalization is run

In the above, steps 1 and 4 are done under the share group lock.  Steps
2 and 3 can be done in threads or without holding the share group lock
if the backend supports it.  Step 2 is not yet made independent of the
Context on some backends, and a frontend feature is used to make that
step either run on the main thread or as a worker thread.

Bug: angleproject:8297
Change-Id: I12f1e6bbaf365543dfcac969e166e0b5aa622104
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4808191
Reviewed-by: Charlie Lao <cclao@google.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Shahbaz Youssefi
2023-08-22 22:02:15 -04:00
committed by Angle LUCI CQ
parent 3c5728f19c
commit 68bfa1edf5
31 changed files with 1225 additions and 916 deletions

View File

@@ -161,6 +161,13 @@ struct FrontendFeatures : FeatureSetBase
&members, "http://anglebug.com/8280"
};
FeatureInfo linkJobIsNotThreadSafe = {
"linkJobIsNotThreadSafe",
FeatureCategory::FrontendWorkarounds,
"If true, parts of the link job cannot be parallelized",
&members, "http://anglebug.com/8297"
};
};
inline FrontendFeatures::FrontendFeatures() = default;

View File

@@ -161,6 +161,14 @@
"Check the filesystem for translated shaders to use instead of the shader translator's"
],
"issue": "http://anglebug.com/8280"
},
{
"name": "link_job_is_not_thread_safe",
"category": "Workarounds",
"description": [
"If true, parts of the link job cannot be parallelized"
],
"issue": "http://anglebug.com/8297"
}
]
}

View File

@@ -182,6 +182,7 @@ void LoadShInterfaceBlock(gl::BinaryInputStream *stream, sh::InterfaceBlock *blo
CompiledShaderState::CompiledShaderState(gl::ShaderType type)
: shaderType(type),
successfullyCompiled(false),
shaderVersion(100),
hasClipDistance(false),
hasDiscard(false),

View File

@@ -54,6 +54,8 @@ struct CompiledShaderState
const gl::ShaderType shaderType;
bool successfullyCompiled;
int shaderVersion;
std::string translatedSource;
sh::BinaryBlob compiledBinary;

View File

@@ -9374,6 +9374,11 @@ std::shared_ptr<angle::WorkerThreadPool> Context::getShaderCompileThreadPool() c
return mDisplay->getSingleThreadPool();
}
std::shared_ptr<angle::WorkerThreadPool> Context::getSingleThreadPool() const
{
return mDisplay->getSingleThreadPool();
}
std::shared_ptr<angle::WorkerThreadPool> Context::getWorkerThreadPool() const
{
return mDisplay->getMultiThreadPool();

View File

@@ -700,6 +700,9 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
// GL_KHR_parallel_shader_compile
std::shared_ptr<angle::WorkerThreadPool> getShaderCompileThreadPool() const;
// Single-threaded pool; runs everything instantly
std::shared_ptr<angle::WorkerThreadPool> getSingleThreadPool() const;
// Generic multithread pool.
std::shared_ptr<angle::WorkerThreadPool> getWorkerThreadPool() const;

View File

@@ -35,6 +35,7 @@
#include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/GLImplFactory.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/trace.h"
#include "platform/PlatformMethods.h"
#include "platform/autogen/FrontendFeatures_autogen.h"
@@ -372,6 +373,34 @@ const std::string GetResourceName(const T &resource)
return resourceName;
}
// Provides a mechanism to access the result of asynchronous linking.
class LinkEvent : angle::NonCopyable
{
public:
virtual ~LinkEvent() {}
// Please be aware that these methods may be called under a gl::Context other
// than the one where the LinkEvent was created.
//
// Waits until the linking is actually done. Returns true if the linking
// succeeded, false otherwise.
virtual angle::Result wait(const gl::Context *context) = 0;
// Peeks whether the linking is still ongoing.
virtual bool isLinking() = 0;
};
// Wraps an already done linking.
class LinkEventDone final : public LinkEvent
{
public:
LinkEventDone(angle::Result result) : mResult(result) {}
angle::Result wait(const gl::Context *context) override { return mResult; }
bool isLinking() override { return false; }
private:
angle::Result mResult;
};
} // anonymous namespace
const char *GetLinkMismatchErrorString(LinkMismatchError linkError)
@@ -515,7 +544,7 @@ struct Program::LinkingState
LinkingVariables linkingVariables;
ProgramLinkedResources resources;
egl::BlobCache::Key programHash;
std::unique_ptr<rx::LinkEvent> linkEvent;
std::unique_ptr<LinkEvent> linkEvent;
bool linkingFromBinary;
};
@@ -976,6 +1005,182 @@ ShaderType ProgramState::getAttachedTransformFeedbackStage() const
return ShaderType::Vertex;
}
// The common portion of parallel link and load jobs
class Program::MainLinkLoadTask : public angle::Closure
{
public:
MainLinkLoadTask(const std::shared_ptr<angle::WorkerThreadPool> &subTaskWorkerPool,
ProgramState *state,
std::shared_ptr<rx::LinkTask> &&linkTask)
: mSubTaskWorkerPool(subTaskWorkerPool), mState(*state), mLinkTask(std::move(linkTask))
{
ASSERT(subTaskWorkerPool.get());
}
~MainLinkLoadTask() override = default;
angle::Result getResult(const Context *context)
{
InfoLog &infoLog = mState.getExecutable().getInfoLog();
ANGLE_TRY(mResult);
ANGLE_TRY(mLinkTask->getResult(context, infoLog));
for (const std::shared_ptr<rx::LinkSubTask> &task : mSubTasks)
{
ANGLE_TRY(task->getResult(context, infoLog));
}
return angle::Result::Continue;
}
void waitSubTasks() { angle::WaitableEvent::WaitMany(&mSubTaskWaitableEvents); }
bool areSubTasksLinking()
{
return !mLinkTask->isLinkingInternally() &&
!angle::WaitableEvent::AllReady(&mSubTaskWaitableEvents);
}
protected:
void scheduleSubTasks(const std::vector<std::shared_ptr<rx::LinkSubTask>> &subTasks)
{
mSubTaskWaitableEvents.reserve(subTasks.size());
for (const std::shared_ptr<rx::LinkSubTask> &subTask : subTasks)
{
mSubTaskWaitableEvents.push_back(mSubTaskWorkerPool->postWorkerTask(subTask));
}
}
std::shared_ptr<angle::WorkerThreadPool> mSubTaskWorkerPool;
ProgramState &mState;
std::shared_ptr<rx::LinkTask> mLinkTask;
// Subtask wait events
std::vector<std::shared_ptr<rx::LinkSubTask>> mSubTasks;
std::vector<std::shared_ptr<angle::WaitableEvent>> mSubTaskWaitableEvents;
// The result of the front-end portion of the link. The backend's result is retrieved via
// mLinkTask->getResult(). The subtask results are retrieved via mSubTasks similarly.
angle::Result mResult;
};
class Program::MainLinkTask final : public Program::MainLinkLoadTask
{
public:
MainLinkTask(const std::shared_ptr<angle::WorkerThreadPool> &subTaskWorkerPool,
const Caps &caps,
const Limitations &limitations,
const Version &clientVersion,
bool isWebGL,
Program *program,
ProgramState *state,
LinkingVariables *linkingVariables,
ProgramLinkedResources *resources,
std::shared_ptr<rx::LinkTask> &&linkTask)
: MainLinkLoadTask(subTaskWorkerPool, state, std::move(linkTask)),
mCaps(caps),
mLimitations(limitations),
mClientVersion(clientVersion),
mIsWebGL(isWebGL),
mProgram(program),
mLinkingVariables(linkingVariables),
mResources(resources)
{}
~MainLinkTask() override = default;
void operator()() override { mResult = linkImpl(); }
private:
angle::Result linkImpl();
// State needed for link
const Caps &mCaps;
const Limitations &mLimitations;
const Version mClientVersion;
const bool mIsWebGL;
Program *mProgram;
LinkingVariables *mLinkingVariables;
ProgramLinkedResources *mResources;
};
class Program::MainLoadTask final : public Program::MainLinkLoadTask
{
public:
MainLoadTask(const std::shared_ptr<angle::WorkerThreadPool> &subTaskWorkerPool,
Program *program,
ProgramState *state,
std::shared_ptr<rx::LinkTask> &&loadTask)
: MainLinkLoadTask(subTaskWorkerPool, state, std::move(loadTask))
{}
~MainLoadTask() override = default;
void operator()() override { mResult = loadImpl(); }
private:
angle::Result loadImpl();
};
class Program::MainLinkLoadEvent final : public LinkEvent
{
public:
MainLinkLoadEvent(const std::shared_ptr<MainLinkLoadTask> &linkTask,
const std::shared_ptr<angle::WaitableEvent> &waitEvent)
: mLinkTask(linkTask), mWaitableEvent(waitEvent)
{}
~MainLinkLoadEvent() override {}
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "Program::MainLinkLoadEvent::wait");
mWaitableEvent->wait();
mLinkTask->waitSubTasks();
return mLinkTask->getResult(context);
}
bool isLinking() override
{
return !mWaitableEvent->isReady() || mLinkTask->areSubTasksLinking();
}
private:
std::shared_ptr<MainLinkLoadTask> mLinkTask;
std::shared_ptr<angle::WaitableEvent> mWaitableEvent;
};
angle::Result Program::MainLinkTask::linkImpl()
{
ProgramMergedVaryings mergedVaryings;
// Do the front-end portion of the link.
ANGLE_TRY(mProgram->linkJobImpl(mCaps, mLimitations, mClientVersion, mIsWebGL,
mLinkingVariables, mResources, &mergedVaryings));
// Next, do the backend portion of the link. If there are any subtasks to be scheduled, they
// are collected now.
std::vector<std::shared_ptr<rx::LinkSubTask>> subTasks =
mLinkTask->link(*mResources, mergedVaryings);
// Must be after backend's link to avoid misleading the linker about input/output variables.
mState.updateProgramInterfaceInputs();
mState.updateProgramInterfaceOutputs();
// Schedule the subtasks
scheduleSubTasks(subTasks);
return angle::Result::Continue;
}
angle::Result Program::MainLoadTask::loadImpl()
{
std::vector<std::shared_ptr<rx::LinkSubTask>> subTasks = mLinkTask->load();
// Schedule the subtasks
scheduleSubTasks(subTasks);
return angle::Result::Continue;
}
Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle)
: mSerial(factory->generateSerial()),
mState(factory),
@@ -1111,6 +1316,8 @@ void Program::bindFragmentOutputIndex(GLuint index, const char *name)
void Program::makeNewExecutable(const Context *context)
{
ASSERT(!mLinkingState);
// Unlink the program, but do not clear the validation-related caching yet, since we can still
// use the previously linked program if linking the shaders fails.
mLinked = false;
@@ -1119,7 +1326,7 @@ void Program::makeNewExecutable(const Context *context)
// By default, set the link event as failing. If link succeeds, it will be replaced by the
// appropriate event.
mLinkingState->linkEvent = std::make_unique<rx::LinkEventDone>(angle::Result::Stop);
mLinkingState->linkEvent = std::make_unique<LinkEventDone>(angle::Result::Stop);
InstallExecutable(
context,
@@ -1191,7 +1398,7 @@ angle::Result Program::linkImpl(const Context *context)
mState.mInfoLog.reset();
// Validate we have properly attached shaders before checking the cache.
if (!linkValidateShaders(context))
if (!linkValidateShaders())
{
return angle::Result::Continue;
}
@@ -1224,41 +1431,76 @@ angle::Result Program::linkImpl(const Context *context)
const Version &clientVersion = context->getClientVersion();
const bool isWebGL = context->isWebGL();
// Ask the backend to prepare the link task.
std::shared_ptr<rx::LinkTask> linkTask;
ANGLE_TRY(mProgram->link(context, &linkTask));
std::unique_ptr<LinkingState> linkingState = std::make_unique<LinkingState>();
// Prepare the main link job
std::shared_ptr<MainLinkLoadTask> mainLinkTask(new MainLinkTask(
context->getShaderCompileThreadPool(), caps, limitations, clientVersion, isWebGL, this,
&mState, &linkingState->linkingVariables, &linkingState->resources, std::move(linkTask)));
// While the subtasks are currently always thread-safe, the main task is not safe on all
// backends. A front-end feature selects whether the single-threaded pool must be used.
std::shared_ptr<angle::WorkerThreadPool> mainLinkWorkerPool =
context->getFrontendFeatures().linkJobIsNotThreadSafe.enabled
? context->getSingleThreadPool()
: context->getShaderCompileThreadPool();
// TODO: add the possibility to perform this in an unlocked tail call. http://anglebug.com/8297
std::shared_ptr<angle::WaitableEvent> mainLinkEvent =
mainLinkWorkerPool->postWorkerTask(mainLinkTask);
mLinkingState = std::move(linkingState);
mLinkingState->linkingFromBinary = false;
mLinkingState->programHash = programHash;
mLinkingState->linkEvent = std::make_unique<MainLinkLoadEvent>(mainLinkTask, mainLinkEvent);
return angle::Result::Continue;
}
angle::Result Program::linkJobImpl(const Caps &caps,
const Limitations &limitations,
const Version &clientVersion,
bool isWebGL,
LinkingVariables *linkingVariables,
ProgramLinkedResources *resources,
ProgramMergedVaryings *mergedVaryingsOut)
{
// Cache load failed, fall through to normal linking.
unlink();
// Re-link shaders after the unlink call.
linkShaders();
ProgramMergedVaryings mergedVaryings;
LinkingVariables &linkingVariables = mLinkingState->linkingVariables;
ProgramLinkedResources &resources = mLinkingState->resources;
linkingVariables.initForProgram(mState);
resources.init(&mState.mExecutable->mUniformBlocks, &mState.mExecutable->mUniforms,
&mState.mExecutable->mUniformNames, &mState.mExecutable->mUniformMappedNames,
&mState.mExecutable->mShaderStorageBlocks, &mState.mExecutable->mBufferVariables,
&mState.mExecutable->mAtomicCounterBuffers);
linkingVariables->initForProgram(mState);
resources->init(&mState.mExecutable->mUniformBlocks, &mState.mExecutable->mUniforms,
&mState.mExecutable->mUniformNames, &mState.mExecutable->mUniformMappedNames,
&mState.mExecutable->mShaderStorageBlocks,
&mState.mExecutable->mBufferVariables,
&mState.mExecutable->mAtomicCounterBuffers);
updateLinkedShaderStages();
InitUniformBlockLinker(mState, &resources.uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker);
InitUniformBlockLinker(mState, &resources->uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources->shaderStorageBlockLinker);
if (mState.mAttachedShaders[ShaderType::Compute])
{
GLuint combinedImageUniforms = 0;
if (!linkUniforms(caps, clientVersion, &resources.unusedUniforms, &combinedImageUniforms))
if (!linkUniforms(caps, clientVersion, &resources->unusedUniforms, &combinedImageUniforms))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
GLuint combinedShaderStorageBlocks = 0u;
if (!LinkValidateProgramInterfaceBlocks(
caps, clientVersion, isWebGL, mState.mExecutable->getLinkedShaderStages(),
resources, mState.mInfoLog, &combinedShaderStorageBlocks))
*resources, mState.mInfoLog, &combinedShaderStorageBlocks))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
// [OpenGL ES 3.1] Chapter 8.22 Page 203:
@@ -1274,38 +1516,38 @@ angle::Result Program::linkImpl(const Context *context)
"and active fragment shader outputs exceeds "
"MAX_COMBINED_SHADER_OUTPUT_RESOURCES ("
<< caps.maxCombinedShaderOutputResources << ")";
return angle::Result::Continue;
return angle::Result::Stop;
}
}
else
{
if (!linkAttributes(caps, limitations, isWebGL))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
if (!linkVaryings())
{
return angle::Result::Continue;
return angle::Result::Stop;
}
GLuint combinedImageUniforms = 0;
if (!linkUniforms(caps, clientVersion, &resources.unusedUniforms, &combinedImageUniforms))
if (!linkUniforms(caps, clientVersion, &resources->unusedUniforms, &combinedImageUniforms))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
GLuint combinedShaderStorageBlocks = 0u;
if (!LinkValidateProgramInterfaceBlocks(
caps, clientVersion, isWebGL, mState.mExecutable->getLinkedShaderStages(),
resources, mState.mInfoLog, &combinedShaderStorageBlocks))
*resources, mState.mInfoLog, &combinedShaderStorageBlocks))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
if (!LinkValidateProgramGlobalNames(mState.mInfoLog, getExecutable(), linkingVariables))
if (!LinkValidateProgramGlobalNames(mState.mInfoLog, getExecutable(), *linkingVariables))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
const SharedCompiledShaderState &vertexShader = mState.mAttachedShaders[ShaderType::Vertex];
@@ -1325,7 +1567,7 @@ angle::Result Program::linkImpl(const Context *context)
fragmentShader->activeOutputVariables, fragmentShader->shaderVersion,
mState.mFragmentOutputLocations, mState.mFragmentOutputIndexes))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
mState.mExecutable->mPODStruct.hasDiscard = fragmentShader->hasDiscard;
@@ -1336,25 +1578,17 @@ angle::Result Program::linkImpl(const Context *context)
mState.mExecutable->mPODStruct.specConstUsageBits |= fragmentShader->specConstUsageBits;
}
mergedVaryings = GetMergedVaryingsFromLinkingVariables(linkingVariables);
*mergedVaryingsOut = GetMergedVaryingsFromLinkingVariables(*linkingVariables);
if (!mState.mExecutable->linkMergedVaryings(caps, limitations, clientVersion, isWebGL,
mergedVaryings, linkingVariables, isSeparable(),
&resources.varyingPacking))
*mergedVaryingsOut, *linkingVariables,
isSeparable(), &resources->varyingPacking))
{
return angle::Result::Continue;
return angle::Result::Stop;
}
}
mState.mExecutable->saveLinkedStateInfo(mState);
mLinkingState->linkingFromBinary = false;
mLinkingState->programHash = programHash;
mLinkingState->linkEvent = mProgram->link(context, resources, std::move(mergedVaryings));
// Must be after mProgram->link() to avoid misleading the linker about output variables.
mState.updateProgramInterfaceInputs();
mState.updateProgramInterfaceOutputs();
return angle::Result::Continue;
}
@@ -1556,8 +1790,36 @@ angle::Result Program::loadBinary(const Context *context,
mDirtyBits.set(uniformBlockIndex);
}
// If load returns incomplete, we know for sure that the binary is not compatible with the
// backend. The loaded binary could have been read from the on-disk shader cache and be
// corrupted or serialized with different revision and subsystem id than the currently loaded
// backend. Returning 'Incomplete' to the caller results in link happening using the original
// shader sources.
std::shared_ptr<rx::LinkTask> loadTask;
angle::Result result = mProgram->load(context, &stream, &loadTask);
if (result == angle::Result::Incomplete)
{
return angle::Result::Incomplete;
}
ANGLE_TRY(result);
std::unique_ptr<LinkEvent> loadEvent;
if (loadTask)
{
std::shared_ptr<MainLinkLoadTask> mainLoadTask(new MainLoadTask(
context->getShaderCompileThreadPool(), this, &mState, std::move(loadTask)));
std::shared_ptr<angle::WaitableEvent> mainLoadEvent =
context->getShaderCompileThreadPool()->postWorkerTask(mainLoadTask);
loadEvent = std::make_unique<MainLinkLoadEvent>(mainLoadTask, mainLoadEvent);
}
else
{
loadEvent = std::make_unique<LinkEventDone>(angle::Result::Continue);
}
mLinkingState->linkingFromBinary = true;
mLinkingState->linkEvent = mProgram->load(context, &stream);
mLinkingState->linkEvent = std::move(loadEvent);
*successOut = true;
@@ -2732,7 +2994,7 @@ GLsizei Program::getTransformFeedbackVaryingMaxLength() const
}
}
bool Program::linkValidateShaders(const Context *context)
bool Program::linkValidateShaders()
{
const ShaderMap<SharedCompiledShaderState> &shaders = mState.mAttachedShaders;
@@ -2754,19 +3016,15 @@ bool Program::linkValidateShaders(const Context *context)
Optional<int> version;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
Shader *shaderObj = getAttachedShader(shaderType);
ASSERT(!shaderObj || shaderObj->getType() == shaderType);
const SharedCompiledShaderState &shader = shaders[shaderType];
ASSERT(!shader || shader->shaderType == shaderType);
if (!shaderObj)
if (!shader)
{
ASSERT(!shader);
continue;
}
if (!shaderObj->isCompiled(context))
if (!shader->successfullyCompiled)
{
mState.mInfoLog << ShaderTypeToString(shaderType) << " shader is not compiled.";
return false;

View File

@@ -851,7 +851,15 @@ class Program final : public LabeledObject, public angle::Subject
}
private:
class MainLinkLoadTask;
class MainLoadTask;
class MainLinkTask;
class MainLinkLoadEvent;
friend class ProgramPipeline;
friend class MainLinkLoadTask;
friend class MainLoadTask;
friend class MainLinkTask;
struct LinkingState;
~Program() override;
@@ -863,10 +871,17 @@ class Program final : public LabeledObject, public angle::Subject
void deleteSelf(const Context *context);
angle::Result linkImpl(const Context *context);
angle::Result linkJobImpl(const Caps &caps,
const Limitations &limitations,
const Version &clientVersion,
bool isWebGL,
LinkingVariables *linkingVariables,
ProgramLinkedResources *resources,
ProgramMergedVaryings *mergedVaryingsOut);
void makeNewExecutable(const Context *context);
bool linkValidateShaders(const Context *context);
bool linkValidateShaders();
void linkShaders();
bool linkAttributes(const Caps &caps, const Limitations &limitations, bool webglCompatibility);
bool linkVaryings();

View File

@@ -615,6 +615,8 @@ void Shader::resolveCompile(const Context *context)
bool success = mCompilingState->compileEvent->postTranslate(&mInfoLog);
mState.mCompileStatus = success ? CompileStatus::COMPILED : CompileStatus::NOT_COMPILED;
mState.mCompiledState->successfullyCompiled = success;
MemoryShaderCache *shaderCache = context->getMemoryShaderCache();
if (success && shaderCache)
{
@@ -772,7 +774,8 @@ angle::Result Shader::loadBinaryImpl(const Context *context,
// Only successfully-compiled shaders are serialized. If deserialization is successful, we can
// assume the CompileStatus.
mState.mCompileStatus = CompileStatus::COMPILED;
mState.mCompileStatus = CompileStatus::COMPILED;
mState.mCompiledState->successfullyCompiled = true;
return angle::Result::Continue;
}

View File

@@ -9,6 +9,22 @@
namespace rx
{
std::vector<std::shared_ptr<LinkSubTask>> LinkTask::link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings)
{
UNREACHABLE();
return {};
}
std::vector<std::shared_ptr<LinkSubTask>> LinkTask::load()
{
UNREACHABLE();
return {};
}
bool LinkTask::isLinkingInternally()
{
return false;
}
angle::Result ProgramImpl::onLabelUpdate(const gl::Context *context)
{

View File

@@ -10,11 +10,13 @@
#define LIBANGLE_RENDERER_PROGRAMIMPL_H_
#include "common/BinaryStream.h"
#include "common/WorkerThread.h"
#include "common/angleutils.h"
#include "libANGLE/Constants.h"
#include "libANGLE/Program.h"
#include "libANGLE/Shader.h"
#include <functional>
#include <map>
namespace gl
@@ -30,44 +32,47 @@ struct BlockMemberInfo;
namespace rx
{
// Provides a mechanism to access the result of asynchronous linking.
class LinkEvent : angle::NonCopyable
// The link job is split as such:
//
// - Front-end link
// - Back-end link
// - Independent back-end link subtasks (typically native driver compile jobs)
// - Post-link finalization
//
// Each step depends on the previous. These steps are executed as such:
//
// 1. Program::link calls into ProgramImpl::link
// - ProgramImpl::link runs whatever needs the Context, such as releasing resources
// - ProgramImpl::link returns a LinkTask
// 2. Program::link implements a closure that calls the front-end link and passes the results to
// the backend's LinkTask.
// 3. The LinkTask potentially returns a set of LinkSubTasks to be scheduled by the worker pool
// 4. Once the link is resolved, the post-link finalization is run
//
// In the above, steps 1 and 4 are done under the share group lock. Steps 2 and 3 can be done in
// threads or without holding the share group lock if the backend supports it.
class LinkSubTask : public angle::Closure
{
public:
virtual ~LinkEvent() {}
// Please be aware that these methods may be called under a gl::Context other
// than the one where the LinkEvent was created.
//
// Waits until the linking is actually done. Returns true if the linking
// succeeded, false otherwise.
virtual angle::Result wait(const gl::Context *context) = 0;
// Peeks whether the linking is still ongoing.
virtual bool isLinking() = 0;
~LinkSubTask() override = default;
virtual angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) = 0;
};
// Wraps an already done linking.
class LinkEventDone final : public LinkEvent
class LinkTask
{
public:
LinkEventDone(angle::Result result) : mResult(result) {}
angle::Result wait(const gl::Context *context) override;
bool isLinking() override;
virtual ~LinkTask() = default;
// Used for link()
virtual std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings);
// Used for load()
virtual std::vector<std::shared_ptr<LinkSubTask>> load();
virtual angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) = 0;
private:
angle::Result mResult;
// Used by the GL backend to query whether the driver is linking in parallel internally.
virtual bool isLinkingInternally();
};
inline angle::Result LinkEventDone::wait(const gl::Context *context)
{
return mResult;
}
inline bool LinkEventDone::isLinking()
{
return false;
}
class ProgramImpl : angle::NonCopyable
{
public:
@@ -75,17 +80,17 @@ class ProgramImpl : angle::NonCopyable
virtual ~ProgramImpl() {}
virtual void destroy(const gl::Context *context) {}
virtual std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) = 0;
virtual angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) = 0;
virtual void save(const gl::Context *context, gl::BinaryOutputStream *stream) = 0;
virtual void setBinaryRetrievableHint(bool retrievable) = 0;
virtual void setSeparable(bool separable) = 0;
virtual void prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders) {}
virtual std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) = 0;
virtual GLboolean validate(const gl::Caps &caps) = 0;
virtual angle::Result link(const gl::Context *context,
std::shared_ptr<LinkTask> *linkTaskOut) = 0;
virtual GLboolean validate(const gl::Caps &caps) = 0;
virtual void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) = 0;
virtual void setUniform2fv(GLint location, GLsizei count, const GLfloat *v) = 0;

View File

@@ -92,6 +92,86 @@ class HLSLBlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
return new sh::HLSLBlockEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
}
};
// GetExecutableTask class
class GetExecutableTask : public LinkSubTask, public d3d::Context
{
public:
GetExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader)
: mProgram(program), mExecutable(program->getExecutable()), mShader(shader)
{}
~GetExecutableTask() override = default;
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
ASSERT((mResult == angle::Result::Continue) == (mStoredHR == S_OK));
ANGLE_TRY(checkTask(context, infoLog));
// Append debug info
if (mShader)
{
mShader->appendDebugInfo(mShaderExecutable->getDebugInfo());
}
return angle::Result::Continue;
}
void handleResult(HRESULT hr,
const char *message,
const char *file,
const char *function,
unsigned int line) override
{
mStoredHR = hr;
mStoredMessage = message;
mStoredFile = file;
mStoredFunction = function;
mStoredLine = line;
}
protected:
void popError(d3d::Context *context)
{
ASSERT(mStoredFile);
ASSERT(mStoredFunction);
context->handleResult(mStoredHR, mStoredMessage.c_str(), mStoredFile, mStoredFunction,
mStoredLine);
}
angle::Result checkTask(const gl::Context *context, gl::InfoLog &infoLog)
{
// Forward any logs
if (!mInfoLog.empty())
{
infoLog << mInfoLog.str();
}
// Forward any errors
if (mResult != angle::Result::Continue)
{
ContextD3D *contextD3D = GetImplAs<ContextD3D>(context);
popError(contextD3D);
return angle::Result::Stop;
}
return angle::Result::Continue;
}
ProgramD3D *mProgram = nullptr;
ProgramExecutableD3D *mExecutable = nullptr;
angle::Result mResult = angle::Result::Continue;
gl::InfoLog mInfoLog;
ShaderExecutableD3D *mShaderExecutable = nullptr;
SharedCompiledShaderStateD3D mShader;
// Error handling
HRESULT mStoredHR = S_OK;
std::string mStoredMessage;
const char *mStoredFile = nullptr;
const char *mStoredFunction = nullptr;
unsigned int mStoredLine = 0;
};
} // anonymous namespace
// ProgramD3DMetadata Implementation
@@ -256,21 +336,117 @@ uint8_t ProgramD3DMetadata::getCullDistanceArraySize() const
return shader ? shader->cullDistanceSize : 0;
}
// ProgramD3D::GetExecutableTask class
class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context
// ProgramD3D Implementation
class ProgramD3D::GetVertexExecutableTask : public GetExecutableTask
{
public:
GetExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable)
: mProgram(program), mExecutable(executable)
GetVertexExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader)
: GetExecutableTask(program, shader)
{}
~GetVertexExecutableTask() override = default;
virtual angle::Result run() = 0;
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "GetVertexExecutableTask::run");
void operator()() override { mResult = run(); }
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Vertex);
mResult = mExecutable->getVertexExecutableForCachedInputLayout(
this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog);
}
};
angle::Result getResult() const { return mResult; }
const gl::InfoLog &getInfoLog() const { return mInfoLog; }
ShaderExecutableD3D *getExecutable() { return mShaderExecutable; }
class ProgramD3D::GetPixelExecutableTask : public GetExecutableTask
{
public:
GetPixelExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader)
: GetExecutableTask(program, shader)
{}
~GetPixelExecutableTask() override = default;
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "GetPixelExecutableTask::run");
if (!mShader)
{
return;
}
mExecutable->updateCachedOutputLayoutFromShader();
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Fragment);
mResult = mExecutable->getPixelExecutableForCachedOutputLayout(
this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog);
}
};
class ProgramD3D::GetGeometryExecutableTask : public GetExecutableTask
{
public:
GetGeometryExecutableTask(ProgramD3D *program,
const SharedCompiledShaderStateD3D &shader,
const gl::Caps &caps,
gl::ProvokingVertexConvention provokingVertex)
: GetExecutableTask(program, shader), mCaps(caps), mProvokingVertex(provokingVertex)
{}
~GetGeometryExecutableTask() override = default;
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "GetGeometryExecutableTask::run");
// Auto-generate the geometry shader here, if we expect to be using point rendering in
// D3D11.
if (mExecutable->usesGeometryShader(mProgram->mRenderer, mProvokingVertex,
gl::PrimitiveMode::Points))
{
mResult = mExecutable->getGeometryExecutableForPrimitiveType(
this, mProgram->mRenderer, mCaps, mProvokingVertex, gl::PrimitiveMode::Points,
&mShaderExecutable, &mInfoLog);
}
}
private:
const gl::Caps &mCaps;
gl::ProvokingVertexConvention mProvokingVertex;
};
class ProgramD3D::GetComputeExecutableTask : public GetExecutableTask
{
public:
GetComputeExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader)
: GetExecutableTask(program, shader)
{}
~GetComputeExecutableTask() override = default;
void operator()() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "GetComputeExecutableTask::run");
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute);
mResult = mExecutable->getComputeExecutableForImage2DBindLayout(
this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog);
}
};
class ProgramD3D::LinkLoadTaskD3D : public d3d::Context, public LinkTask
{
public:
LinkLoadTaskD3D(ProgramD3D *program) : mProgram(program), mExecutable(program->getExecutable())
{
ASSERT(mProgram);
}
~LinkLoadTaskD3D() override = default;
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
if (mStoredHR != S_OK)
{
GetImplAs<ContextD3D>(context)->handleResult(mStoredHR, mStoredMessage.c_str(),
mStoredFile, mStoredFunction, mStoredLine);
return angle::Result::Stop;
}
return angle::Result::Continue;
}
void handleResult(HRESULT hr,
const char *message,
@@ -285,28 +461,115 @@ class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context
mStoredLine = line;
}
void popError(d3d::Context *context)
{
ASSERT(mStoredFile);
ASSERT(mStoredFunction);
context->handleResult(mStoredHR, mStoredMessage.c_str(), mStoredFile, mStoredFunction,
mStoredLine);
}
protected:
ProgramD3D *mProgram = nullptr;
// 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.
ProgramD3D *mProgram;
ProgramExecutableD3D *mExecutable = nullptr;
angle::Result mResult = angle::Result::Continue;
gl::InfoLog mInfoLog;
ShaderExecutableD3D *mShaderExecutable = nullptr;
HRESULT mStoredHR = S_OK;
// Error handling
HRESULT mStoredHR = S_OK;
std::string mStoredMessage;
const char *mStoredFile = nullptr;
const char *mStoredFunction = nullptr;
unsigned int mStoredLine = 0;
};
// ProgramD3D Implementation
class ProgramD3D::LinkTaskD3D final : public LinkLoadTaskD3D
{
public:
LinkTaskD3D(const gl::Version &clientVersion,
const gl::Caps &caps,
EGLenum clientType,
ProgramD3D *program,
gl::ProvokingVertexConvention provokingVertex)
: LinkLoadTaskD3D(program),
mClientVersion(clientVersion),
mCaps(caps),
mClientType(clientType),
mProvokingVertex(provokingVertex)
{}
~LinkTaskD3D() override = default;
std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings) override;
private:
const gl::Version mClientVersion;
const gl::Caps &mCaps;
const EGLenum mClientType;
const gl::ProvokingVertexConvention mProvokingVertex;
};
std::vector<std::shared_ptr<LinkSubTask>> ProgramD3D::LinkTaskD3D::link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings)
{
ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskD3D::link");
angle::Result result =
mProgram->linkJobImpl(this, mCaps, mClientVersion, mClientType, resources, mergedVaryings);
ASSERT((result == angle::Result::Continue) == (mStoredHR == S_OK));
if (result != angle::Result::Continue)
{
return {};
}
// Create the subtasks
std::vector<std::shared_ptr<LinkSubTask>> subTasks;
if (mExecutable->hasShaderStage(gl::ShaderType::Compute))
{
subTasks.push_back(std::make_shared<GetComputeExecutableTask>(
mProgram, mProgram->getAttachedShader(gl::ShaderType::Compute)));
}
else
{
// Geometry shaders are currently only used internally, so there is no corresponding shader
// object at the interface level. For now the geometry shader debug info is prepended to the
// vertex shader.
subTasks.push_back(std::make_shared<GetVertexExecutableTask>(
mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex)));
subTasks.push_back(std::make_shared<GetPixelExecutableTask>(
mProgram, mProgram->getAttachedShader(gl::ShaderType::Fragment)));
subTasks.push_back(std::make_shared<GetGeometryExecutableTask>(
mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex), mCaps,
mProvokingVertex));
}
return subTasks;
}
class ProgramD3D::LoadTaskD3D final : public LinkLoadTaskD3D
{
public:
LoadTaskD3D(ProgramD3D *program, angle::MemoryBuffer &&streamData)
: LinkLoadTaskD3D(program), mStreamData(std::move(streamData))
{}
~LoadTaskD3D() override = default;
std::vector<std::shared_ptr<LinkSubTask>> load() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "LoadTaskD3D::load");
gl::BinaryInputStream stream(mStreamData.data(), mStreamData.size());
mResult = mExecutable->loadBinaryShaderExecutables(this, mProgram->mRenderer, &stream);
return {};
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
ANGLE_TRY(LinkLoadTaskD3D::getResult(context, infoLog));
return mResult;
}
private:
angle::MemoryBuffer mStreamData;
angle::Result mResult = angle::Result::Continue;
};
ProgramD3D::ProgramD3D(const gl::ProgramState &state, RendererD3D *renderer)
: ProgramImpl(state), mRenderer(renderer)
@@ -316,96 +579,35 @@ ProgramD3D::~ProgramD3D() = default;
void ProgramD3D::destroy(const gl::Context *context)
{
reset();
getExecutable()->reset();
}
class ProgramD3D::LoadBinaryTask : public ProgramD3D::GetExecutableTask
{
public:
LoadBinaryTask(ProgramD3D *program,
ProgramExecutableD3D *executable,
gl::BinaryInputStream *stream,
gl::InfoLog &infoLog)
: ProgramD3D::GetExecutableTask(program, executable)
{
ASSERT(mProgram);
ASSERT(mExecutable);
ASSERT(stream);
// Copy the remaining data from the stream locally so that the client can't modify it when
// loading off thread.
size_t dataSize = stream->remainingSize();
mDataCopySucceeded = mStreamData.resize(dataSize);
if (mDataCopySucceeded)
{
memcpy(mStreamData.data(), stream->data() + stream->offset(), dataSize);
}
}
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::LoadBinaryTask::run");
if (!mDataCopySucceeded)
{
mInfoLog << "Failed to copy program binary data to local buffer.";
return angle::Result::Incomplete;
}
gl::BinaryInputStream stream(mStreamData.data(), mStreamData.size());
return mExecutable->loadBinaryShaderExecutables(this, mProgram->mRenderer, &stream,
mInfoLog);
}
private:
bool mDataCopySucceeded;
angle::MemoryBuffer mStreamData;
};
class ProgramD3D::LoadBinaryLinkEvent final : public LinkEvent
{
public:
LoadBinaryLinkEvent(std::shared_ptr<WorkerThreadPool> workerPool,
ProgramD3D *program,
ProgramExecutableD3D *executable,
gl::BinaryInputStream *stream,
gl::InfoLog &infoLog)
: mTask(std::make_shared<ProgramD3D::LoadBinaryTask>(program, executable, stream, infoLog)),
mWaitableEvent(workerPool->postWorkerTask(mTask))
{}
angle::Result wait(const gl::Context *context) override
{
mWaitableEvent->wait();
// Continue and Incomplete are not errors. For Stop, pass the error to the ContextD3D.
if (mTask->getResult() != angle::Result::Stop)
{
return angle::Result::Continue;
}
ContextD3D *contextD3D = GetImplAs<ContextD3D>(context);
mTask->popError(contextD3D);
return angle::Result::Stop;
}
bool isLinking() override { return !mWaitableEvent->isReady(); }
private:
std::shared_ptr<ProgramD3D::LoadBinaryTask> mTask;
std::shared_ptr<WaitableEvent> mWaitableEvent;
};
std::unique_ptr<rx::LinkEvent> ProgramD3D::load(const gl::Context *context,
gl::BinaryInputStream *stream)
angle::Result ProgramD3D::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut)
{
if (!getExecutable()->load(context, mRenderer, stream))
{
return std::make_unique<LinkEventDone>(angle::Result::Stop);
mState.getExecutable().getInfoLog()
<< "Invalid program binary, device configuration has changed.";
return angle::Result::Incomplete;
}
return std::make_unique<LoadBinaryLinkEvent>(context->getShaderCompileThreadPool(), this,
getExecutable(), stream,
mState.getExecutable().getInfoLog());
// Copy the remaining data from the stream locally so that the client can't modify it when
// loading off thread.
angle::MemoryBuffer streamData;
const size_t dataSize = stream->remainingSize();
if (!streamData.resize(dataSize))
{
mState.getExecutable().getInfoLog()
<< "Failed to copy program binary data to local buffer.";
return angle::Result::Stop;
}
memcpy(streamData.data(), stream->data() + stream->offset(), dataSize);
// Note: pretty much all the above can also be moved to the task
*loadTaskOut = std::shared_ptr<LinkTask>(new LoadTaskD3D(this, std::move(streamData)));
return angle::Result::Continue;
}
void ProgramD3D::save(const gl::Context *context, gl::BinaryOutputStream *stream)
@@ -417,277 +619,6 @@ void ProgramD3D::setBinaryRetrievableHint(bool /* retrievable */) {}
void ProgramD3D::setSeparable(bool /* separable */) {}
class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetVertexExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable)
: GetExecutableTask(program, executable)
{}
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetVertexExecutableTask::run");
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Vertex);
ANGLE_TRY(mExecutable->getVertexExecutableForCachedInputLayout(
this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog));
return angle::Result::Continue;
}
};
class ProgramD3D::GetPixelExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetPixelExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable)
: GetExecutableTask(program, executable)
{}
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetPixelExecutableTask::run");
if (!mProgram->mState.getAttachedShader(gl::ShaderType::Fragment))
{
return angle::Result::Continue;
}
mExecutable->updateCachedOutputLayoutFromShader();
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Fragment);
ANGLE_TRY(mExecutable->getPixelExecutableForCachedOutputLayout(
this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog));
return angle::Result::Continue;
}
};
class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetGeometryExecutableTask(ProgramD3D *program,
ProgramExecutableD3D *executable,
const gl::Caps &caps,
gl::ProvokingVertexConvention provokingVertex)
: GetExecutableTask(program, executable), mCaps(caps), mProvokingVertex(provokingVertex)
{}
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetGeometryExecutableTask::run");
// Auto-generate the geometry shader here, if we expect to be using point rendering in
// D3D11.
if (mExecutable->usesGeometryShader(mProgram->mRenderer, mProvokingVertex,
gl::PrimitiveMode::Points))
{
ANGLE_TRY(mExecutable->getGeometryExecutableForPrimitiveType(
this, mProgram->mRenderer, mCaps, mProvokingVertex, gl::PrimitiveMode::Points,
&mShaderExecutable, &mInfoLog));
}
return angle::Result::Continue;
}
private:
const gl::Caps &mCaps;
gl::ProvokingVertexConvention mProvokingVertex;
};
class ProgramD3D::GetComputeExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetComputeExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable)
: GetExecutableTask(program, executable)
{}
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetComputeExecutableTask::run");
mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute);
ShaderExecutableD3D *computeExecutable = nullptr;
ANGLE_TRY(mExecutable->getComputeExecutableForImage2DBindLayout(
this, mProgram->mRenderer, &computeExecutable, &mInfoLog));
return computeExecutable ? angle::Result::Continue : angle::Result::Incomplete;
}
};
// The LinkEvent implementation for linking a rendering(VS, FS, GS) program.
class ProgramD3D::GraphicsProgramLinkEvent final : public LinkEvent
{
public:
GraphicsProgramLinkEvent(gl::InfoLog &infoLog,
std::shared_ptr<WorkerThreadPool> workerPool,
std::shared_ptr<ProgramD3D::GetVertexExecutableTask> vertexTask,
std::shared_ptr<ProgramD3D::GetPixelExecutableTask> pixelTask,
std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> geometryTask,
bool useGS,
const SharedCompiledShaderStateD3D &vertexShader,
const SharedCompiledShaderStateD3D &fragmentShader)
: mInfoLog(infoLog),
mVertexTask(vertexTask),
mPixelTask(pixelTask),
mGeometryTask(geometryTask),
mWaitEvents(
{{std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mVertexTask)),
std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mPixelTask)),
std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mGeometryTask))}}),
mUseGS(useGS),
mVertexShader(vertexShader),
mFragmentShader(fragmentShader)
{}
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GraphicsProgramLinkEvent::wait");
WaitableEvent::WaitMany(&mWaitEvents);
ANGLE_TRY(checkTask(context, mVertexTask.get()));
ANGLE_TRY(checkTask(context, mPixelTask.get()));
ANGLE_TRY(checkTask(context, mGeometryTask.get()));
if (mVertexTask->getResult() == angle::Result::Incomplete ||
mPixelTask->getResult() == angle::Result::Incomplete ||
mGeometryTask->getResult() == angle::Result::Incomplete)
{
return angle::Result::Incomplete;
}
ShaderExecutableD3D *defaultVertexExecutable = mVertexTask->getExecutable();
ShaderExecutableD3D *defaultPixelExecutable = mPixelTask->getExecutable();
ShaderExecutableD3D *pointGS = mGeometryTask->getExecutable();
if (mUseGS && pointGS)
{
// Geometry shaders are currently only used internally, so there is no corresponding
// shader object at the interface level. For now the geometry shader debug info is
// prepended to the vertex shader.
mVertexShader->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n");
mVertexShader->appendDebugInfo(pointGS->getDebugInfo());
mVertexShader->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n");
}
if (defaultVertexExecutable)
{
mVertexShader->appendDebugInfo(defaultVertexExecutable->getDebugInfo());
}
if (defaultPixelExecutable)
{
mFragmentShader->appendDebugInfo(defaultPixelExecutable->getDebugInfo());
}
bool isLinked = (defaultVertexExecutable && defaultPixelExecutable && (!mUseGS || pointGS));
if (!isLinked)
{
mInfoLog << "Failed to create D3D Shaders";
}
return isLinked ? angle::Result::Continue : angle::Result::Incomplete;
}
bool isLinking() override { return !WaitableEvent::AllReady(&mWaitEvents); }
private:
angle::Result checkTask(const gl::Context *context, ProgramD3D::GetExecutableTask *task)
{
if (!task->getInfoLog().empty())
{
mInfoLog << task->getInfoLog().str();
}
// Continue and Incomplete are not errors. For Stop, pass the error to the ContextD3D.
if (task->getResult() != angle::Result::Stop)
{
return angle::Result::Continue;
}
ContextD3D *contextD3D = GetImplAs<ContextD3D>(context);
task->popError(contextD3D);
return angle::Result::Stop;
}
gl::InfoLog &mInfoLog;
std::shared_ptr<ProgramD3D::GetVertexExecutableTask> mVertexTask;
std::shared_ptr<ProgramD3D::GetPixelExecutableTask> mPixelTask;
std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> mGeometryTask;
std::array<std::shared_ptr<WaitableEvent>, 3> mWaitEvents;
bool mUseGS;
SharedCompiledShaderStateD3D mVertexShader;
SharedCompiledShaderStateD3D mFragmentShader;
};
// The LinkEvent implementation for linking a computing program.
class ProgramD3D::ComputeProgramLinkEvent final : public LinkEvent
{
public:
ComputeProgramLinkEvent(gl::InfoLog &infoLog,
std::shared_ptr<ProgramD3D::GetComputeExecutableTask> computeTask,
std::shared_ptr<WaitableEvent> event)
: mInfoLog(infoLog), mComputeTask(computeTask), mWaitEvent(event)
{}
bool isLinking() override { return !mWaitEvent->isReady(); }
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::ComputeProgramLinkEvent::wait");
mWaitEvent->wait();
angle::Result result = mComputeTask->getResult();
if (result != angle::Result::Continue)
{
mInfoLog << "Failed to create D3D compute shader.";
}
return result;
}
private:
gl::InfoLog &mInfoLog;
std::shared_ptr<ProgramD3D::GetComputeExecutableTask> mComputeTask;
std::shared_ptr<WaitableEvent> mWaitEvent;
};
std::unique_ptr<LinkEvent> ProgramD3D::compileProgramExecutables(const gl::Context *context)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileProgramExecutables");
ProgramExecutableD3D *executableD3D = getExecutable();
auto vertexTask = std::make_shared<GetVertexExecutableTask>(this, executableD3D);
auto pixelTask = std::make_shared<GetPixelExecutableTask>(this, executableD3D);
auto geometryTask = std::make_shared<GetGeometryExecutableTask>(
this, executableD3D, context->getCaps(), context->getState().getProvokingVertex());
bool useGS = executableD3D->usesGeometryShader(
mRenderer, context->getState().getProvokingVertex(), gl::PrimitiveMode::Points);
const SharedCompiledShaderStateD3D &vertexShaderD3D =
executableD3D->mAttachedShaders[gl::ShaderType::Vertex];
const SharedCompiledShaderStateD3D &fragmentShaderD3D =
executableD3D->mAttachedShaders[gl::ShaderType::Fragment];
return std::make_unique<GraphicsProgramLinkEvent>(
mState.getExecutable().getInfoLog(), context->getShaderCompileThreadPool(), vertexTask,
pixelTask, geometryTask, useGS, vertexShaderD3D, fragmentShaderD3D);
}
std::unique_ptr<LinkEvent> ProgramD3D::compileComputeExecutable(const gl::Context *context)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileComputeExecutable");
auto computeTask = std::make_shared<GetComputeExecutableTask>(this, getExecutable());
std::shared_ptr<WaitableEvent> waitableEvent;
// TODO(jie.a.chen@intel.com): Fix the flaky bug.
// http://anglebug.com/3349
bool compileInParallel = false;
if (!compileInParallel)
{
(*computeTask)();
waitableEvent = std::make_shared<WaitableEventDone>();
}
else
{
waitableEvent = context->getShaderCompileThreadPool()->postWorkerTask(computeTask);
}
return std::make_unique<ComputeProgramLinkEvent>(mState.getExecutable().getInfoLog(),
computeTask, waitableEvent);
}
void ProgramD3D::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
{
ProgramExecutableD3D *executableD3D = getExecutable();
@@ -704,9 +635,7 @@ void ProgramD3D::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
}
}
std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings && /*mergedVaryings*/)
angle::Result ProgramD3D::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::link");
const gl::Version &clientVersion = context->getClientVersion();
@@ -714,16 +643,10 @@ std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
EGLenum clientType = context->getClientType();
// Ensure the compiler is initialized to avoid race conditions.
angle::Result result = mRenderer->ensureHLSLCompilerInitialized(GetImplAs<ContextD3D>(context));
if (result != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(result);
}
ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized(GetImplAs<ContextD3D>(context)));
ProgramExecutableD3D *executableD3D = getExecutable();
reset();
const gl::SharedCompiledShaderState &computeShader =
mState.getAttachedShader(gl::ShaderType::Compute);
if (!computeShader)
@@ -758,6 +681,23 @@ std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
}
}
*linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskD3D(
clientVersion, caps, clientType, this, context->getState().getProvokingVertex()));
return angle::Result::Continue;
}
angle::Result ProgramD3D::linkJobImpl(d3d::Context *context,
const gl::Caps &caps,
const gl::Version &clientVersion,
EGLenum clientType,
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings)
{
ProgramExecutableD3D *executableD3D = getExecutable();
const gl::SharedCompiledShaderState &computeShader =
mState.getAttachedShader(gl::ShaderType::Compute);
if (computeShader)
{
const gl::SharedCompiledShaderState &shader =
@@ -783,100 +723,98 @@ std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders());
return compileComputeExecutable(context);
return angle::Result::Continue;
}
else
for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType);
if (shader)
{
const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType);
if (shader)
executableD3D->mAttachedShaders[shaderType]->generateWorkarounds(
&executableD3D->mShaderWorkarounds[shaderType]);
executableD3D->mShaderSamplers[shaderType].resize(
caps.maxShaderTextureImageUnits[shaderType]);
executableD3D->mImages[shaderType].resize(caps.maxImageUnits);
executableD3D->mReadonlyImages[shaderType].resize(caps.maxImageUnits);
executableD3D->mShaderUniformsDirty.set(shaderType);
for (const sh::ShaderVariable &uniform : shader->uniforms)
{
executableD3D->mAttachedShaders[shaderType]->generateWorkarounds(
&executableD3D->mShaderWorkarounds[shaderType]);
executableD3D->mShaderSamplers[shaderType].resize(
caps.maxShaderTextureImageUnits[shaderType]);
executableD3D->mImages[shaderType].resize(caps.maxImageUnits);
executableD3D->mReadonlyImages[shaderType].resize(caps.maxImageUnits);
executableD3D->mShaderUniformsDirty.set(shaderType);
for (const sh::ShaderVariable &uniform : shader->uniforms)
if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type))
{
if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type))
{
executableD3D->mImage2DUniforms[shaderType].push_back(uniform);
}
executableD3D->mImage2DUniforms[shaderType].push_back(uniform);
}
}
}
if (mRenderer->getNativeLimitations().noFrontFacingSupport)
{
const SharedCompiledShaderStateD3D &fragmentShader =
executableD3D->mAttachedShaders[gl::ShaderType::Fragment];
if (fragmentShader && fragmentShader->usesFrontFacing)
{
mState.getExecutable().getInfoLog()
<< "The current renderer doesn't support gl_FrontFacing";
return std::make_unique<LinkEventDone>(angle::Result::Incomplete);
}
}
const gl::VaryingPacking &varyingPacking =
resources.varyingPacking.getOutputPacking(gl::ShaderType::Vertex);
ProgramD3DMetadata metadata(
mRenderer, mState.getAttachedShader(gl::ShaderType::Fragment),
executableD3D->mAttachedShaders, clientType,
mState.getAttachedShader(gl::ShaderType::Vertex)->shaderVersion);
BuiltinVaryingsD3D builtins(metadata, varyingPacking);
DynamicHLSL::GenerateShaderLinkHLSL(mRenderer, caps, mState.getAttachedShaders(),
executableD3D->mAttachedShaders, metadata,
varyingPacking, builtins, &executableD3D->mShaderHLSL);
executableD3D->mUsesPointSize =
executableD3D->mAttachedShaders[gl::ShaderType::Vertex] &&
executableD3D->mAttachedShaders[gl::ShaderType::Vertex]->usesPointSize;
DynamicHLSL::GetPixelShaderOutputKey(mRenderer, caps, clientVersion, mState, metadata,
&executableD3D->mPixelShaderKey);
executableD3D->mFragDepthUsage = metadata.getFragDepthUsage();
executableD3D->mUsesSampleMask = metadata.usesSampleMask();
executableD3D->mUsesVertexID = metadata.usesVertexID();
executableD3D->mUsesViewID = metadata.usesViewID();
executableD3D->mHasMultiviewEnabled = metadata.hasMultiviewEnabled();
// Cache if we use flat shading
executableD3D->mUsesFlatInterpolation =
FindFlatInterpolationVarying(mState.getAttachedShaders());
if (mRenderer->getMajorShaderModel() >= 4)
{
executableD3D->mGeometryShaderPreamble = DynamicHLSL::GenerateGeometryShaderPreamble(
mRenderer, varyingPacking, builtins, executableD3D->mHasMultiviewEnabled,
metadata.canSelectViewInVertexShader());
}
executableD3D->initAttribLocationsToD3DSemantic(
mState.getAttachedShader(gl::ShaderType::Vertex));
executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders());
executableD3D->gatherTransformFeedbackVaryings(mRenderer, varyingPacking,
mState.getTransformFeedbackVaryingNames(),
builtins[gl::ShaderType::Vertex]);
linkResources(resources);
if (mState.getAttachedShader(gl::ShaderType::Vertex))
{
executableD3D->updateCachedInputLayoutFromShader(
mRenderer, mState.getAttachedShader(gl::ShaderType::Vertex));
}
return compileProgramExecutables(context);
}
if (mRenderer->getNativeLimitations().noFrontFacingSupport)
{
const SharedCompiledShaderStateD3D &fragmentShader =
executableD3D->mAttachedShaders[gl::ShaderType::Fragment];
if (fragmentShader && fragmentShader->usesFrontFacing)
{
mState.getExecutable().getInfoLog()
<< "The current renderer doesn't support gl_FrontFacing";
// Fail compilation
ANGLE_CHECK_HR(context, false, "gl_FrontFacing not supported", E_NOTIMPL);
}
}
const gl::VaryingPacking &varyingPacking =
resources.varyingPacking.getOutputPacking(gl::ShaderType::Vertex);
ProgramD3DMetadata metadata(mRenderer, mState.getAttachedShader(gl::ShaderType::Fragment),
executableD3D->mAttachedShaders, clientType,
mState.getAttachedShader(gl::ShaderType::Vertex)->shaderVersion);
BuiltinVaryingsD3D builtins(metadata, varyingPacking);
DynamicHLSL::GenerateShaderLinkHLSL(mRenderer, caps, mState.getAttachedShaders(),
executableD3D->mAttachedShaders, metadata, varyingPacking,
builtins, &executableD3D->mShaderHLSL);
executableD3D->mUsesPointSize =
executableD3D->mAttachedShaders[gl::ShaderType::Vertex] &&
executableD3D->mAttachedShaders[gl::ShaderType::Vertex]->usesPointSize;
DynamicHLSL::GetPixelShaderOutputKey(mRenderer, caps, clientVersion, mState, metadata,
&executableD3D->mPixelShaderKey);
executableD3D->mFragDepthUsage = metadata.getFragDepthUsage();
executableD3D->mUsesSampleMask = metadata.usesSampleMask();
executableD3D->mUsesVertexID = metadata.usesVertexID();
executableD3D->mUsesViewID = metadata.usesViewID();
executableD3D->mHasMultiviewEnabled = metadata.hasMultiviewEnabled();
// Cache if we use flat shading
executableD3D->mUsesFlatInterpolation =
FindFlatInterpolationVarying(mState.getAttachedShaders());
if (mRenderer->getMajorShaderModel() >= 4)
{
executableD3D->mGeometryShaderPreamble = DynamicHLSL::GenerateGeometryShaderPreamble(
mRenderer, varyingPacking, builtins, executableD3D->mHasMultiviewEnabled,
metadata.canSelectViewInVertexShader());
}
executableD3D->initAttribLocationsToD3DSemantic(
mState.getAttachedShader(gl::ShaderType::Vertex));
executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders());
executableD3D->gatherTransformFeedbackVaryings(mRenderer, varyingPacking,
mState.getTransformFeedbackVaryingNames(),
builtins[gl::ShaderType::Vertex]);
linkResources(resources);
if (mState.getAttachedShader(gl::ShaderType::Vertex))
{
executableD3D->updateCachedInputLayoutFromShader(
mRenderer, mState.getAttachedShader(gl::ShaderType::Vertex));
}
return angle::Result::Continue;
}
GLboolean ProgramD3D::validate(const gl::Caps & /*caps*/)
@@ -1124,11 +1062,6 @@ void ProgramD3D::setUniformMatrixfvInternal(GLint location,
}
}
void ProgramD3D::reset()
{
getExecutable()->reset();
}
template <typename DestT>
void ProgramD3D::getUniformInternal(GLint location, DestT *dataOut) const
{

View File

@@ -78,16 +78,15 @@ class ProgramD3D : public ProgramImpl
void destroy(const gl::Context *context) override;
std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) override;
angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) override;
void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
void prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders) override;
std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) override;
angle::Result link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut) override;
GLboolean validate(const gl::Caps &caps) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
@@ -155,17 +154,27 @@ class ProgramD3D : public ProgramImpl
}
private:
// These forward-declared tasks are used for multi-thread shader compiles.
class GetExecutableTask;
class GetVertexExecutableTask;
class GetPixelExecutableTask;
class GetGeometryExecutableTask;
class GetComputeExecutableTask;
class GraphicsProgramLinkEvent;
class ComputeProgramLinkEvent;
class LinkLoadTaskD3D;
class LinkTaskD3D;
class LoadTaskD3D;
class LoadBinaryTask;
class LoadBinaryLinkEvent;
friend class LinkTaskD3D;
friend class LoadTaskD3D;
angle::Result linkJobImpl(d3d::Context *context,
const gl::Caps &caps,
const gl::Version &clientVersion,
EGLenum clientType,
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings);
const SharedCompiledShaderStateD3D &getAttachedShader(gl::ShaderType shaderType)
{
return getExecutable()->mAttachedShaders[shaderType];
}
template <typename DestT>
void getUniformInternal(GLint location, DestT *dataOut) const;
@@ -187,11 +196,6 @@ class ProgramD3D : public ProgramImpl
GLboolean transpose,
const GLfloat *value);
std::unique_ptr<LinkEvent> compileProgramExecutables(const gl::Context *context);
std::unique_ptr<LinkEvent> compileComputeExecutable(const gl::Context *context);
void reset();
void linkResources(const gl::ProgramLinkedResources &resources);
RendererD3D *mRenderer;

View File

@@ -416,7 +416,9 @@ ProgramExecutableD3D::ProgramExecutableD3D(const gl::ProgramExecutable *executab
mUsedReadonlyImageRange({}),
mUsedAtomicCounterRange({}),
mSerial(issueSerial())
{}
{
reset();
}
ProgramExecutableD3D::~ProgramExecutableD3D() {}
@@ -706,9 +708,9 @@ bool ProgramExecutableD3D::load(const gl::Context *context,
angle::Result ProgramExecutableD3D::loadBinaryShaderExecutables(d3d::Context *contextD3D,
RendererD3D *renderer,
gl::BinaryInputStream *stream,
gl::InfoLog &infoLog)
gl::BinaryInputStream *stream)
{
gl::InfoLog &infoLog = mExecutable->getInfoLog();
const unsigned char *binary = reinterpret_cast<const unsigned char *>(stream->data());
bool separateAttribs = mExecutable->getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS;

View File

@@ -344,8 +344,7 @@ class ProgramExecutableD3D : public ProgramExecutableImpl
angle::Result loadBinaryShaderExecutables(d3d::Context *contextD3D,
RendererD3D *renderer,
gl::BinaryInputStream *stream,
gl::InfoLog &infoLog);
gl::BinaryInputStream *stream);
unsigned int getSerial() const { return mSerial; }

View File

@@ -91,6 +91,75 @@ std::string GetTransformFeedbackVaryingMappedName(const gl::SharedCompiledShader
} // anonymous namespace
class ProgramGL::LinkTaskGL final : public LinkTask
{
public:
LinkTaskGL(ProgramGL *program,
bool hasNativeParallelCompile,
const FunctionsGL *functions,
const gl::Extensions &extensions,
GLuint programID)
: mProgram(program),
mHasNativeParallelCompile(hasNativeParallelCompile),
mFunctions(functions),
mExtensions(extensions),
mProgramID(programID)
{}
~LinkTaskGL() override = default;
std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings) override
{
mProgram->linkJobImpl(mExtensions);
// If there is no native parallel compile, do the post-link right away.
if (!mHasNativeParallelCompile)
{
mResult = mProgram->postLinkJobImpl(resources);
}
// See comment on mResources
mResources = &resources;
return {};
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskGL::getResult");
if (mHasNativeParallelCompile)
{
mResult = mProgram->postLinkJobImpl(*mResources);
}
return mResult;
}
bool isLinkingInternally() override
{
GLint completionStatus = GL_TRUE;
if (mHasNativeParallelCompile)
{
mFunctions->getProgramiv(mProgramID, GL_COMPLETION_STATUS, &completionStatus);
}
return completionStatus == GL_FALSE;
}
private:
ProgramGL *mProgram;
const bool mHasNativeParallelCompile;
const FunctionsGL *mFunctions;
const gl::Extensions &mExtensions;
const GLuint mProgramID;
angle::Result mResult = angle::Result::Continue;
// Note: resources are kept alive by the front-end for the entire duration of the link,
// including during resolve when getResult() and postLink() are called.
const gl::ProgramLinkedResources *mResources = nullptr;
};
ProgramGL::ProgramGL(const gl::ProgramState &data,
const FunctionsGL *functions,
const angle::FeaturesGL &features,
@@ -117,14 +186,13 @@ void ProgramGL::destroy(const gl::Context *context)
mProgramID = 0;
}
std::unique_ptr<LinkEvent> ProgramGL::load(const gl::Context *context,
gl::BinaryInputStream *stream)
angle::Result ProgramGL::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::load");
ProgramExecutableGL *executableGL = getExecutable();
executableGL->reset();
// Read the binary format, size and blob
GLenum binaryFormat = stream->readInt<GLenum>();
GLint binaryLength = stream->readInt<GLint>();
@@ -137,13 +205,15 @@ std::unique_ptr<LinkEvent> ProgramGL::load(const gl::Context *context,
// Verify that the program linked
if (!checkLinkStatus())
{
return std::make_unique<LinkEventDone>(angle::Result::Stop);
return angle::Result::Incomplete;
}
executableGL->postLink(mFunctions, mFeatures, mProgramID);
reapplyUBOBindingsIfNeeded(context);
return std::make_unique<LinkEventDone>(angle::Result::Continue);
*loadTaskOut = {};
return angle::Result::Continue;
}
void ProgramGL::save(const gl::Context *context, gl::BinaryOutputStream *stream)
@@ -192,44 +262,6 @@ void ProgramGL::setSeparable(bool separable)
mFunctions->programParameteri(mProgramID, GL_PROGRAM_SEPARABLE, separable ? GL_TRUE : GL_FALSE);
}
using PostLinkImplFunctor = std::function<angle::Result()>;
// The event for a parallelized linking using the native driver extension.
class ProgramGL::LinkEventNativeParallel final : public LinkEvent
{
public:
LinkEventNativeParallel(PostLinkImplFunctor &&functor,
const FunctionsGL *functions,
GLuint programID)
: mPostLinkImplFunctor(functor), mFunctions(functions), mProgramID(programID)
{}
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::LinkEventNativeParallel::wait");
GLint linkStatus = GL_FALSE;
mFunctions->getProgramiv(mProgramID, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_TRUE)
{
return mPostLinkImplFunctor();
}
return angle::Result::Incomplete;
}
bool isLinking() override
{
GLint completionStatus = GL_FALSE;
mFunctions->getProgramiv(mProgramID, GL_COMPLETION_STATUS, &completionStatus);
return completionStatus == GL_FALSE;
}
private:
PostLinkImplFunctor mPostLinkImplFunctor;
const FunctionsGL *mFunctions;
GLuint mProgramID;
};
void ProgramGL::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
@@ -244,14 +276,20 @@ void ProgramGL::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
}
}
std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings && /*mergedVaryings*/)
angle::Result ProgramGL::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::link");
ProgramExecutableGL *executableGL = getExecutable();
executableGL->reset();
*linkTaskOut = std::make_shared<LinkTaskGL>(this, mRenderer->hasNativeParallelCompile(),
mFunctions, context->getExtensions(), mProgramID);
return angle::Result::Continue;
}
void ProgramGL::linkJobImpl(const gl::Extensions &extensions)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::linkJobImpl");
ProgramExecutableGL *executableGL = getExecutable();
if (mAttachedShaders[gl::ShaderType::Compute] != 0)
{
@@ -261,14 +299,15 @@ std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
{
// Set the transform feedback state
std::vector<std::string> transformFeedbackVaryingMappedNames;
const gl::ShaderType tfShaderType =
mState.getExecutable().hasLinkedShaderStage(gl::ShaderType::Geometry)
? gl::ShaderType::Geometry
: gl::ShaderType::Vertex;
const gl::SharedCompiledShaderState &tfShaderState = mState.getAttachedShader(tfShaderType);
for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
{
gl::ShaderType tfShaderType =
mState.getExecutable().hasLinkedShaderStage(gl::ShaderType::Geometry)
? gl::ShaderType::Geometry
: gl::ShaderType::Vertex;
std::string tfVaryingMappedName = GetTransformFeedbackVaryingMappedName(
mState.getAttachedShader(tfShaderType), tfVarying);
std::string tfVaryingMappedName =
GetTransformFeedbackVaryingMappedName(tfShaderState, tfVarying);
transformFeedbackVaryingMappedNames.push_back(tfVaryingMappedName);
}
@@ -321,7 +360,7 @@ std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
// Bind the secondary fragment color outputs defined in EXT_blend_func_extended. We only use
// the API to bind fragment output locations in case EXT_blend_func_extended is enabled.
// Otherwise shader-assigned locations will work.
if (context->getExtensions().blendFuncExtendedEXT)
if (extensions.blendFuncExtendedEXT)
{
const gl::SharedCompiledShaderState &fragmentShader =
mState.getAttachedShader(gl::ShaderType::Fragment);
@@ -423,49 +462,44 @@ std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
}
}
}
auto workerPool = context->getShaderCompileThreadPool();
auto postLinkImplTask = [this, &resources]() {
if (mAttachedShaders[gl::ShaderType::Compute] != 0)
{
mFunctions->detachShader(mProgramID, mAttachedShaders[gl::ShaderType::Compute]);
}
else
{
for (const gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
if (mAttachedShaders[shaderType] != 0)
{
mFunctions->detachShader(mProgramID, mAttachedShaders[shaderType]);
}
}
}
// Verify the link
if (!checkLinkStatus())
{
return angle::Result::Incomplete;
}
if (mFeatures.alwaysCallUseProgramAfterLink.enabled)
{
mStateManager->forceUseProgram(mProgramID);
}
linkResources(resources);
getExecutable()->postLink(mFunctions, mFeatures, mProgramID);
return angle::Result::Continue;
};
mFunctions->linkProgram(mProgramID);
if (mRenderer->hasNativeParallelCompile())
}
angle::Result ProgramGL::postLinkJobImpl(const gl::ProgramLinkedResources &resources)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::postLinkJobImpl");
if (mAttachedShaders[gl::ShaderType::Compute] != 0)
{
return std::make_unique<LinkEventNativeParallel>(postLinkImplTask, mFunctions, mProgramID);
mFunctions->detachShader(mProgramID, mAttachedShaders[gl::ShaderType::Compute]);
}
else
{
return std::make_unique<LinkEventDone>(postLinkImplTask());
for (const gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
if (mAttachedShaders[shaderType] != 0)
{
mFunctions->detachShader(mProgramID, mAttachedShaders[shaderType]);
}
}
}
// Verify the link
if (!checkLinkStatus())
{
return angle::Result::Stop;
}
if (mFeatures.alwaysCallUseProgramAfterLink.enabled)
{
mStateManager->forceUseProgram(mProgramID);
}
linkResources(resources);
getExecutable()->postLink(mFunctions, mFeatures, mProgramID);
return angle::Result::Continue;
}
GLboolean ProgramGL::validate(const gl::Caps & /*caps*/)

View File

@@ -39,16 +39,16 @@ class ProgramGL : public ProgramImpl
void destroy(const gl::Context *context) override;
std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) override;
angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) override;
void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
void prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders) override;
std::unique_ptr<LinkEvent> link(const gl::Context *contextImpl,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) override;
angle::Result link(const gl::Context *contextImpl,
std::shared_ptr<LinkTask> *linkTaskOut) override;
GLboolean validate(const gl::Caps &caps) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
@@ -123,9 +123,14 @@ class ProgramGL : public ProgramImpl
}
private:
class LinkTask;
class LinkEventNativeParallel;
class LinkEventGL;
class LinkTaskGL;
class PostLinkGL;
friend class LinkTaskGL;
friend class PostLinkGL;
void linkJobImpl(const gl::Extensions &extensions);
angle::Result postLinkJobImpl(const gl::ProgramLinkedResources &resources);
bool checkLinkStatus();

View File

@@ -2608,6 +2608,15 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea
// https://crbug.com/480992
// Disable shader program cache to workaround PowerVR Rogue issues.
ANGLE_FEATURE_CONDITION(features, disableProgramBinary, IsPowerVrRogue(functions));
// The link job needs a context, and previous experiments showed setting up temp contexts in
// threads for the sake of program link triggers too many driver bugs. See
// https://chromium-review.googlesource.com/c/angle/angle/+/4774785 for context.
//
// As a result, the link job is done in the same thread as the link call. If the native driver
// supports parallel link, it's still done internally by the driver, and ANGLE supports delaying
// post-link operations until that is done.
ANGLE_FEATURE_CONDITION(features, linkJobIsNotThreadSafe, true);
}
void ReInitializeFeaturesAtGPUSwitch(const FunctionsGL *functions, angle::FeaturesGL *features)

View File

@@ -109,6 +109,8 @@ class DisplayMtl : public DisplayImpl
egl::Surface *readSurface,
gl::Context *context) override;
void initializeFrontendFeatures(angle::FrontendFeatures *features) const override;
void populateFeatureList(angle::FeatureList *features) override;
bool isValidNativeWindow(EGLNativeWindowType window) const override;

View File

@@ -516,6 +516,15 @@ void DisplayMtl::generateCaps(egl::Caps *outCaps) const
outCaps->textureNPOT = true;
}
void DisplayMtl::initializeFrontendFeatures(angle::FrontendFeatures *features) const
{
// The link job in this backend references gl::Context and ContextMtl, and thread-safety is not
// guaranteed. The link subtasks are safe however, they are still parallelized.
//
// Once the link jobs are made thread-safe and using mtl::Context, this feature can be removed.
ANGLE_FEATURE_CONDITION(features, linkJobIsNotThreadSafe, true);
}
void DisplayMtl::populateFeatureList(angle::FeatureList *features)
{
mFeatures.populateFeatureList(features);

View File

@@ -318,7 +318,15 @@ DefaultUniformBlockMtl::~DefaultUniformBlockMtl() = default;
ProgramExecutableMtl::ProgramExecutableMtl(const gl::ProgramExecutable *executable)
: ProgramExecutableImpl(executable), mProgramHasFlatAttributes(false), mShadowCompareModes{}
{}
{
mCurrentShaderVariants.fill(nullptr);
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mMslShaderTranslateInfo[shaderType].reset();
}
mMslXfbOnlyVertexShaderInfo.reset();
}
ProgramExecutableMtl::~ProgramExecutableMtl() {}

View File

@@ -35,16 +35,15 @@ class ProgramMtl : public ProgramImpl
void destroy(const gl::Context *context) override;
std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) override;
angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) override;
void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
void prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders) override;
std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) override;
angle::Result link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut) override;
GLboolean validate(const gl::Caps &caps) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
@@ -107,8 +106,14 @@ class ProgramMtl : public ProgramImpl
ProgramExecutableMtl *getExecutable() { return mtl::GetImpl(&mState.getExecutable()); }
private:
class ProgramLinkEvent;
class CompileMslTask;
class LinkTaskMtl;
class LoadTaskMtl;
friend class LinkTaskMtl;
angle::Result linkJobImpl(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut);
template <int cols, int rows>
void setUniformMatrixfv(GLint location,
@@ -121,10 +126,9 @@ class ProgramMtl : public ProgramImpl
template <typename T>
void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType);
void reset(ContextMtl *context);
void linkResources(const gl::ProgramLinkedResources &resources);
std::unique_ptr<LinkEvent> compileMslShaderLibs(const gl::Context *context);
angle::Result compileMslShaderLibs(const gl::Context *context,
std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut);
gl::ShaderMap<SharedCompiledShaderStateMtl> mAttachedShaders;
};

View File

@@ -29,9 +29,6 @@
#include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/trace.h"
#define ANGLE_PARALLEL_LINK_RETURN(X) return std::make_unique<LinkEventDone>(X)
#define ANGLE_PARALLEL_LINK_TRY(EXPR) ANGLE_TRY_TEMPLATE(EXPR, ANGLE_PARALLEL_LINK_RETURN)
namespace rx
{
@@ -129,8 +126,94 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
public:
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
class CompileMslTask final : public LinkSubTask
{
public:
CompileMslTask(ContextMtl *context,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros)
: mContext(context),
mTranslatedMslInfo(translatedMslInfo),
mSubstitutionMacros(substitutionMacros)
{}
~CompileMslTask() override = default;
void operator()() override
{
mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros);
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
if (!mInfoLog.empty())
{
infoLog << mInfoLog.str();
}
return mResult;
}
private:
// TODO: remove this, inherit from mtl::Context and ensure thread-safety.
// http://anglebug.com/8297
ContextMtl *mContext;
gl::InfoLog mInfoLog;
mtl::TranslatedShaderInfo *mTranslatedMslInfo;
std::map<std::string, std::string> mSubstitutionMacros;
angle::Result mResult = angle::Result::Continue;
};
} // namespace
class ProgramMtl::LinkTaskMtl final : public LinkTask
{
public:
LinkTaskMtl(const gl::Context *context, ProgramMtl *program)
: mContext(context), mProgram(program)
{}
~LinkTaskMtl() override = default;
std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings) override
{
std::vector<std::shared_ptr<LinkSubTask>> subTasks;
mResult = mProgram->linkJobImpl(mContext, resources, &subTasks);
return subTasks;
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
return mResult;
}
private:
// TODO: remove this, inherit from mtl::Context and ensure thread-safety.
// http://anglebug.com/8297
const gl::Context *mContext;
ProgramMtl *mProgram;
angle::Result mResult = angle::Result::Continue;
};
class ProgramMtl::LoadTaskMtl final : public LinkTask
{
public:
LoadTaskMtl(std::vector<std::shared_ptr<LinkSubTask>> &&subTasks)
: mSubTasks(std::move(subTasks))
{}
~LoadTaskMtl() override = default;
std::vector<std::shared_ptr<LinkSubTask>> load() override { return mSubTasks; }
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
return angle::Result::Continue;
}
private:
std::vector<std::shared_ptr<LinkSubTask>> mSubTasks;
};
// ProgramArgumentBufferEncoderMtl implementation
void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl)
{
@@ -155,26 +238,26 @@ ProgramMtl::~ProgramMtl() = default;
void ProgramMtl::destroy(const gl::Context *context)
{
reset(mtl::GetImpl(context));
getExecutable()->reset(mtl::GetImpl(context));
}
void ProgramMtl::reset(ContextMtl *context)
{
getExecutable()->reset(context);
}
std::unique_ptr<rx::LinkEvent> ProgramMtl::load(const gl::Context *context,
gl::BinaryInputStream *stream)
angle::Result ProgramMtl::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
reset(contextMtl);
ANGLE_TRY(getExecutable()->load(contextMtl, stream));
ANGLE_PARALLEL_LINK_TRY(getExecutable()->load(contextMtl, stream));
// TODO: parallelize the above too. http://anglebug.com/8297
std::vector<std::shared_ptr<LinkSubTask>> subTasks;
return compileMslShaderLibs(context);
ANGLE_TRY(compileMslShaderLibs(context, &subTasks));
*loadTaskOut = std::shared_ptr<LinkTask>(new LoadTaskMtl(std::move(subTasks)));
return angle::Result::Continue;
}
void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
@@ -203,9 +286,15 @@ void ProgramMtl::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
}
}
std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings)
angle::Result ProgramMtl::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
{
*linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskMtl(context, this));
return angle::Result::Continue;
}
angle::Result ProgramMtl::linkJobImpl(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
ProgramExecutableMtl *executableMtl = getExecutable();
@@ -214,95 +303,24 @@ std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
// assignment done in that function.
linkResources(resources);
reset(contextMtl);
ANGLE_PARALLEL_LINK_TRY(
executableMtl->initDefaultUniformBlocks(contextMtl, mState.getAttachedShaders()));
ANGLE_TRY(executableMtl->initDefaultUniformBlocks(contextMtl, mState.getAttachedShaders()));
executableMtl->linkUpdateHasFlatAttributes(mState.getAttachedShader(gl::ShaderType::Vertex));
gl::ShaderMap<std::string> shaderSources;
mtl::MSLGetShaderSource(mState, resources, &shaderSources);
ANGLE_PARALLEL_LINK_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(),
contextMtl->getCaps(), shaderSources, mAttachedShaders,
&executableMtl->mMslShaderTranslateInfo));
ANGLE_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(), contextMtl->getCaps(),
shaderSources, mAttachedShaders,
&executableMtl->mMslShaderTranslateInfo));
executableMtl->mMslXfbOnlyVertexShaderInfo =
executableMtl->mMslShaderTranslateInfo[gl::ShaderType::Vertex];
return compileMslShaderLibs(context);
return compileMslShaderLibs(context, subTasksOut);
}
class ProgramMtl::CompileMslTask final : public angle::Closure
{
public:
CompileMslTask(ContextMtl *context,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros)
: mContext(context),
mTranslatedMslInfo(translatedMslInfo),
mSubstitutionMacros(substitutionMacros)
{}
void operator()() override
{
mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros);
}
angle::Result getResult(gl::InfoLog &infoLog)
{
if (!mInfoLog.empty())
{
infoLog << mInfoLog.str();
}
return mResult;
}
private:
ContextMtl *mContext;
gl::InfoLog mInfoLog;
mtl::TranslatedShaderInfo *mTranslatedMslInfo;
std::map<std::string, std::string> mSubstitutionMacros;
angle::Result mResult = angle::Result::Continue;
};
// The LinkEvent implementation for linking a Metal program
class ProgramMtl::ProgramLinkEvent final : public LinkEvent
{
public:
ProgramLinkEvent(gl::InfoLog &infoLog,
std::shared_ptr<angle::WorkerThreadPool> workerPool,
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> &&compileTasks)
: mInfoLog(infoLog), mTasks(std::move(compileTasks))
{
mWaitableEvents.reserve(mTasks.size());
for (const auto &task : mTasks)
{
mWaitableEvents.push_back(workerPool->postWorkerTask(task));
}
}
bool isLinking() override { return !angle::WaitableEvent::AllReady(&mWaitableEvents); }
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::ProgramLinkEvent::wait");
angle::WaitableEvent::WaitMany(&mWaitableEvents);
for (const auto &task : mTasks)
{
ANGLE_TRY(task->getResult(mInfoLog));
}
return angle::Result::Continue;
}
private:
gl::InfoLog &mInfoLog;
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> mTasks;
std::vector<std::shared_ptr<angle::WaitableEvent>> mWaitableEvents;
};
std::unique_ptr<LinkEvent> ProgramMtl::compileMslShaderLibs(const gl::Context *context)
angle::Result ProgramMtl::compileMslShaderLibs(
const gl::Context *context,
std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs");
gl::InfoLog &infoLog = mState.getExecutable().getInfoLog();
@@ -313,7 +331,6 @@ std::unique_ptr<LinkEvent> ProgramMtl::compileMslShaderLibs(const gl::Context *c
contextMtl->getDisplay()->getFeatures().enableParallelMtlLibraryCompilation.enabled;
mtl::LibraryCache &libraryCache = contextMtl->getDisplay()->getLibraryCache();
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> asyncTasks;
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
mtl::TranslatedShaderInfo *translateInfo =
@@ -330,26 +347,16 @@ std::unique_ptr<LinkEvent> ProgramMtl::compileMslShaderLibs(const gl::Context *c
{
if (asyncCompile)
{
auto task =
std::make_shared<ProgramMtl::CompileMslTask>(contextMtl, translateInfo, macros);
asyncTasks.push_back(task);
subTasksOut->emplace_back(new CompileMslTask(contextMtl, translateInfo, macros));
}
else
{
ANGLE_PARALLEL_LINK_TRY(
CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros));
ANGLE_TRY(CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros));
}
}
}
if (asyncTasks.empty())
{
// All shaders were in the cache, no async work to do
return std::make_unique<LinkEventDone>(angle::Result::Continue);
}
return std::make_unique<ProgramMtl::ProgramLinkEvent>(
infoLog, context->getShaderCompileThreadPool(), std::move(asyncTasks));
return angle::Result::Continue;
}
void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)

View File

@@ -13,15 +13,35 @@
namespace rx
{
namespace
{
class LinkTaskNULL : public LinkTask
{
public:
~LinkTaskNULL() override = default;
std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings) override
{
return {};
}
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
return angle::Result::Continue;
}
};
} // anonymous namespace
ProgramNULL::ProgramNULL(const gl::ProgramState &state) : ProgramImpl(state) {}
ProgramNULL::~ProgramNULL() {}
std::unique_ptr<LinkEvent> ProgramNULL::load(const gl::Context *context,
gl::BinaryInputStream *stream)
angle::Result ProgramNULL::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut)
{
return std::make_unique<LinkEventDone>(angle::Result::Continue);
*loadTaskOut = {};
return angle::Result::Continue;
}
void ProgramNULL::save(const gl::Context *context, gl::BinaryOutputStream *stream) {}
@@ -30,11 +50,11 @@ void ProgramNULL::setBinaryRetrievableHint(bool retrievable) {}
void ProgramNULL::setSeparable(bool separable) {}
std::unique_ptr<LinkEvent> ProgramNULL::link(const gl::Context *contextImpl,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings && /*mergedVaryings*/)
angle::Result ProgramNULL::link(const gl::Context *contextImpl,
std::shared_ptr<LinkTask> *linkTaskOut)
{
return std::make_unique<LinkEventDone>(angle::Result::Continue);
*linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskNULL);
return angle::Result::Continue;
}
GLboolean ProgramNULL::validate(const gl::Caps &caps)

View File

@@ -21,15 +21,14 @@ class ProgramNULL : public ProgramImpl
ProgramNULL(const gl::ProgramState &state);
~ProgramNULL() override;
std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) override;
angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) override;
void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) override;
angle::Result link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut) override;
GLboolean validate(const gl::Caps &caps) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;

View File

@@ -362,6 +362,7 @@ ProgramExecutableVk::ProgramExecutableVk(const gl::ProgramExecutable *executable
mUniformBufferDescriptorType(VK_DESCRIPTOR_TYPE_MAX_ENUM),
mDynamicUniformDescriptorOffsets{}
{
mDescriptorSets.fill(VK_NULL_HANDLE);
for (std::shared_ptr<DefaultUniformBlockVk> &defaultBlock : mDefaultUniformBlocks)
{
defaultBlock = std::make_shared<DefaultUniformBlockVk>();
@@ -499,9 +500,9 @@ angle::Result ProgramExecutableVk::ensurePipelineCacheInitialized(vk::Context *c
return angle::Result::Continue;
}
std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(ContextVk *contextVk,
bool isSeparable,
gl::BinaryInputStream *stream)
angle::Result ProgramExecutableVk::load(ContextVk *contextVk,
bool isSeparable,
gl::BinaryInputStream *stream)
{
mVariableInfoMap.load(stream);
mOriginalShaderInfo.load(stream);
@@ -528,33 +529,19 @@ std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(ContextVk *contextVk,
stream->readBool(&compressedData);
stream->readBytes(compressedPipelineData.data(), compressedPipelineDataSize);
// Initialize the pipeline cache based on cached data.
angle::Result status =
initializePipelineCache(contextVk, compressedData, compressedPipelineData);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
ANGLE_TRY(initializePipelineCache(contextVk, compressedData, compressedPipelineData));
}
}
// Initialize and resize the mDefaultUniformBlocks' memory
angle::Result status = resizeUniformBlockMemory(contextVk, requiredBufferSize);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
ANGLE_TRY(resizeUniformBlockMemory(contextVk, requiredBufferSize));
resetLayout(contextVk);
status = createPipelineLayout(contextVk, &contextVk->getPipelineLayoutCache(),
&contextVk->getDescriptorSetLayoutCache(), nullptr);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
ANGLE_TRY(createPipelineLayout(contextVk, &contextVk->getPipelineLayoutCache(),
&contextVk->getDescriptorSetLayoutCache(), nullptr));
status = initializeDescriptorPools(contextVk, &contextVk->getDescriptorSetLayoutCache(),
&contextVk->getMetaDescriptorPools());
return std::make_unique<LinkEventDone>(status);
return initializeDescriptorPools(contextVk, &contextVk->getDescriptorSetLayoutCache(),
&contextVk->getMetaDescriptorPools());
}
void ProgramExecutableVk::save(ContextVk *contextVk,

View File

@@ -121,9 +121,7 @@ class ProgramExecutableVk : public ProgramExecutableImpl
void destroy(const gl::Context *context) override;
void save(ContextVk *contextVk, bool isSeparable, gl::BinaryOutputStream *stream);
std::unique_ptr<rx::LinkEvent> load(ContextVk *contextVk,
bool isSeparable,
gl::BinaryInputStream *stream);
angle::Result load(ContextVk *contextVk, bool isSeparable, gl::BinaryInputStream *stream);
void clearVariableInfoMap();

View File

@@ -48,34 +48,35 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
class LinkTaskVk final : public vk::Context, public angle::Closure
class LinkTaskVk final : public vk::Context, public LinkTask
{
public:
LinkTaskVk(RendererVk *renderer,
PipelineLayoutCache &pipelineLayoutCache,
DescriptorSetLayoutCache &descriptorSetLayoutCache,
const gl::ProgramState &state,
gl::ProgramMergedVaryings &&mergedVaryings,
const gl::ProgramLinkedResources &resources,
bool isGLES1,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess)
: vk::Context(renderer),
mState(state),
mExecutable(&mState.getExecutable()),
mMergedVaryings(std::move(mergedVaryings)),
mResources(resources),
mIsGLES1(isGLES1),
mPipelineRobustness(pipelineRobustness),
mPipelineProtectedAccess(pipelineProtectedAccess),
mPipelineLayoutCache(pipelineLayoutCache),
mDescriptorSetLayoutCache(descriptorSetLayoutCache)
{}
~LinkTaskVk() override = default;
void operator()() override
std::vector<std::shared_ptr<LinkSubTask>> link(
const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings) override
{
angle::Result result = linkImpl();
angle::Result result = linkImpl(resources, mergedVaryings);
ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS));
return {};
}
void handleError(VkResult result,
@@ -89,11 +90,12 @@ class LinkTaskVk final : public vk::Context, public angle::Closure
mErrorLine = line;
}
angle::Result getResult(ContextVk *contextVk)
angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
{
ContextVk *contextVk = vk::GetImpl(context);
ProgramExecutableVk *executableVk = vk::GetImpl(mExecutable);
// Clean up garbage first, it's not no matter what may fail below.
// Clean up garbage first, it's done no matter what may fail below.
mCompatibleRenderPass.destroy(contextVk->getDevice());
ANGLE_TRY(executableVk->initializeDescriptorPools(contextVk,
@@ -136,9 +138,10 @@ class LinkTaskVk final : public vk::Context, public angle::Closure
}
private:
angle::Result linkImpl();
angle::Result linkImpl(const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings);
void linkResources();
void linkResources(const gl::ProgramLinkedResources &resources);
angle::Result initDefaultUniformBlocks();
void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
gl::ShaderMap<size_t> *requiredBufferSizeOut);
@@ -148,8 +151,6 @@ class LinkTaskVk final : public vk::Context, public angle::Closure
// direclty access the state from a potentially parallel job.
const gl::ProgramState &mState;
const gl::ProgramExecutable *mExecutable;
const gl::ProgramMergedVaryings mMergedVaryings;
const gl::ProgramLinkedResources &mResources;
const bool mIsGLES1;
const vk::PipelineRobustness mPipelineRobustness;
const vk::PipelineProtectedAccess mPipelineProtectedAccess;
@@ -168,19 +169,20 @@ class LinkTaskVk final : public vk::Context, public angle::Closure
unsigned int mErrorLine = 0;
};
angle::Result LinkTaskVk::linkImpl()
angle::Result LinkTaskVk::linkImpl(const gl::ProgramLinkedResources &resources,
const gl::ProgramMergedVaryings &mergedVaryings)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTaskVk::run");
ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskVk::linkImpl");
ProgramExecutableVk *executableVk = vk::GetImpl(mExecutable);
// Link resources before calling GetShaderSource to make sure they are ready for the set/binding
// assignment done in that function.
linkResources();
linkResources(resources);
executableVk->clearVariableInfoMap();
// Gather variable info and compiled SPIR-V binaries.
executableVk->assignAllSpvLocations(this, mState, mResources);
executableVk->assignAllSpvLocations(this, mState, resources);
gl::ShaderMap<const angle::spirv::Blob *> spirvBlobs;
SpvGetShaderSpirvCode(mState, &spirvBlobs);
@@ -188,7 +190,7 @@ angle::Result LinkTaskVk::linkImpl()
if (getFeatures().varyingsRequireMatchingPrecisionInSpirv.enabled &&
getFeatures().enablePrecisionQualifiers.enabled)
{
executableVk->resolvePrecisionMismatch(mMergedVaryings);
executableVk->resolvePrecisionMismatch(mergedVaryings);
}
// Compile the shaders.
@@ -219,12 +221,12 @@ angle::Result LinkTaskVk::linkImpl()
return angle::Result::Continue;
}
void LinkTaskVk::linkResources()
void LinkTaskVk::linkResources(const gl::ProgramLinkedResources &resources)
{
Std140BlockLayoutEncoderFactory std140EncoderFactory;
gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
linker.linkResources(mState, mResources);
linker.linkResources(mState, resources);
}
angle::Result LinkTaskVk::initDefaultUniformBlocks()
@@ -334,33 +336,6 @@ void LinkTaskVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMa
}
}
// 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,
@@ -430,20 +405,17 @@ ProgramVk::~ProgramVk() = default;
void ProgramVk::destroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
reset(contextVk);
}
void ProgramVk::reset(ContextVk *contextVk)
{
getExecutable()->reset(contextVk);
}
std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
gl::BinaryInputStream *stream)
angle::Result ProgramVk::load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut)
{
ContextVk *contextVk = vk::GetImpl(context);
reset(contextVk);
// TODO: parallelize program load. http://anglebug.com/8297
*loadTaskOut = {};
return getExecutable()->load(contextVk, mState.isSeparable(), stream);
}
@@ -464,21 +436,16 @@ void ProgramVk::setSeparable(bool separable)
// Nothing to do here yet.
}
std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings)
angle::Result ProgramVk::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::link");
ContextVk *contextVk = vk::GetImpl(context);
reset(contextVk);
std::shared_ptr<LinkTaskVk> linkTask = std::make_shared<LinkTaskVk>(
*linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskVk(
contextVk->getRenderer(), contextVk->getPipelineLayoutCache(),
contextVk->getDescriptorSetLayoutCache(), mState, std::move(mergedVaryings), resources,
context->getState().isGLES1(), contextVk->pipelineRobustness(),
contextVk->pipelineProtectedAccess());
return std::make_unique<LinkEventVulkan>(context->getShaderCompileThreadPool(), linkTask);
contextVk->getDescriptorSetLayoutCache(), mState, context->getState().isGLES1(),
contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()));
return angle::Result::Continue;
}
GLboolean ProgramVk::validate(const gl::Caps &caps)

View File

@@ -29,15 +29,14 @@ class ProgramVk : public ProgramImpl
~ProgramVk() override;
void destroy(const gl::Context *context) override;
std::unique_ptr<LinkEvent> load(const gl::Context *context,
gl::BinaryInputStream *stream) override;
angle::Result load(const gl::Context *context,
gl::BinaryInputStream *stream,
std::shared_ptr<LinkTask> *loadTaskOut) override;
void save(const gl::Context *context, gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override;
std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::ProgramMergedVaryings &&mergedVaryings) override;
angle::Result link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut) override;
GLboolean validate(const gl::Caps &caps) override;
angle::Result syncState(const gl::Context *context,
@@ -109,8 +108,6 @@ class ProgramVk : public ProgramImpl
GLboolean transpose,
const GLfloat *value);
void reset(ContextVk *contextVk);
template <class T>
void getUniformImpl(GLint location, T *v, GLenum entryPointType) const;

View File

@@ -195,6 +195,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
{Feature::LimitMaxMSAASamplesTo4, "limitMaxMSAASamplesTo4"},
{Feature::LimitSampleCountTo2, "limitSampleCountTo2"},
{Feature::LimitWebglMaxTextureSizeTo4096, "limitWebglMaxTextureSizeTo4096"},
{Feature::LinkJobIsNotThreadSafe, "linkJobIsNotThreadSafe"},
{Feature::LoadMetalShadersFromBlobCache, "loadMetalShadersFromBlobCache"},
{Feature::LogMemoryReportCallbacks, "logMemoryReportCallbacks"},
{Feature::LogMemoryReportStats, "logMemoryReportStats"},

View File

@@ -195,6 +195,7 @@ enum class Feature
LimitMaxMSAASamplesTo4,
LimitSampleCountTo2,
LimitWebglMaxTextureSizeTo4096,
LinkJobIsNotThreadSafe,
LoadMetalShadersFromBlobCache,
LogMemoryReportCallbacks,
LogMemoryReportStats,