mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Delete GLES framebuffer objects only on the thread where they were created (#179768)
OpenGL does not allow cross-context sharing of container objects (such as framebuffers) that hold references to other objects. ReactorGLES must ensure that framebuffers are deleted using the thread and context where the object was created. Fixes https://github.com/flutter/flutter/issues/178276
This commit is contained in:
parent
795b1d2266
commit
e32ddabf31
@ -28,4 +28,8 @@ std::string HandleTypeToString(HandleType type) {
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool HandleTypeIsShareable(HandleType type) {
|
||||
return type != HandleType::kFrameBuffer;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#include "flutter/fml/hash_combine.h"
|
||||
@ -27,6 +28,9 @@ enum class HandleType {
|
||||
|
||||
std::string HandleTypeToString(HandleType type);
|
||||
|
||||
// Returns whether this type of handle can be shared across OpenGL contexts.
|
||||
bool HandleTypeIsShareable(HandleType type);
|
||||
|
||||
class ReactorGLES;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -80,10 +84,11 @@ class HandleGLES {
|
||||
std::optional<UniqueID> name_;
|
||||
std::size_t hash_;
|
||||
std::optional<uint64_t> untracked_id_;
|
||||
std::optional<std::thread::id> owner_thread_;
|
||||
|
||||
friend class ReactorGLES;
|
||||
|
||||
HandleGLES(HandleType p_type, UniqueID p_name)
|
||||
HandleGLES(HandleType p_type, UniqueID p_name, std::thread::id p_owner_thread)
|
||||
: type_(p_type),
|
||||
name_(p_name),
|
||||
hash_(fml::HashCombine(
|
||||
@ -98,7 +103,11 @@ class HandleGLES {
|
||||
p_name)) {}
|
||||
|
||||
static HandleGLES Create(HandleType type) {
|
||||
return HandleGLES{type, UniqueID{}};
|
||||
HandleGLES handle{type, UniqueID{}};
|
||||
if (!HandleTypeIsShareable(type)) {
|
||||
handle.owner_thread_ = std::this_thread::get_id();
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -296,6 +296,7 @@ bool ReactorGLES::ReactOnce() {
|
||||
bool ReactorGLES::ConsolidateHandles() {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
const auto& gl = GetProcTable();
|
||||
std::thread::id current_thread = std::this_thread::get_id();
|
||||
std::vector<std::tuple<HandleGLES, std::optional<GLStorage>>>
|
||||
handles_to_delete;
|
||||
std::vector<std::tuple<DebugResourceType, GLint, std::string>>
|
||||
@ -307,6 +308,13 @@ bool ReactorGLES::ConsolidateHandles() {
|
||||
for (auto& handle : handles_) {
|
||||
// Collect dead handles.
|
||||
if (handle.second.pending_collection) {
|
||||
// Some objects can only be deleted on a specific thread. Collection
|
||||
// of these objects will be deferred until the owner thead executes
|
||||
// a reaction and triggers collection of handles.
|
||||
const auto& owner_thread = handle.first.owner_thread_;
|
||||
if (owner_thread.has_value() && *owner_thread != current_thread) {
|
||||
continue;
|
||||
}
|
||||
handles_to_delete.emplace_back(
|
||||
std::make_tuple(handle.first, handle.second.name));
|
||||
continue;
|
||||
|
||||
@ -180,5 +180,40 @@ TEST(ReactorGLES, CanDeferOperations) {
|
||||
EXPECT_TRUE(did_run);
|
||||
}
|
||||
|
||||
TEST(ReactorGLES, FramebufferDeletedOnOwnerThread) {
|
||||
auto mock_gles_impl = std::make_unique<MockGLESImpl>();
|
||||
|
||||
EXPECT_CALL(*mock_gles_impl, GenFramebuffers(1, _))
|
||||
.WillOnce([](GLsizei size, GLuint* queries) { queries[0] = 1234; });
|
||||
|
||||
std::shared_ptr<MockGLES> mock_gles =
|
||||
MockGLES::Init(std::move(mock_gles_impl));
|
||||
ProcTableGLES::Resolver resolver = kMockResolverGLES;
|
||||
auto proc_table = std::make_unique<ProcTableGLES>(resolver);
|
||||
auto worker = std::make_shared<TestWorker>();
|
||||
auto reactor = std::make_shared<ReactorGLES>(std::move(proc_table));
|
||||
reactor->AddWorker(worker);
|
||||
|
||||
HandleGLES handle = reactor->CreateHandle(HandleType::kFrameBuffer);
|
||||
|
||||
std::thread::id cleanup_thread;
|
||||
reactor->RegisterCleanupCallback(
|
||||
handle, [&]() { cleanup_thread = std::this_thread::get_id(); });
|
||||
|
||||
reactor->CollectHandle(handle);
|
||||
|
||||
std::thread thread([&] {
|
||||
EXPECT_TRUE(reactor->AddOperation([](const ReactorGLES& reactor) {}));
|
||||
EXPECT_TRUE(reactor->React());
|
||||
});
|
||||
thread.join();
|
||||
|
||||
EXPECT_EQ(cleanup_thread, std::thread::id());
|
||||
|
||||
EXPECT_TRUE(reactor->AddOperation([](const ReactorGLES& reactor) {}));
|
||||
EXPECT_TRUE(reactor->React());
|
||||
EXPECT_EQ(cleanup_thread, std::this_thread::get_id());
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user