[Flutter GPU] Fix Android/GLES crashers. (#172588)

Continuation of https://github.com/flutter/flutter/pull/165630.

Resolves https://github.com/flutter/flutter/issues/164278.
Resolves https://github.com/bdero/flutter_scene/issues/35.
Partly fixes: https://github.com/flutter/flutter/issues/160948.

* Fixes OpenGLES crashers.
  * Threadsafe GLES pipeline creation.
  * Threadsafe GLES texture creation.
  * Fix GLES internal float texture formats.
  * Encode GLES passes on the raster thread.
  * Overwrite GLES textures on the raster thread.
* Fixes vulkan memory leak
(https://github.com/flutter/flutter/issues/164278).
This commit is contained in:
Brandon DeRosier 2025-07-28 14:16:15 -07:00 committed by GitHub
parent 24fc562d82
commit 831a7a179d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 190 additions and 85 deletions

View File

@ -46,8 +46,8 @@ std::shared_ptr<DeviceBuffer> Allocator::CreateBuffer(
return OnCreateBuffer(desc);
}
std::shared_ptr<Texture> Allocator::CreateTexture(
const TextureDescriptor& desc) {
std::shared_ptr<Texture> Allocator::CreateTexture(const TextureDescriptor& desc,
bool threadsafe) {
const auto max_size = GetMaxTextureSizeSupported();
if (desc.size.width > max_size.width || desc.size.height > max_size.height) {
VALIDATION_LOG << "Requested texture size " << desc.size
@ -60,10 +60,10 @@ std::shared_ptr<Texture> Allocator::CreateTexture(
<< " exceeds maximum supported for size " << desc.size;
TextureDescriptor corrected_desc = desc;
corrected_desc.mip_count = desc.size.MipCount();
return OnCreateTexture(corrected_desc);
return OnCreateTexture(corrected_desc, threadsafe);
}
return OnCreateTexture(desc);
return OnCreateTexture(desc, threadsafe);
}
uint16_t Allocator::MinimumBytesPerRow(PixelFormat format) const {

View File

@ -30,7 +30,23 @@ class Allocator {
std::shared_ptr<DeviceBuffer> CreateBuffer(
const DeviceBufferDescriptor& desc);
std::shared_ptr<Texture> CreateTexture(const TextureDescriptor& desc);
//------------------------------------------------------------------------------
/// @brief Creates a new texture.
///
/// @param[in] desc The descriptor of the texture to create.
/// @param[in] threadsafe Whether mutations to this texture should be
/// protected with a threadsafe barrier.
///
/// This parameter only affects the OpenGLES rendering
/// backend.
///
/// If any interaction with this texture (including
/// creation) will be done on a thread other than
/// where the OpenGLES context resides, then
/// `threadsafe`, must be set to `true`.
///
std::shared_ptr<Texture> CreateTexture(const TextureDescriptor& desc,
bool threadsafe = false);
//------------------------------------------------------------------------------
/// @brief Minimum value for `row_bytes` on a Texture. The row
@ -62,7 +78,8 @@ class Allocator {
const DeviceBufferDescriptor& desc) = 0;
virtual std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) = 0;
const TextureDescriptor& desc,
bool threadsafe = false) = 0;
private:
Allocator(const Allocator&) = delete;

View File

@ -206,7 +206,8 @@ class FailingAllocator : public Allocator {
return delegate_->CreateBuffer(desc);
}
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc) {
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
bool threadsafe) {
return delegate_->CreateTexture(desc);
}

View File

@ -39,7 +39,8 @@ class TestAllocator : public Allocator {
};
virtual std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override {
const TextureDescriptor& desc,
bool threadsafe) override {
if (should_fail) {
return nullptr;
}

View File

@ -39,8 +39,9 @@ std::shared_ptr<DeviceBuffer> AllocatorGLES::OnCreateBuffer(
// |Allocator|
std::shared_ptr<Texture> AllocatorGLES::OnCreateTexture(
const TextureDescriptor& desc) {
return std::make_shared<TextureGLES>(reactor_, desc);
const TextureDescriptor& desc,
bool threadsafe) {
return std::make_shared<TextureGLES>(reactor_, desc, threadsafe);
}
// |Allocator|

View File

@ -31,8 +31,8 @@ class AllocatorGLES final : public Allocator {
const DeviceBufferDescriptor& desc) override;
// |Allocator|
std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override;
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
bool threadsafe) override;
// |Allocator|
ISize GetMaxTextureSizeSupported() const override;

View File

@ -185,7 +185,8 @@ std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
const std::weak_ptr<PipelineLibrary>& weak_library,
const PipelineDescriptor& desc,
const std::shared_ptr<const ShaderFunction>& vert_function,
const std::shared_ptr<const ShaderFunction>& frag_function) {
const std::shared_ptr<const ShaderFunction>& frag_function,
bool threadsafe) {
auto strong_library = weak_library.lock();
if (!strong_library) {
@ -209,14 +210,22 @@ std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
const auto has_cached_program = !!cached_program;
auto pipeline = std::shared_ptr<PipelineGLES>(new PipelineGLES(
reactor, //
weak_library, //
desc, //
has_cached_program
? std::move(cached_program)
: std::make_shared<UniqueHandleGLES>(UniqueHandleGLES::MakeUntracked(
reactor, HandleType::kProgram))));
std::shared_ptr<UniqueHandleGLES> program_handle = nullptr;
if (has_cached_program) {
program_handle = std::move(cached_program);
} else {
program_handle = threadsafe ? std::make_shared<UniqueHandleGLES>(
reactor, HandleType::kProgram)
: std::make_shared<UniqueHandleGLES>(
UniqueHandleGLES::MakeUntracked(
reactor, HandleType::kProgram));
}
auto pipeline = std::shared_ptr<PipelineGLES>(
new PipelineGLES(reactor, //
weak_library, //
desc, //
std::move(program_handle)));
auto program = reactor->GetGLHandle(pipeline->GetProgramHandle());
@ -258,7 +267,8 @@ std::shared_ptr<PipelineGLES> PipelineLibraryGLES::CreatePipeline(
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
PipelineDescriptor descriptor,
bool async) {
bool async,
bool threadsafe) {
if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
return found->second;
}
@ -290,10 +300,11 @@ PipelineFuture<PipelineDescriptor> PipelineLibraryGLES::GetPipeline(
weak_this = weak_from_this(), //
descriptor, //
vert_function, //
frag_function //
frag_function, //
threadsafe //
](const ReactorGLES& reactor) {
promise->set_value(
CreatePipeline(weak_this, descriptor, vert_function, frag_function));
promise->set_value(CreatePipeline(weak_this, descriptor, vert_function,
frag_function, threadsafe));
});
FML_CHECK(result);

View File

@ -99,7 +99,8 @@ class PipelineLibraryGLES final
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> GetPipeline(PipelineDescriptor descriptor,
bool async) override;
bool async,
bool threadsafe) override;
// |PipelineLibrary|
PipelineFuture<ComputePipelineDescriptor> GetPipeline(
@ -119,7 +120,8 @@ class PipelineLibraryGLES final
const std::weak_ptr<PipelineLibrary>& weak_library,
const PipelineDescriptor& desc,
const std::shared_ptr<const ShaderFunction>& vert_shader,
const std::shared_ptr<const ShaderFunction>& frag_shader);
const std::shared_ptr<const ShaderFunction>& frag_shader,
bool threadsafe);
std::shared_ptr<UniqueHandleGLES> GetProgramForKey(const ProgramKey& key);

View File

@ -88,12 +88,12 @@ struct TexImage2DData {
type = GL_UNSIGNED_BYTE;
break;
case PixelFormat::kR32G32B32A32Float:
internal_format = GL_RGBA;
internal_format = GL_RGBA32F;
external_format = GL_RGBA;
type = GL_FLOAT;
break;
case PixelFormat::kR16G16B16A16Float:
internal_format = GL_RGBA;
internal_format = GL_RGBA16F;
external_format = GL_RGBA;
type = GL_HALF_FLOAT;
break;
@ -149,7 +149,7 @@ std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(
TextureDescriptor desc,
GLuint fbo) {
auto texture = std::shared_ptr<TextureGLES>(
new TextureGLES(std::move(reactor), desc, fbo, std::nullopt));
new TextureGLES(std::move(reactor), desc, false, fbo, std::nullopt));
if (!texture->IsValid()) {
return nullptr;
}
@ -168,8 +168,8 @@ std::shared_ptr<TextureGLES> TextureGLES::WrapTexture(
VALIDATION_LOG << "Cannot wrap a non-texture handle.";
return nullptr;
}
auto texture = std::shared_ptr<TextureGLES>(
new TextureGLES(std::move(reactor), desc, std::nullopt, external_handle));
auto texture = std::shared_ptr<TextureGLES>(new TextureGLES(
std::move(reactor), desc, false, std::nullopt, external_handle));
if (!texture->IsValid()) {
return nullptr;
}
@ -183,15 +183,18 @@ std::shared_ptr<TextureGLES> TextureGLES::CreatePlaceholder(
}
TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc)
TextureDescriptor desc,
bool threadsafe)
: TextureGLES(std::move(reactor), //
desc, //
threadsafe, //
std::nullopt, //
std::nullopt //
) {}
TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool threadsafe,
std::optional<GLuint> fbo,
std::optional<HandleGLES> external_handle)
: Texture(desc),
@ -201,7 +204,9 @@ TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
reactor_->GetProcTable().GetCapabilities())),
handle_(external_handle.has_value()
? external_handle.value()
: reactor_->CreateUntrackedHandle(ToHandleType(type_))),
: (threadsafe ? reactor_->CreateHandle(ToHandleType(type_))
: reactor_->CreateUntrackedHandle(
ToHandleType(type_)))),
is_wrapped_(fbo.has_value() || external_handle.has_value()),
wrapped_fbo_(fbo) {
// Ensure the texture descriptor itself is valid.

View File

@ -75,7 +75,9 @@ class TextureGLES final : public Texture,
std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc);
TextureGLES(std::shared_ptr<ReactorGLES> reactor, TextureDescriptor desc);
TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool threadsafe = false);
// |Texture|
~TextureGLES() override;
@ -164,6 +166,7 @@ class TextureGLES final : public Texture,
TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool threadsafe,
std::optional<GLuint> fbo,
std::optional<HandleGLES> external_handle);

View File

@ -73,8 +73,8 @@ class AllocatorMTL final : public Allocator {
const DeviceBufferDescriptor& desc) override;
// |Allocator|
std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override;
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
bool threadsafe) override;
// |Allocator|
uint16_t MinimumBytesPerRow(PixelFormat format) const override;

View File

@ -159,7 +159,8 @@ std::shared_ptr<DeviceBuffer> AllocatorMTL::OnCreateBuffer(
}
std::shared_ptr<Texture> AllocatorMTL::OnCreateTexture(
const TextureDescriptor& desc) {
const TextureDescriptor& desc,
bool threadsafe) {
if (!IsValid()) {
return nullptr;
}

View File

@ -34,7 +34,8 @@ class PipelineLibraryMTL final : public PipelineLibrary {
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> GetPipeline(PipelineDescriptor descriptor,
bool async) override;
bool async,
bool threadsafe) override;
// |PipelineLibrary|
PipelineFuture<ComputePipelineDescriptor> GetPipeline(

View File

@ -113,7 +113,8 @@ bool PipelineLibraryMTL::IsValid() const {
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> PipelineLibraryMTL::GetPipeline(
PipelineDescriptor descriptor,
bool async) {
bool async,
bool threadsafe) {
if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
return found->second;
}

View File

@ -468,7 +468,8 @@ class AllocatedTextureSourceVK final : public TextureSourceVK {
// |Allocator|
std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
const TextureDescriptor& desc) {
const TextureDescriptor& desc,
bool threadsafe) {
if (!IsValid()) {
return nullptr;
}

View File

@ -68,8 +68,8 @@ class AllocatorVK final : public Allocator {
const DeviceBufferDescriptor& desc) override;
// |Allocator|
std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override;
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
bool threadsafe) override;
// |Allocator|
ISize GetMaxTextureSizeSupported() const override;

View File

@ -152,7 +152,8 @@ std::unique_ptr<ComputePipelineVK> PipelineLibraryVK::CreateComputePipeline(
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
PipelineDescriptor descriptor,
bool async) {
bool async,
bool threadsafe) {
Lock lock(pipelines_mutex_);
if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
return found->second;

View File

@ -59,8 +59,10 @@ class PipelineLibraryVK final
bool IsValid() const override;
// |PipelineLibrary|
PipelineFuture<PipelineDescriptor> GetPipeline(PipelineDescriptor descriptor,
bool async) override;
PipelineFuture<PipelineDescriptor> GetPipeline(
PipelineDescriptor descriptor,
bool async,
bool threadsafe = false) override;
// |PipelineLibrary|
PipelineFuture<ComputePipelineDescriptor> GetPipeline(

View File

@ -41,9 +41,29 @@ class PipelineLibrary : public std::enable_shared_from_this<PipelineLibrary> {
virtual bool IsValid() const = 0;
//------------------------------------------------------------------------------
/// @brief Creates a pipeline.
///
/// @param[in] descriptor The descriptor of the texture to create.
/// @param[in] async Whether to allow pipeline creation to be deferred.
/// If `false`, pipeline creation will block on the
/// current thread.
///
/// @param[in] threadsafe Whether mutations to this texture should be
/// protected with a threadsafe barrier.
///
/// This parameter only affects the OpenGLES rendering
/// backend.
///
/// If any interaction with this texture (including
/// creation) will be done on a thread other than
/// where the OpenGLES context resides, then
/// `threadsafe`, must be set to `true`.
///
virtual PipelineFuture<PipelineDescriptor> GetPipeline(
PipelineDescriptor descriptor,
bool async = true) = 0;
bool async = true,
bool threadsafe = false) = 0;
virtual PipelineFuture<ComputePipelineDescriptor> GetPipeline(
ComputePipelineDescriptor descriptor,

View File

@ -53,7 +53,7 @@ class MockAllocator : public Allocator {
(override));
MOCK_METHOD(std::shared_ptr<Texture>,
OnCreateTexture,
(const TextureDescriptor& desc),
(const TextureDescriptor& desc, bool threadsafe),
(override));
};

View File

@ -39,19 +39,20 @@ bool CommandBuffer::Submit() {
bool CommandBuffer::Submit(
const impeller::CommandBuffer::CompletionCallback& completion_callback) {
for (auto& encodable : encodables_) {
encodable->EncodeCommands();
}
// For the GLES backend, command queue submission just flushes the reactor,
// which needs to happen on the raster thread.
if (context_->GetBackendType() == impeller::Context::BackendType::kOpenGLES) {
auto dart_state = flutter::UIDartState::Current();
auto& task_runners = dart_state->GetTaskRunners();
task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
[context = context_, command_buffer = command_buffer_,
completion_callback = completion_callback]() mutable {
task_runners.GetRasterTaskRunner()->PostTask(
fml::MakeCopyable([context = context_, command_buffer = command_buffer_,
completion_callback = completion_callback,
encodables = encodables_]() mutable {
for (auto& encodable : encodables) {
encodable->EncodeCommands();
}
context->GetCommandQueue()
->Submit({command_buffer}, completion_callback)
.ok();
@ -59,6 +60,10 @@ bool CommandBuffer::Submit(
return true;
}
for (auto& encodable : encodables_) {
encodable->EncodeCommands();
}
return context_->GetCommandQueue()
->Submit({command_buffer_}, completion_callback)
.ok();
@ -75,8 +80,8 @@ bool InternalFlutterGpu_CommandBuffer_Initialize(
Dart_Handle wrapper,
flutter::gpu::Context* contextWrapper) {
auto res = fml::MakeRefCounted<flutter::gpu::CommandBuffer>(
contextWrapper->GetContext(),
contextWrapper->GetContext()->CreateCommandBuffer());
contextWrapper->GetContextShared(),
contextWrapper->GetContext().CreateCommandBuffer());
res->AssociateWithDartWrapper(wrapper);
return true;

View File

@ -74,7 +74,11 @@ Context::Context(std::shared_ptr<impeller::Context> context)
Context::~Context() = default;
std::shared_ptr<impeller::Context> Context::GetContext() {
impeller::Context& Context::GetContext() {
return *context_;
}
std::shared_ptr<impeller::Context>& Context::GetContextShared() {
return context_;
}
@ -101,29 +105,27 @@ Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) {
extern int InternalFlutterGpu_Context_GetDefaultColorFormat(
flutter::gpu::Context* wrapper) {
return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
wrapper->GetContext()->GetCapabilities()->GetDefaultColorFormat()));
wrapper->GetContext().GetCapabilities()->GetDefaultColorFormat()));
}
extern int InternalFlutterGpu_Context_GetDefaultStencilFormat(
flutter::gpu::Context* wrapper) {
return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
wrapper->GetContext()->GetCapabilities()->GetDefaultStencilFormat()));
wrapper->GetContext().GetCapabilities()->GetDefaultStencilFormat()));
}
extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat(
flutter::gpu::Context* wrapper) {
return static_cast<int>(flutter::gpu::FromImpellerPixelFormat(
wrapper->GetContext()
->GetCapabilities()
->GetDefaultDepthStencilFormat()));
wrapper->GetContext().GetCapabilities()->GetDefaultDepthStencilFormat()));
}
extern int InternalFlutterGpu_Context_GetMinimumUniformByteAlignment(
flutter::gpu::Context* wrapper) {
return wrapper->GetContext()->GetCapabilities()->GetMinimumUniformAlignment();
return wrapper->GetContext().GetCapabilities()->GetMinimumUniformAlignment();
}
extern bool InternalFlutterGpu_Context_GetSupportsOffscreenMSAA(
flutter::gpu::Context* wrapper) {
return flutter::gpu::SupportsNormalOffscreenMSAA(*wrapper->GetContext());
return flutter::gpu::SupportsNormalOffscreenMSAA(wrapper->GetContext());
}

View File

@ -30,7 +30,9 @@ class Context : public RefCountedDartWrappable<Context> {
explicit Context(std::shared_ptr<impeller::Context> context);
~Context() override;
std::shared_ptr<impeller::Context> GetContext();
impeller::Context& GetContext();
std::shared_ptr<impeller::Context>& GetContextShared();
private:
/// An Impeller context that takes precedent over the IO state context when

View File

@ -56,7 +56,7 @@ bool InternalFlutterGpu_DeviceBuffer_Initialize(
desc.storage_mode = flutter::gpu::ToImpellerStorageMode(storage_mode);
desc.size = size_in_bytes;
auto device_buffer =
gpu_context->GetContext()->GetResourceAllocator()->CreateBuffer(desc);
gpu_context->GetContext().GetResourceAllocator()->CreateBuffer(desc);
if (!device_buffer) {
FML_LOG(ERROR) << "Failed to create device buffer.";
return false;
@ -82,7 +82,7 @@ bool InternalFlutterGpu_DeviceBuffer_InitializeWithHostData(
auto mapping = fml::NonOwnedMapping(reinterpret_cast<uint8_t*>(data.data()),
data.length_in_bytes());
device_buffer =
gpu_context->GetContext()->GetResourceAllocator()->CreateBufferWithCopy(
gpu_context->GetContext().GetResourceAllocator()->CreateBufferWithCopy(
mapping);
}
if (!device_buffer) {

View File

@ -24,7 +24,8 @@ base class Texture extends NativeFieldWrapperClass1 {
this.enableRenderTargetUsage,
this.enableShaderReadUsage,
this.enableShaderWriteUsage,
) : _coordinateSystem = coordinateSystem {
) : _gpuContext = gpuContext,
_coordinateSystem = coordinateSystem {
if (sampleCount != 1 && sampleCount != 4) {
throw Exception("Only a sample count of 1 or 4 is currently supported");
}
@ -42,6 +43,8 @@ base class Texture extends NativeFieldWrapperClass1 {
);
}
GpuContext _gpuContext;
final StorageMode storageMode;
final PixelFormat format;
final int width;
@ -99,7 +102,7 @@ base class Texture extends NativeFieldWrapperClass1 {
'The length of sourceBytes (bytes: ${sourceBytes.lengthInBytes}) must exactly match the size of the base mip level (bytes: ${baseMipSize})',
);
}
bool success = _overwrite(sourceBytes);
bool success = _overwrite(_gpuContext, sourceBytes);
if (!success) {
throw Exception("Texture overwrite failed");
}
@ -153,10 +156,10 @@ base class Texture extends NativeFieldWrapperClass1 {
)
external int _bytesPerTexel();
@Native<Bool Function(Pointer<Void>, Handle)>(
@Native<Bool Function(Pointer<Void>, Pointer<Void>, Handle)>(
symbol: 'InternalFlutterGpu_Texture_Overwrite',
)
external bool _overwrite(ByteData bytes);
external bool _overwrite(GpuContext gpuContext, ByteData bytes);
@Native<Handle Function(Pointer<Void>)>(
symbol: 'InternalFlutterGpu_Texture_AsImage',

View File

@ -166,8 +166,9 @@ RenderPass::GetOrCreatePipeline() {
dart_state->GetTaskRunners().GetRasterTaskRunner(),
fml::MakeCopyable([promise = std::move(pipeline_promise),
context = GetContext(), pipeline_desc]() mutable {
promise.set_value(
context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get());
promise.set_value(context->GetPipelineLibrary()
->GetPipeline(pipeline_desc, true, true)
.Get());
}));
pipeline = pipeline_future.get();
} else {
@ -269,7 +270,7 @@ Dart_Handle InternalFlutterGpu_RenderPass_SetColorAttachment(
// If the backend doesn't support normal MSAA, gracefully fallback to
// rendering without MSAA.
if (!flutter::gpu::SupportsNormalOffscreenMSAA(*context->GetContext())) {
if (!flutter::gpu::SupportsNormalOffscreenMSAA(context->GetContext())) {
desc.texture = desc.resolve_texture;
desc.resolve_texture = nullptr;
desc.store_action = impeller::StoreAction::kStore;

View File

@ -62,7 +62,7 @@ std::shared_ptr<const impeller::ShaderFunction> Shader::GetFunctionFromLibrary(
}
bool Shader::IsRegistered(Context& context) {
auto& lib = *context.GetContext()->GetShaderLibrary();
auto& lib = *context.GetContext().GetShaderLibrary();
return GetFunctionFromLibrary(lib) != nullptr;
}
@ -71,7 +71,7 @@ bool Shader::RegisterSync(Context& context) {
return true; // Already registered.
}
auto& lib = *context.GetContext()->GetShaderLibrary();
auto& lib = *context.GetContext().GetShaderLibrary();
std::promise<bool> promise;
auto future = promise.get_future();

View File

@ -6,6 +6,7 @@
#include "flutter/lib/gpu/formats.h"
#include "flutter/lib/ui/painting/image.h"
#include "fml/make_copyable.h"
#include "fml/mapping.h"
#include "impeller/core/allocator.h"
#include "impeller/core/formats.h"
@ -32,15 +33,33 @@ void Texture::SetCoordinateSystem(
texture_->SetCoordinateSystem(coordinate_system);
}
bool Texture::Overwrite(const tonic::DartByteData& source_bytes) {
bool Texture::Overwrite(Context& gpu_context,
const tonic::DartByteData& source_bytes) {
const uint8_t* data = static_cast<const uint8_t*>(source_bytes.data());
auto copy = std::vector<uint8_t>(data, data + source_bytes.length_in_bytes());
// Texture::SetContents is a bit funky right now. It takes a shared_ptr of a
// mapping and we're forced to copy here.
auto mapping = std::make_shared<fml::DataMapping>(copy);
// For the GLES backend, command queue submission just flushes the reactor,
// which needs to happen on the raster thread.
if (gpu_context.GetContext().GetBackendType() ==
impeller::Context::BackendType::kOpenGLES) {
auto dart_state = flutter::UIDartState::Current();
auto& task_runners = dart_state->GetTaskRunners();
task_runners.GetRasterTaskRunner()->PostTask(
fml::MakeCopyable([texture = texture_, mapping = mapping]() mutable {
if (!texture->SetContents(mapping)) {
FML_LOG(ERROR) << "Failed to set texture contents.";
}
}));
}
if (!texture_->SetContents(mapping)) {
return false;
}
gpu_context.GetContext().DisposeThreadLocalCachedResources();
return true;
}
@ -109,7 +128,8 @@ bool InternalFlutterGpu_Texture_Initialize(Dart_Handle wrapper,
return false;
}
auto texture =
gpu_context->GetContext()->GetResourceAllocator()->CreateTexture(desc);
gpu_context->GetContext().GetResourceAllocator()->CreateTexture(desc,
true);
if (!texture) {
FML_LOG(ERROR) << "Failed to create texture.";
return false;
@ -132,8 +152,10 @@ void InternalFlutterGpu_Texture_SetCoordinateSystem(
}
bool InternalFlutterGpu_Texture_Overwrite(flutter::gpu::Texture* texture,
flutter::gpu::Context* gpu_context,
Dart_Handle source_byte_data) {
return texture->Overwrite(tonic::DartByteData(source_byte_data));
return texture->Overwrite(*gpu_context,
tonic::DartByteData(source_byte_data));
}
extern int InternalFlutterGpu_Texture_BytesPerTexel(

View File

@ -27,7 +27,7 @@ class Texture : public RefCountedDartWrappable<Texture> {
void SetCoordinateSystem(impeller::TextureCoordinateSystem coordinate_system);
bool Overwrite(const tonic::DartByteData& source_bytes);
bool Overwrite(Context& gpu_context, const tonic::DartByteData& source_bytes);
size_t GetBytesPerTexel();
@ -68,8 +68,10 @@ extern void InternalFlutterGpu_Texture_SetCoordinateSystem(
int coordinate_system);
FLUTTER_GPU_EXPORT
extern bool InternalFlutterGpu_Texture_Overwrite(flutter::gpu::Texture* wrapper,
Dart_Handle source_byte_data);
extern bool InternalFlutterGpu_Texture_Overwrite(
flutter::gpu::Texture* wrapper,
flutter::gpu::Context* gpu_context,
Dart_Handle source_byte_data);
FLUTTER_GPU_EXPORT
extern int InternalFlutterGpu_Texture_BytesPerTexel(

View File

@ -81,8 +81,8 @@ class TestImpellerAllocator : public impeller::Allocator {
return std::make_shared<TestImpellerDeviceBuffer>(desc);
}
std::shared_ptr<Texture> OnCreateTexture(
const TextureDescriptor& desc) override {
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
bool threadsafe) override {
return std::make_shared<TestImpellerTexture>(desc);
}
};