diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5ea50c1f38d6e..37523d04b4a2c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1728,7 +1728,10 @@ ORIGIN: ../../../flutter/lib/gpu/context.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/context.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/export.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/export.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/gpu/host_buffer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/gpu/host_buffer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/lib/gpu.dart + ../../../flutter/LICENSE +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/smoketest.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/gpu/smoketest.cc + ../../../flutter/LICENSE @@ -4456,7 +4459,10 @@ FILE: ../../../flutter/lib/gpu/context.cc FILE: ../../../flutter/lib/gpu/context.h FILE: ../../../flutter/lib/gpu/export.cc FILE: ../../../flutter/lib/gpu/export.h +FILE: ../../../flutter/lib/gpu/host_buffer.cc +FILE: ../../../flutter/lib/gpu/host_buffer.h FILE: ../../../flutter/lib/gpu/lib/gpu.dart +FILE: ../../../flutter/lib/gpu/lib/src/buffer.dart FILE: ../../../flutter/lib/gpu/lib/src/context.dart FILE: ../../../flutter/lib/gpu/lib/src/smoketest.dart FILE: ../../../flutter/lib/gpu/smoketest.cc diff --git a/impeller/fixtures/dart_tests.dart b/impeller/fixtures/dart_tests.dart index ad6ad54c335da..7e213429cd87d 100644 --- a/impeller/fixtures/dart_tests.dart +++ b/impeller/fixtures/dart_tests.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:typed_data'; import 'dart:ui' as ui; import '../../lib/gpu/lib/gpu.dart' as gpu; @@ -17,3 +18,18 @@ void instantiateDefaultContext() { // ignore: unused_local_variable final gpu.GpuContext context = gpu.gpuContext; } + +@pragma('vm:entry-point') +void canEmplaceHostBuffer() { + final gpu.HostBuffer hostBuffer = gpu.HostBuffer(); + + final gpu.BufferView view0 = hostBuffer + .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + assert(view0.offsetInBytes == 0); + assert(view0.lengthInBytes == 4); + + final gpu.BufferView view1 = hostBuffer + .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + assert(view1.offsetInBytes >= 4); + assert(view1.lengthInBytes == 4); +} diff --git a/impeller/renderer/renderer_dart_unittests.cc b/impeller/renderer/renderer_dart_unittests.cc index 91a84bae2b7b9..b8893461e7d7c 100644 --- a/impeller/renderer/renderer_dart_unittests.cc +++ b/impeller/renderer/renderer_dart_unittests.cc @@ -111,5 +111,19 @@ TEST_P(RendererDartTest, CanInstantiateFlutterGPUContext) { ASSERT_TRUE(result); } +TEST_P(RendererDartTest, CanEmplaceHostBuffer) { + auto isolate = GetIsolate(); + bool result = isolate->RunInIsolateScope([]() -> bool { + if (tonic::CheckAndHandleError( + ::Dart_Invoke(Dart_RootLibrary(), + tonic::ToDart("canEmplaceHostBuffer"), 0, nullptr))) { + return false; + } + return true; + }); + + ASSERT_TRUE(result); +} + } // namespace testing } // namespace impeller diff --git a/lib/gpu/BUILD.gn b/lib/gpu/BUILD.gn index 188637666c731..689e2f3ef8974 100644 --- a/lib/gpu/BUILD.gn +++ b/lib/gpu/BUILD.gn @@ -36,6 +36,8 @@ source_set("gpu") { "context.h", "export.cc", "export.h", + "host_buffer.cc", + "host_buffer.h", "smoketest.cc", "smoketest.h", ] diff --git a/lib/gpu/context.cc b/lib/gpu/context.cc index 303c5d4e82c17..34418320b5ddc 100644 --- a/lib/gpu/context.cc +++ b/lib/gpu/context.cc @@ -15,14 +15,14 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(gpu, Context); -std::shared_ptr Context::override_context_; +std::shared_ptr Context::default_context_; void Context::SetOverrideContext(std::shared_ptr context) { - override_context_ = std::move(context); + default_context_ = std::move(context); } -std::shared_ptr Context::GetOverrideContext() { - return override_context_; +std::shared_ptr Context::GetDefaultContext() { + return default_context_; } Context::Context(std::shared_ptr context) @@ -30,6 +30,10 @@ Context::Context(std::shared_ptr context) Context::~Context() = default; +std::shared_ptr Context::GetContext() { + return context_; +} + } // namespace flutter //---------------------------------------------------------------------------- @@ -40,7 +44,7 @@ Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { auto dart_state = flutter::UIDartState::Current(); std::shared_ptr impeller_context = - flutter::Context::GetOverrideContext(); + flutter::Context::GetDefaultContext(); if (!impeller_context) { if (!dart_state->IsImpellerEnabled()) { diff --git a/lib/gpu/context.h b/lib/gpu/context.h index 3133845624f15..15365386ad0aa 100644 --- a/lib/gpu/context.h +++ b/lib/gpu/context.h @@ -18,18 +18,19 @@ class Context : public RefCountedDartWrappable { public: static void SetOverrideContext(std::shared_ptr context); - static std::shared_ptr GetOverrideContext(); + static std::shared_ptr GetDefaultContext(); explicit Context(std::shared_ptr context); ~Context() override; - protected: + std::shared_ptr GetContext(); + + private: /// An Impeller context that takes precedent over the IO state context when /// set. This is used to inject the context when running with the Impeller /// playground, which doesn't instantiate an Engine instance. - static std::shared_ptr override_context_; + static std::shared_ptr default_context_; - private: std::shared_ptr context_; FML_DISALLOW_COPY_AND_ASSIGN(Context); diff --git a/lib/gpu/host_buffer.cc b/lib/gpu/host_buffer.cc new file mode 100644 index 0000000000000..25581cba78d28 --- /dev/null +++ b/lib/gpu/host_buffer.cc @@ -0,0 +1,40 @@ +// 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/host_buffer.h" + +#include "impeller/core/host_buffer.h" +#include "impeller/core/platform.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { + +IMPLEMENT_WRAPPERTYPEINFO(gpu, HostBuffer); + +HostBuffer::HostBuffer() : host_buffer_(impeller::HostBuffer::Create()) {} + +HostBuffer::~HostBuffer() = default; + +size_t HostBuffer::EmplaceBytes(const tonic::DartByteData& byte_data) { + auto view = + host_buffer_->Emplace(byte_data.data(), byte_data.length_in_bytes(), + impeller::DefaultUniformAlignment()); + return view.range.offset; +} + +} // namespace flutter + +//---------------------------------------------------------------------------- +/// Exports +/// + +void InternalFlutterGpu_HostBuffer_Initialize(Dart_Handle wrapper) { + auto res = fml::MakeRefCounted(); + res->AssociateWithDartWrapper(wrapper); +} + +size_t InternalFlutterGpu_HostBuffer_EmplaceBytes(flutter::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 new file mode 100644 index 0000000000000..52d24930de9a6 --- /dev/null +++ b/lib/gpu/host_buffer.h @@ -0,0 +1,47 @@ +// 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/export.h" +#include "flutter/lib/ui/dart_wrapper.h" +#include "impeller/core/host_buffer.h" +#include "third_party/tonic/typed_data/dart_byte_data.h" + +namespace flutter { + +class HostBuffer : public RefCountedDartWrappable { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(HostBuffer); + + public: + explicit HostBuffer(); + + ~HostBuffer() override; + + size_t EmplaceBytes(const tonic::DartByteData& byte_data); + + private: + std::shared_ptr host_buffer_; + + FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); +}; + +} // namespace flutter + +//---------------------------------------------------------------------------- +/// Exports +/// + +extern "C" { + +FLUTTER_GPU_EXPORT +extern void InternalFlutterGpu_HostBuffer_Initialize(Dart_Handle wrapper); + +FLUTTER_GPU_EXPORT +extern size_t InternalFlutterGpu_HostBuffer_EmplaceBytes( + flutter::HostBuffer* wrapper, + Dart_Handle byte_data); + +} // extern "C" diff --git a/lib/gpu/lib/gpu.dart b/lib/gpu/lib/gpu.dart index 72cf205f1f49d..b12b4a574b7e9 100644 --- a/lib/gpu/lib/gpu.dart +++ b/lib/gpu/lib/gpu.dart @@ -11,5 +11,7 @@ /// * [Flutter GPU Wiki page](https://github.com/flutter/flutter/wiki/Flutter-GPU). library flutter_gpu; -export 'src/context.dart'; export 'src/smoketest.dart'; + +export 'src/context.dart'; +export 'src/buffer.dart'; diff --git a/lib/gpu/lib/src/buffer.dart b/lib/gpu/lib/src/buffer.dart new file mode 100644 index 0000000000000..6864f20e12627 --- /dev/null +++ b/lib/gpu/lib/src/buffer.dart @@ -0,0 +1,67 @@ +// 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. + +import 'dart:ffi'; +import 'dart:nativewrappers'; +import 'dart:typed_data'; + +/// A reference to a byte range within a GPU-resident [Buffer]. +class BufferView { + /// The buffer of this view. + final HostBuffer buffer; + + /// The start of the view, in bytes starting from the beginning of the + /// [buffer]. + final int offsetInBytes; + + /// The length of the view. + final int lengthInBytes; + + /// Create a new view into a buffer on the GPU. + const BufferView(this.buffer, + {required this.offsetInBytes, required this.lengthInBytes}); +} + +/// [HostBuffer] is a [Buffer] which is allocated on the host (native CPU +/// resident memory) and lazily uploaded to the GPU. A [HostBuffer] can be +/// safely mutated or extended at any time on the host, and will be +/// automatically re-uploaded to the GPU the next time a GPU operation needs to +/// access it. +/// +/// This is useful for efficiently chunking sparse data uploads, especially +/// ephemeral uniform data that needs to change from frame to frame. +/// +/// Different platforms have different data alignment requirements for accessing +/// device buffer data. The [HostBuffer] takes these requirements into account +/// and automatically inserts padding between emplaced data if necessary. +base class HostBuffer extends NativeFieldWrapperClass1 { + /// Creates a new HostBuffer. + HostBuffer() { + _initialize(); + } + + /// Wrap with native counterpart. + @Native( + symbol: 'InternalFlutterGpu_HostBuffer_Initialize') + external void _initialize(); + + /// Append byte data to the end of the [HostBuffer] and produce a [BufferView] + /// that references the new data in the buffer. + /// + /// This method automatically inserts padding in-between emplace calls in the + /// buffer if necessary to abide by platform-specific uniform alignment + /// requirements. + /// + /// The updated buffer will be uploaded to the GPU if the returned + /// [BufferView] is used by a rendering command. + BufferView emplace(ByteData bytes) { + int resultOffset = _emplaceBytes(bytes); + return BufferView(this, + offsetInBytes: resultOffset, lengthInBytes: bytes.lengthInBytes); + } + + @Native, Handle)>( + symbol: 'InternalFlutterGpu_HostBuffer_EmplaceBytes') + external int _emplaceBytes(ByteData bytes); +}