Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Add EGL Surface backing store #43683

Merged
merged 30 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4535e89
Add FlutterOpenGLSurface backing store
ardera Jul 12, 2023
1d1b0a0
fix scoping & style
ardera Jul 16, 2023
c303603
implement EGL surface backing stores
ardera Jul 25, 2023
28992bd
fix embedder gl surface docs
ardera Aug 19, 2023
2858e26
Add tests for OpenGL Surface backing stores
ardera Aug 19, 2023
7485655
fix formatting
ardera Jan 8, 2024
ce1e545
Don't use GLUserData for software tests
ardera Feb 20, 2024
1770240
fix one remaining use of GLUserData in software context
ardera Feb 20, 2024
0c9c5ba
fix memory leak
ardera Feb 20, 2024
e8b7cae
don't use different backing store userdatas per graphics API
ardera Feb 20, 2024
3e85028
fix lints
ardera Feb 20, 2024
4e8c828
fix more lints
ardera Feb 20, 2024
3b08cc5
fix more lints again
ardera Feb 21, 2024
b903405
Update shell/platform/embedder/embedder.cc
ardera Jul 11, 2024
4214fa2
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
961ba09
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
759d78c
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
0d41d3d
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
3a6c547
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
03510cb
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
e55c4c5
Update shell/platform/embedder/embedder.h
ardera Jul 11, 2024
83eba98
Update shell/platform/embedder/embedder_external_view.cc
ardera Jul 11, 2024
2c0a61a
Update shell/platform/embedder/embedder_external_view.cc
ardera Jul 11, 2024
afd99b9
replace all ocurrences of `userdata` with `user_data`
ardera Jul 11, 2024
47440ea
fix egl surface tests present callback type signature
ardera Jul 11, 2024
b9b71f1
fix formatting in embedder_text_backingstore_producer.cc
ardera Jul 12, 2024
e564866
add github issue TODO comment
ardera Jul 12, 2024
2f6c326
only include `InvalidateApiState` when building with Skia
ardera Jul 12, 2024
8174373
improve documentation for make_current_callback
ardera Jul 12, 2024
d315fdc
capture result of surface ops in a struct instead of a std::pair
ardera Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@ static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) {
static_cast<int32_t>(flutter_rect.bottom)};
return rect;
}

// We need GL_BGRA8_EXT for creating SkSurfaces from FlutterOpenGLSurfaces
// below.
#ifndef GL_BGRA8_EXT
#define GL_BGRA8_EXT 0x93A1
#endif

#endif

static inline flutter::Shell::CreateCallback<flutter::PlatformView>
Expand Down Expand Up @@ -826,6 +833,47 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
#endif
}

static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLSurface* surface) {
#ifdef SHELL_ENABLE_GL
GrGLFramebufferInfo framebuffer_info = {};
framebuffer_info.fFormat = GL_BGRA8_EXT;
framebuffer_info.fFBOID = 0;

auto backend_render_target =
GrBackendRenderTargets::MakeGL(config.size.width, // width
config.size.height, // height
1, // sample count
0, // stencil bits
framebuffer_info // framebuffer info
);

SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);

auto sk_surface = SkSurfaces::WrapBackendRenderTarget(
context, // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::RenderTargetReleaseProc>(
surface->destruction_callback), // release proc
surface->user_data // release context
);

if (!sk_surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied frame-buffer.";
return nullptr;
}
return sk_surface;
#else
return nullptr;
#endif
}

static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
Expand Down Expand Up @@ -1153,14 +1201,27 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
}

static std::unique_ptr<flutter::EmbedderRenderTarget>
MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store,
sk_sp<SkSurface> skia_surface,
fml::closure on_release) {
MakeRenderTargetFromSkSurface(
FlutterBackingStore backing_store,
sk_sp<SkSurface> skia_surface,
fml::closure on_release,
flutter::EmbedderRenderTarget::MakeOrClearCurrentCallback on_make_current,
flutter::EmbedderRenderTarget::MakeOrClearCurrentCallback
on_clear_current) {
if (!skia_surface) {
return nullptr;
}
return std::make_unique<flutter::EmbedderRenderTargetSkia>(
backing_store, std::move(skia_surface), std::move(on_release));
backing_store, std::move(skia_surface), std::move(on_release),
std::move(on_make_current), std::move(on_clear_current));
}

static std::unique_ptr<flutter::EmbedderRenderTarget>
MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store,
sk_sp<SkSurface> skia_surface,
fml::closure on_release) {
return MakeRenderTargetFromSkSurface(backing_store, std::move(skia_surface),
std::move(on_release), nullptr, nullptr);
}

static std::unique_ptr<flutter::EmbedderRenderTarget>
Expand Down Expand Up @@ -1232,9 +1293,45 @@ CreateEmbedderRenderTarget(
break;
}
}

case kFlutterOpenGLTargetTypeSurface: {
auto on_make_current =
[callback = backing_store.open_gl.surface.make_current_callback,
context = backing_store.open_gl.surface.user_data]()
-> flutter::EmbedderRenderTarget::SetCurrentResult {
bool invalidate_api_state = false;
bool ok = callback(context, &invalidate_api_state);
return {ok, invalidate_api_state};
};

auto on_clear_current =
[callback = backing_store.open_gl.surface.clear_current_callback,
context = backing_store.open_gl.surface.user_data]()
-> flutter::EmbedderRenderTarget::SetCurrentResult {
bool invalidate_api_state = false;
bool ok = callback(context, &invalidate_api_state);
return {ok, invalidate_api_state};
};

if (enable_impeller) {
// TODO(https://github.com/flutter/flutter/issues/151670): Implement
// GL Surface backing stores for Impeller.
FML_LOG(ERROR) << "Unimplemented";
break;
} else {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.open_gl.surface);

render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface),
collect_callback.Release(), on_make_current, on_clear_current);
break;
}
}
}
break;
}

case kFlutterBackingStoreTypeSoftware: {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.software);
Expand Down
55 changes: 55 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ typedef enum {
/// Specifies an OpenGL frame-buffer target type. Framebuffers are specified
/// using the FlutterOpenGLFramebuffer struct.
kFlutterOpenGLTargetTypeFramebuffer,
/// Specifies an OpenGL on-screen surface target type. Surfaces are specified
/// using the FlutterOpenGLSurface struct.
kFlutterOpenGLTargetTypeSurface,
} FlutterOpenGLTargetType;

/// A pixel format to be used for software rendering.
Expand Down Expand Up @@ -408,6 +411,55 @@ typedef struct {
VoidCallback destruction_callback;
} FlutterOpenGLFramebuffer;

typedef bool (*FlutterOpenGLSurfaceCallback)(void* /* user data */,
bool* /* opengl state changed */);

typedef struct {
/// The size of this struct. Must be sizeof(FlutterOpenGLSurface).
size_t struct_size;

/// User data to be passed to the make_current, clear_current and
/// destruction callbacks.
void* user_data;

/// Callback invoked (on an engine-managed thread) that asks the embedder to
/// make the surface current.
///
/// Should return true if the operation succeeded, false if the surface could
/// not be made current and rendering should be cancelled.
///
/// The second parameter 'opengl state changed' should be set to true if
/// any OpenGL API state is different than before this callback was called.
/// In that case, Flutter will invalidate the internal OpenGL API state cache,
/// which is a somewhat expensive operation.
///
/// @attention required. (non-null)
FlutterOpenGLSurfaceCallback make_current_callback;

/// Callback invoked (on an engine-managed thread) when the current surface
/// can be cleared.
///
/// Should return true if the operation succeeded, false if an error ocurred.
/// That error will be logged but otherwise not handled by the engine.
///
/// The second parameter 'opengl state changed' is the same as with the
/// @ref make_current_callback.
///
/// The embedder might clear the surface here after it was previously made
/// current. That's not required however, it's also possible to clear it in
/// the destruction callback. There's no way to signal OpenGL state
/// changes in the destruction callback though.
///
/// @attention required. (non-null)
FlutterOpenGLSurfaceCallback clear_current_callback;

/// Callback invoked (on an engine-managed thread) that asks the embedder to
/// collect the surface.
///
/// @attention required. (non-null)
VoidCallback destruction_callback;
} FlutterOpenGLSurface;

typedef bool (*BoolCallback)(void* /* user data */);
typedef FlutterTransformation (*TransformationCallback)(void* /* user data */);
typedef uint32_t (*UIntCallback)(void* /* user data */);
Expand Down Expand Up @@ -1619,6 +1671,9 @@ typedef struct {
/// A framebuffer for Flutter to render into. The embedder must ensure that
/// the framebuffer is complete.
FlutterOpenGLFramebuffer framebuffer;
/// A surface for Flutter to render into. Basically a wrapper around
/// a closure that'll be called when the surface should be made current.
FlutterOpenGLSurface surface;
};
} FlutterOpenGLBackingStore;

Expand Down
45 changes: 45 additions & 0 deletions shell/platform/embedder/embedder_external_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "flutter/display_list/dl_builder.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/dl_op_spy.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/GrRecordingContext.h"

#ifdef IMPELLER_SUPPORTS_RENDERING
#include "impeller/display_list/dl_dispatcher.h" // nogncheck
Expand Down Expand Up @@ -88,6 +90,27 @@ const EmbeddedViewParams* EmbedderExternalView::GetEmbeddedViewParams() const {
return embedded_view_params_.get();
}

// TODO(https://github.com/flutter/flutter/issues/151670): Implement this for
// Impeller as well.
#if !SLIMPELLER
static void InvalidateApiState(SkSurface& skia_surface) {
auto recording_context = skia_surface.recordingContext();

// Should never happen.
FML_DCHECK(recording_context) << "Recording context was null.";

auto direct_context = recording_context->asDirectContext();
if (direct_context == nullptr) {
// Can happen when using software rendering.
// Print an error but otherwise continue in that case.
FML_LOG(ERROR) << "Embedder asked to invalidate cached graphics API state "
"but Flutter is not using a graphics API.";
} else {
direct_context->resetContext(kAll_GrBackendState);
}
}
#endif

bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target,
bool clear_surface) {
TRACE_EVENT0("flutter", "EmbedderExternalView::Render");
Expand Down Expand Up @@ -143,6 +166,28 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target,
return false;
}

auto [ok, invalidate_api_state] = render_target.MaybeMakeCurrent();

if (invalidate_api_state) {
InvalidateApiState(*skia_surface);
}
if (!ok) {
FML_LOG(ERROR) << "Could not make the surface current.";
return false;
}

// Clear the current render target (most likely EGLSurface) at the
// end of this scope.
fml::ScopedCleanupClosure clear_current_surface([&]() {
auto [ok, invalidate_api_state] = render_target.MaybeClearCurrent();
if (invalidate_api_state) {
InvalidateApiState(*skia_surface);
}
if (!ok) {
FML_LOG(ERROR) << "Could not clear the current surface.";
}
});

FML_DCHECK(render_target.GetRenderTargetSize() == render_surface_size_);

auto canvas = skia_surface->getCanvas();
Expand Down
32 changes: 32 additions & 0 deletions shell/platform/embedder/embedder_render_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ namespace flutter {
///
class EmbedderRenderTarget {
public:
struct SetCurrentResult {
/// This is true if the operation succeeded (even if it was a no-op),
/// false if the surface could not be made current / the current surface
/// could not be cleared.
bool success;

/// This is true if any native graphics API (e.g. GL, but not EGL) state has
/// changed and Skia/Impeller should not assume any GL state values are the
/// same as before the context change operation was attempted.
bool gl_state_trampled;
};

using MakeOrClearCurrentCallback = std::function<SetCurrentResult()>;

//----------------------------------------------------------------------------
/// @brief Destroys this instance of the render target and invokes the
/// callback for the embedder to release its resource associated
Expand Down Expand Up @@ -77,6 +91,24 @@ class EmbedderRenderTarget {
///
const FlutterBackingStore* GetBackingStore() const;

//----------------------------------------------------------------------------
/// @brief Make the render target current.
///
/// Sometimes render targets are actually (for example)
/// EGL surfaces instead of framebuffers or textures.
/// In that case, we can't fully wrap them as SkSurfaces, instead,
/// the embedder will provide a callback that should be called
/// when the target surface should be made current.
///
/// @return The result of the operation.
virtual SetCurrentResult MaybeMakeCurrent() const { return {true, false}; }

//----------------------------------------------------------------------------
/// @brief Clear the current render target. @see MaybeMakeCurrent
///
/// @return The result of the operation.
virtual SetCurrentResult MaybeClearCurrent() const { return {true, false}; }

protected:
//----------------------------------------------------------------------------
/// @brief Creates a render target whose backing store is managed by the
Expand Down
26 changes: 24 additions & 2 deletions shell/platform/embedder/embedder_render_target_skia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ namespace flutter {
EmbedderRenderTargetSkia::EmbedderRenderTargetSkia(
FlutterBackingStore backing_store,
sk_sp<SkSurface> render_surface,
fml::closure on_release)
fml::closure on_release,
MakeOrClearCurrentCallback on_make_current,
MakeOrClearCurrentCallback on_clear_current)
: EmbedderRenderTarget(backing_store, std::move(on_release)),
render_surface_(std::move(render_surface)) {
render_surface_(std::move(render_surface)),
on_make_current_(std::move(on_make_current)),
on_clear_current_(std::move(on_clear_current)) {
FML_DCHECK(render_surface_);
}

Expand All @@ -37,4 +41,22 @@ SkISize EmbedderRenderTargetSkia::GetRenderTargetSize() const {
return SkISize::Make(render_surface_->width(), render_surface_->height());
}

EmbedderRenderTarget::SetCurrentResult
EmbedderRenderTargetSkia::MaybeMakeCurrent() const {
if (on_make_current_ != nullptr) {
return on_make_current_();
}

return {true, false};
}

EmbedderRenderTarget::SetCurrentResult
EmbedderRenderTargetSkia::MaybeClearCurrent() const {
if (on_clear_current_ != nullptr) {
return on_clear_current_();
}

return {true, false};
}

} // namespace flutter
13 changes: 12 additions & 1 deletion shell/platform/embedder/embedder_render_target_skia.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class EmbedderRenderTargetSkia final : public EmbedderRenderTarget {
public:
EmbedderRenderTargetSkia(FlutterBackingStore backing_store,
sk_sp<SkSurface> render_surface,
fml::closure on_release);
fml::closure on_release,
MakeOrClearCurrentCallback on_make_current,
MakeOrClearCurrentCallback on_clear_current);

// |EmbedderRenderTarget|
~EmbedderRenderTargetSkia() override;
Expand All @@ -30,9 +32,18 @@ class EmbedderRenderTargetSkia final : public EmbedderRenderTarget {
// |EmbedderRenderTarget|
SkISize GetRenderTargetSize() const override;

// |EmbedderRenderTarget|
SetCurrentResult MaybeMakeCurrent() const override;

// |EmbedderRenderTarget|
SetCurrentResult MaybeClearCurrent() const override;

private:
sk_sp<SkSurface> render_surface_;

MakeOrClearCurrentCallback on_make_current_;
MakeOrClearCurrentCallback on_clear_current_;

FML_DISALLOW_COPY_AND_ASSIGN(EmbedderRenderTargetSkia);
};

Expand Down
Loading