diff --git a/engine/src/flutter/lib/gpu/context.cc b/engine/src/flutter/lib/gpu/context.cc index 6f5d0bd29b5..c0c3345ce84 100644 --- a/engine/src/flutter/lib/gpu/context.cc +++ b/engine/src/flutter/lib/gpu/context.cc @@ -9,6 +9,7 @@ #include "flutter/lib/gpu/formats.h" #include "flutter/lib/ui/ui_dart_state.h" #include "fml/make_copyable.h" +#include "impeller/core/platform.h" #include "tonic/converter/dart_converter.h" namespace flutter { @@ -105,3 +106,8 @@ extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat( ->GetCapabilities() ->GetDefaultDepthStencilFormat())); } + +extern int InternalFlutterGpu_Context_GetMinimumUniformByteAlignment( + flutter::gpu::Context* wrapper) { + return impeller::DefaultUniformAlignment(); +} diff --git a/engine/src/flutter/lib/gpu/context.h b/engine/src/flutter/lib/gpu/context.h index 903b4a0af98..b482ae47993 100644 --- a/engine/src/flutter/lib/gpu/context.h +++ b/engine/src/flutter/lib/gpu/context.h @@ -70,6 +70,10 @@ FLUTTER_GPU_EXPORT extern int InternalFlutterGpu_Context_GetDefaultDepthStencilFormat( flutter::gpu::Context* wrapper); +FLUTTER_GPU_EXPORT +extern int InternalFlutterGpu_Context_GetMinimumUniformByteAlignment( + flutter::gpu::Context* wrapper); + } // extern "C" #endif // FLUTTER_LIB_GPU_CONTEXT_H_ diff --git a/engine/src/flutter/lib/gpu/device_buffer.cc b/engine/src/flutter/lib/gpu/device_buffer.cc index 1b495350543..7de08bcffc7 100644 --- a/engine/src/flutter/lib/gpu/device_buffer.cc +++ b/engine/src/flutter/lib/gpu/device_buffer.cc @@ -99,3 +99,12 @@ bool InternalFlutterGpu_DeviceBuffer_Overwrite( return device_buffer->Overwrite(tonic::DartByteData(source_byte_data), destination_offset_in_bytes); } + +bool InternalFlutterGpu_DeviceBuffer_Flush( + flutter::gpu::DeviceBuffer* device_buffer, + int offset_in_bytes, + int size_in_bytes) { + device_buffer->GetBuffer()->Flush( + impeller::Range(offset_in_bytes, size_in_bytes)); + return true; +} diff --git a/engine/src/flutter/lib/gpu/device_buffer.h b/engine/src/flutter/lib/gpu/device_buffer.h index a2b262b3795..af8c8b0c9d1 100644 --- a/engine/src/flutter/lib/gpu/device_buffer.h +++ b/engine/src/flutter/lib/gpu/device_buffer.h @@ -62,6 +62,12 @@ extern bool InternalFlutterGpu_DeviceBuffer_Overwrite( Dart_Handle source_byte_data, int destination_offset_in_bytes); +FLUTTER_GPU_EXPORT +extern bool InternalFlutterGpu_DeviceBuffer_Flush( + flutter::gpu::DeviceBuffer* wrapper, + int offset_in_bytes, + int size_in_bytes); + } // extern "C" #endif // FLUTTER_LIB_GPU_DEVICE_BUFFER_H_ diff --git a/engine/src/flutter/lib/gpu/lib/src/buffer.dart b/engine/src/flutter/lib/gpu/lib/src/buffer.dart index 51ab0a3513b..e836c71662d 100644 --- a/engine/src/flutter/lib/gpu/lib/src/buffer.dart +++ b/engine/src/flutter/lib/gpu/lib/src/buffer.dart @@ -93,11 +93,15 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 with Buffer { symbol: 'InternalFlutterGpu_DeviceBuffer_InitializeWithHostData') external bool _initializeWithHostData(GpuContext gpuContext, ByteData data); - /// Overwrite a range of bytes in the already created [DeviceBuffer]. + /// Overwrite a range of bytes within an existing [DeviceBuffer]. /// /// This method can only be used if the [DeviceBuffer] was created with /// [StorageMode.hostVisible]. An exception will be thrown otherwise. /// + /// After new writes have been staged, the [DeviceBuffer.flush] should be + /// called prior to accessing the data. Otherwise, the updated data will not + /// be copied to the GPU on devices that don't have host coherent memory. + /// /// The entire length of [sourceBytes] will be copied into the [DeviceBuffer], /// starting at byte index [destinationOffsetInBytes] in the [DeviceBuffer]. /// If performing this copy would result in an out of bounds write to the @@ -119,6 +123,37 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 with Buffer { @Native, Handle, Int)>( symbol: 'InternalFlutterGpu_DeviceBuffer_Overwrite') external bool _overwrite(ByteData bytes, int destinationOffsetInBytes); + + /// Flush the contents of the [DeviceBuffer] to the GPU. + /// + /// This method can only be used if the [DeviceBuffer] was created with + /// [StorageMode.hostVisible]. An exception will be thrown otherwise. + /// + /// If [lengthInBytes] is set to -1, the entire buffer will be flushed. + /// + /// On devices with coherent host memory (memory shared between the CPU and + /// GPU), this method is a no-op. + void flush({int offsetInBytes = 0, int lengthInBytes = -1}) { + if (storageMode != StorageMode.hostVisible) { + throw Exception( + 'DeviceBuffer.flush can only be used with DeviceBuffers that are host visible'); + } + if (offsetInBytes < 0 || offsetInBytes >= sizeInBytes) { + throw Exception('offsetInBytes must be within the bounds of the buffer'); + } + if (lengthInBytes < -1) { + throw Exception('lengthInBytes must be either positive or -1'); + } + if (lengthInBytes != -1 && offsetInBytes + lengthInBytes > sizeInBytes) { + throw Exception( + 'The provided range must not be too large to fit within the buffer'); + } + _flush(offsetInBytes, lengthInBytes); + } + + @Native, Int, Int)>( + symbol: 'InternalFlutterGpu_DeviceBuffer_Flush') + external void _flush(int offsetInBytes, int lengthInBytes); } /// [HostBuffer] is a [Buffer] which is allocated on the host (native CPU diff --git a/engine/src/flutter/lib/gpu/lib/src/context.dart b/engine/src/flutter/lib/gpu/lib/src/context.dart index c4ae71eabf5..217b3873149 100644 --- a/engine/src/flutter/lib/gpu/lib/src/context.dart +++ b/engine/src/flutter/lib/gpu/lib/src/context.dart @@ -40,6 +40,12 @@ base class GpuContext extends NativeFieldWrapperClass1 { return PixelFormat.values[_getDefaultDepthStencilFormat()]; } + /// The minimum alignment required when referencing uniform blocks stored in a + /// `DeviceBuffer`. + int get minimumUniformByteAlignment { + return _getMinimumUniformByteAlignment(); + } + /// Allocates a new region of GPU-resident memory. /// /// The [storageMode] must be either [StorageMode.hostVisible] or @@ -124,6 +130,10 @@ base class GpuContext extends NativeFieldWrapperClass1 { @Native)>( symbol: 'InternalFlutterGpu_Context_GetDefaultDepthStencilFormat') external int _getDefaultDepthStencilFormat(); + + @Native)>( + symbol: 'InternalFlutterGpu_Context_GetMinimumUniformByteAlignment') + external int _getMinimumUniformByteAlignment(); } /// The default graphics context. diff --git a/engine/src/flutter/testing/dart/gpu_test.dart b/engine/src/flutter/testing/dart/gpu_test.dart index 08d696264dc..87dcbc87940 100644 --- a/engine/src/flutter/testing/dart/gpu_test.dart +++ b/engine/src/flutter/testing/dart/gpu_test.dart @@ -67,6 +67,11 @@ void main() async { } }); + test('GpuContext.minimumUniformByteAlignment', () async { + final int alignment = gpu.gpuContext.minimumUniformByteAlignment; + expect(alignment, greaterThanOrEqualTo(16)); + }, skip: !impellerEnabled); + test('HostBuffer.emplace', () async { final gpu.HostBuffer hostBuffer = gpu.gpuContext.createHostBuffer(); @@ -96,6 +101,7 @@ void main() async { final bool success = deviceBuffer! .overwrite(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + deviceBuffer.flush(); expect(success, true); }, skip: !impellerEnabled); @@ -107,6 +113,7 @@ void main() async { final bool success = deviceBuffer!.overwrite( Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), destinationOffsetInBytes: 1); + deviceBuffer.flush(); expect(success, false); }, skip: !impellerEnabled); @@ -120,6 +127,7 @@ void main() async { deviceBuffer!.overwrite( Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), destinationOffsetInBytes: -1); + deviceBuffer.flush(); fail('Exception not thrown for negative destination offset.'); } catch (e) { expect(