Commit 01074255 by Jamie Madill Committed by Commit Bot

D3D: Implement multi-thread shader compilation.

Choose to use std::async for now to implement multi-threaded compiles. This should work across platforms and also be usable for other threading tasks. Note that std::async does not have a good way to wait for multiple std::futures. Also the Linux compile of std::async is broken due to a bug in an old STL version, so disable it on this platform. The implementation uses a static polymorphism approach, which should have very good performance (no virtual calls). This design leaves the door open for other future implementations, such as a Win32 thread pool, or one based on angle::Platform. BUG=angleproject:422 Change-Id: Ia2f13c3af0339efaca1d19b40b3e08ecca61b8e8 Reviewed-on: https://chromium-review.googlesource.com/413712 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 57ce9ea2
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WorkerThread:
// Task running thread for ANGLE, similar to a TaskRunner in Chromium.
// Might be implemented differently depending on platform.
//
#include "libANGLE/WorkerThread.h"
namespace angle
{
namespace priv
{
// SingleThreadedWorkerPool implementation.
SingleThreadedWorkerPool::SingleThreadedWorkerPool(size_t maxThreads)
: WorkerThreadPoolBase(maxThreads)
{
}
SingleThreadedWorkerPool::~SingleThreadedWorkerPool()
{
}
SingleThreadedWaitableEvent SingleThreadedWorkerPool::postWorkerTaskImpl(Closure *task)
{
(*task)();
return SingleThreadedWaitableEvent(EventResetPolicy::Automatic, EventInitialState::Signaled);
}
// SingleThreadedWaitableEvent implementation.
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent()
: SingleThreadedWaitableEvent(EventResetPolicy::Automatic, EventInitialState::NonSignaled)
{
}
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent(EventResetPolicy resetPolicy,
EventInitialState initialState)
: WaitableEventBase(resetPolicy, initialState)
{
}
SingleThreadedWaitableEvent::~SingleThreadedWaitableEvent()
{
}
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent(SingleThreadedWaitableEvent &&other)
: WaitableEventBase(std::move(other))
{
}
SingleThreadedWaitableEvent &SingleThreadedWaitableEvent::operator=(
SingleThreadedWaitableEvent &&other)
{
return copyBase(std::move(other));
}
void SingleThreadedWaitableEvent::resetImpl()
{
mSignaled = false;
}
void SingleThreadedWaitableEvent::waitImpl()
{
}
void SingleThreadedWaitableEvent::signalImpl()
{
mSignaled = true;
}
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// AsyncWorkerPool implementation.
AsyncWorkerPool::AsyncWorkerPool(size_t maxThreads) : WorkerThreadPoolBase(maxThreads)
{
}
AsyncWorkerPool::~AsyncWorkerPool()
{
}
AsyncWaitableEvent AsyncWorkerPool::postWorkerTaskImpl(Closure *task)
{
auto future = std::async(std::launch::async, [task] { (*task)(); });
AsyncWaitableEvent waitable(EventResetPolicy::Automatic, EventInitialState::NonSignaled);
waitable.setFuture(std::move(future));
return waitable;
}
// AsyncWaitableEvent implementation.
AsyncWaitableEvent::AsyncWaitableEvent()
: AsyncWaitableEvent(EventResetPolicy::Automatic, EventInitialState::NonSignaled)
{
}
AsyncWaitableEvent::AsyncWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState)
: WaitableEventBase(resetPolicy, initialState)
{
}
AsyncWaitableEvent::~AsyncWaitableEvent()
{
}
AsyncWaitableEvent::AsyncWaitableEvent(AsyncWaitableEvent &&other)
: WaitableEventBase(std::move(other)), mFuture(std::move(other.mFuture))
{
}
AsyncWaitableEvent &AsyncWaitableEvent::operator=(AsyncWaitableEvent &&other)
{
std::swap(mFuture, other.mFuture);
return copyBase(std::move(other));
}
void AsyncWaitableEvent::setFuture(std::future<void> &&future)
{
mFuture = std::move(future);
}
void AsyncWaitableEvent::resetImpl()
{
mSignaled = false;
mFuture = std::future<void>();
}
void AsyncWaitableEvent::waitImpl()
{
if (mSignaled || !mFuture.valid())
{
return;
}
mFuture.wait();
signal();
}
void AsyncWaitableEvent::signalImpl()
{
mSignaled = true;
if (mResetPolicy == EventResetPolicy::Automatic)
{
reset();
}
}
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace priv
} // namespace angle
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WorkerThread:
// Asychronous tasks/threads for ANGLE, similar to a TaskRunner in Chromium.
// Can be implemented as different targets, depending on platform.
//
#ifndef LIBANGLE_WORKER_THREAD_H_
#define LIBANGLE_WORKER_THREAD_H_
#include <array>
#include <vector>
#include "common/debug.h"
#include "libANGLE/features.h"
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
#include <future>
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
namespace angle
{
// Indicates whether a WaitableEvent should automatically reset the event state after a single
// waiting thread has been released or remain signaled until reset() is manually invoked.
enum class EventResetPolicy
{
Manual,
Automatic
};
// Specify the initial state on creation.
enum class EventInitialState
{
NonSignaled,
Signaled
};
// A callback function with no return value and no arguments.
class Closure
{
public:
virtual void operator()() = 0;
};
namespace priv
{
// An event that we can wait on, useful for joining worker threads.
template <typename Impl>
class WaitableEventBase : angle::NonCopyable
{
public:
WaitableEventBase(EventResetPolicy resetPolicy, EventInitialState initialState);
WaitableEventBase(WaitableEventBase &&other);
// Puts the event in the un-signaled state.
void reset();
// Waits indefinitely for the event to be signaled.
void wait();
// Puts the event in the signaled state, causing any thread blocked on Wait to be woken up.
// The event state is reset to non-signaled after a waiting thread has been released.
void signal();
protected:
Impl &copyBase(Impl &&other);
template <size_t Count>
static size_t WaitManyBase(std::array<Impl, Count> *waitables);
EventResetPolicy mResetPolicy;
bool mSignaled;
};
template <typename Impl>
WaitableEventBase<Impl>::WaitableEventBase(EventResetPolicy resetPolicy,
EventInitialState initialState)
: mResetPolicy(resetPolicy), mSignaled(initialState == EventInitialState::Signaled)
{
}
template <typename Impl>
WaitableEventBase<Impl>::WaitableEventBase(WaitableEventBase &&other)
: mResetPolicy(other.mResetPolicy), mSignaled(other.mSignaled)
{
}
template <typename Impl>
void WaitableEventBase<Impl>::reset()
{
static_cast<Impl *>(this)->resetImpl();
}
template <typename Impl>
void WaitableEventBase<Impl>::wait()
{
static_cast<Impl *>(this)->waitImpl();
}
template <typename Impl>
void WaitableEventBase<Impl>::signal()
{
static_cast<Impl *>(this)->signalImpl();
}
template <typename Impl>
template <size_t Count>
// static
size_t WaitableEventBase<Impl>::WaitManyBase(std::array<Impl, Count> *waitables)
{
ASSERT(Count > 0);
for (size_t index = 0; index < Count; ++index)
{
(*waitables)[index].wait();
}
return 0;
}
template <typename Impl>
Impl &WaitableEventBase<Impl>::copyBase(Impl &&other)
{
std::swap(mSignaled, other.mSignaled);
std::swap(mResetPolicy, other.mResetPolicy);
return *static_cast<Impl *>(this);
}
class SingleThreadedWaitableEvent : public WaitableEventBase<SingleThreadedWaitableEvent>
{
public:
SingleThreadedWaitableEvent();
SingleThreadedWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState);
~SingleThreadedWaitableEvent();
SingleThreadedWaitableEvent(SingleThreadedWaitableEvent &&other);
SingleThreadedWaitableEvent &operator=(SingleThreadedWaitableEvent &&other);
void resetImpl();
void waitImpl();
void signalImpl();
// Wait, synchronously, on multiple events.
// returns the index of a WaitableEvent which has been signaled.
template <size_t Count>
static size_t WaitMany(std::array<SingleThreadedWaitableEvent, Count> *waitables);
};
template <size_t Count>
// static
size_t SingleThreadedWaitableEvent::WaitMany(
std::array<SingleThreadedWaitableEvent, Count> *waitables)
{
return WaitableEventBase<SingleThreadedWaitableEvent>::WaitManyBase(waitables);
}
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWaitableEvent : public WaitableEventBase<AsyncWaitableEvent>
{
public:
AsyncWaitableEvent();
AsyncWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState);
~AsyncWaitableEvent();
AsyncWaitableEvent(AsyncWaitableEvent &&other);
AsyncWaitableEvent &operator=(AsyncWaitableEvent &&other);
void resetImpl();
void waitImpl();
void signalImpl();
// Wait, synchronously, on multiple events.
// returns the index of a WaitableEvent which has been signaled.
template <size_t Count>
static size_t WaitMany(std::array<AsyncWaitableEvent, Count> *waitables);
private:
friend class AsyncWorkerPool;
void setFuture(std::future<void> &&future);
std::future<void> mFuture;
};
template <size_t Count>
// static
size_t AsyncWaitableEvent::WaitMany(std::array<AsyncWaitableEvent, Count> *waitables)
{
return WaitableEventBase<AsyncWaitableEvent>::WaitManyBase(waitables);
}
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// The traits class allows the the thread pool to return the "Typed" waitable event from postTask.
// Otherwise postTask would always think it returns the current active type, so the unit tests
// could not run on multiple worker types in the same compilation.
template <typename Impl>
struct WorkerThreadPoolTraits;
class SingleThreadedWorkerPool;
template <>
struct WorkerThreadPoolTraits<SingleThreadedWorkerPool>
{
using WaitableEventType = SingleThreadedWaitableEvent;
};
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWorkerPool;
template <>
struct WorkerThreadPoolTraits<AsyncWorkerPool>
{
using WaitableEventType = AsyncWaitableEvent;
};
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// Request WorkerThreads from the WorkerThreadPool. Each pool can keep worker threads around so
// we avoid the costly spin up and spin down time.
template <typename Impl>
class WorkerThreadPoolBase : angle::NonCopyable
{
public:
WorkerThreadPoolBase(size_t maxThreads);
~WorkerThreadPoolBase();
using WaitableEventType = typename WorkerThreadPoolTraits<Impl>::WaitableEventType;
// Returns an event to wait on for the task to finish.
// If the pool fails to create the task, returns null.
WaitableEventType postWorkerTask(Closure *task);
};
template <typename Impl>
WorkerThreadPoolBase<Impl>::WorkerThreadPoolBase(size_t maxThreads)
{
}
template <typename Impl>
WorkerThreadPoolBase<Impl>::~WorkerThreadPoolBase()
{
}
template <typename Impl>
typename WorkerThreadPoolBase<Impl>::WaitableEventType WorkerThreadPoolBase<Impl>::postWorkerTask(
Closure *task)
{
return static_cast<Impl *>(this)->postWorkerTaskImpl(task);
}
class SingleThreadedWorkerPool : public WorkerThreadPoolBase<SingleThreadedWorkerPool>
{
public:
SingleThreadedWorkerPool(size_t maxThreads);
~SingleThreadedWorkerPool();
SingleThreadedWaitableEvent postWorkerTaskImpl(Closure *task);
};
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWorkerPool : public WorkerThreadPoolBase<AsyncWorkerPool>
{
public:
AsyncWorkerPool(size_t maxThreads);
~AsyncWorkerPool();
AsyncWaitableEvent postWorkerTaskImpl(Closure *task);
};
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace priv
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
using WaitableEvent = priv::AsyncWaitableEvent;
using WorkerThreadPool = priv::AsyncWorkerPool;
#else
using WaitableEvent = priv::SingleThreadedWaitableEvent;
using WorkerThreadPool = priv::SingleThreadedWorkerPool;
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace angle
#endif // LIBANGLE_WORKER_THREAD_H_
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WorkerThread_unittest:
// Simple tests for the worker thread class.
#include <array>
#include <gtest/gtest.h>
#include "libANGLE/WorkerThread.h"
using namespace angle;
namespace
{
template <typename T>
class WorkerPoolTest : public ::testing::Test
{
public:
T workerPool = {4};
};
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
using WorkerPoolTypes = ::testing::Types<priv::AsyncWorkerPool, priv::SingleThreadedWorkerPool>;
#else
using WorkerPoolTypes = ::testing::Types<priv::SingleThreadedWorkerPool>;
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
TYPED_TEST_CASE(WorkerPoolTest, WorkerPoolTypes);
// Tests simple worker pool application.
TYPED_TEST(WorkerPoolTest, SimpleTask)
{
class TestTask : public Closure
{
public:
void operator()() override { fired = true; }
bool fired = false;
};
std::array<TestTask, 4> tasks;
std::array<typename TypeParam::WaitableEventType, 4> waitables = {{
this->workerPool.postWorkerTask(&tasks[0]), this->workerPool.postWorkerTask(&tasks[1]),
this->workerPool.postWorkerTask(&tasks[2]), this->workerPool.postWorkerTask(&tasks[3]),
}};
TypeParam::WaitableEventType::WaitMany(&waitables);
for (const auto &task : tasks)
{
EXPECT_TRUE(task.fired);
}
}
} // anonymous namespace
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#ifndef LIBANGLE_FEATURES_H_ #ifndef LIBANGLE_FEATURES_H_
#define LIBANGLE_FEATURES_H_ #define LIBANGLE_FEATURES_H_
#include "common/platform.h"
#define ANGLE_DISABLED 0 #define ANGLE_DISABLED 0
#define ANGLE_ENABLED 1 #define ANGLE_ENABLED 1
...@@ -50,4 +52,14 @@ ...@@ -50,4 +52,14 @@
#define ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION ANGLE_ENABLED #define ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION ANGLE_ENABLED
#endif #endif
// Controls if our threading code uses std::async or falls back to single-threaded operations.
// TODO(jmadill): Enable on Linux once STL chrono headers are updated.
#if !defined(ANGLE_STD_ASYNC_WORKERS)
#if defined(ANGLE_PLATFORM_WINDOWS)
#define ANGLE_STD_ASYNC_WORKERS ANGLE_ENABLED
#else
#define ANGLE_STD_ASYNC_WORKERS ANGLE_DISABLED
#endif // defined(ANGLE_PLATFORM_WINDOWS)
#endif // !defined(ANGLE_STD_ASYNC_WORKERS)
#endif // LIBANGLE_FEATURES_H_ #endif // LIBANGLE_FEATURES_H_
...@@ -106,11 +106,11 @@ HLSLCompiler::~HLSLCompiler() ...@@ -106,11 +106,11 @@ HLSLCompiler::~HLSLCompiler()
release(); release();
} }
gl::Error HLSLCompiler::initialize() gl::Error HLSLCompiler::ensureInitialized()
{ {
if (mInitialized) if (mInitialized)
{ {
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
TRACE_EVENT0("gpu.angle", "HLSLCompiler::initialize"); TRACE_EVENT0("gpu.angle", "HLSLCompiler::initialize");
...@@ -160,7 +160,7 @@ gl::Error HLSLCompiler::initialize() ...@@ -160,7 +160,7 @@ gl::Error HLSLCompiler::initialize()
} }
mInitialized = true; mInitialized = true;
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
void HLSLCompiler::release() void HLSLCompiler::release()
...@@ -179,11 +179,7 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string ...@@ -179,11 +179,7 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string
const std::vector<CompileConfig> &configs, const D3D_SHADER_MACRO *overrideMacros, const std::vector<CompileConfig> &configs, const D3D_SHADER_MACRO *overrideMacros,
ID3DBlob **outCompiledBlob, std::string *outDebugInfo) ID3DBlob **outCompiledBlob, std::string *outDebugInfo)
{ {
gl::Error error = initialize(); ASSERT(mInitialized);
if (error.isError())
{
return error;
}
#if !defined(ANGLE_ENABLE_WINDOWS_STORE) #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
ASSERT(mD3DCompilerModule); ASSERT(mD3DCompilerModule);
...@@ -270,14 +266,10 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string ...@@ -270,14 +266,10 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string
} }
std::string disassembly; std::string disassembly;
error = disassembleBinary(binary, &disassembly); ANGLE_TRY(disassembleBinary(binary, &disassembly));
if (error.isError())
{
return error;
}
(*outDebugInfo) += "\n" + disassembly + "\n// ASSEMBLY END\n"; (*outDebugInfo) += "\n" + disassembly + "\n// ASSEMBLY END\n";
#endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED #endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
if (result == E_OUTOFMEMORY) if (result == E_OUTOFMEMORY)
...@@ -297,16 +289,12 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string ...@@ -297,16 +289,12 @@ gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string
// None of the configurations succeeded in compiling this shader but the compiler is still intact // None of the configurations succeeded in compiling this shader but the compiler is still intact
*outCompiledBlob = nullptr; *outCompiledBlob = nullptr;
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *disassemblyOut) gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *disassemblyOut)
{ {
gl::Error error = initialize(); ANGLE_TRY(ensureInitialized());
if (error.isError())
{
return error;
}
// Retrieve disassembly // Retrieve disassembly
UINT flags = D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS | D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING; UINT flags = D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS | D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING;
...@@ -327,7 +315,7 @@ gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *d ...@@ -327,7 +315,7 @@ gl::Error HLSLCompiler::disassembleBinary(ID3DBlob *shaderBinary, std::string *d
SafeRelease(disassembly); SafeRelease(disassembly);
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
} // namespace rx } // namespace rx
...@@ -49,9 +49,9 @@ class HLSLCompiler : angle::NonCopyable ...@@ -49,9 +49,9 @@ class HLSLCompiler : angle::NonCopyable
ID3DBlob **outCompiledBlob, std::string *outDebugInfo); ID3DBlob **outCompiledBlob, std::string *outDebugInfo);
gl::Error disassembleBinary(ID3DBlob *shaderBinary, std::string *disassemblyOut); gl::Error disassembleBinary(ID3DBlob *shaderBinary, std::string *disassemblyOut);
gl::Error ensureInitialized();
private: private:
gl::Error initialize();
bool mInitialized; bool mInitialized;
HMODULE mD3DCompilerModule; HMODULE mD3DCompilerModule;
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include "libANGLE/renderer/d3d/VaryingPacking.h" #include "libANGLE/renderer/d3d/VaryingPacking.h"
#include "libANGLE/renderer/d3d/VertexDataManager.h" #include "libANGLE/renderer/d3d/VertexDataManager.h"
using namespace angle;
namespace rx namespace rx
{ {
...@@ -1147,14 +1149,10 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vector<GLenum ...@@ -1147,14 +1149,10 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vector<GLenum
gl::InfoLog tempInfoLog; gl::InfoLog tempInfoLog;
gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog; gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog;
gl::Error error = mRenderer->compileToExecutable( ANGLE_TRY(mRenderer->compileToExecutable(
*currentInfoLog, finalPixelHLSL, SHADER_PIXEL, mStreamOutVaryings, *currentInfoLog, finalPixelHLSL, SHADER_PIXEL, mStreamOutVaryings,
(mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mPixelWorkarounds, (mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mPixelWorkarounds,
&pixelExecutable); &pixelExecutable));
if (error.isError())
{
return error;
}
if (pixelExecutable) if (pixelExecutable)
{ {
...@@ -1168,7 +1166,7 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vector<GLenum ...@@ -1168,7 +1166,7 @@ gl::Error ProgramD3D::getPixelExecutableForOutputLayout(const std::vector<GLenum
} }
*outExectuable = pixelExecutable; *outExectuable = pixelExecutable;
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout, gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout,
...@@ -1196,14 +1194,10 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i ...@@ -1196,14 +1194,10 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i
gl::InfoLog tempInfoLog; gl::InfoLog tempInfoLog;
gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog; gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog;
gl::Error error = mRenderer->compileToExecutable( ANGLE_TRY(mRenderer->compileToExecutable(
*currentInfoLog, finalVertexHLSL, SHADER_VERTEX, mStreamOutVaryings, *currentInfoLog, finalVertexHLSL, SHADER_VERTEX, mStreamOutVaryings,
(mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mVertexWorkarounds, (mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mVertexWorkarounds,
&vertexExecutable); &vertexExecutable));
if (error.isError())
{
return error;
}
if (vertexExecutable) if (vertexExecutable)
{ {
...@@ -1218,7 +1212,7 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i ...@@ -1218,7 +1212,7 @@ gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &i
} }
*outExectuable = vertexExecutable; *outExectuable = vertexExecutable;
return gl::Error(GL_NO_ERROR); return gl::NoError();
} }
gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::ContextState &data, gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::ContextState &data,
...@@ -1274,25 +1268,115 @@ gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::ContextSta ...@@ -1274,25 +1268,115 @@ gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::ContextSta
return error; return error;
} }
LinkResult ProgramD3D::compileProgramExecutables(const gl::ContextState &data, gl::InfoLog &infoLog) class ProgramD3D::GetExecutableTask : public Closure
{ {
const gl::InputLayout &defaultInputLayout = public:
GetDefaultInputLayoutFromShader(mState.getAttachedVertexShader()); GetExecutableTask(ProgramD3D *program)
ShaderExecutableD3D *defaultVertexExecutable = nullptr; : mProgram(program), mError(GL_NO_ERROR), mInfoLog(), mResult(nullptr)
ANGLE_TRY( {
getVertexExecutableForInputLayout(defaultInputLayout, &defaultVertexExecutable, &infoLog)); }
virtual gl::Error run() = 0;
std::vector<GLenum> defaultPixelOutput = GetDefaultOutputLayoutFromShader(getPixelShaderKey()); void operator()() override { mError = run(); }
ShaderExecutableD3D *defaultPixelExecutable = nullptr;
ANGLE_TRY(
getPixelExecutableForOutputLayout(defaultPixelOutput, &defaultPixelExecutable, &infoLog));
// Auto-generate the geometry shader here, if we expect to be using point rendering in D3D11. const gl::Error &getError() const { return mError; }
ShaderExecutableD3D *pointGS = nullptr; const gl::InfoLog &getInfoLog() const { return mInfoLog; }
if (usesGeometryShader(GL_POINTS)) ShaderExecutableD3D *getResult() { return mResult; }
protected:
ProgramD3D *mProgram;
gl::Error mError;
gl::InfoLog mInfoLog;
ShaderExecutableD3D *mResult;
};
class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetVertexExecutableTask(ProgramD3D *program) : GetExecutableTask(program) {}
gl::Error run() override
{ {
getGeometryExecutableForPrimitiveType(data, GL_POINTS, &pointGS, &infoLog); const auto &defaultInputLayout =
GetDefaultInputLayoutFromShader(mProgram->mState.getAttachedVertexShader());
ANGLE_TRY(
mProgram->getVertexExecutableForInputLayout(defaultInputLayout, &mResult, &mInfoLog));
return gl::NoError();
}
};
class ProgramD3D::GetPixelExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetPixelExecutableTask(ProgramD3D *program) : GetExecutableTask(program) {}
gl::Error run() override
{
const auto &defaultPixelOutput =
GetDefaultOutputLayoutFromShader(mProgram->getPixelShaderKey());
ANGLE_TRY(
mProgram->getPixelExecutableForOutputLayout(defaultPixelOutput, &mResult, &mInfoLog));
return gl::NoError();
} }
};
class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTask
{
public:
GetGeometryExecutableTask(ProgramD3D *program, const gl::ContextState &contextState)
: GetExecutableTask(program), mContextState(contextState)
{
}
gl::Error run() override
{
// Auto-generate the geometry shader here, if we expect to be using point rendering in
// D3D11.
if (mProgram->usesGeometryShader(GL_POINTS))
{
ANGLE_TRY(mProgram->getGeometryExecutableForPrimitiveType(mContextState, GL_POINTS,
&mResult, &mInfoLog));
}
return gl::NoError();
}
private:
const gl::ContextState &mContextState;
};
LinkResult ProgramD3D::compileProgramExecutables(const gl::ContextState &contextState,
gl::InfoLog &infoLog)
{
// Ensure the compiler is initialized to avoid race conditions.
ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized());
WorkerThreadPool *workerPool = mRenderer->getWorkerThreadPool();
GetVertexExecutableTask vertexTask(this);
GetPixelExecutableTask pixelTask(this);
GetGeometryExecutableTask geometryTask(this, contextState);
std::array<WaitableEvent, 3> waitEvents = {{workerPool->postWorkerTask(&vertexTask),
workerPool->postWorkerTask(&pixelTask),
workerPool->postWorkerTask(&geometryTask)}};
WaitableEvent::WaitMany(&waitEvents);
infoLog << vertexTask.getInfoLog().str();
infoLog << pixelTask.getInfoLog().str();
infoLog << geometryTask.getInfoLog().str();
ANGLE_TRY(vertexTask.getError());
ANGLE_TRY(pixelTask.getError());
ANGLE_TRY(geometryTask.getError());
ShaderExecutableD3D *defaultVertexExecutable = vertexTask.getResult();
ShaderExecutableD3D *defaultPixelExecutable = pixelTask.getResult();
ShaderExecutableD3D *pointGS = geometryTask.getResult();
const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(mState.getAttachedVertexShader()); const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(mState.getAttachedVertexShader());
...@@ -1528,11 +1612,7 @@ gl::Error ProgramD3D::applyUniforms(GLenum drawMode) ...@@ -1528,11 +1612,7 @@ gl::Error ProgramD3D::applyUniforms(GLenum drawMode)
{ {
ASSERT(!mDirtySamplerMapping); ASSERT(!mDirtySamplerMapping);
gl::Error error = mRenderer->applyUniforms(*this, drawMode, mD3DUniforms); ANGLE_TRY(mRenderer->applyUniforms(*this, drawMode, mD3DUniforms));
if (error.isError())
{
return error;
}
for (D3DUniform *d3dUniform : mD3DUniforms) for (D3DUniform *d3dUniform : mD3DUniforms)
{ {
...@@ -2173,7 +2253,7 @@ void ProgramD3D::updateCachedInputLayout(const gl::State &state) ...@@ -2173,7 +2253,7 @@ void ProgramD3D::updateCachedInputLayout(const gl::State &state)
mCachedInputLayout.clear(); mCachedInputLayout.clear();
const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes(); const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes();
for (unsigned int locationIndex : angle::IterateBitSet(mState.getActiveAttribLocationsMask())) for (unsigned int locationIndex : IterateBitSet(mState.getActiveAttribLocationsMask()))
{ {
int d3dSemantic = mAttribLocationToD3DSemantic[locationIndex]; int d3dSemantic = mAttribLocationToD3DSemantic[locationIndex];
......
...@@ -248,6 +248,12 @@ class ProgramD3D : public ProgramImpl ...@@ -248,6 +248,12 @@ class ProgramD3D : public ProgramImpl
bool isSamplerMappingDirty() { return mDirtySamplerMapping; } bool isSamplerMappingDirty() { return mDirtySamplerMapping; }
private: private:
// These forward-declared tasks are used for multi-thread shader compiles.
class GetExecutableTask;
class GetVertexExecutableTask;
class GetPixelExecutableTask;
class GetGeometryExecutableTask;
class VertexExecutable class VertexExecutable
{ {
public: public:
......
...@@ -35,7 +35,8 @@ RendererD3D::RendererD3D(egl::Display *display) ...@@ -35,7 +35,8 @@ RendererD3D::RendererD3D(egl::Display *display)
mCapsInitialized(false), mCapsInitialized(false),
mWorkaroundsInitialized(false), mWorkaroundsInitialized(false),
mDisjoint(false), mDisjoint(false),
mDeviceLost(false) mDeviceLost(false),
mWorkerThreadPool(4)
{ {
} }
...@@ -369,4 +370,9 @@ const gl::Limitations &RendererD3D::getNativeLimitations() const ...@@ -369,4 +370,9 @@ const gl::Limitations &RendererD3D::getNativeLimitations() const
return mNativeLimitations; return mNativeLimitations;
} }
angle::WorkerThreadPool *RendererD3D::getWorkerThreadPool()
{
return &mWorkerThreadPool;
}
} // namespace rx } // namespace rx
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "libANGLE/renderer/d3d/formatutilsD3D.h" #include "libANGLE/renderer/d3d/formatutilsD3D.h"
#include "libANGLE/renderer/d3d/WorkaroundsD3D.h" #include "libANGLE/renderer/d3d/WorkaroundsD3D.h"
#include "libANGLE/Version.h" #include "libANGLE/Version.h"
#include "libANGLE/WorkerThread.h"
//FIXME(jmadill): std::array is currently prohibited by Chromium style guide //FIXME(jmadill): std::array is currently prohibited by Chromium style guide
#include <array> #include <array>
...@@ -200,6 +201,8 @@ class RendererD3D : public BufferFactoryD3D ...@@ -200,6 +201,8 @@ class RendererD3D : public BufferFactoryD3D
bool separatedOutputBuffers, bool separatedOutputBuffers,
const D3DCompilerWorkarounds &workarounds, const D3DCompilerWorkarounds &workarounds,
ShaderExecutableD3D **outExectuable) = 0; ShaderExecutableD3D **outExectuable) = 0;
virtual gl::Error ensureHLSLCompilerInitialized() = 0;
virtual UniformStorageD3D *createUniformStorage(size_t storageSize) = 0; virtual UniformStorageD3D *createUniformStorage(size_t storageSize) = 0;
// Image operations // Image operations
...@@ -260,6 +263,8 @@ class RendererD3D : public BufferFactoryD3D ...@@ -260,6 +263,8 @@ class RendererD3D : public BufferFactoryD3D
virtual gl::Version getMaxSupportedESVersion() const = 0; virtual gl::Version getMaxSupportedESVersion() const = 0;
angle::WorkerThreadPool *getWorkerThreadPool();
protected: protected:
virtual bool getLUID(LUID *adapterLuid) const = 0; virtual bool getLUID(LUID *adapterLuid) const = 0;
virtual void generateCaps(gl::Caps *outCaps, virtual void generateCaps(gl::Caps *outCaps,
...@@ -310,6 +315,8 @@ class RendererD3D : public BufferFactoryD3D ...@@ -310,6 +315,8 @@ class RendererD3D : public BufferFactoryD3D
bool mDisjoint; bool mDisjoint;
bool mDeviceLost; bool mDeviceLost;
angle::WorkerThreadPool mWorkerThreadPool;
}; };
} // namespace rx } // namespace rx
......
...@@ -3546,25 +3546,27 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, ...@@ -3546,25 +3546,27 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog,
const D3DCompilerWorkarounds &workarounds, const D3DCompilerWorkarounds &workarounds,
ShaderExecutableD3D **outExectuable) ShaderExecutableD3D **outExectuable)
{ {
const char *profileType = NULL; std::stringstream profileStream;
switch (type) switch (type)
{ {
case SHADER_VERTEX: case SHADER_VERTEX:
profileType = "vs"; profileStream << "vs";
break; break;
case SHADER_PIXEL: case SHADER_PIXEL:
profileType = "ps"; profileStream << "ps";
break; break;
case SHADER_GEOMETRY: case SHADER_GEOMETRY:
profileType = "gs"; profileStream << "gs";
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
} }
std::string profile = FormatString("%s_%d_%d%s", profileType, getMajorShaderModel(), profileStream << "_" << getMajorShaderModel() << "_" << getMinorShaderModel()
getMinorShaderModel(), getShaderModelSuffix().c_str()); << getShaderModelSuffix();
std::string profile = profileStream.str();
UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL2;
...@@ -3630,6 +3632,11 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog, ...@@ -3630,6 +3632,11 @@ gl::Error Renderer11::compileToExecutable(gl::InfoLog &infoLog,
return gl::NoError(); return gl::NoError();
} }
gl::Error Renderer11::ensureHLSLCompilerInitialized()
{
return mCompiler.ensureInitialized();
}
UniformStorageD3D *Renderer11::createUniformStorage(size_t storageSize) UniformStorageD3D *Renderer11::createUniformStorage(size_t storageSize)
{ {
return new UniformStorage11(this, storageSize); return new UniformStorage11(this, storageSize);
......
...@@ -250,6 +250,8 @@ class Renderer11 : public RendererD3D ...@@ -250,6 +250,8 @@ class Renderer11 : public RendererD3D
bool separatedOutputBuffers, bool separatedOutputBuffers,
const D3DCompilerWorkarounds &workarounds, const D3DCompilerWorkarounds &workarounds,
ShaderExecutableD3D **outExectuable) override; ShaderExecutableD3D **outExectuable) override;
gl::Error ensureHLSLCompilerInitialized() override;
UniformStorageD3D *createUniformStorage(size_t storageSize) override; UniformStorageD3D *createUniformStorage(size_t storageSize) override;
// Image operations // Image operations
......
...@@ -2626,22 +2626,26 @@ gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog, ...@@ -2626,22 +2626,26 @@ gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog,
// Transform feedback is not supported in ES2 or D3D9 // Transform feedback is not supported in ES2 or D3D9
ASSERT(streamOutVaryings.empty()); ASSERT(streamOutVaryings.empty());
const char *profileType = NULL; std::stringstream profileStream;
switch (type) switch (type)
{ {
case SHADER_VERTEX: case SHADER_VERTEX:
profileType = "vs"; profileStream << "vs";
break; break;
case SHADER_PIXEL: case SHADER_PIXEL:
profileType = "ps"; profileStream << "ps";
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_INVALID_OPERATION);
} }
unsigned int profileMajorVersion = (getMajorShaderModel() >= 3) ? 3 : 2;
unsigned int profileMinorVersion = 0; profileStream << "_" << ((getMajorShaderModel() >= 3) ? 3 : 2);
std::string profile = FormatString("%s_%u_%u", profileType, profileMajorVersion, profileMinorVersion); profileStream << "_"
<< "0";
std::string profile = profileStream.str();
UINT flags = ANGLE_COMPILE_OPTIMIZATION_LEVEL; UINT flags = ANGLE_COMPILE_OPTIMIZATION_LEVEL;
...@@ -2703,6 +2707,11 @@ gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog, ...@@ -2703,6 +2707,11 @@ gl::Error Renderer9::compileToExecutable(gl::InfoLog &infoLog,
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error Renderer9::ensureHLSLCompilerInitialized()
{
return mCompiler.ensureInitialized();
}
UniformStorageD3D *Renderer9::createUniformStorage(size_t storageSize) UniformStorageD3D *Renderer9::createUniformStorage(size_t storageSize)
{ {
return new UniformStorageD3D(storageSize); return new UniformStorageD3D(storageSize);
......
...@@ -247,6 +247,8 @@ class Renderer9 : public RendererD3D ...@@ -247,6 +247,8 @@ class Renderer9 : public RendererD3D
bool separatedOutputBuffers, bool separatedOutputBuffers,
const D3DCompilerWorkarounds &workarounds, const D3DCompilerWorkarounds &workarounds,
ShaderExecutableD3D **outExectuable) override; ShaderExecutableD3D **outExectuable) override;
gl::Error ensureHLSLCompilerInitialized() override;
UniformStorageD3D *createUniformStorage(size_t storageSize) override; UniformStorageD3D *createUniformStorage(size_t storageSize) override;
// Image operations // Image operations
......
...@@ -151,6 +151,8 @@ ...@@ -151,6 +151,8 @@
'libANGLE/VertexAttribute.cpp', 'libANGLE/VertexAttribute.cpp',
'libANGLE/VertexAttribute.h', 'libANGLE/VertexAttribute.h',
'libANGLE/VertexAttribute.inl', 'libANGLE/VertexAttribute.inl',
'libANGLE/WorkerThread.cpp',
'libANGLE/WorkerThread.h',
'libANGLE/angletypes.cpp', 'libANGLE/angletypes.cpp',
'libANGLE/angletypes.h', 'libANGLE/angletypes.h',
'libANGLE/angletypes.inl', 'libANGLE/angletypes.inl',
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
'<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp', '<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp',
'<(angle_path)/src/libANGLE/Surface_unittest.cpp', '<(angle_path)/src/libANGLE/Surface_unittest.cpp',
'<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp', '<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp',
'<(angle_path)/src/libANGLE/WorkerThread_unittest.cpp',
'<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/FramebufferImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/FramebufferImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/ProgramImpl_mock.h', '<(angle_path)/src/libANGLE/renderer/ProgramImpl_mock.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment