Skip to content

Commit

Permalink
[impeller] OpenGL: Add support for threads and contexts. (flutter#33575)
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored May 24, 2022
1 parent 26816a7 commit f91ccb4
Show file tree
Hide file tree
Showing 18 changed files with 357 additions and 56 deletions.
8 changes: 8 additions & 0 deletions impeller/base/comparable.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,12 @@ struct hash<impeller::UniqueID> {
}
};

template <>
struct less<impeller::UniqueID> {
constexpr bool operator()(const impeller::UniqueID& lhs,
const impeller::UniqueID& rhs) const {
return lhs.id < rhs.id;
}
};

} // namespace std
46 changes: 43 additions & 3 deletions impeller/playground/backend/gles/playground_impl_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@

namespace impeller {

class PlaygroundImplGLES::ReactorWorker final : public ReactorGLES::Worker {
public:
ReactorWorker() = default;

// |ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const override {
ReaderLock lock(mutex_);
auto found = reactions_allowed_.find(std::this_thread::get_id());
if (found == reactions_allowed_.end()) {
return false;
}
return found->second;
}

void SetReactionsAllowedOnCurrentThread(bool allowed) {
WriterLock lock(mutex_);
reactions_allowed_[std::this_thread::get_id()] = allowed;
}

private:
mutable RWMutex mutex_;
std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);

FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
};

void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
if (!handle) {
return;
Expand All @@ -24,7 +51,8 @@ void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
}

PlaygroundImplGLES::PlaygroundImplGLES()
: handle_(nullptr, &DestroyWindowHandle) {
: handle_(nullptr, &DestroyWindowHandle),
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
::glfwDefaultWindowHints();

#if FML_OS_MACOSX
Expand All @@ -48,6 +76,7 @@ PlaygroundImplGLES::PlaygroundImplGLES()
auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr);

::glfwMakeContextCurrent(window);
worker_->SetReactionsAllowedOnCurrentThread(true);

handle_.reset(window);
}
Expand Down Expand Up @@ -79,8 +108,19 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
return nullptr;
}

return ContextGLES::Create(std::move(gl),
ShaderLibraryMappingsForPlayground());
auto context =
ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground());
if (!context) {
FML_LOG(ERROR) << "Could not create context.";
return nullptr;
}

auto worker_id = context->AddReactorWorker(worker_);
if (!worker_id.has_value()) {
FML_LOG(ERROR) << "Could not add reactor worker.";
return nullptr;
}
return context;
}

// |PlaygroundImpl|
Expand Down
3 changes: 3 additions & 0 deletions impeller/playground/backend/gles/playground_impl_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ class PlaygroundImplGLES final : public PlaygroundImpl {
~PlaygroundImplGLES();

private:
class ReactorWorker;

static void DestroyWindowHandle(WindowHandle handle);
using UniqueHandle = std::unique_ptr<void, decltype(&DestroyWindowHandle)>;
UniqueHandle handle_;
std::shared_ptr<ReactorWorker> worker_;

// |PlaygroundImpl|
std::shared_ptr<Context> GetContext() const override;
Expand Down
21 changes: 18 additions & 3 deletions impeller/renderer/backend/gles/context_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

namespace impeller {

std::shared_ptr<Context> ContextGLES::Create(
std::shared_ptr<ContextGLES> ContextGLES::Create(
std::unique_ptr<ProcTableGLES> gl,
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries) {
return std::shared_ptr<Context>(
return std::shared_ptr<ContextGLES>(
new ContextGLES(std::move(gl), std::move(shader_libraries)));
}

Expand Down Expand Up @@ -70,10 +70,25 @@ ContextGLES::ContextGLES(

ContextGLES::~ContextGLES() = default;

const ReactorGLES::Ref ContextGLES::GetReactor() const {
const ReactorGLES::Ref& ContextGLES::GetReactor() const {
return reactor_;
}

std::optional<ReactorGLES::WorkerID> ContextGLES::AddReactorWorker(
std::shared_ptr<ReactorGLES::Worker> worker) {
if (!IsValid()) {
return std::nullopt;
}
return reactor_->AddWorker(std::move(worker));
}

bool ContextGLES::RemoveReactorWorker(ReactorGLES::WorkerID id) {
if (!IsValid()) {
return false;
}
return reactor_->RemoveWorker(id);
}

bool ContextGLES::IsValid() const {
return is_valid_;
}
Expand Down
9 changes: 7 additions & 2 deletions impeller/renderer/backend/gles/context_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ namespace impeller {
class ContextGLES final : public Context,
public BackendCast<ContextGLES, Context> {
public:
static std::shared_ptr<Context> Create(
static std::shared_ptr<ContextGLES> Create(
std::unique_ptr<ProcTableGLES> gl,
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries);

// |Context|
~ContextGLES() override;

const ReactorGLES::Ref GetReactor() const;
const ReactorGLES::Ref& GetReactor() const;

std::optional<ReactorGLES::WorkerID> AddReactorWorker(
std::shared_ptr<ReactorGLES::Worker> worker);

bool RemoveReactorWorker(ReactorGLES::WorkerID id);

private:
ReactorGLES::Ref reactor_;
Expand Down
8 changes: 5 additions & 3 deletions impeller/renderer/backend/gles/proc_table_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ struct AutoErrorCheck {
~AutoErrorCheck() {
if (error_fn) {
auto error = error_fn();
FML_CHECK(error == GL_NO_ERROR)
<< "GL Error " << GLErrorToString(error) << "(" << error << ")"
<< " encountered on call to " << name;
if (error != GL_NO_ERROR) {
FML_LOG(ERROR) << "GL Error " << GLErrorToString(error) << "(" << error
<< ")"
<< " encountered on call to " << name;
}
}
}
};
Expand Down
128 changes: 92 additions & 36 deletions impeller/renderer/backend/gles/reactor_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,20 @@ bool ReactorGLES::IsValid() const {
return is_valid_;
}

ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
Lock lock(workers_mutex_);
auto id = WorkerID{};
workers_[id] = std::move(worker);
return id;
}

bool ReactorGLES::RemoveWorker(WorkerID worker) {
Lock lock(workers_mutex_);
return workers_.erase(worker) == 1;
}

bool ReactorGLES::HasPendingOperations() const {
Lock ops_lock(ops_mutex_);
return !pending_operations_.empty() || !gl_handles_to_collect_.empty();
}

Expand All @@ -37,6 +50,7 @@ const ProcTableGLES& ReactorGLES::GetProcTable() const {
}

std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
ReaderLock handles_lock(handles_mutex_);
auto found = live_gl_handles_.find(handle);
if (found != live_gl_handles_.end()) {
return found->second;
Expand All @@ -48,8 +62,13 @@ bool ReactorGLES::AddOperation(Operation operation) {
if (!operation) {
return false;
}
pending_operations_.emplace_back(std::move(operation));
return React();
{
Lock ops_lock(ops_mutex_);
pending_operations_.emplace_back(std::move(operation));
}
// Attempt a reaction if able but it is not an error if this isn't possible.
[[maybe_unused]] auto result = React();
return true;
}

static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
Expand Down Expand Up @@ -109,24 +128,30 @@ HandleGLES ReactorGLES::CreateHandle(HandleType type) {
if (new_handle.IsDead()) {
return HandleGLES::DeadHandle();
}
WriterLock handles_lock(handles_mutex_);
live_gl_handles_[new_handle] =
in_reaction_ ? CreateGLHandle(GetProcTable(), type) : std::nullopt;
return new_handle;
}

void ReactorGLES::CollectHandle(HandleGLES handle) {
WriterLock handles_lock(handles_mutex_);
auto live_handle = live_gl_handles_.find(handle);
if (live_handle == live_gl_handles_.end()) {
return;
}
if (live_handle->second.has_value()) {
Lock ops_lock(ops_mutex_);
gl_handles_to_collect_[live_handle->first] = live_handle->second.value();
}
live_gl_handles_.erase(live_handle);
}

bool ReactorGLES::React() {
TRACE_EVENT0("impeller", "ReactorGLES::React");
if (!CanReactOnCurrentThread()) {
return false;
}
in_reaction_ = true;
fml::ScopedCleanupClosure reset_in_reaction([&]() { in_reaction_ = false; });
while (HasPendingOperations()) {
Expand Down Expand Up @@ -165,7 +190,13 @@ bool ReactorGLES::ReactOnce() {
//----------------------------------------------------------------------------
/// Collect all the handles for whom there is a GL handle sibling.
///
for (const auto& handle_to_collect : gl_handles_to_collect_) {
decltype(gl_handles_to_collect_) gl_handles_to_collect;
{
Lock ops_lock(ops_mutex_);
std::swap(gl_handles_to_collect_, gl_handles_to_collect);
FML_DCHECK(gl_handles_to_collect_.empty());
}
for (const auto& handle_to_collect : gl_handles_to_collect) {
if (!CollectGLHandle(gl, // proc table
handle_to_collect.first.type, // handle type
handle_to_collect.second // GL handle name
Expand All @@ -174,26 +205,52 @@ bool ReactorGLES::ReactOnce() {
return false;
}
}
gl_handles_to_collect_.clear();

//----------------------------------------------------------------------------
/// Make sure all pending handles have a GL handle sibling.
///
for (auto& live_handle : live_gl_handles_) {
if (live_handle.second.has_value()) {
// Already a realized GL handle.
continue;
}
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
if (!gl_handle.has_value()) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
{
WriterLock handles_lock(handles_mutex_);
for (auto& live_handle : live_gl_handles_) {
if (live_handle.second.has_value()) {
// Already a realized GL handle.
continue;
}
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
if (!gl_handle.has_value()) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
}
live_handle.second = gl_handle;
}
live_handle.second = gl_handle;
}

if (can_set_debug_labels_) {
for (const auto& label : pending_debug_labels_) {
//----------------------------------------------------------------------------
/// Flush all pending operations in order.
///
decltype(pending_operations_) pending_operations;
{
Lock ops_lock(ops_mutex_);
std::swap(pending_operations_, pending_operations);
FML_DCHECK(pending_operations_.empty());
}
for (const auto& operation : pending_operations) {
TRACE_EVENT0("impeller", "ReactorGLES::Operation");
operation(*this);
}

//----------------------------------------------------------------------------
/// Make sure all pending debug labels have been flushed.
///
decltype(pending_debug_labels_) pending_debug_labels;
{
WriterLock handles_lock(handles_mutex_);
std::swap(pending_debug_labels_, pending_debug_labels);
FML_DCHECK(pending_debug_labels_.empty());
}
if (!pending_debug_labels.empty()) {
ReaderLock handles_lock(handles_mutex_);
for (const auto& label : pending_debug_labels) {
auto live_handle = live_gl_handles_.find(label.first);
if (live_handle == live_gl_handles_.end() ||
!live_handle->second.has_value()) {
Expand All @@ -205,16 +262,6 @@ bool ReactorGLES::ReactOnce() {
);
}
}
pending_debug_labels_.clear();

//----------------------------------------------------------------------------
/// Flush all pending operations in order.
///
auto operations = std::move(pending_operations_);
for (const auto& operation : operations) {
operation(*this);
}
pending_operations_.clear();

return true;
}
Expand All @@ -229,18 +276,27 @@ void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
if (handle.IsDead()) {
return;
}
if (in_reaction_) {
if (auto found = live_gl_handles_.find(handle);
found != live_gl_handles_.end() && found->second.has_value()) {
GetProcTable().SetDebugLabel(
ToDebugResourceType(found->first.type), // type
found->second.value(), // name
label // label
);
return;
WriterLock handles_lock(handles_mutex_);
pending_debug_labels_[handle] = std::move(label);
}

bool ReactorGLES::CanReactOnCurrentThread() const {
std::vector<WorkerID> dead_workers;
Lock lock(workers_mutex_);
for (const auto& worker : workers_) {
auto worker_ptr = worker.second.lock();
if (!worker_ptr) {
dead_workers.push_back(worker.first);
continue;
}
if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
return true;
}
}
pending_debug_labels_[handle] = std::move(label);
for (const auto& worker_id : dead_workers) {
workers_.erase(worker_id);
}
return false;
}

} // namespace impeller
Loading

0 comments on commit f91ccb4

Please sign in to comment.