blob: dc89501ccceae3151a6feb43901f017e980f6893 [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/texture.h"
#include "flutter/lib/gpu/formats.h"
#include "flutter/lib/ui/painting/image.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "fml/make_copyable.h"
#include "fml/mapping.h"
#include "impeller/core/allocator.h"
#include "impeller/core/buffer_view.h"
#include "impeller/core/device_buffer.h"
#include "impeller/core/formats.h"
#include "impeller/core/texture.h"
#include "impeller/geometry/rect.h"
#include "impeller/renderer/blit_pass.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/command_queue.h"
#include "impeller/renderer/context.h"
#if IMPELLER_SUPPORTS_RENDERING
#include "impeller/display_list/dl_image_impeller.h" // nogncheck
#endif
#include "third_party/tonic/typed_data/dart_byte_data.h"
namespace flutter {
namespace gpu {
IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, Texture);
Texture::Texture(std::shared_ptr<impeller::Texture> texture)
: texture_(std::move(texture)) {}
Texture::~Texture() = default;
std::shared_ptr<impeller::Texture> Texture::GetTexture() {
return texture_;
}
void Texture::SetCoordinateSystem(
impeller::TextureCoordinateSystem coordinate_system) {
texture_->SetCoordinateSystem(coordinate_system);
}
// Returns the size in pixels of the given dimension at `mip_level`, clamped
// at 1, matching standard mip-chain semantics. The Dart-side helper
// `Texture.getMipLevelSizeInBytes` uses the same `max(1, dim >> level)`
// formula in pixel-count form; if either is updated, both must be kept in
// sync.
static int32_t MipDimensionAtLevel(int32_t base_dimension, uint32_t mip_level) {
const int32_t shifted = base_dimension >> mip_level;
return shifted > 0 ? shifted : 1;
}
// Records a blit-pass that copies `source_bytes` into the given mip level and
// slice of `texture` on `context`, then submits the command buffer. Returns
// true if the encode and submit both succeed. The actual GPU upload may
// complete asynchronously after this call returns.
static bool EncodeAndSubmitOverwrite(
impeller::Context& context,
const std::shared_ptr<impeller::Texture>& texture,
const std::shared_ptr<impeller::DeviceBuffer>& staging_buffer,
size_t source_length,
impeller::IRect destination_region,
uint32_t mip_level,
uint32_t slice) {
auto command_buffer = context.CreateCommandBuffer();
if (!command_buffer) {
FML_LOG(ERROR) << "Failed to create command buffer for texture overwrite.";
return false;
}
auto blit_pass = command_buffer->CreateBlitPass();
if (!blit_pass) {
FML_LOG(ERROR) << "Failed to create blit pass for texture overwrite.";
return false;
}
impeller::BufferView buffer_view(staging_buffer,
impeller::Range(0, source_length));
if (!blit_pass->AddCopy(std::move(buffer_view), texture, destination_region,
/*label=*/"Texture.overwrite", mip_level, slice)) {
return false;
}
if (!blit_pass->EncodeCommands()) {
return false;
}
return context.GetCommandQueue()->Submit({std::move(command_buffer)}).ok();
}
bool Texture::Overwrite(Context& gpu_context,
const tonic::DartByteData& source_bytes,
uint32_t mip_level,
uint32_t slice) {
const uint8_t* data = static_cast<const uint8_t*>(source_bytes.data());
const size_t length = source_bytes.length_in_bytes();
auto& impeller_context = gpu_context.GetContext();
auto staging_buffer =
impeller_context.GetResourceAllocator()->CreateBufferWithCopy(data,
length);
if (!staging_buffer) {
FML_LOG(ERROR) << "Failed to allocate staging buffer for texture "
"overwrite.";
return false;
}
// Compute the destination region for the requested mip level. The
// BlitPass::AddCopy validation requires the region to fit within the base
// texture size, and the actual GPU copy uses this rectangle as the
// destination on the chosen mip level. The same `max(1, dim >> level)`
// formula is used by `Texture.getMipLevelSizeInBytes` on the Dart side; if
// either is updated, both must be kept in sync.
const impeller::ISize base_size = texture_->GetSize();
const impeller::IRect destination_region = impeller::IRect::MakeXYWH(
0, 0, MipDimensionAtLevel(base_size.width, mip_level),
MipDimensionAtLevel(base_size.height, mip_level));
// For the GLES backend, command queue submission just flushes the reactor,
// which needs to happen on the raster thread.
if (impeller_context.GetBackendType() ==
impeller::Context::BackendType::kOpenGLES) {
auto dart_state = flutter::UIDartState::Current();
auto& task_runners = dart_state->GetTaskRunners();
auto context_shared = gpu_context.GetContextShared();
task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
[context_shared, texture = texture_, staging_buffer, length,
destination_region, mip_level, slice]() mutable {
if (!EncodeAndSubmitOverwrite(*context_shared, texture,
staging_buffer, length,
destination_region, mip_level, slice)) {
FML_LOG(ERROR) << "Failed to encode texture overwrite blit on the "
"raster thread.";
}
context_shared->DisposeThreadLocalCachedResources();
}));
return true;
}
if (!EncodeAndSubmitOverwrite(impeller_context, texture_, staging_buffer,
length, destination_region, mip_level, slice)) {
return false;
}
impeller_context.DisposeThreadLocalCachedResources();
return true;
}
size_t Texture::GetBytesPerTexel() {
return impeller::BytesPerPixelForPixelFormat(
texture_->GetTextureDescriptor().format);
}
Dart_Handle Texture::AsImage() const {
// DlImageImpeller isn't compiled in builds with Impeller disabled. If
// Impeller is disabled, it's impossible to get here anyhow, so just ifdef it
// out.
#if IMPELLER_SUPPORTS_RENDERING
auto image = flutter::CanvasImage::Create();
auto dl_image = impeller::DlImageImpeller::Make(texture_);
image->set_image(dl_image);
auto wrapped = image->CreateOuterWrapping();
return wrapped;
#else
return Dart_Null();
#endif
}
} // namespace gpu
} // namespace flutter
//----------------------------------------------------------------------------
/// Exports
///
bool InternalFlutterGpu_Texture_Initialize(Dart_Handle wrapper,
flutter::gpu::Context* gpu_context,
int storage_mode,
int format,
int width,
int height,
int sample_count,
int coordinate_system,
int texture_type,
bool enable_render_target_usage,
bool enable_shader_read_usage,
bool enable_shader_write_usage,
int mip_level_count) {
if (mip_level_count < 1) {
return false;
}
impeller::TextureDescriptor desc;
desc.storage_mode = flutter::gpu::ToImpellerStorageMode(storage_mode);
desc.size = {width, height};
desc.format = flutter::gpu::ToImpellerPixelFormat(format);
desc.mip_count = static_cast<size_t>(mip_level_count);
desc.usage = {};
if (enable_render_target_usage) {
desc.usage |= impeller::TextureUsage::kRenderTarget;
}
if (enable_shader_read_usage) {
desc.usage |= impeller::TextureUsage::kShaderRead;
}
if (enable_shader_write_usage) {
desc.usage |= impeller::TextureUsage::kShaderWrite;
}
switch (sample_count) {
case 1:
desc.sample_count = impeller::SampleCount::kCount1;
break;
case 4:
desc.sample_count = impeller::SampleCount::kCount4;
break;
default:
return false;
}
desc.type = static_cast<impeller::TextureType>(texture_type);
if (!impeller::IsMultisampleCapable(desc.type) &&
desc.sample_count != impeller::SampleCount::kCount1) {
return false;
}
auto texture =
gpu_context->GetContext().GetResourceAllocator()->CreateTexture(desc,
true);
if (!texture) {
FML_LOG(ERROR) << "Failed to create texture.";
return false;
}
texture->SetCoordinateSystem(
flutter::gpu::ToImpellerTextureCoordinateSystem(coordinate_system));
auto res = fml::MakeRefCounted<flutter::gpu::Texture>(std::move(texture));
res->AssociateWithDartWrapper(wrapper);
return true;
}
void InternalFlutterGpu_Texture_SetCoordinateSystem(
flutter::gpu::Texture* wrapper,
int coordinate_system) {
return wrapper->SetCoordinateSystem(
flutter::gpu::ToImpellerTextureCoordinateSystem(coordinate_system));
}
bool InternalFlutterGpu_Texture_Overwrite(flutter::gpu::Texture* texture,
flutter::gpu::Context* gpu_context,
Dart_Handle source_byte_data,
int mip_level,
int slice) {
if (mip_level < 0 || slice < 0) {
return false;
}
return texture->Overwrite(*gpu_context, tonic::DartByteData(source_byte_data),
static_cast<uint32_t>(mip_level),
static_cast<uint32_t>(slice));
}
extern int InternalFlutterGpu_Texture_BytesPerTexel(
flutter::gpu::Texture* wrapper) {
return wrapper->GetBytesPerTexel();
}
Dart_Handle InternalFlutterGpu_Texture_AsImage(flutter::gpu::Texture* wrapper) {
return wrapper->AsImage();
}