未验证 提交 1892e031 编写于 作者: K Kaushik Iska 提交者: GitHub

[embedder] Add gl present callback that takes present info (#20672)

上级 8844f87e
......@@ -55,13 +55,13 @@ bool ShellTestPlatformViewGL::GLContextClearCurrent() {
}
// |GPUSurfaceGLDelegate|
bool ShellTestPlatformViewGL::GLContextPresent() {
bool ShellTestPlatformViewGL::GLContextPresent(uint32_t fbo_id) {
return gl_surface_.Present();
}
// |GPUSurfaceGLDelegate|
intptr_t ShellTestPlatformViewGL::GLContextFBO(GLFrameInfo frame_info) const {
return gl_surface_.GetFramebuffer();
return gl_surface_.GetFramebuffer(frame_info.width, frame_info.height);
}
// |GPUSurfaceGLDelegate|
......
......@@ -53,7 +53,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView,
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
bool GLContextPresent(uint32_t fbo_id) override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
......
......@@ -125,6 +125,7 @@ GPUSurfaceGL::~GPUSurfaceGL() {
}
onscreen_surface_ = nullptr;
fbo_id_ = 0;
if (context_owner_) {
context_->releaseResourcesAndAbandonContext();
}
......@@ -196,6 +197,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
// Either way, we need to get rid of previous surface.
onscreen_surface_ = nullptr;
fbo_id_ = 0;
if (size.isEmpty()) {
FML_LOG(ERROR) << "Cannot create surfaces of empty size.";
......@@ -206,11 +208,11 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
GLFrameInfo frame_info = {static_cast<uint32_t>(size.width()),
static_cast<uint32_t>(size.height())};
onscreen_surface =
WrapOnscreenSurface(context_.get(), // GL context
size, // root surface size
delegate_->GLContextFBO(frame_info) // window FBO ID
);
const uint32_t fbo_id = delegate_->GLContextFBO(frame_info);
onscreen_surface = WrapOnscreenSurface(context_.get(), // GL context
size, // root surface size
fbo_id // window FBO ID
);
if (onscreen_surface == nullptr) {
// If the onscreen surface could not be wrapped. There is absolutely no
......@@ -220,6 +222,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
}
onscreen_surface_ = std::move(onscreen_surface);
fbo_id_ = fbo_id;
return true;
}
......@@ -281,7 +284,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
onscreen_surface_->getCanvas()->flush();
}
if (!delegate_->GLContextPresent()) {
if (!delegate_->GLContextPresent(fbo_id_)) {
return false;
}
......@@ -294,17 +297,19 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
// The FBO has changed, ask the delegate for the new FBO and do a surface
// re-wrap.
auto new_onscreen_surface = WrapOnscreenSurface(
context_.get(), // GL context
current_size, // root surface size
delegate_->GLContextFBO(frame_info) // window FBO ID
);
const uint32_t fbo_id = delegate_->GLContextFBO(frame_info);
auto new_onscreen_surface =
WrapOnscreenSurface(context_.get(), // GL context
current_size, // root surface size
fbo_id // window FBO ID
);
if (!new_onscreen_surface) {
return false;
}
onscreen_surface_ = std::move(new_onscreen_surface);
fbo_id_ = fbo_id;
}
return true;
......
......@@ -52,6 +52,8 @@ class GPUSurfaceGL : public Surface {
GPUSurfaceGLDelegate* delegate_;
sk_sp<GrDirectContext> context_;
sk_sp<SkSurface> onscreen_surface_;
/// FBO backing the current `onscreen_surface_`.
uint32_t fbo_id_;
bool context_owner_;
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
// external view embedder may want to render to the root surface. This is a
......
......@@ -37,7 +37,7 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate {
// Called to present the main GL surface. This is only called for the main GL
// context and not any of the contexts dedicated for IO.
virtual bool GLContextPresent() = 0;
virtual bool GLContextPresent(uint32_t fbo_id) = 0;
// The ID of the main window bound framebuffer. Typically FBO0.
virtual intptr_t GLContextFBO(GLFrameInfo frame_info) const = 0;
......
......@@ -113,7 +113,7 @@ bool AndroidSurfaceGL::GLContextClearCurrent() {
return android_context_->ClearCurrent();
}
bool AndroidSurfaceGL::GLContextPresent() {
bool AndroidSurfaceGL::GLContextPresent(uint32_t fbo_id) {
FML_DCHECK(IsValid());
FML_DCHECK(onscreen_surface_);
return onscreen_surface_->SwapBuffers();
......
......@@ -56,7 +56,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate,
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
bool GLContextPresent(uint32_t fbo_id) override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
......
......@@ -14,7 +14,7 @@ bool AndroidSurfaceMock::GLContextClearCurrent() {
return true;
}
bool AndroidSurfaceMock::GLContextPresent() {
bool AndroidSurfaceMock::GLContextPresent(uint32_t fbo_id) {
return true;
}
......
......@@ -45,7 +45,7 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate,
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
bool GLContextPresent(uint32_t fbo_id) override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
......
......@@ -40,7 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, public GPUSurfaceGLDelegate {
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
bool GLContextPresent(uint32_t fbo_id) override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
......
......@@ -77,7 +77,7 @@ bool IOSSurfaceGL::GLContextClearCurrent() {
}
// |GPUSurfaceGLDelegate|
bool IOSSurfaceGL::GLContextPresent() {
bool IOSSurfaceGL::GLContextPresent(uint32_t fbo_id) {
TRACE_EVENT0("flutter", "IOSSurfaceGL::GLContextPresent");
return IsValid() && render_target_->PresentRenderBuffer();
}
......
......@@ -91,19 +91,11 @@ static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) {
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
if (SAFE_ACCESS(open_gl_config, make_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, clear_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, present, nullptr) == nullptr) {
return false;
}
bool fbo_callback_exists =
SAFE_ACCESS(open_gl_config, fbo_callback, nullptr) != nullptr;
bool fbo_with_frame_info_callback_exists =
SAFE_ACCESS(open_gl_config, fbo_with_frame_info_callback, nullptr) !=
nullptr;
// only one of these callbacks must exist.
if (fbo_callback_exists == fbo_with_frame_info_callback_exists) {
if (!SAFE_EXISTS(open_gl_config, make_current) ||
!SAFE_EXISTS(open_gl_config, clear_current) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, fbo_callback,
fbo_with_frame_info_callback) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, present, present_with_info)) {
return false;
}
......@@ -173,8 +165,17 @@ InferOpenGLPlatformViewCreationCallback(
auto gl_clear_current = [ptr = config->open_gl.clear_current,
user_data]() -> bool { return ptr(user_data); };
auto gl_present = [ptr = config->open_gl.present, user_data]() -> bool {
return ptr(user_data);
auto gl_present = [present = config->open_gl.present,
present_with_info = config->open_gl.present_with_info,
user_data](uint32_t fbo_id) -> bool {
if (present) {
return present(user_data);
} else {
FlutterPresentInfo present_info = {};
present_info.struct_size = sizeof(FlutterPresentInfo);
present_info.fbo_id = fbo_id;
return present_with_info(user_data, &present_info);
}
};
auto gl_fbo_callback =
......
......@@ -346,7 +346,7 @@ typedef struct {
/// This information is passed to the embedder when requesting a frame buffer
/// object.
///
/// See: \ref FlutterSoftwareRendererConfig.fbo_with_frame_info_callback.
/// See: \ref FlutterOpenGLRendererConfig.fbo_with_frame_info_callback.
typedef struct {
/// The size of this struct. Must be sizeof(FlutterFrameInfo).
size_t struct_size;
......@@ -354,15 +354,34 @@ typedef struct {
FlutterUIntSize size;
} FlutterFrameInfo;
/// Callback for when a frame buffer object is requested.
typedef uint32_t (*UIntFrameInfoCallback)(
void* /* user data */,
const FlutterFrameInfo* /* frame info */);
/// This information is passed to the embedder when a surface is presented.
///
/// See: \ref FlutterOpenGLRendererConfig.present_with_info.
typedef struct {
/// The size of this struct. Must be sizeof(FlutterFrameInfo).
size_t struct_size;
/// Id of the fbo backing the surface that was presented.
uint32_t fbo_id;
} FlutterPresentInfo;
/// Callback for when a surface is presented.
typedef bool (*BoolPresentInfoCallback)(
void* /* user data */,
const FlutterPresentInfo* /* present info */);
typedef struct {
/// The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
size_t struct_size;
BoolCallback make_current;
BoolCallback clear_current;
/// Specifying one (and only one) of `present` or `present_with_info` is
/// required. Specifying both is an error and engine initialization will be
/// terminated. The return value indicates success of the present call.
BoolCallback present;
/// Specifying one (and only one) of the `fbo_callback` or
/// `fbo_with_frame_info_callback` is required. Specifying both is an error
......@@ -407,6 +426,12 @@ typedef struct {
/// `FlutterFrameInfo` struct that indicates the properties of the surface
/// that flutter will acquire from the returned fbo.
UIntFrameInfoCallback fbo_with_frame_info_callback;
/// Specifying one (and only one) of `present` or `present_with_info` is
/// required. Specifying both is an error and engine initialization will be
/// terminated. When using this variant, the embedder is passed a
/// `FlutterPresentInfo` struct that the embedder can use to release any
/// resources. The return value indicates success of the present call.
BoolPresentInfoCallback present_with_info;
} FlutterOpenGLRendererConfig;
typedef struct {
......
......@@ -17,4 +17,12 @@
return static_cast<decltype(pointer->member)>((default_value)); \
})()
/// Checks if the member exists.
#define SAFE_EXISTS(pointer, member) \
(SAFE_ACCESS(pointer, member, nullptr) != nullptr)
/// Checks if exactly one of member1 or member2 exists.
#define SAFE_EXISTS_ONE_OF(pointer, member1, member2) \
(SAFE_EXISTS(pointer, member1) != SAFE_EXISTS(pointer, member2))
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_
......@@ -45,8 +45,8 @@ bool EmbedderSurfaceGL::GLContextClearCurrent() {
}
// |GPUSurfaceGLDelegate|
bool EmbedderSurfaceGL::GLContextPresent() {
return gl_dispatch_table_.gl_present_callback();
bool EmbedderSurfaceGL::GLContextPresent(uint32_t fbo_id) {
return gl_dispatch_table_.gl_present_callback(fbo_id);
}
// |GPUSurfaceGLDelegate|
......
......@@ -18,7 +18,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface,
struct GLDispatchTable {
std::function<bool(void)> gl_make_current_callback; // required
std::function<bool(void)> gl_clear_current_callback; // required
std::function<bool(void)> gl_present_callback; // required
std::function<bool(uint32_t)> gl_present_callback; // required
std::function<intptr_t(GLFrameInfo)> gl_fbo_callback; // required
std::function<bool(void)> gl_make_resource_current_callback; // optional
std::function<SkMatrix(void)>
......@@ -56,7 +56,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface,
bool GLContextClearCurrent() override;
// |GPUSurfaceGLDelegate|
bool GLContextPresent() override;
bool GLContextPresent(uint32_t fbo_id) override;
// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
......
......@@ -32,8 +32,10 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
opengl_renderer_config_.clear_current = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLClearCurrent();
};
opengl_renderer_config_.present = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLPresent();
opengl_renderer_config_.present_with_info =
[](void* context, const FlutterPresentInfo* present_info) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLPresent(
present_info->fbo_id);
};
opengl_renderer_config_.fbo_with_frame_info_callback =
[](void* context, const FlutterFrameInfo* frame_info) -> uint32_t {
......@@ -127,6 +129,15 @@ void EmbedderConfigBuilder::SetOpenGLFBOCallBack() {
};
}
void EmbedderConfigBuilder::SetOpenGLPresentCallBack() {
// SetOpenGLRendererConfig must be called before this.
FML_CHECK(renderer_config_.type == FlutterRendererType::kOpenGL);
renderer_config_.open_gl.present = [](void* context) -> bool {
// passing a placeholder fbo_id.
return reinterpret_cast<EmbedderTestContext*>(context)->GLPresent(0);
};
}
void EmbedderConfigBuilder::SetOpenGLRendererConfig(SkISize surface_size) {
renderer_config_.type = FlutterRendererType::kOpenGL;
renderer_config_.open_gl = opengl_renderer_config_;
......
......@@ -55,6 +55,12 @@ class EmbedderConfigBuilder {
// explicitly test this behavior.
void SetOpenGLFBOCallBack();
// Used to explicitly set an `open_gl.present`. Using this method will cause
// your test to fail since the ctor for this class sets
// `open_gl.present_with_info`. This method exists as a utility to explicitly
// test this behavior.
void SetOpenGLPresentCallBack();
void SetAssetsPath();
void SetSnapshots();
......
......@@ -185,10 +185,20 @@ bool EmbedderTestContext::GLClearCurrent() {
return gl_surface_->ClearCurrent();
}
bool EmbedderTestContext::GLPresent() {
bool EmbedderTestContext::GLPresent(uint32_t fbo_id) {
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
gl_surface_present_count_++;
GLPresentCallback callback;
{
std::scoped_lock lock(gl_callback_mutex_);
callback = gl_present_callback_;
}
if (callback) {
callback(fbo_id);
}
FireRootSurfacePresentCallbackIfPresent(
[&]() { return gl_surface_->GetRasterSurfaceSnapshot(); });
......@@ -200,16 +210,21 @@ bool EmbedderTestContext::GLPresent() {
}
void EmbedderTestContext::SetGLGetFBOCallback(GLGetFBOCallback callback) {
std::scoped_lock lock(gl_get_fbo_callback_mutex_);
std::scoped_lock lock(gl_callback_mutex_);
gl_get_fbo_callback_ = callback;
}
void EmbedderTestContext::SetGLPresentCallback(GLPresentCallback callback) {
std::scoped_lock lock(gl_callback_mutex_);
gl_present_callback_ = callback;
}
uint32_t EmbedderTestContext::GLGetFramebuffer(FlutterFrameInfo frame_info) {
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
GLGetFBOCallback callback;
{
std::scoped_lock lock(gl_get_fbo_callback_mutex_);
std::scoped_lock lock(gl_callback_mutex_);
callback = gl_get_fbo_callback_;
}
......@@ -217,7 +232,8 @@ uint32_t EmbedderTestContext::GLGetFramebuffer(FlutterFrameInfo frame_info) {
callback(frame_info);
}
return gl_surface_->GetFramebuffer();
const auto size = frame_info.size;
return gl_surface_->GetFramebuffer(size.width, size.height);
}
bool EmbedderTestContext::GLMakeResourceCurrent() {
......@@ -295,5 +311,10 @@ void EmbedderTestContext::FireRootSurfacePresentCallbackIfPresent(
callback(image_callback());
}
uint32_t EmbedderTestContext::GetWindowFBOId() const {
FML_CHECK(gl_surface_);
return gl_surface_->GetWindowFBOId();
}
} // namespace testing
} // namespace flutter
......@@ -95,6 +95,23 @@ class EmbedderTestContext {
///
void SetGLGetFBOCallback(GLGetFBOCallback callback);
uint32_t GetWindowFBOId() const;
using GLPresentCallback = std::function<void(uint32_t fbo_id)>;
//----------------------------------------------------------------------------
/// @brief Sets a callback that will be invoked (on the raster task
/// runner) when the engine presents an fbo that was given by the
/// embedder.
///
/// @attention The callback will be invoked on the raster task runner. The
/// callback can be set on the tests host thread.
///
/// @param[in] callback The callback to set. The previous callback will be
/// un-registered.
///
void SetGLPresentCallback(GLPresentCallback callback);
private:
// This allows the builder to access the hooks.
friend class EmbedderConfigBuilder;
......@@ -119,8 +136,9 @@ class EmbedderTestContext {
SkMatrix root_surface_transformation_;
size_t gl_surface_present_count_ = 0;
size_t software_surface_present_count_ = 0;
std::mutex gl_get_fbo_callback_mutex_;
std::mutex gl_callback_mutex_;
GLGetFBOCallback gl_get_fbo_callback_;
GLPresentCallback gl_present_callback_;
static VoidCallback GetIsolateCreateCallbackHook();
......@@ -149,7 +167,7 @@ class EmbedderTestContext {
bool GLClearCurrent();
bool GLPresent();
bool GLPresent(uint32_t fbo_id);
uint32_t GLGetFramebuffer(FlutterFrameInfo frame_info);
......
......@@ -4406,5 +4406,58 @@ TEST_F(EmbedderTest, MustNotRunWithBothFBOCallbacksSet) {
ASSERT_FALSE(engine.is_valid());
}
TEST_F(EmbedderTest, MustNotRunWithBothPresentCallbacksSet) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024));
builder.SetOpenGLPresentCallBack();
auto engine = builder.LaunchEngine();
ASSERT_FALSE(engine.is_valid());
}
TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024));
builder.SetDartEntrypoint("push_frames_over_and_over");
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 1024).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
auto engine = builder.LaunchEngine();
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 1024;
event.height = 600;
event.pixel_ratio = 1.0;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());
fml::CountDownLatch frame_latch(10);
context.AddNativeCallback("SignalNativeTest",
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
/* Nothing to do. */
}));
const uint32_t window_fbo_id = context.GetWindowFBOId();
context.SetGLPresentCallback(
[window_fbo_id = window_fbo_id, &frame_latch](uint32_t fbo_id) {
ASSERT_EQ(fbo_id, window_fbo_id);
frame_latch.CountDown();
});
frame_latch.Wait();
}
} // namespace testing
} // namespace flutter
......@@ -220,9 +220,8 @@ bool TestGLSurface::Present() {
return result == EGL_TRUE;
}
uint32_t TestGLSurface::GetFramebuffer() const {
// Return FBO0
return 0;
uint32_t TestGLSurface::GetFramebuffer(uint32_t width, uint32_t height) const {
return GetWindowFBOId();
}
bool TestGLSurface::MakeResourceCurrent() {
......@@ -296,7 +295,9 @@ sk_sp<SkSurface> TestGLSurface::GetOnscreenSurface() {
FML_CHECK(::eglGetCurrentContext() != EGL_NO_CONTEXT);
GrGLFramebufferInfo framebuffer_info = {};
framebuffer_info.fFBOID = GetFramebuffer();
const uint32_t width = surface_size_.width();
const uint32_t height = surface_size_.height();
framebuffer_info.fFBOID = GetFramebuffer(width, height);
#if OS_MACOSX
framebuffer_info.fFormat = GR_GL_RGBA8;
#else
......@@ -304,11 +305,11 @@ sk_sp<SkSurface> TestGLSurface::GetOnscreenSurface() {
#endif
GrBackendRenderTarget backend_render_target(
surface_size_.width(), // width
surface_size_.height(), // height
1, // sample count
8, // stencil bits
framebuffer_info // framebuffer info
width, // width
height, // height
1, // sample count
8, // stencil bits
framebuffer_info // framebuffer info
);
SkSurfaceProps surface_properties(
......@@ -362,5 +363,9 @@ sk_sp<SkImage> TestGLSurface::GetRasterSurfaceSnapshot() {
return host_snapshot;
}
uint32_t TestGLSurface::GetWindowFBOId() const {
return 0u;
}
} // namespace testing
} // namespace flutter
......@@ -27,7 +27,7 @@ class TestGLSurface {
bool Present();
uint32_t GetFramebuffer() const;
uint32_t GetFramebuffer(uint32_t width, uint32_t height) const;
bool MakeResourceCurrent();
......@@ -41,6 +41,8 @@ class TestGLSurface {
sk_sp<SkImage> GetRasterSurfaceSnapshot();
uint32_t GetWindowFBOId() const;
private:
// Importing the EGL.h pulls in platform headers which are problematic
// (especially X11 which #defineds types like Bool). Any TUs importing
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册