diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index d08dd961de49f..87f547cfa2323 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -3613,8 +3613,11 @@ ORIGIN: ../../../flutter/lib/gpu/lib/src/buffer.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/lib/src/context.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/lib/src/formats.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/lib/src/smoketest.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/gpu/lib/src/texture.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/smoketest.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/smoketest.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/gpu/texture.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/gpu/texture.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/io/dart_io.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/io/dart_io.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/snapshot/snapshot.h + ../../../flutter/LICENSE @@ -6389,8 +6392,11 @@ FILE: ../../../flutter/lib/gpu/lib/src/buffer.dart FILE: ../../../flutter/lib/gpu/lib/src/context.dart FILE: ../../../flutter/lib/gpu/lib/src/formats.dart FILE: ../../../flutter/lib/gpu/lib/src/smoketest.dart +FILE: ../../../flutter/lib/gpu/lib/src/texture.dart FILE: ../../../flutter/lib/gpu/smoketest.cc FILE: ../../../flutter/lib/gpu/smoketest.h +FILE: ../../../flutter/lib/gpu/texture.cc +FILE: ../../../flutter/lib/gpu/texture.h FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h FILE: ../../../flutter/lib/snapshot/libraries_experimental.json diff --git a/impeller/fixtures/dart_tests.dart b/impeller/fixtures/dart_tests.dart index 01d810e1babe4..2413c1b5546ce 100644 --- a/impeller/fixtures/dart_tests.dart +++ b/impeller/fixtures/dart_tests.dart @@ -78,3 +78,84 @@ void deviceBufferOverwriteThrowsForNegativeDestinationOffset() { } assert(exception!.contains('destinationOffsetInBytes must be positive')); } + +@pragma('vm:entry-point') +void canCreateTexture() { + final gpu.Texture? texture = + gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + assert(texture != null); + + // Check the defaults. + assert( + texture!.coordinateSystem == gpu.TextureCoordinateSystem.renderToTexture); + assert(texture!.width == 100); + assert(texture!.height == 100); + assert(texture!.storageMode == gpu.StorageMode.hostVisible); + assert(texture!.sampleCount == 1); + assert(texture!.format == gpu.PixelFormat.r8g8b8a8UNormInt); + assert(texture!.enableRenderTargetUsage == true); + assert(texture!.enableShaderReadUsage == true); + assert(texture!.enableShaderWriteUsage == false); + assert(texture!.bytesPerTexel == 4); + assert(texture!.GetBaseMipLevelSizeInBytes() == 40000); +} + +@pragma('vm:entry-point') +void canOverwriteTexture() { + final gpu.Texture? texture = + gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 2, 2); + assert(texture != null); + final ui.Color red = ui.Color.fromARGB(0xFF, 0xFF, 0, 0); + final ui.Color green = ui.Color.fromARGB(0xFF, 0, 0xFF, 0); + final bool success = texture!.overwrite( + Int32List.fromList([red.value, green.value, green.value, red.value]) + .buffer + .asByteData()); + assert(success); +} + +@pragma('vm:entry-point') +void textureOverwriteThrowsForWrongBufferSize() { + final gpu.Texture? texture = + gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + assert(texture != null); + final ui.Color red = ui.Color.fromARGB(0xFF, 0xFF, 0, 0); + String? exception; + try { + texture!.overwrite( + Int32List.fromList([red.value, red.value, red.value, red.value]) + .buffer + .asByteData()); + } catch (e) { + exception = e.toString(); + } + assert(exception!.contains( + 'The length of sourceBytes (bytes: 16) must exactly match the size of the base mip level (bytes: 40000)')); +} + +@pragma('vm:entry-point') +void textureAsImageReturnsAValidUIImageHandle() { + final gpu.Texture? texture = + gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + assert(texture != null); + + final ui.Image image = texture!.asImage(); + assert(image.width == 100); + assert(image.height == 100); +} + +@pragma('vm:entry-point') +void textureAsImageThrowsWhenNotShaderReadable() { + final gpu.Texture? texture = gpu.gpuContext.createTexture( + gpu.StorageMode.hostVisible, 100, 100, + enableShaderReadUsage: false); + assert(texture != null); + String? exception; + try { + texture!.asImage(); + } catch (e) { + exception = e.toString(); + } + assert(exception!.contains( + 'Only shader readable Flutter GPU textures can be used as UI Images')); +} diff --git a/impeller/renderer/renderer_dart_unittests.cc b/impeller/renderer/renderer_dart_unittests.cc index 63922a0b06a4c..89faca58540fe 100644 --- a/impeller/renderer/renderer_dart_unittests.cc +++ b/impeller/renderer/renderer_dart_unittests.cc @@ -50,7 +50,7 @@ class RendererDartTest : public PlaygroundTest, flutter::testing::AutoIsolateShutdown* GetIsolate() { // Sneak the context into the Flutter GPU API. assert(GetContext() != nullptr); - flutter::Context::SetOverrideContext(GetContext()); + flutter::gpu::Context::SetOverrideContext(GetContext()); return isolate_.get(); } @@ -128,11 +128,17 @@ TEST_P(RendererDartTest, CanInstantiateFlutterGPUContext) { /// `flutter/impeller/fixtures/dart_tests.dart` DART_TEST_CASE(canEmplaceHostBuffer); -DART_TEST_CASE(canCreateDeviceBuffer); +DART_TEST_CASE(canCreateDeviceBuffer); DART_TEST_CASE(canOverwriteDeviceBuffer); DART_TEST_CASE(deviceBufferOverwriteFailsWhenOutOfBounds); DART_TEST_CASE(deviceBufferOverwriteThrowsForNegativeDestinationOffset); +DART_TEST_CASE(canCreateTexture); +DART_TEST_CASE(canOverwriteTexture); +DART_TEST_CASE(textureOverwriteThrowsForWrongBufferSize); +DART_TEST_CASE(textureAsImageReturnsAValidUIImageHandle); +DART_TEST_CASE(textureAsImageThrowsWhenNotShaderReadable); + } // namespace testing } // namespace impeller diff --git a/lib/gpu/BUILD.gn b/lib/gpu/BUILD.gn index 625a11cc1e3cb..d24d75738458e 100644 --- a/lib/gpu/BUILD.gn +++ b/lib/gpu/BUILD.gn @@ -42,6 +42,8 @@ source_set("gpu") { "host_buffer.h", "smoketest.cc", "smoketest.h", + "texture.cc", + "texture.h", ] } deps = [ diff --git a/lib/gpu/context.cc b/lib/gpu/context.cc index 7539f0cb7300a..25f663a9a4a10 100644 --- a/lib/gpu/context.cc +++ b/lib/gpu/context.cc @@ -12,6 +12,7 @@ #include "tonic/converter/dart_converter.h" namespace flutter { +namespace gpu { IMPLEMENT_WRAPPERTYPEINFO(gpu, Context); @@ -34,6 +35,7 @@ std::shared_ptr Context::GetContext() { return context_; } +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- @@ -44,7 +46,7 @@ Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { auto dart_state = flutter::UIDartState::Current(); std::shared_ptr impeller_context = - flutter::Context::GetDefaultContext(); + flutter::gpu::Context::GetDefaultContext(); if (!impeller_context) { if (!dart_state->IsImpellerEnabled()) { @@ -68,8 +70,27 @@ Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { if (!impeller_context) { return tonic::ToDart("Unable to retrieve the Impeller context."); } - auto res = fml::MakeRefCounted(impeller_context); + auto res = fml::MakeRefCounted(impeller_context); res->AssociateWithDartWrapper(wrapper); return Dart_Null(); } + +/// +extern int InternalFlutterGpu_Context_GetDefaultColorFormat( + flutter::gpu::Context* wrapper) { + return static_cast( + wrapper->GetContext()->GetCapabilities()->GetDefaultColorFormat()); +} + +extern int InternalFlutterGpu_Context_GetDefaultStencilFormat( + flutter::gpu::Context* wrapper) { + return static_cast( + wrapper->GetContext()->GetCapabilities()->GetDefaultStencilFormat()); +} + +extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat( + flutter::gpu::Context* wrapper) { + return static_cast( + wrapper->GetContext()->GetCapabilities()->GetDefaultDepthStencilFormat()); +} diff --git a/lib/gpu/context.h b/lib/gpu/context.h index 15365386ad0aa..13ec0946bb75a 100644 --- a/lib/gpu/context.h +++ b/lib/gpu/context.h @@ -10,6 +10,7 @@ #include "impeller/renderer/context.h" namespace flutter { +namespace gpu { class Context : public RefCountedDartWrappable { DEFINE_WRAPPERTYPEINFO(); @@ -36,6 +37,7 @@ class Context : public RefCountedDartWrappable { FML_DISALLOW_COPY_AND_ASSIGN(Context); }; +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- @@ -48,4 +50,20 @@ FLUTTER_GPU_EXPORT extern Dart_Handle InternalFlutterGpu_Context_InitializeDefault( Dart_Handle wrapper); +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Context_GetBackendType( + flutter::gpu::Context* wrapper); + +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Context_GetDefaultColorFormat( + flutter::gpu::Context* wrapper); + +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Context_GetDefaultStencilFormat( + flutter::gpu::Context* wrapper); + +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat( + flutter::gpu::Context* wrapper); + } // extern "C" diff --git a/lib/gpu/device_buffer.cc b/lib/gpu/device_buffer.cc index cc4f8b4bcb624..cbe07afb6b6d8 100644 --- a/lib/gpu/device_buffer.cc +++ b/lib/gpu/device_buffer.cc @@ -15,6 +15,7 @@ #include "tonic/converter/dart_converter.h" namespace flutter { +namespace gpu { IMPLEMENT_WRAPPERTYPEINFO(gpu, DeviceBuffer); @@ -35,16 +36,18 @@ bool DeviceBuffer::Overwrite(const tonic::DartByteData& source_bytes, return true; } +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- /// Exports /// -bool InternalFlutterGpu_DeviceBuffer_Initialize(Dart_Handle wrapper, - flutter::Context* gpu_context, - int storage_mode, - int size_in_bytes) { +bool InternalFlutterGpu_DeviceBuffer_Initialize( + Dart_Handle wrapper, + flutter::gpu::Context* gpu_context, + int storage_mode, + int size_in_bytes) { impeller::DeviceBufferDescriptor desc; desc.storage_mode = static_cast(storage_mode); desc.size = size_in_bytes; @@ -56,7 +59,7 @@ bool InternalFlutterGpu_DeviceBuffer_Initialize(Dart_Handle wrapper, } auto res = - fml::MakeRefCounted(std::move(device_buffer)); + fml::MakeRefCounted(std::move(device_buffer)); res->AssociateWithDartWrapper(wrapper); return true; @@ -64,7 +67,7 @@ bool InternalFlutterGpu_DeviceBuffer_Initialize(Dart_Handle wrapper, bool InternalFlutterGpu_DeviceBuffer_InitializeWithHostData( Dart_Handle wrapper, - flutter::Context* gpu_context, + flutter::gpu::Context* gpu_context, Dart_Handle byte_data) { auto data = tonic::DartByteData(byte_data); auto mapping = fml::NonOwnedMapping(reinterpret_cast(data.data()), @@ -78,14 +81,14 @@ bool InternalFlutterGpu_DeviceBuffer_InitializeWithHostData( } auto res = - fml::MakeRefCounted(std::move(device_buffer)); + fml::MakeRefCounted(std::move(device_buffer)); res->AssociateWithDartWrapper(wrapper); return true; } bool InternalFlutterGpu_DeviceBuffer_Overwrite( - flutter::DeviceBuffer* device_buffer, + flutter::gpu::DeviceBuffer* device_buffer, Dart_Handle source_byte_data, int destination_offset_in_bytes) { return device_buffer->Overwrite(tonic::DartByteData(source_byte_data), diff --git a/lib/gpu/device_buffer.h b/lib/gpu/device_buffer.h index 932c56a6a246d..df2f73ed82646 100644 --- a/lib/gpu/device_buffer.h +++ b/lib/gpu/device_buffer.h @@ -11,6 +11,7 @@ #include "third_party/tonic/typed_data/dart_byte_data.h" namespace flutter { +namespace gpu { class DeviceBuffer : public RefCountedDartWrappable { DEFINE_WRAPPERTYPEINFO(); @@ -30,6 +31,7 @@ class DeviceBuffer : public RefCountedDartWrappable { FML_DISALLOW_COPY_AND_ASSIGN(DeviceBuffer); }; +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- @@ -41,19 +43,19 @@ extern "C" { FLUTTER_GPU_EXPORT extern bool InternalFlutterGpu_DeviceBuffer_Initialize( Dart_Handle wrapper, - flutter::Context* gpu_context, + flutter::gpu::Context* gpu_context, int storage_mode, int size_in_bytes); FLUTTER_GPU_EXPORT extern bool InternalFlutterGpu_DeviceBuffer_InitializeWithHostData( Dart_Handle wrapper, - flutter::Context* gpu_context, + flutter::gpu::Context* gpu_context, Dart_Handle byte_data); FLUTTER_GPU_EXPORT extern bool InternalFlutterGpu_DeviceBuffer_Overwrite( - flutter::DeviceBuffer* wrapper, + flutter::gpu::DeviceBuffer* wrapper, Dart_Handle source_byte_data, int destination_offset_in_bytes); diff --git a/lib/gpu/host_buffer.cc b/lib/gpu/host_buffer.cc index 25581cba78d28..ad79d7aebe717 100644 --- a/lib/gpu/host_buffer.cc +++ b/lib/gpu/host_buffer.cc @@ -9,6 +9,7 @@ #include "third_party/tonic/typed_data/dart_byte_data.h" namespace flutter { +namespace gpu { IMPLEMENT_WRAPPERTYPEINFO(gpu, HostBuffer); @@ -23,6 +24,7 @@ size_t HostBuffer::EmplaceBytes(const tonic::DartByteData& byte_data) { return view.range.offset; } +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- @@ -30,11 +32,12 @@ size_t HostBuffer::EmplaceBytes(const tonic::DartByteData& byte_data) { /// void InternalFlutterGpu_HostBuffer_Initialize(Dart_Handle wrapper) { - auto res = fml::MakeRefCounted(); + auto res = fml::MakeRefCounted(); res->AssociateWithDartWrapper(wrapper); } -size_t InternalFlutterGpu_HostBuffer_EmplaceBytes(flutter::HostBuffer* wrapper, - Dart_Handle byte_data) { +size_t InternalFlutterGpu_HostBuffer_EmplaceBytes( + flutter::gpu::HostBuffer* wrapper, + Dart_Handle byte_data) { return wrapper->EmplaceBytes(tonic::DartByteData(byte_data)); } diff --git a/lib/gpu/host_buffer.h b/lib/gpu/host_buffer.h index 52d24930de9a6..0662c1f9fab9d 100644 --- a/lib/gpu/host_buffer.h +++ b/lib/gpu/host_buffer.h @@ -10,6 +10,7 @@ #include "third_party/tonic/typed_data/dart_byte_data.h" namespace flutter { +namespace gpu { class HostBuffer : public RefCountedDartWrappable { DEFINE_WRAPPERTYPEINFO(); @@ -28,6 +29,7 @@ class HostBuffer : public RefCountedDartWrappable { FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); }; +} // namespace gpu } // namespace flutter //---------------------------------------------------------------------------- @@ -41,7 +43,7 @@ extern void InternalFlutterGpu_HostBuffer_Initialize(Dart_Handle wrapper); FLUTTER_GPU_EXPORT extern size_t InternalFlutterGpu_HostBuffer_EmplaceBytes( - flutter::HostBuffer* wrapper, + flutter::gpu::HostBuffer* wrapper, Dart_Handle byte_data); } // extern "C" diff --git a/lib/gpu/lib/gpu.dart b/lib/gpu/lib/gpu.dart index 78014297e3fd6..0215900cb2b5c 100644 --- a/lib/gpu/lib/gpu.dart +++ b/lib/gpu/lib/gpu.dart @@ -14,9 +14,12 @@ library flutter_gpu; import 'dart:ffi'; import 'dart:nativewrappers'; import 'dart:typed_data'; +// ignore: uri_does_not_exist +import 'dart:ui' as ui; export 'src/smoketest.dart'; part 'src/formats.dart'; part 'src/context.dart'; part 'src/buffer.dart'; +part 'src/texture.dart'; diff --git a/lib/gpu/lib/src/context.dart b/lib/gpu/lib/src/context.dart index 41ed4108382e1..3963211a4ebc5 100644 --- a/lib/gpu/lib/src/context.dart +++ b/lib/gpu/lib/src/context.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + part of flutter_gpu; /// A handle to a graphics context. Used to create and manage GPU resources. @@ -17,6 +19,27 @@ base class GpuContext extends NativeFieldWrapperClass1 { } } + /// A supported [PixelFormat] for textures that store 4-channel colors + /// (red/green/blue/alpha). + PixelFormat get defaultColorFormat { + return PixelFormat.values[_getDefaultColorFormat()]; + } + + /// A supported [PixelFormat] for textures that store stencil information. + /// May include a depth channel if a stencil-only format is not available. + PixelFormat get defaultStencilFormat { + return PixelFormat.values[_getDefaultStencilFormat()]; + } + + /// A supported `PixelFormat` for textures that store both a stencil and depth + /// component. This will never return a depth-only or stencil-only texture. + /// + /// May be [PixelFormat.unknown] if no suitable depth+stencil format was + /// found. + PixelFormat get defaultDepthStencilFormat { + return PixelFormat.values[_getDefaultDepthStencilFormat()]; + } + /// Allocates a new region of GPU-resident memory. /// /// The [storageMode] must be either [StorageMode.hostVisible] or @@ -46,10 +69,47 @@ base class GpuContext extends NativeFieldWrapperClass1 { return result.isValid ? result : null; } + /// Allocates a new texture in GPU-resident memory. + /// + /// Returns [null] if the [Texture] creation failed. + Texture? createTexture(StorageMode storageMode, int width, int height, + {PixelFormat format = PixelFormat.r8g8b8a8UNormInt, + sampleCount = 1, + TextureCoordinateSystem coordinateSystem = + TextureCoordinateSystem.renderToTexture, + bool enableRenderTargetUsage = true, + bool enableShaderReadUsage = true, + bool enableShaderWriteUsage = false}) { + Texture result = Texture._initialize( + this, + storageMode, + format, + width, + height, + sampleCount, + coordinateSystem, + enableRenderTargetUsage, + enableShaderReadUsage, + enableShaderWriteUsage); + return result.isValid ? result : null; + } + /// Associates the default Impeller context with this Context. @Native( symbol: 'InternalFlutterGpu_Context_InitializeDefault') external String? _initializeDefault(); + + @Native)>( + symbol: 'InternalFlutterGpu_Context_GetDefaultColorFormat') + external int _getDefaultColorFormat(); + + @Native)>( + symbol: 'InternalFlutterGpu_Context_GetDefaultStencilFormat') + external int _getDefaultStencilFormat(); + + @Native)>( + symbol: 'InternalFlutterGpu_Context_GetDefaultDepthStencilFormat') + external int _getDefaultDepthStencilFormat(); } /// The default graphics context. diff --git a/lib/gpu/lib/src/formats.dart b/lib/gpu/lib/src/formats.dart index 0f0b6a37dd404..50256ecdc0bd5 100644 --- a/lib/gpu/lib/src/formats.dart +++ b/lib/gpu/lib/src/formats.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + part of flutter_gpu; /// Specifies where an allocation resides and how it may be used. @@ -26,3 +28,34 @@ enum StorageMode { /// and stencil buffers. deviceTransient, } + +enum PixelFormat { + unknown, + a8UNormInt, + r8UNormInt, + r8g8UNormInt, + r8g8b8a8UNormInt, + r8g8b8a8UNormIntSRGB, + b8g8r8a8UNormInt, + b8g8r8a8UNormIntSRGB, + r32g32b32a32Float, + r16g16b16a16Float, + b10g10r10XR, + b10g10r10XRSRGB, + b10g10r10a10XR, + // Depth and stencil formats. + s8UInt, + d24UnormS8Uint, + d32FloatS8UInt, +} + +enum TextureCoordinateSystem { + /// Alternative coordinate system used when uploading texture data from the + /// host. + /// (0, 0) is the bottom-left of the image with +Y going up. + uploadFromHost, + + /// Default coordinate system. + /// (0, 0) is the top-left of the image with +Y going down. + renderToTexture, +} diff --git a/lib/gpu/lib/src/texture.dart b/lib/gpu/lib/src/texture.dart new file mode 100644 index 0000000000000..12455ccaf3b84 --- /dev/null +++ b/lib/gpu/lib/src/texture.dart @@ -0,0 +1,142 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +part of flutter_gpu; + +base class Texture extends NativeFieldWrapperClass1 { + bool _valid = false; + get isValid { + return _valid; + } + + /// Creates a new Texture. + Texture._initialize( + GpuContext gpuContext, + this.storageMode, + this.format, + this.width, + this.height, + this.sampleCount, + TextureCoordinateSystem coordinateSystem, + this.enableRenderTargetUsage, + this.enableShaderReadUsage, + this.enableShaderWriteUsage) + : _coordinateSystem = coordinateSystem { + if (sampleCount != 1 && sampleCount != 4) { + throw Exception("Only a sample count of 1 or 4 is currently supported"); + } + _valid = _initialize( + gpuContext, + storageMode.index, + format.index, + width, + height, + sampleCount, + coordinateSystem.index, + enableRenderTargetUsage, + enableShaderReadUsage, + enableShaderWriteUsage); + } + + final StorageMode storageMode; + final PixelFormat format; + final int width; + final int height; + final int sampleCount; + + /// Enable using this texture as a render pass attachment. + final bool enableRenderTargetUsage; + + /// Enable reading or sampling from this texture in a shader. + final bool enableShaderReadUsage; + + /// Enable writing to the texture in a shader. + /// + /// Note that this is distinct from [enableRenderTargetUsage]. + final bool enableShaderWriteUsage; + + TextureCoordinateSystem _coordinateSystem; + TextureCoordinateSystem get coordinateSystem { + return _coordinateSystem; + } + + set coordinateSystem(TextureCoordinateSystem value) { + value; + _setCoordinateSystem(value.index); + } + + int get bytesPerTexel { + return _bytesPerTexel(); + } + + int GetBaseMipLevelSizeInBytes() { + return bytesPerTexel * width * height; + } + + /// Overwrite the entire base mipmap level of this [Texture]. + /// + /// This method can only be used if the [Texture] was created with + /// [StorageMode.hostVisible]. An exception will be thrown otherwise. + /// + /// The length of [sourceBytes] must be exactly the size of the base mip + /// level, otherwise an exception will be thrown. The size of the base mip + /// level is always `width * height * bytesPerPixel`. + /// + /// Returns [true] if the write was successful, or [false] if the write + /// failed due to an internal error. + bool overwrite(ByteData sourceBytes) { + if (storageMode != StorageMode.hostVisible) { + throw Exception( + 'Texture.overwrite can only be used with Textures that are host visible'); + } + int baseMipSize = GetBaseMipLevelSizeInBytes(); + if (sourceBytes.lengthInBytes != baseMipSize) { + throw Exception( + 'The length of sourceBytes (bytes: ${sourceBytes.lengthInBytes}) must exactly match the size of the base mip level (bytes: ${baseMipSize})'); + } + return _overwrite(sourceBytes); + } + + ui.Image asImage() { + if (!enableShaderReadUsage) { + throw Exception( + 'Only shader readable Flutter GPU textures can be used as UI Images'); + } + return _asImage(); + } + + /// Wrap with native counterpart. + @Native< + Bool Function(Handle, Pointer, Int, Int, Int, Int, Int, Int, Bool, + Bool, Bool)>(symbol: 'InternalFlutterGpu_Texture_Initialize') + external bool _initialize( + GpuContext gpuContext, + int storageMode, + int format, + int width, + int height, + int sampleCount, + int coordinateSystem, + bool enableRenderTargetUsage, + bool enableShaderReadUsage, + bool enableShaderWriteUsage); + + @Native( + symbol: 'InternalFlutterGpu_Texture_SetCoordinateSystem') + external void _setCoordinateSystem(int coordinateSystem); + + @Native)>( + symbol: 'InternalFlutterGpu_Texture_BytesPerTexel') + external int _bytesPerTexel(); + + @Native, Handle)>( + symbol: 'InternalFlutterGpu_Texture_Overwrite') + external bool _overwrite(ByteData bytes); + + @Native)>( + symbol: 'InternalFlutterGpu_Texture_AsImage') + external ui.Image _asImage(); +} diff --git a/lib/gpu/texture.cc b/lib/gpu/texture.cc new file mode 100644 index 0000000000000..648af7643d6d6 --- /dev/null +++ b/lib/gpu/texture.cc @@ -0,0 +1,144 @@ +// 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/ui/painting/image.h" +#include "fml/mapping.h" +#include "impeller/core/allocator.h" +#include "impeller/core/formats.h" +#include "impeller/core/texture.h" +#include "impeller/display_list/dl_image_impeller.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { +namespace gpu { + +IMPLEMENT_WRAPPERTYPEINFO(gpu, Texture); + +Texture::Texture(std::shared_ptr texture) + : texture_(std::move(texture)) {} + +Texture::~Texture() = default; + +void Texture::SetCoordinateSystem( + impeller::TextureCoordinateSystem coordinate_system) { + texture_->SetCoordinateSystem(coordinate_system); +} + +bool Texture::Overwrite(const tonic::DartByteData& source_bytes) { + const uint8_t* data = static_cast(source_bytes.data()); + auto copy = std::vector(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(copy); + if (!texture_->SetContents(mapping)) { + return false; + } + 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, + bool enable_render_target_usage, + bool enable_shader_read_usage, + bool enable_shader_write_usage) { + impeller::TextureDescriptor desc; + desc.storage_mode = static_cast(storage_mode); + desc.size = {width, height}; + desc.format = static_cast(format); + desc.usage = 0; + if (enable_render_target_usage) { + desc.usage |= static_cast( + impeller::TextureUsage::kRenderTarget); + } + if (enable_shader_read_usage) { + desc.usage |= static_cast( + impeller::TextureUsage::kShaderRead); + } + if (enable_shader_write_usage) { + desc.usage |= static_cast( + impeller::TextureUsage::kShaderWrite); + } + switch (sample_count) { + case 1: + desc.type = impeller::TextureType::kTexture2D; + desc.sample_count = impeller::SampleCount::kCount1; + break; + case 4: + desc.type = impeller::TextureType::kTexture2DMultisample; + desc.sample_count = impeller::SampleCount::kCount4; + break; + default: + return false; + } + auto texture = + gpu_context->GetContext()->GetResourceAllocator()->CreateTexture(desc); + if (!texture) { + FML_LOG(ERROR) << "Failed to create texture."; + return false; + } + + texture->SetCoordinateSystem( + static_cast(coordinate_system)); + + auto res = fml::MakeRefCounted(std::move(texture)); + res->AssociateWithDartWrapper(wrapper); + + return true; +} + +void InternalFlutterGpu_Texture_SetCoordinateSystem( + flutter::gpu::Texture* wrapper, + int coordinate_system) { + return wrapper->SetCoordinateSystem( + static_cast(coordinate_system)); +} + +bool InternalFlutterGpu_Texture_Overwrite(flutter::gpu::Texture* texture, + Dart_Handle source_byte_data) { + return texture->Overwrite(tonic::DartByteData(source_byte_data)); +} + +extern int InternalFlutterGpu_Texture_BytesPerTexel( + flutter::gpu::Texture* wrapper) { + return wrapper->GetBytesPerTexel(); +} + +Dart_Handle InternalFlutterGpu_Texture_AsImage(flutter::gpu::Texture* wrapper) { + return wrapper->AsImage(); +} diff --git a/lib/gpu/texture.h b/lib/gpu/texture.h new file mode 100644 index 0000000000000..f6329f936c5bb --- /dev/null +++ b/lib/gpu/texture.h @@ -0,0 +1,79 @@ +// 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. + +#pragma once + +#include "flutter/lib/gpu/context.h" +#include "flutter/lib/gpu/export.h" +#include "flutter/lib/ui/dart_wrapper.h" +#include "impeller/core/formats.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { +namespace gpu { + +class Texture : public RefCountedDartWrappable { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(Texture); + + public: + explicit Texture(std::shared_ptr texture); + + ~Texture() override; + + void SetCoordinateSystem(impeller::TextureCoordinateSystem coordinate_system); + + bool Overwrite(const tonic::DartByteData& source_bytes); + + size_t GetBytesPerTexel(); + + Dart_Handle AsImage() const; + + private: + std::shared_ptr texture_; + + FML_DISALLOW_COPY_AND_ASSIGN(Texture); +}; + +} // namespace gpu +} // namespace flutter + +//---------------------------------------------------------------------------- +/// Exports +/// + +extern "C" { + +FLUTTER_GPU_EXPORT +extern 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, + bool enable_render_target_usage, + bool enable_shader_read_usage, + bool enable_shader_write_usage); + +FLUTTER_GPU_EXPORT +extern void InternalFlutterGpu_Texture_SetCoordinateSystem( + flutter::gpu::Texture* wrapper, + int coordinate_system); + +FLUTTER_GPU_EXPORT +extern bool InternalFlutterGpu_Texture_Overwrite(flutter::gpu::Texture* wrapper, + Dart_Handle source_byte_data); + +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Texture_BytesPerTexel( + flutter::gpu::Texture* wrapper); + +FLUTTER_GPU_EXPORT +extern Dart_Handle InternalFlutterGpu_Texture_AsImage( + flutter::gpu::Texture* wrapper); + +} // extern "C" diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index d77ecb363f27a..b2a143669e131 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1970,6 +1970,11 @@ base class _Image extends NativeFieldWrapperClass1 { String toString() => '[$width\u00D7$height]'; } +@pragma('vm:entry-point') +Image _wrapImage(_Image image) { + return Image._(image, image.width, image.height); +} + /// Callback signature for [decodeImageFromList]. typedef ImageDecoderCallback = void Function(Image result); diff --git a/lib/ui/painting/image.cc b/lib/ui/painting/image.cc index a0b646ad795fc..b31ceb71ed73a 100644 --- a/lib/ui/painting/image.cc +++ b/lib/ui/painting/image.cc @@ -6,6 +6,7 @@ #include #include +#include "tonic/logging/dart_invoke.h" #if IMPELLER_SUPPORTS_RENDERING #include "flutter/lib/ui/painting/image_encoding_impeller.h" @@ -29,6 +30,11 @@ CanvasImage::CanvasImage() = default; CanvasImage::~CanvasImage() = default; +Dart_Handle CanvasImage::CreateOuterWrapping() { + Dart_Handle ui_lib = Dart_LookupLibrary(tonic::ToDart("dart:ui")); + return tonic::DartInvokeField(ui_lib, "_wrapImage", {ToDart(this)}); +} + Dart_Handle CanvasImage::toByteData(int format, Dart_Handle callback) { return EncodeImage(this, format, callback); } diff --git a/lib/ui/painting/image.h b/lib/ui/painting/image.h index ad3a7d7d18dbe..23ff80c99d459 100644 --- a/lib/ui/painting/image.h +++ b/lib/ui/painting/image.h @@ -29,6 +29,8 @@ class CanvasImage final : public RefCountedDartWrappable { return fml::MakeRefCounted(); } + Dart_Handle CreateOuterWrapping(); + int width() { return image_ ? image_->width() : 0; } int height() { return image_ ? image_->height() : 0; }