Skip to content

Commit

Permalink
headless: Support multiple views
Browse files Browse the repository at this point in the history
Support more than one view. This is achieved by opting-in to use the new
CogView.create_backend vfunc, which makes each web view responsible for
creating its own WPE view backend. The platform keeps track of viewports
using CogPlatform.viewport_{created,disposed}, and on each frame timer
tick all views in from all viewports are checked for dispatching frame
acknowledgements.
  • Loading branch information
aperezdc committed Dec 20, 2023
1 parent f0e90d1 commit c967895
Showing 1 changed file with 77 additions and 26 deletions.
103 changes: 77 additions & 26 deletions platform/headless/cog-platform-headless.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* cog-platform-headless.c
* Copyright (C) 2021 Igalia S.L
* Copyright (C) 2021, 2023 Igalia S.L
*
* SPDX-License-Identifier: MIT
*/
Expand All @@ -11,10 +11,15 @@
#include <wpe/fdo.h>
#include <wpe/unstable/fdo-shm.h>

typedef struct {
struct _CogHeadlessView {
CogView parent;

bool frame_ack_pending;
struct wpe_view_backend_exportable_fdo *exportable;
} CogHeadlessView;
};

G_DECLARE_FINAL_TYPE(CogHeadlessView, cog_headless_view, COG, HEADLESS_VIEW, CogView)
G_DEFINE_DYNAMIC_TYPE(CogHeadlessView, cog_headless_view, COG_TYPE_VIEW)

struct _CogHeadlessPlatformClass {
CogPlatformClass parent_class;
Expand All @@ -26,8 +31,7 @@ struct _CogHeadlessPlatform {
unsigned max_fps;
unsigned tick_source;

/* TODO: Move elsewhere once multiple views are supported */
CogHeadlessView view;
GPtrArray *viewports; /* CogViewport */
};

G_DECLARE_FINAL_TYPE(CogHeadlessPlatform, cog_headless_platform, COG, HEADLESS_PLATFORM, CogPlatform)
Expand All @@ -46,25 +50,66 @@ static void on_export_shm_buffer(void* data, struct wpe_fdo_shm_exported_buffer*
view->frame_ack_pending = true;
}

static gboolean
on_cog_headless_platform_tick(CogHeadlessPlatform *self)
static void
on_cog_headless_view_backend_destroy(CogHeadlessView *self)
{
g_debug("%s: view %p, exportable %p", G_STRFUNC, self, self->exportable);
g_assert(self->exportable);
g_clear_pointer(&self->exportable, wpe_view_backend_exportable_fdo_destroy);
}

static WebKitWebViewBackend *
cog_headless_view_create_backend(CogView *view)
{
CogHeadlessView *self = COG_HEADLESS_VIEW(view);

static const struct wpe_view_backend_exportable_fdo_client client = {
.export_shm_buffer = on_export_shm_buffer,
};

self->exportable = wpe_view_backend_exportable_fdo_create(&client, self, 800, 600);

struct wpe_view_backend *view_backend = wpe_view_backend_exportable_fdo_get_view_backend(self->exportable);
return webkit_web_view_backend_new(view_backend, (GDestroyNotify) on_cog_headless_view_backend_destroy, self);
}

static void
cog_headless_view_class_init(CogHeadlessViewClass *klass)
{
CogViewClass *view_class = COG_VIEW_CLASS(klass);
view_class->create_backend = cog_headless_view_create_backend;
}

static void
cog_headless_view_class_finalize(CogHeadlessViewClass *klass G_GNUC_UNUSED)
{
}

static void
cog_headless_view_init(CogHeadlessView *self G_GNUC_UNUSED)
{
}

static void
cog_headless_view_tick(CogHeadlessView *view)
{
CogHeadlessView *view = &self->view;
if (view->frame_ack_pending) {
view->frame_ack_pending = false;
wpe_view_backend_exportable_fdo_dispatch_frame_complete(view->exportable);
}
return G_SOURCE_CONTINUE;
}

static void
cog_headless_view_initialize(CogHeadlessView *self, CogHeadlessPlatform *platform)
cog_headless_viewport_tick(CogViewport *viewport)
{
static const struct wpe_view_backend_exportable_fdo_client client = {
.export_shm_buffer = on_export_shm_buffer,
};
cog_viewport_foreach(viewport, (GFunc) cog_headless_view_tick, NULL);
}

self->exportable = wpe_view_backend_exportable_fdo_create(&client, self, 800, 600);
static gboolean
on_cog_headless_platform_tick(CogHeadlessPlatform *self)
{
g_ptr_array_foreach(self->viewports, (GFunc) cog_headless_viewport_tick, NULL);
return G_SOURCE_CONTINUE;
}

static gboolean
Expand All @@ -84,7 +129,6 @@ cog_headless_platform_setup(CogPlatform* platform, CogShell* shell G_GNUC_UNUSED
}
g_debug("Maximum refresh rate: %u FPS", self->max_fps);

cog_headless_view_initialize(&self->view, self);
self->tick_source = g_timeout_add(1000.0 / self->max_fps, G_SOURCE_FUNC(on_cog_headless_platform_tick), self);
return TRUE;
}
Expand All @@ -95,28 +139,31 @@ cog_headless_platform_finalize(GObject* object)
CogHeadlessPlatform *self = COG_HEADLESS_PLATFORM(object);

g_clear_handle_id(&self->tick_source, g_source_remove);
g_clear_pointer(&self->viewports, g_ptr_array_unref);

G_OBJECT_CLASS(cog_headless_platform_parent_class)->finalize(object);
}

static void
on_cog_headless_view_backend_destroy(CogHeadlessView *self)
cog_headless_viewport_created(CogPlatform *platform, CogViewport *viewport)
{
g_assert(self->exportable);
g_clear_pointer(&self->exportable, wpe_view_backend_exportable_fdo_destroy);
CogHeadlessPlatform *self = COG_HEADLESS_PLATFORM(platform);

g_assert(!g_ptr_array_find(self->viewports, viewport, NULL));
g_ptr_array_add(self->viewports, viewport);

g_debug("%s: new viewport %p", G_STRFUNC, viewport);
}

static WebKitWebViewBackend*
cog_headless_platform_get_view_backend(CogPlatform* platform, WebKitWebView* related_view, GError** error)
static void
cog_headless_viewport_disposed(CogPlatform *platform, CogViewport *viewport)
{
CogHeadlessPlatform *self = COG_HEADLESS_PLATFORM(platform);

struct wpe_view_backend *view_backend = wpe_view_backend_exportable_fdo_get_view_backend(self->view.exportable);
WebKitWebViewBackend *wk_view_backend =
webkit_web_view_backend_new(view_backend, (GDestroyNotify) on_cog_headless_view_backend_destroy, &self->view);
gboolean removed G_GNUC_UNUSED = g_ptr_array_remove_fast(self->viewports, viewport);
g_assert(removed);

g_assert(wk_view_backend);
return wk_view_backend;
g_debug("%s: removed viewport %p", G_STRFUNC, viewport);
}

static void
Expand All @@ -127,7 +174,9 @@ cog_headless_platform_class_init(CogHeadlessPlatformClass* klass)

CogPlatformClass* platform_class = COG_PLATFORM_CLASS(klass);
platform_class->setup = cog_headless_platform_setup;
platform_class->get_view_backend = cog_headless_platform_get_view_backend;
platform_class->get_view_type = cog_headless_view_get_type;
platform_class->viewport_created = cog_headless_viewport_created;
platform_class->viewport_disposed = cog_headless_viewport_disposed;
}

static void
Expand All @@ -138,6 +187,7 @@ cog_headless_platform_class_finalize(CogHeadlessPlatformClass* klass G_GNUC_UNUS
static void
cog_headless_platform_init(CogHeadlessPlatform* self)
{
self->viewports = g_ptr_array_sized_new(3);
self->max_fps = 30; /* Default value */
}

Expand All @@ -146,6 +196,7 @@ g_io_cogplatform_headless_load(GIOModule* module)
{
GTypeModule* type_module = G_TYPE_MODULE(module);
cog_headless_platform_register_type(type_module);
cog_headless_view_register_type(type_module);
}

G_MODULE_EXPORT void
Expand Down

0 comments on commit c967895

Please sign in to comment.