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

Commit c25cf09

Browse files
authored
Add EGL Surface backing store (#43683)
Allows using an EGL surface as a flutter backing store. Way more convenient for GBM than hacking gbm bo's into GL FBOs. This resolves flutter/flutter#58363 Currently, the embedder API assumes that the compositor (if it exists) will let flutter render into FBOs or Textures and then composite the whole thing onto the actual (EGL) window surface. I think this assumption is also documented a bit in flutter/flutter#38466 However, in my case, I want let the hardware do the composition (using the linux KMS API), and render each flutter layer into it's own EGL surface. It's possible to hack around this by creating your own GBM BOs, importing those as EGL images, then importing those as GL Render Buffers and attaching those to GL FBOs and that works (tested it). However, that's basically reimplementing 50% of the whole GBM/EGL "window" system integration for no reason. This PR adds: 1. To the embedder API: - a new kind of OpenGL Backing store: `FlutterOpenGLSurface` - consisting of just a `make_current` and destruction callback (plus userdata) - the make_current callback should make the target surface current, i.e. `eglMakeCurrent(..., surf, surf)` - will be called by the engine before rendering onto the backing store 2. Some wiring to call make_current before rendering into the backing store ## TODO: [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 597082d commit c25cf09

19 files changed

+936
-155
lines changed

shell/platform/embedder/embedder.cc

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) {
279279
static_cast<int32_t>(flutter_rect.bottom)};
280280
return rect;
281281
}
282+
283+
// We need GL_BGRA8_EXT for creating SkSurfaces from FlutterOpenGLSurfaces
284+
// below.
285+
#ifndef GL_BGRA8_EXT
286+
#define GL_BGRA8_EXT 0x93A1
287+
#endif
288+
282289
#endif
283290

284291
static inline flutter::Shell::CreateCallback<flutter::PlatformView>
@@ -827,6 +834,47 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
827834
#endif
828835
}
829836

837+
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
838+
GrDirectContext* context,
839+
const FlutterBackingStoreConfig& config,
840+
const FlutterOpenGLSurface* surface) {
841+
#ifdef SHELL_ENABLE_GL
842+
GrGLFramebufferInfo framebuffer_info = {};
843+
framebuffer_info.fFormat = GL_BGRA8_EXT;
844+
framebuffer_info.fFBOID = 0;
845+
846+
auto backend_render_target =
847+
GrBackendRenderTargets::MakeGL(config.size.width, // width
848+
config.size.height, // height
849+
1, // sample count
850+
0, // stencil bits
851+
framebuffer_info // framebuffer info
852+
);
853+
854+
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
855+
856+
auto sk_surface = SkSurfaces::WrapBackendRenderTarget(
857+
context, // context
858+
backend_render_target, // backend render target
859+
kBottomLeft_GrSurfaceOrigin, // surface origin
860+
kN32_SkColorType, // color type
861+
SkColorSpace::MakeSRGB(), // color space
862+
&surface_properties, // surface properties
863+
static_cast<SkSurfaces::RenderTargetReleaseProc>(
864+
surface->destruction_callback), // release proc
865+
surface->user_data // release context
866+
);
867+
868+
if (!sk_surface) {
869+
FML_LOG(ERROR) << "Could not wrap embedder supplied frame-buffer.";
870+
return nullptr;
871+
}
872+
return sk_surface;
873+
#else
874+
return nullptr;
875+
#endif
876+
}
877+
830878
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
831879
GrDirectContext* context,
832880
const FlutterBackingStoreConfig& config,
@@ -1154,14 +1202,27 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
11541202
}
11551203

11561204
static std::unique_ptr<flutter::EmbedderRenderTarget>
1157-
MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store,
1158-
sk_sp<SkSurface> skia_surface,
1159-
fml::closure on_release) {
1205+
MakeRenderTargetFromSkSurface(
1206+
FlutterBackingStore backing_store,
1207+
sk_sp<SkSurface> skia_surface,
1208+
fml::closure on_release,
1209+
flutter::EmbedderRenderTarget::MakeOrClearCurrentCallback on_make_current,
1210+
flutter::EmbedderRenderTarget::MakeOrClearCurrentCallback
1211+
on_clear_current) {
11601212
if (!skia_surface) {
11611213
return nullptr;
11621214
}
11631215
return std::make_unique<flutter::EmbedderRenderTargetSkia>(
1164-
backing_store, std::move(skia_surface), std::move(on_release));
1216+
backing_store, std::move(skia_surface), std::move(on_release),
1217+
std::move(on_make_current), std::move(on_clear_current));
1218+
}
1219+
1220+
static std::unique_ptr<flutter::EmbedderRenderTarget>
1221+
MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store,
1222+
sk_sp<SkSurface> skia_surface,
1223+
fml::closure on_release) {
1224+
return MakeRenderTargetFromSkSurface(backing_store, std::move(skia_surface),
1225+
std::move(on_release), nullptr, nullptr);
11651226
}
11661227

11671228
static std::unique_ptr<flutter::EmbedderRenderTarget>
@@ -1233,9 +1294,45 @@ CreateEmbedderRenderTarget(
12331294
break;
12341295
}
12351296
}
1297+
1298+
case kFlutterOpenGLTargetTypeSurface: {
1299+
auto on_make_current =
1300+
[callback = backing_store.open_gl.surface.make_current_callback,
1301+
context = backing_store.open_gl.surface.user_data]()
1302+
-> flutter::EmbedderRenderTarget::SetCurrentResult {
1303+
bool invalidate_api_state = false;
1304+
bool ok = callback(context, &invalidate_api_state);
1305+
return {ok, invalidate_api_state};
1306+
};
1307+
1308+
auto on_clear_current =
1309+
[callback = backing_store.open_gl.surface.clear_current_callback,
1310+
context = backing_store.open_gl.surface.user_data]()
1311+
-> flutter::EmbedderRenderTarget::SetCurrentResult {
1312+
bool invalidate_api_state = false;
1313+
bool ok = callback(context, &invalidate_api_state);
1314+
return {ok, invalidate_api_state};
1315+
};
1316+
1317+
if (enable_impeller) {
1318+
// TODO(https://github.com/flutter/flutter/issues/151670): Implement
1319+
// GL Surface backing stores for Impeller.
1320+
FML_LOG(ERROR) << "Unimplemented";
1321+
break;
1322+
} else {
1323+
auto skia_surface = MakeSkSurfaceFromBackingStore(
1324+
context, config, &backing_store.open_gl.surface);
1325+
1326+
render_target = MakeRenderTargetFromSkSurface(
1327+
backing_store, std::move(skia_surface),
1328+
collect_callback.Release(), on_make_current, on_clear_current);
1329+
break;
1330+
}
1331+
}
12361332
}
12371333
break;
12381334
}
1335+
12391336
case kFlutterBackingStoreTypeSoftware: {
12401337
auto skia_surface = MakeSkSurfaceFromBackingStore(
12411338
context, config, &backing_store.software);

shell/platform/embedder/embedder.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ typedef enum {
304304
/// Specifies an OpenGL frame-buffer target type. Framebuffers are specified
305305
/// using the FlutterOpenGLFramebuffer struct.
306306
kFlutterOpenGLTargetTypeFramebuffer,
307+
/// Specifies an OpenGL on-screen surface target type. Surfaces are specified
308+
/// using the FlutterOpenGLSurface struct.
309+
kFlutterOpenGLTargetTypeSurface,
307310
} FlutterOpenGLTargetType;
308311

309312
/// A pixel format to be used for software rendering.
@@ -408,6 +411,55 @@ typedef struct {
408411
VoidCallback destruction_callback;
409412
} FlutterOpenGLFramebuffer;
410413

414+
typedef bool (*FlutterOpenGLSurfaceCallback)(void* /* user data */,
415+
bool* /* opengl state changed */);
416+
417+
typedef struct {
418+
/// The size of this struct. Must be sizeof(FlutterOpenGLSurface).
419+
size_t struct_size;
420+
421+
/// User data to be passed to the make_current, clear_current and
422+
/// destruction callbacks.
423+
void* user_data;
424+
425+
/// Callback invoked (on an engine-managed thread) that asks the embedder to
426+
/// make the surface current.
427+
///
428+
/// Should return true if the operation succeeded, false if the surface could
429+
/// not be made current and rendering should be cancelled.
430+
///
431+
/// The second parameter 'opengl state changed' should be set to true if
432+
/// any OpenGL API state is different than before this callback was called.
433+
/// In that case, Flutter will invalidate the internal OpenGL API state cache,
434+
/// which is a somewhat expensive operation.
435+
///
436+
/// @attention required. (non-null)
437+
FlutterOpenGLSurfaceCallback make_current_callback;
438+
439+
/// Callback invoked (on an engine-managed thread) when the current surface
440+
/// can be cleared.
441+
///
442+
/// Should return true if the operation succeeded, false if an error ocurred.
443+
/// That error will be logged but otherwise not handled by the engine.
444+
///
445+
/// The second parameter 'opengl state changed' is the same as with the
446+
/// @ref make_current_callback.
447+
///
448+
/// The embedder might clear the surface here after it was previously made
449+
/// current. That's not required however, it's also possible to clear it in
450+
/// the destruction callback. There's no way to signal OpenGL state
451+
/// changes in the destruction callback though.
452+
///
453+
/// @attention required. (non-null)
454+
FlutterOpenGLSurfaceCallback clear_current_callback;
455+
456+
/// Callback invoked (on an engine-managed thread) that asks the embedder to
457+
/// collect the surface.
458+
///
459+
/// @attention required. (non-null)
460+
VoidCallback destruction_callback;
461+
} FlutterOpenGLSurface;
462+
411463
typedef bool (*BoolCallback)(void* /* user data */);
412464
typedef FlutterTransformation (*TransformationCallback)(void* /* user data */);
413465
typedef uint32_t (*UIntCallback)(void* /* user data */);
@@ -1619,6 +1671,9 @@ typedef struct {
16191671
/// A framebuffer for Flutter to render into. The embedder must ensure that
16201672
/// the framebuffer is complete.
16211673
FlutterOpenGLFramebuffer framebuffer;
1674+
/// A surface for Flutter to render into. Basically a wrapper around
1675+
/// a closure that'll be called when the surface should be made current.
1676+
FlutterOpenGLSurface surface;
16221677
};
16231678
} FlutterOpenGLBackingStore;
16241679

shell/platform/embedder/embedder_external_view.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "flutter/display_list/dl_builder.h"
88
#include "flutter/fml/trace_event.h"
99
#include "flutter/shell/common/dl_op_spy.h"
10+
#include "third_party/skia/include/gpu/GrDirectContext.h"
11+
#include "third_party/skia/include/gpu/GrRecordingContext.h"
1012

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

93+
// TODO(https://github.com/flutter/flutter/issues/151670): Implement this for
94+
// Impeller as well.
95+
#if !SLIMPELLER
96+
static void InvalidateApiState(SkSurface& skia_surface) {
97+
auto recording_context = skia_surface.recordingContext();
98+
99+
// Should never happen.
100+
FML_DCHECK(recording_context) << "Recording context was null.";
101+
102+
auto direct_context = recording_context->asDirectContext();
103+
if (direct_context == nullptr) {
104+
// Can happen when using software rendering.
105+
// Print an error but otherwise continue in that case.
106+
FML_LOG(ERROR) << "Embedder asked to invalidate cached graphics API state "
107+
"but Flutter is not using a graphics API.";
108+
} else {
109+
direct_context->resetContext(kAll_GrBackendState);
110+
}
111+
}
112+
#endif
113+
91114
bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target,
92115
bool clear_surface) {
93116
TRACE_EVENT0("flutter", "EmbedderExternalView::Render");
@@ -143,6 +166,28 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target,
143166
return false;
144167
}
145168

169+
auto [ok, invalidate_api_state] = render_target.MaybeMakeCurrent();
170+
171+
if (invalidate_api_state) {
172+
InvalidateApiState(*skia_surface);
173+
}
174+
if (!ok) {
175+
FML_LOG(ERROR) << "Could not make the surface current.";
176+
return false;
177+
}
178+
179+
// Clear the current render target (most likely EGLSurface) at the
180+
// end of this scope.
181+
fml::ScopedCleanupClosure clear_current_surface([&]() {
182+
auto [ok, invalidate_api_state] = render_target.MaybeClearCurrent();
183+
if (invalidate_api_state) {
184+
InvalidateApiState(*skia_surface);
185+
}
186+
if (!ok) {
187+
FML_LOG(ERROR) << "Could not clear the current surface.";
188+
}
189+
});
190+
146191
FML_DCHECK(render_target.GetRenderTargetSize() == render_surface_size_);
147192

148193
auto canvas = skia_surface->getCanvas();

shell/platform/embedder/embedder_render_target.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ namespace flutter {
2727
///
2828
class EmbedderRenderTarget {
2929
public:
30+
struct SetCurrentResult {
31+
/// This is true if the operation succeeded (even if it was a no-op),
32+
/// false if the surface could not be made current / the current surface
33+
/// could not be cleared.
34+
bool success;
35+
36+
/// This is true if any native graphics API (e.g. GL, but not EGL) state has
37+
/// changed and Skia/Impeller should not assume any GL state values are the
38+
/// same as before the context change operation was attempted.
39+
bool gl_state_trampled;
40+
};
41+
42+
using MakeOrClearCurrentCallback = std::function<SetCurrentResult()>;
43+
3044
//----------------------------------------------------------------------------
3145
/// @brief Destroys this instance of the render target and invokes the
3246
/// callback for the embedder to release its resource associated
@@ -77,6 +91,24 @@ class EmbedderRenderTarget {
7791
///
7892
const FlutterBackingStore* GetBackingStore() const;
7993

94+
//----------------------------------------------------------------------------
95+
/// @brief Make the render target current.
96+
///
97+
/// Sometimes render targets are actually (for example)
98+
/// EGL surfaces instead of framebuffers or textures.
99+
/// In that case, we can't fully wrap them as SkSurfaces, instead,
100+
/// the embedder will provide a callback that should be called
101+
/// when the target surface should be made current.
102+
///
103+
/// @return The result of the operation.
104+
virtual SetCurrentResult MaybeMakeCurrent() const { return {true, false}; }
105+
106+
//----------------------------------------------------------------------------
107+
/// @brief Clear the current render target. @see MaybeMakeCurrent
108+
///
109+
/// @return The result of the operation.
110+
virtual SetCurrentResult MaybeClearCurrent() const { return {true, false}; }
111+
80112
protected:
81113
//----------------------------------------------------------------------------
82114
/// @brief Creates a render target whose backing store is managed by the

shell/platform/embedder/embedder_render_target_skia.cc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ namespace flutter {
1111
EmbedderRenderTargetSkia::EmbedderRenderTargetSkia(
1212
FlutterBackingStore backing_store,
1313
sk_sp<SkSurface> render_surface,
14-
fml::closure on_release)
14+
fml::closure on_release,
15+
MakeOrClearCurrentCallback on_make_current,
16+
MakeOrClearCurrentCallback on_clear_current)
1517
: EmbedderRenderTarget(backing_store, std::move(on_release)),
16-
render_surface_(std::move(render_surface)) {
18+
render_surface_(std::move(render_surface)),
19+
on_make_current_(std::move(on_make_current)),
20+
on_clear_current_(std::move(on_clear_current)) {
1721
FML_DCHECK(render_surface_);
1822
}
1923

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

44+
EmbedderRenderTarget::SetCurrentResult
45+
EmbedderRenderTargetSkia::MaybeMakeCurrent() const {
46+
if (on_make_current_ != nullptr) {
47+
return on_make_current_();
48+
}
49+
50+
return {true, false};
51+
}
52+
53+
EmbedderRenderTarget::SetCurrentResult
54+
EmbedderRenderTargetSkia::MaybeClearCurrent() const {
55+
if (on_clear_current_ != nullptr) {
56+
return on_clear_current_();
57+
}
58+
59+
return {true, false};
60+
}
61+
4062
} // namespace flutter

shell/platform/embedder/embedder_render_target_skia.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class EmbedderRenderTargetSkia final : public EmbedderRenderTarget {
1313
public:
1414
EmbedderRenderTargetSkia(FlutterBackingStore backing_store,
1515
sk_sp<SkSurface> render_surface,
16-
fml::closure on_release);
16+
fml::closure on_release,
17+
MakeOrClearCurrentCallback on_make_current,
18+
MakeOrClearCurrentCallback on_clear_current);
1719

1820
// |EmbedderRenderTarget|
1921
~EmbedderRenderTargetSkia() override;
@@ -30,9 +32,18 @@ class EmbedderRenderTargetSkia final : public EmbedderRenderTarget {
3032
// |EmbedderRenderTarget|
3133
SkISize GetRenderTargetSize() const override;
3234

35+
// |EmbedderRenderTarget|
36+
SetCurrentResult MaybeMakeCurrent() const override;
37+
38+
// |EmbedderRenderTarget|
39+
SetCurrentResult MaybeClearCurrent() const override;
40+
3341
private:
3442
sk_sp<SkSurface> render_surface_;
3543

44+
MakeOrClearCurrentCallback on_make_current_;
45+
MakeOrClearCurrentCallback on_clear_current_;
46+
3647
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderRenderTargetSkia);
3748
};
3849

0 commit comments

Comments
 (0)