blob: fb55795698241cc746d016868558d2f37c5de278 [file]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/gpu/command_buffer.h"
#include "dart_api.h"
#include "fml/make_copyable.h"
#include "impeller/core/buffer_view.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/render_pass.h"
#include "lib/ui/ui_dart_state.h"
#include "tonic/converter/dart_converter.h"
namespace flutter {
namespace gpu {
IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, CommandBuffer);
CommandBuffer::CommandBuffer(
std::shared_ptr<impeller::Context> context,
std::shared_ptr<impeller::CommandBuffer> command_buffer)
: context_(std::move(context)),
command_buffer_(std::move(command_buffer)) {}
CommandBuffer::~CommandBuffer() = default;
bool CommandBuffer::Encodable::EncodeCommands() const {
if (render_pass) {
return render_pass->EncodeCommands();
}
if (blit_pass) {
return blit_pass->EncodeCommands();
}
return false;
}
std::shared_ptr<impeller::CommandBuffer> CommandBuffer::GetCommandBuffer() {
return command_buffer_;
}
void CommandBuffer::AddRenderPass(
std::shared_ptr<impeller::RenderPass> render_pass) {
Encodable encodable;
encodable.render_pass = std::move(render_pass);
encodables_.push_back(std::move(encodable));
}
std::shared_ptr<impeller::BlitPass> CommandBuffer::GetOrCreateBlitPass() {
if (!encodables_.empty() && encodables_.back().blit_pass) {
return encodables_.back().blit_pass;
}
auto blit_pass = command_buffer_->CreateBlitPass();
if (!blit_pass) {
return nullptr;
}
Encodable encodable;
encodable.blit_pass = blit_pass;
encodables_.push_back(std::move(encodable));
return blit_pass;
}
bool CommandBuffer::CopyBufferToTexture(DeviceBuffer& source,
size_t source_offset,
size_t source_length,
Texture& destination,
impeller::IRect destination_region,
uint32_t mip_level,
uint32_t slice) {
auto blit_pass = GetOrCreateBlitPass();
if (!blit_pass) {
return false;
}
impeller::BufferView source_view(
source.GetBuffer(), impeller::Range(source_offset, source_length));
return blit_pass->AddCopy(
std::move(source_view), destination.GetTexture(), destination_region,
/*label=*/"CommandBuffer.copyBufferToTexture", mip_level, slice);
}
bool CommandBuffer::CopyTextureToBuffer(Texture& source,
impeller::IRect source_region,
DeviceBuffer& destination,
size_t destination_offset) {
auto blit_pass = GetOrCreateBlitPass();
if (!blit_pass) {
return false;
}
return blit_pass->AddCopy(source.GetTexture(), destination.GetBuffer(),
source_region, destination_offset,
"CommandBuffer.copyTextureToBuffer");
}
bool CommandBuffer::CopyTextureToTexture(Texture& source,
Texture& destination,
impeller::IRect source_region,
impeller::IPoint destination_origin) {
auto blit_pass = GetOrCreateBlitPass();
if (!blit_pass) {
return false;
}
return blit_pass->AddCopy(source.GetTexture(), destination.GetTexture(),
source_region, destination_origin,
"CommandBuffer.copyTextureToTexture");
}
bool CommandBuffer::AddCompletionCallback(
impeller::CommandBuffer::CompletionCallback completion_callback) {
if (submitted_) {
return false;
}
if (completion_callback) {
completion_callbacks_.push_back(std::move(completion_callback));
}
return true;
}
bool CommandBuffer::Submit() {
return CommandBuffer::Submit({});
}
bool CommandBuffer::Submit(
const impeller::CommandBuffer::CompletionCallback& completion_callback) {
if (submitted_) {
return false;
}
submitted_ = true;
std::vector<impeller::CommandBuffer::CompletionCallback> callbacks =
std::move(completion_callbacks_);
if (completion_callback) {
callbacks.push_back(completion_callback);
}
impeller::CommandBuffer::CompletionCallback combined_completion_callback;
if (!callbacks.empty()) {
combined_completion_callback =
[callbacks = std::move(callbacks)](
impeller::CommandBuffer::Status status) mutable {
for (auto& callback : callbacks) {
callback(status);
}
};
}
// 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 = combined_completion_callback,
encodables = encodables_]() mutable {
for (auto& encodable : encodables) {
if (!encodable.EncodeCommands()) {
if (completion_callback) {
completion_callback(impeller::CommandBuffer::Status::kError);
}
context->DisposeThreadLocalCachedResources();
return;
}
}
auto status = context->GetCommandQueue()->Submit({command_buffer},
completion_callback);
if (!status.ok() && completion_callback) {
completion_callback(impeller::CommandBuffer::Status::kError);
}
context->DisposeThreadLocalCachedResources();
}));
return true;
}
for (auto& encodable : encodables_) {
if (!encodable.EncodeCommands()) {
return false;
}
}
auto status = context_->GetCommandQueue()->Submit(
{command_buffer_}, combined_completion_callback);
context_->DisposeThreadLocalCachedResources();
if (!status.ok() && combined_completion_callback) {
combined_completion_callback(impeller::CommandBuffer::Status::kError);
}
return status.ok();
}
} // namespace gpu
} // namespace flutter
//----------------------------------------------------------------------------
/// Exports
///
bool InternalFlutterGpu_CommandBuffer_Initialize(
Dart_Handle wrapper,
flutter::gpu::Context* contextWrapper) {
auto res = fml::MakeRefCounted<flutter::gpu::CommandBuffer>(
contextWrapper->GetContextShared(),
contextWrapper->GetContext().CreateCommandBuffer());
res->AssociateWithDartWrapper(wrapper);
return true;
}
Dart_Handle InternalFlutterGpu_CommandBuffer_Submit(
flutter::gpu::CommandBuffer* wrapper,
Dart_Handle completion_callback) {
if (Dart_IsNull(completion_callback)) {
bool success = wrapper->Submit();
if (!success) {
return tonic::ToDart("Failed to submit CommandBuffer");
}
return Dart_Null();
}
if (!Dart_IsClosure(completion_callback)) {
return tonic::ToDart("Completion callback must be a function");
}
auto dart_state = flutter::UIDartState::Current();
auto& task_runners = dart_state->GetTaskRunners();
auto persistent_completion_callback =
std::make_unique<tonic::DartPersistentValue>(dart_state,
completion_callback);
auto ui_task_completion_callback = fml::MakeCopyable(
[callback = std::move(persistent_completion_callback),
task_runners](impeller::CommandBuffer::Status status) mutable {
bool success = status != impeller::CommandBuffer::Status::kError;
auto ui_completion_task = fml::MakeCopyable(
[callback = std::move(callback), success]() mutable {
auto dart_state = callback->dart_state().lock();
if (!dart_state) {
// The root isolate could have died in the meantime.
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::DartInvoke(callback->Get(), {tonic::ToDart(success)});
// callback is associated with the Dart isolate and must be
// deleted on the UI thread.
callback.reset();
});
task_runners.GetUITaskRunner()->PostTask(ui_completion_task);
});
bool success = wrapper->Submit(ui_task_completion_callback);
if (!success) {
return tonic::ToDart("Failed to submit CommandBuffer");
}
return Dart_Null();
}
Dart_Handle InternalFlutterGpu_CommandBuffer_CopyBufferToTexture(
flutter::gpu::CommandBuffer* command_buffer,
flutter::gpu::DeviceBuffer* source,
int source_offset_in_bytes,
int source_length_in_bytes,
flutter::gpu::Texture* destination,
int destination_x,
int destination_y,
int destination_width,
int destination_height,
int mip_level,
int slice) {
if (!command_buffer->CopyBufferToTexture(
*source, static_cast<size_t>(source_offset_in_bytes),
static_cast<size_t>(source_length_in_bytes), *destination,
impeller::IRect::MakeXYWH(destination_x, destination_y,
destination_width, destination_height),
static_cast<uint32_t>(mip_level), static_cast<uint32_t>(slice))) {
return tonic::ToDart("Failed to append copyBufferToTexture");
}
return Dart_Null();
}
Dart_Handle InternalFlutterGpu_CommandBuffer_CopyTextureToBuffer(
flutter::gpu::CommandBuffer* command_buffer,
flutter::gpu::Texture* source,
int source_x,
int source_y,
int source_width,
int source_height,
flutter::gpu::DeviceBuffer* destination,
int destination_offset_in_bytes) {
if (!command_buffer->CopyTextureToBuffer(
*source,
impeller::IRect::MakeXYWH(source_x, source_y, source_width,
source_height),
*destination, static_cast<size_t>(destination_offset_in_bytes))) {
return tonic::ToDart("Failed to append copyTextureToBuffer");
}
return Dart_Null();
}
Dart_Handle InternalFlutterGpu_CommandBuffer_CopyTextureToTexture(
flutter::gpu::CommandBuffer* command_buffer,
flutter::gpu::Texture* source,
flutter::gpu::Texture* destination,
int source_x,
int source_y,
int source_width,
int source_height,
int destination_x,
int destination_y) {
if (!command_buffer->CopyTextureToTexture(
*source, *destination,
impeller::IRect::MakeXYWH(source_x, source_y, source_width,
source_height),
impeller::IPoint(destination_x, destination_y))) {
return tonic::ToDart("Failed to append copyTextureToTexture");
}
return Dart_Null();
}