Consolidate parallel compilation in front-end
This cleans up the multiple compilation task implementations in the
backends and consolidates them in the front-end. The front-end is then
able to do the compilation in an unlocked tail call instead if
desired (in a future change).
This change is in preparation for having the program link tasks directly
wait on the shader compilation tasks. As a result, the "shader resolve"
should not be needed to access the shader compilation results; it should
be enough to wait for the compilation job. This change therefore moves
post-processing of results to the compilation job itself as they did not
need to actually be done after compilation is done (merely after
translation is done). As a side effect, shader substition and other
debug features should now work for the GL backend as they are now done
before back-end compilation.
Bug: angleproject:8297
Change-Id: Ib9274b1149fadca7545956a864d6635b6cba5c3a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4994655
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Charlie Lao <cclao@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/include/platform/autogen/FrontendFeatures_autogen.h b/include/platform/autogen/FrontendFeatures_autogen.h
index 7e4cc5c..d8bb26b 100644
--- a/include/platform/autogen/FrontendFeatures_autogen.h
+++ b/include/platform/autogen/FrontendFeatures_autogen.h
@@ -161,6 +161,13 @@
&members, "http://anglebug.com/8280"
};
+ FeatureInfo compileJobIsThreadSafe = {
+ "compileJobIsThreadSafe",
+ FeatureCategory::FrontendFeatures,
+ "If false, parts of the compile job cannot be parallelized",
+ &members, "http://anglebug.com/8297"
+ };
+
FeatureInfo linkJobIsThreadSafe = {
"linkJobIsThreadSafe",
FeatureCategory::FrontendFeatures,
diff --git a/include/platform/frontend_features.json b/include/platform/frontend_features.json
index fbee157..722f666 100644
--- a/include/platform/frontend_features.json
+++ b/include/platform/frontend_features.json
@@ -163,6 +163,14 @@
"issue": "http://anglebug.com/8280"
},
{
+ "name": "compile_job_is_thread_safe",
+ "category": "Features",
+ "description": [
+ "If false, parts of the compile job cannot be parallelized"
+ ],
+ "issue": "http://anglebug.com/8297"
+ },
+ {
"name": "link_job_is_thread_safe",
"category": "Features",
"description": [
diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp
index 17a45a0..281500e 100644
--- a/src/libANGLE/Shader.cpp
+++ b/src/libANGLE/Shader.cpp
@@ -28,6 +28,7 @@
#include "libANGLE/ResourceManager.h"
#include "libANGLE/renderer/GLImplFactory.h"
#include "libANGLE/renderer/ShaderImpl.h"
+#include "libANGLE/trace.h"
#include "platform/autogen/FrontendFeatures_autogen.h"
namespace gl
@@ -59,6 +60,229 @@
return path.str();
}
+
+class CompileTask final : public angle::Closure
+{
+ public:
+ CompileTask(const angle::FrontendFeatures &frontendFeatures,
+ ShHandle compilerHandle,
+ ShShaderOutput outputType,
+ const ShCompileOptions &options,
+ const std::string &source,
+ size_t sourceHash,
+ const SharedCompiledShaderState &compiledState,
+ size_t maxComputeWorkGroupInvocations,
+ size_t maxComputeSharedMemory,
+ std::shared_ptr<rx::ShaderTranslateTask> &&translateTask)
+ : mFrontendFeatures(frontendFeatures),
+ mMaxComputeWorkGroupInvocations(maxComputeWorkGroupInvocations),
+ mMaxComputeSharedMemory(maxComputeSharedMemory),
+ mCompilerHandle(compilerHandle),
+ mOutputType(outputType),
+ mOptions(options),
+ mSource(source),
+ mSourceHash(sourceHash),
+ mCompiledState(compiledState),
+ mTranslateTask(std::move(translateTask))
+ {}
+ ~CompileTask() override = default;
+
+ void operator()() override { mResult = compileImpl(); }
+
+ angle::Result getResult()
+ {
+ ANGLE_TRY(mResult);
+ ANGLE_TRY(mTranslateTask->getResult(mInfoLog));
+
+ return angle::Result::Continue;
+ }
+
+ bool isCompilingInternally() { return mTranslateTask->isCompilingInternally(); }
+
+ std::string &&getInfoLog() { return std::move(mInfoLog); }
+
+ private:
+ angle::Result compileImpl();
+ angle::Result postTranslate();
+
+ // Global constants that are safe to access by the worker thread
+ const angle::FrontendFeatures &mFrontendFeatures;
+ size_t mMaxComputeWorkGroupInvocations;
+ size_t mMaxComputeSharedMemory;
+
+ // Access to the compile information which are unchanged for the duration of compilation (for
+ // example shader source which cannot be changed until compilation is finished) or are kept
+ // alive (for example the compiler instance in CompilingState)
+ ShHandle mCompilerHandle;
+ ShShaderOutput mOutputType;
+ ShCompileOptions mOptions;
+ const std::string mSource;
+ size_t mSourceHash;
+ SharedCompiledShaderState mCompiledState;
+
+ std::shared_ptr<rx::ShaderTranslateTask> mTranslateTask;
+ angle::Result mResult;
+ std::string mInfoLog;
+};
+
+class CompileEvent final
+{
+ public:
+ CompileEvent(const std::shared_ptr<CompileTask> &compileTask,
+ const std::shared_ptr<angle::WaitableEvent> &waitEvent)
+ : mCompileTask(compileTask), mWaitableEvent(waitEvent)
+ {}
+ ~CompileEvent() = default;
+
+ angle::Result wait()
+ {
+ ANGLE_TRACE_EVENT0("gpu.angle", "CompileEvent::wait");
+
+ mWaitableEvent->wait();
+
+ return mCompileTask->getResult();
+ }
+ bool isCompiling()
+ {
+ return !mWaitableEvent->isReady() || mCompileTask->isCompilingInternally();
+ }
+
+ std::string &&getInfoLog() { return std::move(mCompileTask->getInfoLog()); }
+
+ private:
+ std::shared_ptr<CompileTask> mCompileTask;
+ std::shared_ptr<angle::WaitableEvent> mWaitableEvent;
+};
+
+angle::Result CompileTask::compileImpl()
+{
+ // Call the translator and get the info log
+ bool result = mTranslateTask->translate(mCompilerHandle, mOptions, mSource);
+ mInfoLog = sh::GetInfoLog(mCompilerHandle);
+ if (!result)
+ {
+ return angle::Result::Stop;
+ }
+
+ // Process the translation results itself; gather compilation info, substitute the shader if
+ // being overriden, etc.
+ return postTranslate();
+}
+
+angle::Result CompileTask::postTranslate()
+{
+ const bool isBinaryOutput = mOutputType == SH_SPIRV_VULKAN_OUTPUT;
+ mCompiledState->buildCompiledShaderState(mCompilerHandle, isBinaryOutput);
+
+ ASSERT(!mCompiledState->translatedSource.empty() || !mCompiledState->compiledBinary.empty());
+
+ // Validation checks for compute shaders
+ if (mCompiledState->shaderType == ShaderType::Compute && mCompiledState->localSize.isDeclared())
+ {
+ angle::CheckedNumeric<size_t> checked_local_size_product(mCompiledState->localSize[0]);
+ checked_local_size_product *= mCompiledState->localSize[1];
+ checked_local_size_product *= mCompiledState->localSize[2];
+
+ if (!checked_local_size_product.IsValid() ||
+ checked_local_size_product.ValueOrDie() > mMaxComputeWorkGroupInvocations)
+ {
+ mInfoLog +=
+ "\nThe total number of invocations within a work group exceeds "
+ "MAX_COMPUTE_WORK_GROUP_INVOCATIONS.";
+ return angle::Result::Stop;
+ }
+ }
+
+ unsigned int sharedMemSize = sh::GetShaderSharedMemorySize(mCompilerHandle);
+ if (sharedMemSize > mMaxComputeSharedMemory)
+ {
+ mInfoLog += "\nShared memory size exceeds GL_MAX_COMPUTE_SHARED_MEMORY_SIZE";
+ return angle::Result::Stop;
+ }
+
+ bool substitutedTranslatedShader = false;
+ const char *suffix = "translated";
+ if (mFrontendFeatures.enableTranslatedShaderSubstitution.enabled)
+ {
+ // To support reading/writing compiled binaries (SPIR-V representation), need more file
+ // input/output facilities, and figure out the byte ordering of writing the 32-bit words to
+ // disk.
+ if (isBinaryOutput)
+ {
+ INFO() << "Can not substitute compiled binary (SPIR-V) shaders yet";
+ }
+ else
+ {
+ std::string substituteShaderPath = GetShaderDumpFilePath(mSourceHash, suffix);
+
+ std::string substituteShader;
+ if (angle::ReadFileToString(substituteShaderPath, &substituteShader))
+ {
+ mCompiledState->translatedSource = std::move(substituteShader);
+ substitutedTranslatedShader = true;
+ INFO() << "Translated shader substitute found, loading from "
+ << substituteShaderPath;
+ }
+ }
+ }
+
+ // Only dump translated shaders that have not been previously substituted. It would write the
+ // same data back to the file.
+ if (mFrontendFeatures.dumpTranslatedShaders.enabled && !substitutedTranslatedShader)
+ {
+ if (isBinaryOutput)
+ {
+ INFO() << "Can not dump compiled binary (SPIR-V) shaders yet";
+ }
+ else
+ {
+ std::string dumpFile = GetShaderDumpFilePath(mSourceHash, suffix);
+
+ const std::string &translatedSource = mCompiledState->translatedSource;
+ writeFile(dumpFile.c_str(), translatedSource.c_str(), translatedSource.length());
+ INFO() << "Dumped translated source: " << dumpFile;
+ }
+ }
+
+#if defined(ANGLE_ENABLE_ASSERTS)
+ if (!isBinaryOutput)
+ {
+ // Prefix translated shader with commented out un-translated shader.
+ // Useful in diagnostics tools which capture the shader source.
+ std::ostringstream shaderStream;
+ shaderStream << "// GLSL\n";
+ shaderStream << "//\n";
+
+ std::istringstream inputSourceStream(mSource);
+ std::string line;
+ while (std::getline(inputSourceStream, line))
+ {
+ // Remove null characters from the source line
+ line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
+
+ shaderStream << "// " << line;
+
+ // glslang complains if a comment ends with backslash
+ if (!line.empty() && line.back() == '\\')
+ {
+ shaderStream << "\\";
+ }
+
+ shaderStream << std::endl;
+ }
+ shaderStream << "\n\n";
+ shaderStream << mCompiledState->translatedSource;
+ mCompiledState->translatedSource = shaderStream.str();
+ }
+#endif // defined(ANGLE_ENABLE_ASSERTS)
+
+ // Let the backend process the result of the compilation. For the GL backend, this means
+ // kicking off compilation internally. Some of the other backends fill in their internal
+ // "compiled state" at this point.
+ mTranslateTask->postTranslate(mCompilerHandle, *mCompiledState.get());
+
+ return angle::Result::Continue;
+}
} // anonymous namespace
const char *GetShaderTypeString(ShaderType type)
@@ -111,19 +335,9 @@
return name.str();
}
-class [[nodiscard]] ScopedExit final : angle::NonCopyable
-{
- public:
- ScopedExit(std::function<void()> exit) : mExit(exit) {}
- ~ScopedExit() { mExit(); }
-
- private:
- std::function<void()> mExit;
-};
-
struct Shader::CompilingState
{
- std::shared_ptr<rx::WaitableCompileEvent> compileEvent;
+ std::unique_ptr<CompileEvent> compileEvent;
ShCompilerInstance shCompilerInstance;
};
@@ -144,9 +358,7 @@
mHandle(handle),
mRefCount(0),
mDeleteStatus(false),
- mResourceManager(manager),
- mCurrentMaxComputeWorkGroupInvocations(0u),
- mMaxComputeSharedMemory(0u)
+ mResourceManager(manager)
{
ASSERT(mImplementation);
}
@@ -392,10 +604,6 @@
mInfoLog.clear();
- mCurrentMaxComputeWorkGroupInvocations =
- static_cast<GLuint>(context->getCaps().maxComputeWorkGroupInvocations);
- mMaxComputeSharedMemory = context->getCaps().maxComputeSharedMemorySize;
-
ShCompileOptions options = {};
options.objectCode = true;
options.emulateGLDrawID = true;
@@ -431,6 +639,10 @@
options.initializeUninitializedLocals = true;
}
+#if defined(ANGLE_ENABLE_ASSERTS)
+ options.validateAST = true;
+#endif
+
mBoundCompiler.set(context, context->getCompiler());
ASSERT(mBoundCompiler.get());
@@ -457,10 +669,37 @@
// Cache load failed, fall through normal compiling.
mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
- mCompilingState.reset(new CompilingState());
+
+ // Ask the backend to prepare the translate task
+ std::shared_ptr<rx::ShaderTranslateTask> translateTask =
+ mImplementation->compile(context, &options);
+
+ // Prepare the complete compile task
+ const size_t maxComputeWorkGroupInvocations =
+ static_cast<size_t>(context->getCaps().maxComputeWorkGroupInvocations);
+ const size_t maxComputeSharedMemory = context->getCaps().maxComputeSharedMemorySize;
+
+ std::shared_ptr<CompileTask> compileTask(
+ new CompileTask(context->getFrontendFeatures(), compilerInstance.getHandle(),
+ compilerInstance.getShaderOutputType(), options, mState.mSource,
+ mState.mSourceHash, mState.mCompiledState, maxComputeWorkGroupInvocations,
+ maxComputeSharedMemory, std::move(translateTask)));
+
+ // The GL backend relies on the driver's internal parallel compilation, and thus does not use a
+ // thread to compile. A front-end feature selects whether the single-threaded pool must be
+ // used.
+ std::shared_ptr<angle::WorkerThreadPool> compileWorkerPool =
+ context->getFrontendFeatures().compileJobIsThreadSafe.enabled
+ ? context->getShaderCompileThreadPool()
+ : context->getSingleThreadPool();
+
+ // TODO: add the possibility to perform this in an unlocked tail call. http://anglebug.com/8297
+ std::shared_ptr<angle::WaitableEvent> compileEvent =
+ compileWorkerPool->postWorkerTask(compileTask);
+
+ mCompilingState = std::make_unique<CompilingState>();
mCompilingState->shCompilerInstance = std::move(compilerInstance);
- mCompilingState->compileEvent =
- mImplementation->compile(context, &(mCompilingState->shCompilerInstance), &options);
+ mCompilingState->compileEvent = std::make_unique<CompileEvent>(compileTask, compileEvent);
}
void Shader::resolveCompile(const Context *context)
@@ -473,160 +712,30 @@
ASSERT(mCompilingState.get());
mState.mCompileStatus = CompileStatus::IS_RESOLVING;
- mCompilingState->compileEvent->wait();
+ angle::Result result = mCompilingState->compileEvent->wait();
+ mInfoLog = std::move(mCompilingState->compileEvent->getInfoLog());
- mInfoLog += mCompilingState->compileEvent->getInfoLog();
-
- ScopedExit exit([this]() {
- mBoundCompiler->putInstance(std::move(mCompilingState->shCompilerInstance));
- mCompilingState->compileEvent.reset();
- mCompilingState.reset();
- });
-
- ShHandle compilerHandle = mCompilingState->shCompilerInstance.getHandle();
- if (!mCompilingState->compileEvent->getResult())
- {
- mInfoLog += sh::GetInfoLog(compilerHandle);
- INFO() << std::endl << mInfoLog;
- mState.mCompileStatus = CompileStatus::NOT_COMPILED;
- return;
- }
-
- const ShShaderOutput outputType = mCompilingState->shCompilerInstance.getShaderOutputType();
- bool isBinaryOutput = outputType == SH_SPIRV_VULKAN_OUTPUT;
- mState.mCompiledState->buildCompiledShaderState(compilerHandle, isBinaryOutput);
-
- const angle::FrontendFeatures &frontendFeatures = context->getFrontendFeatures();
- bool substitutedTranslatedShader = false;
- const char *suffix = "translated";
- if (frontendFeatures.enableTranslatedShaderSubstitution.enabled)
- {
- // To support reading/writing compiled binaries (SPIR-V
- // representation), need more file input/output facilities,
- // and figure out the byte ordering of writing the 32-bit
- // words to disk.
- if (isBinaryOutput)
- {
- INFO() << "Can not substitute compiled binary (SPIR-V) shaders yet";
- }
- else
- {
- std::string substituteShaderPath = GetShaderDumpFilePath(mState.mSourceHash, suffix);
-
- std::string substituteShader;
- if (angle::ReadFileToString(substituteShaderPath, &substituteShader))
- {
- mState.mCompiledState->translatedSource = std::move(substituteShader);
- substitutedTranslatedShader = true;
- INFO() << "Trasnslated shader substitute found, loading from "
- << substituteShaderPath;
- }
- }
- }
-
- // Only dump translated shaders that have not been previously substituted. It would write the
- // same data back to the file.
- if (frontendFeatures.dumpTranslatedShaders.enabled && !substitutedTranslatedShader)
- {
- if (isBinaryOutput)
- {
- INFO() << "Can not dump compiled binary (SPIR-V) shaders yet";
- }
- else
- {
- std::string dumpFile = GetShaderDumpFilePath(mState.mSourceHash, suffix);
-
- const std::string &translatedSource = mState.mCompiledState->translatedSource;
- writeFile(dumpFile.c_str(), translatedSource.c_str(), translatedSource.length());
- INFO() << "Dumped translated source: " << dumpFile;
- }
- }
-
-#if !defined(NDEBUG)
- if (outputType != SH_SPIRV_VULKAN_OUTPUT)
- {
- // Prefix translated shader with commented out un-translated shader.
- // Useful in diagnostics tools which capture the shader source.
- std::ostringstream shaderStream;
- shaderStream << "// GLSL\n";
- shaderStream << "//\n";
-
- std::istringstream inputSourceStream(mState.mSource);
- std::string line;
- while (std::getline(inputSourceStream, line))
- {
- // Remove null characters from the source line
- line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
-
- shaderStream << "// " << line;
-
- // glslang complains if a comment ends with backslash
- if (!line.empty() && line.back() == '\\')
- {
- shaderStream << "\\";
- }
-
- shaderStream << std::endl;
- }
- shaderStream << "\n\n";
- shaderStream << mState.mCompiledState->translatedSource;
- mState.mCompiledState->translatedSource = shaderStream.str();
- }
-#endif // !defined(NDEBUG)
-
- // Validation checks for compute shaders
- if (mState.mCompiledState->shaderType == ShaderType::Compute &&
- mState.mCompiledState->localSize.isDeclared())
- {
- angle::CheckedNumeric<uint32_t> checked_local_size_product(
- mState.mCompiledState->localSize[0]);
- checked_local_size_product *= mState.mCompiledState->localSize[1];
- checked_local_size_product *= mState.mCompiledState->localSize[2];
-
- if (!checked_local_size_product.IsValid())
- {
- WARN() << std::endl
- << "Integer overflow when computing the product of local_size_x, "
- << "local_size_y and local_size_z.";
- mState.mCompileStatus = CompileStatus::NOT_COMPILED;
- return;
- }
- if (checked_local_size_product.ValueOrDie() > mCurrentMaxComputeWorkGroupInvocations)
- {
- WARN() << std::endl
- << "The total number of invocations within a work group exceeds "
- << "MAX_COMPUTE_WORK_GROUP_INVOCATIONS.";
- mState.mCompileStatus = CompileStatus::NOT_COMPILED;
- return;
- }
- }
-
- unsigned int sharedMemSize = sh::GetShaderSharedMemorySize(compilerHandle);
- if (sharedMemSize > mMaxComputeSharedMemory)
- {
- WARN() << std::endl << "Exceeded maximum shared memory size";
- mState.mCompileStatus = CompileStatus::NOT_COMPILED;
- return;
- }
-
- ASSERT(!mState.mCompiledState->translatedSource.empty() ||
- !mState.mCompiledState->compiledBinary.empty());
-
- bool success = mCompilingState->compileEvent->postTranslate(&mInfoLog);
+ bool success = result == angle::Result::Continue;
mState.mCompileStatus = success ? CompileStatus::COMPILED : CompileStatus::NOT_COMPILED;
-
mState.mCompiledState->successfullyCompiled = success;
- MemoryShaderCache *shaderCache = context->getMemoryShaderCache();
- if (success && shaderCache)
+ if (success)
{
- // Save to the shader cache.
- if (shaderCache->putShader(context, mShaderHash, this) != angle::Result::Continue)
+ MemoryShaderCache *shaderCache = context->getMemoryShaderCache();
+ if (shaderCache != nullptr)
{
- ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
- "Failed to save compiled shader to memory shader cache.");
+ // Save to the shader cache.
+ if (shaderCache->putShader(context, mShaderHash, this) != angle::Result::Continue)
+ {
+ ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
+ "Failed to save compiled shader to memory shader cache.");
+ }
}
}
+
+ mBoundCompiler->putInstance(std::move(mCompilingState->shCompilerInstance));
+ mCompilingState->compileEvent.reset();
+ mCompilingState.reset();
}
void Shader::addRef()
@@ -667,7 +776,7 @@
bool Shader::isCompleted()
{
- return (!mState.compilePending() || mCompilingState->compileEvent->isReady());
+ return !mState.compilePending() || !mCompilingState->compileEvent->isCompiling();
}
angle::Result Shader::serialize(const Context *context, angle::MemoryBuffer *binaryOut) const
diff --git a/src/libANGLE/Shader.h b/src/libANGLE/Shader.h
index cb64da2..2c292cc 100644
--- a/src/libANGLE/Shader.h
+++ b/src/libANGLE/Shader.h
@@ -47,7 +47,6 @@
namespace gl
{
-class CompileTask;
class Context;
class ShaderProgramManager;
class State;
@@ -161,12 +160,6 @@
const ShaderState &getState() const { return mState; }
- GLuint getCurrentMaxComputeWorkGroupInvocations() const
- {
- return mCurrentMaxComputeWorkGroupInvocations;
- }
-
- unsigned int getMaxComputeSharedMemory() const { return mMaxComputeSharedMemory; }
bool hasBeenDeleted() const { return mDeleteStatus; }
// Block until compiling is finished and resolve it.
@@ -190,7 +183,6 @@
private:
struct CompilingState;
-
~Shader() override;
static std::string joinShaderSources(GLsizei count,
const char *const *string,
@@ -224,9 +216,6 @@
egl::BlobCache::Key mShaderHash;
ShaderProgramManager *mResourceManager;
-
- GLuint mCurrentMaxComputeWorkGroupInvocations;
- unsigned int mMaxComputeSharedMemory;
};
const char *GetShaderTypeString(ShaderType type);
diff --git a/src/libANGLE/capture/serialize.cpp b/src/libANGLE/capture/serialize.cpp
index 822cf97..68b1692 100644
--- a/src/libANGLE/capture/serialize.cpp
+++ b/src/libANGLE/capture/serialize.cpp
@@ -972,9 +972,6 @@
// Do not serialize mType because it is already serialized in SerializeCompiledShaderState.
json->addString("InfoLogString", shader->getInfoLogString());
// Do not serialize compiler resources string because it can vary between test modes.
- json->addScalar("CurrentMaxComputeWorkGroupInvocations",
- shader->getCurrentMaxComputeWorkGroupInvocations());
- json->addScalar("MaxComputeSharedMemory", shader->getMaxComputeSharedMemory());
}
void SerializeVariableLocationsVector(JsonSerializer *json,
diff --git a/src/libANGLE/renderer/ShaderImpl.cpp b/src/libANGLE/renderer/ShaderImpl.cpp
index 9d774d6..fd3aa57 100644
--- a/src/libANGLE/renderer/ShaderImpl.cpp
+++ b/src/libANGLE/renderer/ShaderImpl.cpp
@@ -13,88 +13,13 @@
namespace rx
{
-
-WaitableCompileEvent::WaitableCompileEvent(std::shared_ptr<angle::WaitableEvent> waitableEvent)
- : mWaitableEvent(waitableEvent)
-{}
-
-WaitableCompileEvent::~WaitableCompileEvent()
+bool ShaderTranslateTask::translate(ShHandle compiler,
+ const ShCompileOptions &options,
+ const std::string &source)
{
- mWaitableEvent.reset();
-}
-
-void WaitableCompileEvent::wait()
-{
- mWaitableEvent->wait();
-}
-
-bool WaitableCompileEvent::isReady()
-{
- return mWaitableEvent->isReady();
-}
-
-const std::string &WaitableCompileEvent::getInfoLog()
-{
- return mInfoLog;
-}
-
-class TranslateTask : public angle::Closure
-{
- public:
- TranslateTask(ShHandle handle, const ShCompileOptions &options, const std::string &source)
- : mHandle(handle), mOptions(options), mSource(source), mResult(false)
- {}
-
- void operator()() override
- {
- ANGLE_TRACE_EVENT1("gpu.angle", "TranslateTask::run", "source", mSource);
- const char *source = mSource.c_str();
- mResult = sh::Compile(mHandle, &source, 1, mOptions);
- }
-
- bool getResult() { return mResult; }
-
- ShHandle getHandle() { return mHandle; }
-
- private:
- ShHandle mHandle;
- ShCompileOptions mOptions;
- std::string mSource;
- bool mResult;
-};
-
-class WaitableCompileEventImpl final : public WaitableCompileEvent
-{
- public:
- WaitableCompileEventImpl(std::shared_ptr<angle::WaitableEvent> waitableEvent,
- std::shared_ptr<TranslateTask> translateTask)
- : WaitableCompileEvent(waitableEvent), mTranslateTask(translateTask)
- {}
-
- bool getResult() override { return mTranslateTask->getResult(); }
-
- bool postTranslate(std::string *infoLog) override { return true; }
-
- private:
- std::shared_ptr<TranslateTask> mTranslateTask;
-};
-
-std::shared_ptr<WaitableCompileEvent> ShaderImpl::compileImpl(
- const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- const std::string &source,
- ShCompileOptions *compileOptions)
-{
-#if defined(ANGLE_ENABLE_ASSERTS)
- compileOptions->validateAST = true;
-#endif
-
- auto workerThreadPool = context->getShaderCompileThreadPool();
- auto translateTask =
- std::make_shared<TranslateTask>(compilerInstance->getHandle(), *compileOptions, source);
-
- return std::make_shared<WaitableCompileEventImpl>(
- workerThreadPool->postWorkerTask(translateTask), translateTask);
+ ANGLE_TRACE_EVENT1("gpu.angle", "ShaderTranslateTask::run", "source", source);
+ const char *src = source.c_str();
+ return sh::Compile(compiler, &src, 1, options);
}
angle::Result ShaderImpl::onLabelUpdate(const gl::Context *context)
diff --git a/src/libANGLE/renderer/ShaderImpl.h b/src/libANGLE/renderer/ShaderImpl.h
index 4b22a57..d3fae14 100644
--- a/src/libANGLE/renderer/ShaderImpl.h
+++ b/src/libANGLE/renderer/ShaderImpl.h
@@ -11,6 +11,7 @@
#include <functional>
+#include "common/CompiledShaderState.h"
#include "common/WorkerThread.h"
#include "common/angleutils.h"
#include "libANGLE/Shader.h"
@@ -22,27 +23,34 @@
namespace rx
{
-
-using UpdateShaderStateFunctor = std::function<void(bool compiled, ShHandle handle)>;
-class WaitableCompileEvent : public angle::WaitableEvent
+// The compile task is generally just a call to the translator. However, different backends behave
+// differently afterwards:
+//
+// - The Vulkan backend which generates binary (i.e. SPIR-V), does nothing more
+// - The backends that generate text (HLSL and MSL), do nothing at this stage, but modify the text
+// at link time before invoking the native compiler. These expensive calls are handled in link
+// sub-tasks (see LinkSubTask in ProgramImpl.h).
+// - The GL backend needs to invoke the native driver, which is problematic when done in another
+// thread (and is avoided).
+//
+// The call to the translator can thus be done in a separate thread or without holding the share
+// group lock on all backends except GL. On the GL backend, the translator call is done on the main
+// thread followed by a call to the native driver. If the driver supports
+// GL_KHR_parallel_shader_compile, ANGLE still delays post-processing of the results to when
+// compilation is done (just as if it was ANGLE itself that was doing the compilation in a thread).
+class ShaderTranslateTask
{
public:
- WaitableCompileEvent(std::shared_ptr<angle::WaitableEvent> waitableEvent);
- ~WaitableCompileEvent() override;
+ virtual ~ShaderTranslateTask() = default;
+ virtual bool translate(ShHandle compiler,
+ const ShCompileOptions &options,
+ const std::string &source);
+ virtual void postTranslate(ShHandle compiler, const gl::CompiledShaderState &compiledState) {}
- void wait() override;
-
- bool isReady() override;
-
- virtual bool getResult() = 0;
-
- virtual bool postTranslate(std::string *infoLog) = 0;
-
- const std::string &getInfoLog();
-
- protected:
- std::shared_ptr<angle::WaitableEvent> mWaitableEvent;
- std::string mInfoLog;
+ // Used by the GL backend to query whether the driver is compiling in parallel internally.
+ virtual bool isCompilingInternally() { return false; }
+ // Used by the GL backend to finish internal compilation and return results.
+ virtual angle::Result getResult(std::string &infoLog) { return angle::Result::Continue; }
};
class ShaderImpl : angle::NonCopyable
@@ -53,9 +61,8 @@
virtual void destroy() {}
- virtual std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) = 0;
+ virtual std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) = 0;
virtual std::string getDebugInfo() const = 0;
@@ -64,11 +71,6 @@
virtual angle::Result onLabelUpdate(const gl::Context *context);
protected:
- std::shared_ptr<WaitableCompileEvent> compileImpl(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- const std::string &source,
- ShCompileOptions *compileOptions);
-
const gl::ShaderState &mState;
};
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index d26e59d..ed0247b 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -22,72 +22,153 @@
namespace rx
{
+namespace
+{
+const std::map<std::string, unsigned int> &GetUniformRegisterMap(
+ const std::map<std::string, unsigned int> *uniformRegisterMap)
+{
+ ASSERT(uniformRegisterMap);
+ return *uniformRegisterMap;
+}
-class TranslateTaskD3D : public angle::Closure
+const std::set<std::string> &GetSlowCompilingUniformBlockSet(
+ const std::set<std::string> *slowCompilingUniformBlockSet)
+{
+ ASSERT(slowCompilingUniformBlockSet);
+ return *slowCompilingUniformBlockSet;
+}
+
+const std::set<std::string> &GetUsedImage2DFunctionNames(
+ const std::set<std::string> *usedImage2DFunctionNames)
+{
+ ASSERT(usedImage2DFunctionNames);
+ return *usedImage2DFunctionNames;
+}
+
+class ShaderTranslateTaskD3D final : public ShaderTranslateTask
{
public:
- TranslateTaskD3D(ShHandle handle,
- const ShCompileOptions &options,
- const std::string &source,
- const std::string &sourcePath)
- : mHandle(handle),
- mOptions(options),
- mSource(source),
- mSourcePath(sourcePath),
- mResult(false)
+ ShaderTranslateTaskD3D(const SharedCompiledShaderStateD3D &shader, std::string &&sourcePath)
+ : mSourcePath(std::move(sourcePath)), mShader(shader)
{}
+ ~ShaderTranslateTaskD3D() override = default;
- void operator()() override
+ bool translate(ShHandle compiler,
+ const ShCompileOptions &options,
+ const std::string &source) override
{
- ANGLE_TRACE_EVENT1("gpu.angle", "TranslateTask::run", "source", mSource);
- std::vector<const char *> srcStrings;
+ ANGLE_TRACE_EVENT1("gpu.angle", "ShaderTranslateTaskD3D::run", "source", source);
+ angle::FixedVector<const char *, 2> srcStrings;
if (!mSourcePath.empty())
{
srcStrings.push_back(mSourcePath.c_str());
}
- srcStrings.push_back(mSource.c_str());
+ srcStrings.push_back(source.c_str());
- mResult = sh::Compile(mHandle, &srcStrings[0], srcStrings.size(), mOptions);
+ return sh::Compile(compiler, srcStrings.data(), srcStrings.size(), options);
}
- bool getResult() { return mResult; }
-
- private:
- ShHandle mHandle;
- ShCompileOptions mOptions;
- std::string mSource;
- std::string mSourcePath;
- bool mResult;
-};
-
-using PostTranslateFunctor =
- std::function<bool(gl::ShCompilerInstance *compiler, std::string *infoLog)>;
-
-class WaitableCompileEventD3D final : public WaitableCompileEvent
-{
- public:
- WaitableCompileEventD3D(std::shared_ptr<angle::WaitableEvent> waitableEvent,
- gl::ShCompilerInstance *compilerInstance,
- PostTranslateFunctor &&postTranslateFunctor,
- std::shared_ptr<TranslateTaskD3D> translateTask)
- : WaitableCompileEvent(waitableEvent),
- mCompilerInstance(compilerInstance),
- mPostTranslateFunctor(std::move(postTranslateFunctor)),
- mTranslateTask(translateTask)
- {}
-
- bool getResult() override { return mTranslateTask->getResult(); }
-
- bool postTranslate(std::string *infoLog) override
+ void postTranslate(ShHandle compiler, const gl::CompiledShaderState &compiledState) override
{
- return mPostTranslateFunctor(mCompilerInstance, infoLog);
+ const std::string &translatedSource = compiledState.translatedSource;
+ CompiledShaderStateD3D *state = mShader.get();
+
+ // Note: We shouldn't need to cache this.
+ state->compilerOutputType = sh::GetShaderOutputType(compiler);
+
+ state->usesMultipleRenderTargets =
+ translatedSource.find("GL_USES_MRT") != std::string::npos;
+ state->usesFragColor = translatedSource.find("GL_USES_FRAG_COLOR") != std::string::npos;
+ state->usesFragData = translatedSource.find("GL_USES_FRAG_DATA") != std::string::npos;
+ state->usesSecondaryColor =
+ translatedSource.find("GL_USES_SECONDARY_COLOR") != std::string::npos;
+ state->usesFragCoord = translatedSource.find("GL_USES_FRAG_COORD") != std::string::npos;
+ state->usesFrontFacing = translatedSource.find("GL_USES_FRONT_FACING") != std::string::npos;
+ state->usesSampleID = translatedSource.find("GL_USES_SAMPLE_ID") != std::string::npos;
+ state->usesSamplePosition =
+ translatedSource.find("GL_USES_SAMPLE_POSITION") != std::string::npos;
+ state->usesSampleMaskIn =
+ translatedSource.find("GL_USES_SAMPLE_MASK_IN") != std::string::npos;
+ state->usesSampleMask =
+ translatedSource.find("GL_USES_SAMPLE_MASK_OUT") != std::string::npos;
+ state->usesHelperInvocation =
+ translatedSource.find("GL_USES_HELPER_INVOCATION") != std::string::npos;
+ state->usesPointSize = translatedSource.find("GL_USES_POINT_SIZE") != std::string::npos;
+ state->usesPointCoord = translatedSource.find("GL_USES_POINT_COORD") != std::string::npos;
+ state->usesDepthRange = translatedSource.find("GL_USES_DEPTH_RANGE") != std::string::npos;
+ state->hasMultiviewEnabled =
+ translatedSource.find("GL_MULTIVIEW_ENABLED") != std::string::npos;
+ state->usesVertexID = translatedSource.find("GL_USES_VERTEX_ID") != std::string::npos;
+ state->usesViewID = translatedSource.find("GL_USES_VIEW_ID") != std::string::npos;
+ state->usesDiscardRewriting =
+ translatedSource.find("ANGLE_USES_DISCARD_REWRITING") != std::string::npos;
+ state->usesNestedBreak =
+ translatedSource.find("ANGLE_USES_NESTED_BREAK") != std::string::npos;
+ state->requiresIEEEStrictCompiling =
+ translatedSource.find("ANGLE_REQUIRES_IEEE_STRICT_COMPILING") != std::string::npos;
+
+ if (translatedSource.find("GL_USES_FRAG_DEPTH_GREATER") != std::string::npos)
+ {
+ state->fragDepthUsage = FragDepthUsage::Greater;
+ }
+ else if (translatedSource.find("GL_USES_FRAG_DEPTH_LESS") != std::string::npos)
+ {
+ state->fragDepthUsage = FragDepthUsage::Less;
+ }
+ else if (translatedSource.find("GL_USES_FRAG_DEPTH") != std::string::npos)
+ {
+ state->fragDepthUsage = FragDepthUsage::Any;
+ }
+ state->clipDistanceSize = sh::GetClipDistanceArraySize(compiler);
+ state->cullDistanceSize = sh::GetCullDistanceArraySize(compiler);
+ state->uniformRegisterMap = GetUniformRegisterMap(sh::GetUniformRegisterMap(compiler));
+ state->readonlyImage2DRegisterIndex = sh::GetReadonlyImage2DRegisterIndex(compiler);
+ state->image2DRegisterIndex = sh::GetImage2DRegisterIndex(compiler);
+ state->usedImage2DFunctionNames =
+ GetUsedImage2DFunctionNames(sh::GetUsedImage2DFunctionNames(compiler));
+
+ for (const sh::InterfaceBlock &interfaceBlock : compiledState.uniformBlocks)
+ {
+ if (interfaceBlock.active)
+ {
+ unsigned int index = static_cast<unsigned int>(-1);
+ bool blockRegisterResult =
+ sh::GetUniformBlockRegister(compiler, interfaceBlock.name, &index);
+ ASSERT(blockRegisterResult);
+ bool useStructuredBuffer =
+ sh::ShouldUniformBlockUseStructuredBuffer(compiler, interfaceBlock.name);
+
+ state->uniformBlockRegisterMap[interfaceBlock.name] = index;
+ state->uniformBlockUseStructuredBufferMap[interfaceBlock.name] =
+ useStructuredBuffer;
+ }
+ }
+
+ state->slowCompilingUniformBlockSet =
+ GetSlowCompilingUniformBlockSet(sh::GetSlowCompilingUniformBlockSet(compiler));
+
+ for (const sh::InterfaceBlock &interfaceBlock : compiledState.shaderStorageBlocks)
+ {
+ if (interfaceBlock.active)
+ {
+ unsigned int index = static_cast<unsigned int>(-1);
+ bool blockRegisterResult =
+ sh::GetShaderStorageBlockRegister(compiler, interfaceBlock.name, &index);
+ ASSERT(blockRegisterResult);
+
+ state->shaderStorageBlockRegisterMap[interfaceBlock.name] = index;
+ }
+ }
+
+ state->debugInfo +=
+ "// INITIAL HLSL BEGIN\n\n" + translatedSource + "\n// INITIAL HLSL END\n\n\n";
}
private:
- gl::ShCompilerInstance *mCompilerInstance;
- PostTranslateFunctor mPostTranslateFunctor;
- std::shared_ptr<TranslateTaskD3D> mTranslateTask;
+ std::string mSourcePath;
+ SharedCompiledShaderStateD3D mShader;
};
+} // anonymous namespace
CompiledShaderStateD3D::CompiledShaderStateD3D()
: compilerOutputType(SH_ESSL_OUTPUT),
@@ -204,30 +285,8 @@
return slowCompilingUniformBlockSet;
}
-const std::map<std::string, unsigned int> &GetUniformRegisterMap(
- const std::map<std::string, unsigned int> *uniformRegisterMap)
-{
- ASSERT(uniformRegisterMap);
- return *uniformRegisterMap;
-}
-
-const std::set<std::string> &GetSlowCompilingUniformBlockSet(
- const std::set<std::string> *slowCompilingUniformBlockSet)
-{
- ASSERT(slowCompilingUniformBlockSet);
- return *slowCompilingUniformBlockSet;
-}
-
-const std::set<std::string> &GetUsedImage2DFunctionNames(
- const std::set<std::string> *usedImage2DFunctionNames)
-{
- ASSERT(usedImage2DFunctionNames);
- return *usedImage2DFunctionNames;
-}
-
-std::shared_ptr<WaitableCompileEvent> ShaderD3D::compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options)
+std::shared_ptr<ShaderTranslateTask> ShaderD3D::compile(const gl::Context *context,
+ ShCompileOptions *options)
{
// Create a new compiled shader state. Currently running program link jobs will use the
// previous state.
@@ -294,117 +353,11 @@
options->pls = mRenderer->getNativePixelLocalStorageOptions();
}
- auto postTranslateFunctor = [this](gl::ShCompilerInstance *compiler, std::string *infoLog) {
- const std::string &translatedSource = mState.getCompiledState()->translatedSource;
- CompiledShaderStateD3D *state = mCompiledState.get();
+ // The D3D translations are not currently validation-error-free
+ options->validateAST = false;
- // TODO(jmadill): We shouldn't need to cache this.
- state->compilerOutputType = compiler->getShaderOutputType();
-
- state->usesMultipleRenderTargets =
- translatedSource.find("GL_USES_MRT") != std::string::npos;
- state->usesFragColor = translatedSource.find("GL_USES_FRAG_COLOR") != std::string::npos;
- state->usesFragData = translatedSource.find("GL_USES_FRAG_DATA") != std::string::npos;
- state->usesSecondaryColor =
- translatedSource.find("GL_USES_SECONDARY_COLOR") != std::string::npos;
- state->usesFragCoord = translatedSource.find("GL_USES_FRAG_COORD") != std::string::npos;
- state->usesFrontFacing = translatedSource.find("GL_USES_FRONT_FACING") != std::string::npos;
- state->usesSampleID = translatedSource.find("GL_USES_SAMPLE_ID") != std::string::npos;
- state->usesSamplePosition =
- translatedSource.find("GL_USES_SAMPLE_POSITION") != std::string::npos;
- state->usesSampleMaskIn =
- translatedSource.find("GL_USES_SAMPLE_MASK_IN") != std::string::npos;
- state->usesSampleMask =
- translatedSource.find("GL_USES_SAMPLE_MASK_OUT") != std::string::npos;
- state->usesHelperInvocation =
- translatedSource.find("GL_USES_HELPER_INVOCATION") != std::string::npos;
- state->usesPointSize = translatedSource.find("GL_USES_POINT_SIZE") != std::string::npos;
- state->usesPointCoord = translatedSource.find("GL_USES_POINT_COORD") != std::string::npos;
- state->usesDepthRange = translatedSource.find("GL_USES_DEPTH_RANGE") != std::string::npos;
- state->hasMultiviewEnabled =
- translatedSource.find("GL_MULTIVIEW_ENABLED") != std::string::npos;
- state->usesVertexID = translatedSource.find("GL_USES_VERTEX_ID") != std::string::npos;
- state->usesViewID = translatedSource.find("GL_USES_VIEW_ID") != std::string::npos;
- state->usesDiscardRewriting =
- translatedSource.find("ANGLE_USES_DISCARD_REWRITING") != std::string::npos;
- state->usesNestedBreak =
- translatedSource.find("ANGLE_USES_NESTED_BREAK") != std::string::npos;
- state->requiresIEEEStrictCompiling =
- translatedSource.find("ANGLE_REQUIRES_IEEE_STRICT_COMPILING") != std::string::npos;
-
- ShHandle compilerHandle = compiler->getHandle();
-
- if (translatedSource.find("GL_USES_FRAG_DEPTH_GREATER") != std::string::npos)
- {
- state->fragDepthUsage = FragDepthUsage::Greater;
- }
- else if (translatedSource.find("GL_USES_FRAG_DEPTH_LESS") != std::string::npos)
- {
- state->fragDepthUsage = FragDepthUsage::Less;
- }
- else if (translatedSource.find("GL_USES_FRAG_DEPTH") != std::string::npos)
- {
- state->fragDepthUsage = FragDepthUsage::Any;
- }
- state->clipDistanceSize = sh::GetClipDistanceArraySize(compilerHandle);
- state->cullDistanceSize = sh::GetCullDistanceArraySize(compilerHandle);
- state->uniformRegisterMap =
- GetUniformRegisterMap(sh::GetUniformRegisterMap(compilerHandle));
- state->readonlyImage2DRegisterIndex = sh::GetReadonlyImage2DRegisterIndex(compilerHandle);
- state->image2DRegisterIndex = sh::GetImage2DRegisterIndex(compilerHandle);
- state->usedImage2DFunctionNames =
- GetUsedImage2DFunctionNames(sh::GetUsedImage2DFunctionNames(compilerHandle));
-
- for (const sh::InterfaceBlock &interfaceBlock : mState.getCompiledState()->uniformBlocks)
- {
- if (interfaceBlock.active)
- {
- unsigned int index = static_cast<unsigned int>(-1);
- bool blockRegisterResult =
- sh::GetUniformBlockRegister(compilerHandle, interfaceBlock.name, &index);
- ASSERT(blockRegisterResult);
- bool useStructuredBuffer =
- sh::ShouldUniformBlockUseStructuredBuffer(compilerHandle, interfaceBlock.name);
-
- state->uniformBlockRegisterMap[interfaceBlock.name] = index;
- state->uniformBlockUseStructuredBufferMap[interfaceBlock.name] =
- useStructuredBuffer;
- }
- }
-
- state->slowCompilingUniformBlockSet =
- GetSlowCompilingUniformBlockSet(sh::GetSlowCompilingUniformBlockSet(compilerHandle));
-
- for (const sh::InterfaceBlock &interfaceBlock :
- mState.getCompiledState()->shaderStorageBlocks)
- {
- if (interfaceBlock.active)
- {
- unsigned int index = static_cast<unsigned int>(-1);
- bool blockRegisterResult =
- sh::GetShaderStorageBlockRegister(compilerHandle, interfaceBlock.name, &index);
- ASSERT(blockRegisterResult);
-
- state->shaderStorageBlockRegisterMap[interfaceBlock.name] = index;
- }
- }
-
- state->debugInfo += std::string("// ") + gl::GetShaderTypeString(mState.getShaderType()) +
- " SHADER BEGIN\n";
- state->debugInfo += "\n// GLSL BEGIN\n\n" + mState.getSource() + "\n\n// GLSL END\n\n\n";
- state->debugInfo +=
- "// INITIAL HLSL BEGIN\n\n" + translatedSource + "\n// INITIAL HLSL END\n\n\n";
- // Successive steps will append more info
- return true;
- };
-
- auto workerThreadPool = context->getShaderCompileThreadPool();
- auto translateTask = std::make_shared<TranslateTaskD3D>(compilerInstance->getHandle(), *options,
- source, sourcePath);
-
- return std::make_shared<WaitableCompileEventD3D>(
- workerThreadPool->postWorkerTask(translateTask), compilerInstance,
- std::move(postTranslateFunctor), translateTask);
+ return std::shared_ptr<ShaderTranslateTask>(
+ new ShaderTranslateTaskD3D(mCompiledState, std::move(sourcePath)));
}
bool CompiledShaderStateD3D::hasUniform(const std::string &name) const
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.h b/src/libANGLE/renderer/d3d/ShaderD3D.h
index 99259eb..14adb44 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -114,9 +114,8 @@
ShaderD3D(const gl::ShaderState &state, RendererD3D *renderer);
~ShaderD3D() override;
- std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) override;
+ std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) override;
std::string getDebugInfo() const override;
diff --git a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
index 11c19d8..f42fe54 100644
--- a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -2604,7 +2604,8 @@
ANGLE_FEATURE_CONDITION(features, forceDepthAttachmentInitOnClear, isAMD);
- // The D3D backend's handling of link is thread-safe
+ // The D3D backend's handling of compile and link is thread-safe
+ ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true);
ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, true);
}
diff --git a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
index 42205c0..08abaad 100644
--- a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
@@ -846,7 +846,8 @@
void InitializeFrontendFeatures(angle::FrontendFeatures *features, DWORD vendorID)
{
- // The D3D backend's handling of link is thread-safe
+ // The D3D backend's handling of compile and link is thread-safe
+ ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true);
ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, true);
}
} // namespace d3d9
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index e985091..616fb56 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -21,57 +21,76 @@
namespace rx
{
-
-using PostTranslateFunctor = std::function<bool(std::string *infoLog)>;
-using PeekCompletionFunctor = std::function<bool()>;
-using CheckShaderFunctor = std::function<void()>;
-
-class WaitableCompileEventNativeParallel final : public WaitableCompileEvent
+namespace
+{
+class ShaderTranslateTaskGL final : public ShaderTranslateTask
{
public:
- WaitableCompileEventNativeParallel(PostTranslateFunctor &&postTranslateFunctor,
- bool result,
- CheckShaderFunctor &&checkShaderFunctor,
- PeekCompletionFunctor &&peekCompletionFunctor)
- : WaitableCompileEvent(std::shared_ptr<angle::WaitableEventDone>()),
- mPostTranslateFunctor(std::move(postTranslateFunctor)),
- mResult(result),
- mCheckShaderFunctor(std::move(checkShaderFunctor)),
- mPeekCompletionFunctor(std::move(peekCompletionFunctor))
+ ShaderTranslateTaskGL(const FunctionsGL *functions,
+ GLuint shaderID,
+ bool hasNativeParallelCompile)
+ : mFunctions(functions),
+ mShaderID(shaderID),
+ mHasNativeParallelCompile(hasNativeParallelCompile)
{}
+ ~ShaderTranslateTaskGL() override = default;
- void wait() override { mCheckShaderFunctor(); }
+ void postTranslate(ShHandle compiler, const gl::CompiledShaderState &compiledState) override
+ {
+ const char *source = compiledState.translatedSource.c_str();
+ mFunctions->shaderSource(mShaderID, 1, &source, nullptr);
+ mFunctions->compileShader(mShaderID);
+ }
- bool isReady() override { return mPeekCompletionFunctor(); }
+ bool isCompilingInternally() override
+ {
+ if (!mHasNativeParallelCompile)
+ {
+ return false;
+ }
- bool getResult() override { return mResult; }
+ GLint status = GL_FALSE;
+ mFunctions->getShaderiv(mShaderID, GL_COMPLETION_STATUS, &status);
+ return status != GL_TRUE;
+ }
- bool postTranslate(std::string *infoLog) override { return mPostTranslateFunctor(infoLog); }
+ angle::Result getResult(std::string &infoLog) override
+ {
+ // Check for compile errors from the native driver
+ GLint compileStatus = GL_FALSE;
+ mFunctions->getShaderiv(mShaderID, GL_COMPILE_STATUS, &compileStatus);
+ if (compileStatus != GL_FALSE)
+ {
+ return angle::Result::Continue;
+ }
+
+ // Compilation failed, put the error into the info log
+ GLint infoLogLength = 0;
+ mFunctions->getShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+ // Info log length includes the null terminator, so 1 means that the info log is an empty
+ // string.
+ if (infoLogLength > 1)
+ {
+ std::vector<char> buf(infoLogLength);
+ mFunctions->getShaderInfoLog(mShaderID, infoLogLength, nullptr, &buf[0]);
+
+ infoLog += buf.data();
+ }
+ else
+ {
+ WARN() << std::endl << "Shader compilation failed with no info log.";
+ }
+
+ return angle::Result::Stop;
+ }
private:
- PostTranslateFunctor mPostTranslateFunctor;
- bool mResult;
- CheckShaderFunctor mCheckShaderFunctor;
- PeekCompletionFunctor mPeekCompletionFunctor;
+ const FunctionsGL *mFunctions;
+ GLuint mShaderID;
+ bool mHasNativeParallelCompile;
};
-
-class WaitableCompileEventDone final : public WaitableCompileEvent
-{
- public:
- WaitableCompileEventDone(PostTranslateFunctor &&postTranslateFunctor, bool result)
- : WaitableCompileEvent(std::make_shared<angle::WaitableEventDone>()),
- mPostTranslateFunctor(std::move(postTranslateFunctor)),
- mResult(result)
- {}
-
- bool getResult() override { return mResult; }
-
- bool postTranslate(std::string *infoLog) override { return mPostTranslateFunctor(infoLog); }
-
- private:
- PostTranslateFunctor mPostTranslateFunctor;
- bool mResult;
-};
+} // anonymous namespace
ShaderGL::ShaderGL(const gl::ShaderState &data,
GLuint shaderID,
@@ -80,8 +99,7 @@
: ShaderImpl(data),
mShaderID(shaderID),
mMultiviewImplementationType(multiviewImplementationType),
- mRenderer(renderer),
- mCompileStatus(GL_FALSE)
+ mRenderer(renderer)
{}
ShaderGL::~ShaderGL()
@@ -95,63 +113,9 @@
mShaderID = 0;
}
-void ShaderGL::compileAndCheckShader(const char *source)
+std::shared_ptr<ShaderTranslateTask> ShaderGL::compile(const gl::Context *context,
+ ShCompileOptions *options)
{
- compileShader(source);
- checkShader();
-}
-
-void ShaderGL::compileShader(const char *source)
-{
- const FunctionsGL *functions = mRenderer->getFunctions();
- functions->shaderSource(mShaderID, 1, &source, nullptr);
- functions->compileShader(mShaderID);
-}
-
-void ShaderGL::checkShader()
-{
- const FunctionsGL *functions = mRenderer->getFunctions();
-
- // Check for compile errors from the native driver
- mCompileStatus = GL_FALSE;
- functions->getShaderiv(mShaderID, GL_COMPILE_STATUS, &mCompileStatus);
- if (mCompileStatus == GL_FALSE)
- {
- // Compilation failed, put the error into the info log
- GLint infoLogLength = 0;
- functions->getShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
-
- // Info log length includes the null terminator, so 1 means that the info log is an empty
- // string.
- if (infoLogLength > 1)
- {
- std::vector<char> buf(infoLogLength);
- functions->getShaderInfoLog(mShaderID, infoLogLength, nullptr, &buf[0]);
-
- mInfoLog += &buf[0];
- WARN() << std::endl << mInfoLog;
- }
- else
- {
- WARN() << std::endl << "Shader compilation failed with no info log.";
- }
- }
-}
-
-bool ShaderGL::peekCompletion()
-{
- const FunctionsGL *functions = mRenderer->getFunctions();
- GLint status = GL_FALSE;
- functions->getShaderiv(mShaderID, GL_COMPLETION_STATUS, &status);
- return status == GL_TRUE;
-}
-
-std::shared_ptr<WaitableCompileEvent> ShaderGL::compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options)
-{
- mInfoLog.clear();
-
options->initGLPosition = true;
bool isWebGL = context->isWebGL();
@@ -308,48 +272,8 @@
options->pls = mRenderer->getNativePixelLocalStorageOptions();
}
- auto workerThreadPool = context->getShaderCompileThreadPool();
-
- const std::string &source = mState.getSource();
-
- auto postTranslateFunctor = [this](std::string *infoLog) {
- if (mCompileStatus == GL_FALSE)
- {
- *infoLog = mInfoLog;
- return false;
- }
- return true;
- };
-
- ShHandle handle = compilerInstance->getHandle();
- const char *str = source.c_str();
- bool result = sh::Compile(handle, &str, 1, *options);
-
- if (mRenderer->hasNativeParallelCompile())
- {
- if (result)
- {
- compileShader(sh::GetObjectCode(handle).c_str());
- auto checkShaderFunctor = [this]() { checkShader(); };
- auto peekCompletionFunctor = [this]() { return peekCompletion(); };
- return std::make_shared<WaitableCompileEventNativeParallel>(
- std::move(postTranslateFunctor), result, std::move(checkShaderFunctor),
- std::move(peekCompletionFunctor));
- }
- else
- {
- return std::make_shared<WaitableCompileEventDone>([](std::string *) { return true; },
- result);
- }
- }
- else
- {
- if (result)
- {
- compileAndCheckShader(sh::GetObjectCode(handle).c_str());
- }
- return std::make_shared<WaitableCompileEventDone>(std::move(postTranslateFunctor), result);
- }
+ return std::shared_ptr<ShaderTranslateTask>(new ShaderTranslateTaskGL(
+ mRenderer->getFunctions(), mShaderID, mRenderer->hasNativeParallelCompile()));
}
std::string ShaderGL::getDebugInfo() const
diff --git a/src/libANGLE/renderer/gl/ShaderGL.h b/src/libANGLE/renderer/gl/ShaderGL.h
index b47c2a3..5f739fc 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.h
+++ b/src/libANGLE/renderer/gl/ShaderGL.h
@@ -27,25 +27,17 @@
void destroy() override;
- std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) override;
+ std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) override;
std::string getDebugInfo() const override;
GLuint getShaderID() const;
private:
- void compileAndCheckShader(const char *source);
- void compileShader(const char *source);
- void checkShader();
- bool peekCompletion();
-
GLuint mShaderID;
MultiviewImplementationTypeGL mMultiviewImplementationType;
std::shared_ptr<RendererGL> mRenderer;
- GLint mCompileStatus;
- std::string mInfoLog;
};
} // namespace rx
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 4657094..4892c0a 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -2640,13 +2640,14 @@
// 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
+ // The compile and link jobs need 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.
+ // As a result, the compile and link jobs are done in the same thread as the call. If the
+ // native driver supports parallel compile/link, it's still done internally by the driver, and
+ // ANGLE supports delaying post-compile and post-link operations until that is done.
+ ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, false);
ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, false);
}
diff --git a/src/libANGLE/renderer/metal/DisplayMtl.mm b/src/libANGLE/renderer/metal/DisplayMtl.mm
index 3fe6594..a860b6b 100644
--- a/src/libANGLE/renderer/metal/DisplayMtl.mm
+++ b/src/libANGLE/renderer/metal/DisplayMtl.mm
@@ -514,6 +514,9 @@
void DisplayMtl::initializeFrontendFeatures(angle::FrontendFeatures *features) const
{
+ // The Metal backend's handling of compile is thread-safe
+ ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true);
+
// 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.
//
diff --git a/src/libANGLE/renderer/metal/ShaderMtl.h b/src/libANGLE/renderer/metal/ShaderMtl.h
index e9f7572..a67d092 100644
--- a/src/libANGLE/renderer/metal/ShaderMtl.h
+++ b/src/libANGLE/renderer/metal/ShaderMtl.h
@@ -22,20 +22,14 @@
ShaderMtl(const gl::ShaderState &state);
~ShaderMtl() override;
- std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) override;
+ std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) override;
const SharedCompiledShaderStateMtl &getCompiledState() const { return mCompiledState; }
std::string getDebugInfo() const override;
private:
- std::shared_ptr<WaitableCompileEvent> compileImplMtl(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- const std::string &source,
- ShCompileOptions *compileOptions);
-
SharedCompiledShaderStateMtl mCompiledState;
};
diff --git a/src/libANGLE/renderer/metal/ShaderMtl.mm b/src/libANGLE/renderer/metal/ShaderMtl.mm
index 943fb7f..c34a872 100644
--- a/src/libANGLE/renderer/metal/ShaderMtl.mm
+++ b/src/libANGLE/renderer/metal/ShaderMtl.mm
@@ -19,89 +19,37 @@
namespace rx
{
+namespace
+{
+class ShaderTranslateTaskMtl final : public ShaderTranslateTask
+{
+ public:
+ ShaderTranslateTaskMtl(const SharedCompiledShaderStateMtl &shader) : mShader(shader) {}
+ ~ShaderTranslateTaskMtl() override = default;
+
+ void postTranslate(ShHandle compiler, const gl::CompiledShaderState &compiledState) override
+ {
+ sh::TranslatorMSL *translatorMSL =
+ static_cast<sh::TShHandleBase *>(compiler)->getAsTranslatorMSL();
+ if (translatorMSL != nullptr)
+ {
+ // Copy reflection data from translation
+ mShader->translatorMetalReflection = *translatorMSL->getTranslatorMetalReflection();
+ translatorMSL->getTranslatorMetalReflection()->reset();
+ }
+ }
+
+ private:
+ SharedCompiledShaderStateMtl mShader;
+};
+} // anonymous namespace
ShaderMtl::ShaderMtl(const gl::ShaderState &state) : ShaderImpl(state) {}
ShaderMtl::~ShaderMtl() {}
-class TranslateTask : public angle::Closure
-{
- public:
- TranslateTask(ShHandle handle, const ShCompileOptions &options, const std::string &source)
- : mHandle(handle), mOptions(options), mSource(source), mResult(false)
- {}
-
- void operator()() override
- {
- ANGLE_TRACE_EVENT1("gpu.angle", "TranslateTaskMetal::run", "source", mSource);
- const char *source = mSource.c_str();
- mResult = sh::Compile(mHandle, &source, 1, mOptions);
- }
-
- bool getResult() { return mResult; }
-
- ShHandle getHandle() { return mHandle; }
-
- private:
- ShHandle mHandle;
- ShCompileOptions mOptions;
- std::string mSource;
- bool mResult;
-};
-
-class MTLWaitableCompileEventImpl final : public WaitableCompileEvent
-{
- public:
- MTLWaitableCompileEventImpl(const SharedCompiledShaderStateMtl &shader,
- std::shared_ptr<angle::WaitableEvent> waitableEvent,
- std::shared_ptr<TranslateTask> translateTask)
- : WaitableCompileEvent(waitableEvent), mTranslateTask(translateTask), mShader(shader)
- {}
-
- bool getResult() override { return mTranslateTask->getResult(); }
-
- bool postTranslate(std::string *infoLog) override
- {
- sh::TShHandleBase *base = static_cast<sh::TShHandleBase *>(mTranslateTask->getHandle());
- auto translatorMetalDirect = base->getAsTranslatorMSL();
- if (translatorMetalDirect != nullptr)
- {
- // Copy reflection from translation.
- mShader->translatorMetalReflection =
- *(translatorMetalDirect->getTranslatorMetalReflection());
- translatorMetalDirect->getTranslatorMetalReflection()->reset();
- }
- return true;
- }
-
- private:
- std::shared_ptr<TranslateTask> mTranslateTask;
- SharedCompiledShaderStateMtl mShader;
-};
-
-std::shared_ptr<WaitableCompileEvent> ShaderMtl::compileImplMtl(
- const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- const std::string &source,
- ShCompileOptions *compileOptions)
-{
-// TODO(jcunningham): Remove this workaround once correct fix to move validation to the very end is
-// in place. See: https://bugs.webkit.org/show_bug.cgi?id=224991
-#if defined(ANGLE_ENABLE_ASSERTS) && 0
- compileOptions->validateAst = true;
-#endif
-
- auto workerThreadPool = context->getShaderCompileThreadPool();
- auto translateTask =
- std::make_shared<TranslateTask>(compilerInstance->getHandle(), *compileOptions, source);
-
- return std::make_shared<MTLWaitableCompileEventImpl>(
- mCompiledState, workerThreadPool->postWorkerTask(translateTask), translateTask);
-}
-
-std::shared_ptr<WaitableCompileEvent> ShaderMtl::compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options)
+std::shared_ptr<ShaderTranslateTask> ShaderMtl::compile(const gl::Context *context,
+ ShCompileOptions *options)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
DisplayMtl *displayMtl = contextMtl->getDisplay();
@@ -110,6 +58,10 @@
// previous state.
mCompiledState = std::make_shared<CompiledShaderStateMtl>();
+ // TODO(jcunningham): Remove this workaround once correct fix to move validation to the very end
+ // is in place. https://bugs.webkit.org/show_bug.cgi?id=224991
+ options->validateAST = false;
+
options->initializeUninitializedLocals = true;
if (context->isWebGL() && mState.getShaderType() != gl::ShaderType::Compute)
@@ -149,7 +101,7 @@
options->rescopeGlobalVariables = displayMtl->getFeatures().rescopeGlobalVariables.enabled;
- return compileImplMtl(context, compilerInstance, getState().getSource(), options);
+ return std::shared_ptr<ShaderTranslateTask>(new ShaderTranslateTaskMtl(mCompiledState));
}
std::string ShaderMtl::getDebugInfo() const
diff --git a/src/libANGLE/renderer/null/ShaderNULL.cpp b/src/libANGLE/renderer/null/ShaderNULL.cpp
index 98502b5..38c4a08 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.cpp
+++ b/src/libANGLE/renderer/null/ShaderNULL.cpp
@@ -20,16 +20,15 @@
ShaderNULL::~ShaderNULL() {}
-std::shared_ptr<WaitableCompileEvent> ShaderNULL::compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options)
+std::shared_ptr<ShaderTranslateTask> ShaderNULL::compile(const gl::Context *context,
+ ShCompileOptions *options)
{
const gl::Extensions &extensions = context->getImplementation()->getExtensions();
if (extensions.shaderPixelLocalStorageANGLE)
{
options->pls = context->getImplementation()->getNativePixelLocalStorageOptions();
}
- return compileImpl(context, compilerInstance, mState.getSource(), options);
+ return std::shared_ptr<ShaderTranslateTask>(new ShaderTranslateTask);
}
std::string ShaderNULL::getDebugInfo() const
diff --git a/src/libANGLE/renderer/null/ShaderNULL.h b/src/libANGLE/renderer/null/ShaderNULL.h
index b51ef79..11f6469 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.h
+++ b/src/libANGLE/renderer/null/ShaderNULL.h
@@ -21,9 +21,8 @@
ShaderNULL(const gl::ShaderState &data);
~ShaderNULL() override;
- std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) override;
+ std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) override;
std::string getDebugInfo() const override;
};
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index ddd2524..cee83f7 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -4941,7 +4941,8 @@
// https://issuetracker.google.com/292285899
ANGLE_FEATURE_CONDITION(features, uncurrentEglSurfaceUponSurfaceDestroy, true);
- // The Vulkan backend's handling of link is thread-safe
+ // The Vulkan backend's handling of compile and link is thread-safe
+ ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true);
ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, true);
}
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index 365d7eb..04f5935 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -16,14 +16,12 @@
namespace rx
{
-
ShaderVk::ShaderVk(const gl::ShaderState &state) : ShaderImpl(state) {}
ShaderVk::~ShaderVk() {}
-std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options)
+std::shared_ptr<ShaderTranslateTask> ShaderVk::compile(const gl::Context *context,
+ ShCompileOptions *options)
{
ContextVk *contextVk = vk::GetImpl(context);
@@ -128,7 +126,8 @@
options->pls = contextVk->getNativePixelLocalStorageOptions();
}
- return compileImpl(context, compilerInstance, mState.getSource(), options);
+ // The Vulkan backend needs no post-processing of the translated shader.
+ return std::shared_ptr<ShaderTranslateTask>(new ShaderTranslateTask);
}
std::string ShaderVk::getDebugInfo() const
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.h b/src/libANGLE/renderer/vulkan/ShaderVk.h
index 7413321..85305cb 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.h
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.h
@@ -21,9 +21,8 @@
ShaderVk(const gl::ShaderState &state);
~ShaderVk() override;
- std::shared_ptr<WaitableCompileEvent> compile(const gl::Context *context,
- gl::ShCompilerInstance *compilerInstance,
- ShCompileOptions *options) override;
+ std::shared_ptr<ShaderTranslateTask> compile(const gl::Context *context,
+ ShCompileOptions *options) override;
std::string getDebugInfo() const override;
};
diff --git a/util/autogen/angle_features_autogen.cpp b/util/autogen/angle_features_autogen.cpp
index 5489130..6aa0442 100644
--- a/util/autogen/angle_features_autogen.cpp
+++ b/util/autogen/angle_features_autogen.cpp
@@ -64,6 +64,7 @@
{Feature::ClampPointSize, "clampPointSize"},
{Feature::ClearToZeroOrOneBroken, "clearToZeroOrOneBroken"},
{Feature::ClipSrcRegionForBlitFramebuffer, "clipSrcRegionForBlitFramebuffer"},
+ {Feature::CompileJobIsThreadSafe, "compileJobIsThreadSafe"},
{Feature::CompileMetalShaders, "compileMetalShaders"},
{Feature::CompressVertexData, "compressVertexData"},
{Feature::CopyIOSurfaceToNonIOSurfaceForReadOptimization, "copyIOSurfaceToNonIOSurfaceForReadOptimization"},
diff --git a/util/autogen/angle_features_autogen.h b/util/autogen/angle_features_autogen.h
index 23814e1..9b8acd0 100644
--- a/util/autogen/angle_features_autogen.h
+++ b/util/autogen/angle_features_autogen.h
@@ -64,6 +64,7 @@
ClampPointSize,
ClearToZeroOrOneBroken,
ClipSrcRegionForBlitFramebuffer,
+ CompileJobIsThreadSafe,
CompileMetalShaders,
CompressVertexData,
CopyIOSurfaceToNonIOSurfaceForReadOptimization,