1 /* Copyright (C) <2018> Philippe Normand <philn@igalia.com>
2 * Copyright (C) <2018> Žan Doberšek <zdobersek@igalia.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include "WPEThreadedView.h"
25 #define GST_CAT_DEFAULT wpe_src_debug
27 // -70 is the GLib priority we use internally in WebKit, for WPE.
28 #define WPE_GLIB_SOURCE_PRIORITY -70
32 GMutexHolder(GMutex& mutex)
46 WPEThreadedView::WPEThreadedView()
48 g_mutex_init(&threading.mutex);
49 g_cond_init(&threading.cond);
50 g_mutex_init(&threading.ready_mutex);
51 g_cond_init(&threading.ready_cond);
53 g_mutex_init(&images.mutex);
56 GMutexHolder lock(threading.mutex);
57 threading.thread = g_thread_new("WPEThreadedView",
59 g_cond_wait(&threading.cond, &threading.mutex);
60 GST_DEBUG("thread spawned");
64 WPEThreadedView::~WPEThreadedView()
67 GMutexHolder lock(images.mutex);
70 gst_egl_image_unref(images.pending);
71 images.pending = nullptr;
73 if (images.committed) {
74 gst_egl_image_unref(images.committed);
75 images.committed = nullptr;
80 gst_object_unref(gst.display);
81 gst.display = nullptr;
85 gst_object_unref(gst.context);
86 gst.context = nullptr;
89 if (threading.thread) {
90 g_thread_unref(threading.thread);
91 threading.thread = nullptr;
94 g_mutex_clear(&threading.mutex);
95 g_cond_clear(&threading.cond);
96 g_mutex_clear(&threading.ready_mutex);
97 g_cond_clear(&threading.ready_cond);
98 g_mutex_clear(&images.mutex);
101 gpointer WPEThreadedView::s_viewThread(gpointer data)
103 auto& view = *static_cast<WPEThreadedView*>(data);
105 view.glib.context = g_main_context_new();
106 view.glib.loop = g_main_loop_new(view.glib.context, FALSE);
108 g_main_context_push_thread_default(view.glib.context);
111 GSource* source = g_idle_source_new();
112 g_source_set_callback(source,
113 [](gpointer data) -> gboolean {
114 auto& view = *static_cast<WPEThreadedView*>(data);
115 GMutexHolder lock(view.threading.mutex);
116 g_cond_signal(&view.threading.cond);
117 return G_SOURCE_REMOVE;
120 g_source_attach(source, view.glib.context);
121 g_source_unref(source);
124 g_main_loop_run(view.glib.loop);
126 g_main_loop_unref(view.glib.loop);
127 view.glib.loop = nullptr;
129 if (view.webkit.view) {
130 g_object_unref(view.webkit.view);
131 view.webkit.view = nullptr;
133 if (view.webkit.uri) {
134 g_free(view.webkit.uri);
135 view.webkit.uri = nullptr;
138 g_main_context_pop_thread_default(view.glib.context);
139 g_main_context_unref(view.glib.context);
140 view.glib.context = nullptr;
144 struct wpe_view_backend* WPEThreadedView::backend() const
146 return wpe.exportable ? wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable) : nullptr;
149 void WPEThreadedView::s_loadEvent(WebKitWebView*, WebKitLoadEvent event, gpointer data)
151 if (event == WEBKIT_LOAD_COMMITTED) {
152 auto& view = *static_cast<WPEThreadedView*>(data);
153 GMutexHolder lock(view.threading.ready_mutex);
154 g_cond_signal(&view.threading.ready_cond);
158 void WPEThreadedView::initialize(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height)
160 GST_DEBUG("context %p display %p, size (%d,%d)", context, display, width, height);
162 static std::once_flag s_loaderFlag;
163 std::call_once(s_loaderFlag,
165 #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(0, 2, 0)
166 wpe_loader_init("libWPEBackend-fdo-0.1.so");
170 struct InitializeContext {
172 WPEThreadedView& view;
173 GstGLContext* context;
174 GstGLDisplay* display;
177 } initializeContext{ src, *this, context, display, width, height };
179 GSource* source = g_idle_source_new();
180 g_source_set_callback(source,
181 [](gpointer data) -> gboolean {
182 GST_DEBUG("on view thread");
183 auto& initializeContext = *static_cast<InitializeContext*>(data);
184 auto& view = initializeContext.view;
186 GMutexHolder lock(view.threading.mutex);
188 view.gst.context = GST_GL_CONTEXT(gst_object_ref(initializeContext.context));
189 view.gst.display = GST_GL_DISPLAY(gst_object_ref(initializeContext.display));
191 view.wpe.width = initializeContext.width;
192 view.wpe.height = initializeContext.height;
194 EGLDisplay eglDisplay = gst_gl_display_egl_get_from_native(
195 GST_GL_DISPLAY_TYPE_WAYLAND,
196 gst_gl_display_get_handle(initializeContext.display));
197 GST_DEBUG("eglDisplay %p", eglDisplay);
198 wpe_fdo_initialize_for_egl_display(eglDisplay);
200 view.wpe.exportable = wpe_view_backend_exportable_fdo_egl_create(&s_exportableClient,
201 &view, view.wpe.width, view.wpe.height);
202 auto* viewBackend = webkit_web_view_backend_new(
203 wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), nullptr, nullptr);
205 view.webkit.view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
206 "backend", viewBackend, nullptr));
208 gst_wpe_src_configure_web_view(initializeContext.src, view.webkit.view);
210 g_signal_connect(view.webkit.view, "load-changed", G_CALLBACK(s_loadEvent), &view);
212 const gchar* location;
213 gboolean drawBackground = TRUE;
214 g_object_get(initializeContext.src, "location", &location, "draw-background", &drawBackground, nullptr);
216 g_warning("Invalid location");
218 view.setDrawBackground(drawBackground);
219 view.loadUriUnlocked(location);
221 g_cond_signal(&view.threading.cond);
222 return G_SOURCE_REMOVE;
224 &initializeContext, nullptr);
225 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
228 GMutexHolder lock(threading.mutex);
229 g_source_attach(source, glib.context);
230 g_cond_wait(&threading.cond, &threading.mutex);
233 g_source_unref(source);
236 GST_DEBUG("waiting load to finish");
237 GMutexHolder lock(threading.ready_mutex);
238 g_cond_wait(&threading.ready_cond, &threading.ready_mutex);
243 GstEGLImage* WPEThreadedView::image()
245 GstEGLImage* ret = nullptr;
246 GMutexHolder lock(images.mutex);
248 GST_TRACE("pending %" GST_PTR_FORMAT " committed %" GST_PTR_FORMAT, images.pending, images.committed);
250 if (images.pending) {
251 auto* previousImage = images.committed;
252 images.committed = images.pending;
253 images.pending = nullptr;
258 gst_egl_image_unref(previousImage);
261 if (images.committed)
262 ret = images.committed;
267 void WPEThreadedView::resize(int width, int height)
271 GSource* source = g_idle_source_new();
272 g_source_set_callback(source,
273 [](gpointer data) -> gboolean {
274 auto& view = *static_cast<WPEThreadedView*>(data);
275 GMutexHolder lock(view.threading.mutex);
277 GST_DEBUG("dispatching");
278 if (view.wpe.exportable && wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable))
279 wpe_view_backend_dispatch_set_size(wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), view.wpe.width, view.wpe.height);
281 g_cond_signal(&view.threading.cond);
282 return G_SOURCE_REMOVE;
285 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
288 GMutexHolder lock(threading.mutex);
289 g_source_attach(source, glib.context);
290 g_cond_wait(&threading.cond, &threading.mutex);
293 g_source_unref(source);
296 void WPEThreadedView::frameComplete()
298 GST_DEBUG("frame complete");
300 GSource* source = g_idle_source_new();
301 g_source_set_callback(source,
302 [](gpointer data) -> gboolean {
303 auto& view = *static_cast<WPEThreadedView*>(data);
304 GMutexHolder lock(view.threading.mutex);
306 GST_DEBUG("dispatching");
307 wpe_view_backend_exportable_fdo_dispatch_frame_complete(view.wpe.exportable);
309 g_cond_signal(&view.threading.cond);
310 return G_SOURCE_REMOVE;
313 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
316 GMutexHolder lock(threading.mutex);
317 g_source_attach(source, glib.context);
318 g_cond_wait(&threading.cond, &threading.mutex);
321 g_source_unref(source);
324 void WPEThreadedView::loadUriUnlocked(const gchar* uri)
328 webkit.uri = g_strdup(uri);
329 webkit_web_view_load_uri(webkit.view, webkit.uri);
332 void WPEThreadedView::loadUri(const gchar* uri)
334 GST_DEBUG("loading %s", uri);
337 WPEThreadedView& view;
339 } uriContext{ *this, uri };
341 GSource* source = g_idle_source_new();
342 g_source_set_callback(source,
343 [](gpointer data) -> gboolean {
344 GST_DEBUG("on view thread");
345 auto& uriContext = *static_cast<UriContext*>(data);
346 auto& view = uriContext.view;
347 GMutexHolder lock(view.threading.mutex);
349 view.loadUriUnlocked(uriContext.uri);
351 g_cond_signal(&view.threading.cond);
352 return G_SOURCE_REMOVE;
354 &uriContext, nullptr);
355 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
358 GMutexHolder lock(threading.mutex);
359 g_source_attach(source, glib.context);
360 g_cond_wait(&threading.cond, &threading.mutex);
364 g_source_unref(source);
367 void WPEThreadedView::setDrawBackground(gboolean drawsBackground)
370 // See https://bugs.webkit.org/show_bug.cgi?id=192305
371 GST_FIXME("set_draws_background API not upstream yet");
373 webkit_web_view_set_draws_background(webkit.view, drawsBackground);
377 void WPEThreadedView::releaseImage(EGLImageKHR image)
379 struct ReleaseImageContext {
380 WPEThreadedView& view;
382 } releaseImageContext{ *this, image };
384 GSource* source = g_idle_source_new();
385 g_source_set_callback(source,
386 [](gpointer data) -> gboolean {
387 auto& releaseImageContext = *static_cast<ReleaseImageContext*>(data);
388 auto& view = releaseImageContext.view;
389 GMutexHolder lock(view.threading.mutex);
391 wpe_view_backend_exportable_fdo_egl_dispatch_release_image(
392 releaseImageContext.view.wpe.exportable, releaseImageContext.image);
394 g_cond_signal(&view.threading.cond);
395 return G_SOURCE_REMOVE;
397 &releaseImageContext, nullptr);
398 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
401 GMutexHolder lock(threading.mutex);
402 g_source_attach(source, glib.context);
403 g_cond_wait(&threading.cond, &threading.mutex);
406 g_source_unref(source);
409 struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableClient = {
410 // export_buffer_resource
411 [](void* data, EGLImageKHR image) {
412 auto& view = *static_cast<WPEThreadedView*>(data);
413 auto* gstImage = gst_egl_image_new_wrapped(view.gst.context, image,
414 GST_GL_RGBA, &view, s_releaseImage);
415 GMutexHolder lock(view.images.mutex);
417 view.images.pending = gstImage;
420 nullptr, nullptr, nullptr, nullptr
423 void WPEThreadedView::s_releaseImage(GstEGLImage* image, gpointer data)
425 auto& view = *static_cast<WPEThreadedView*>(data);
426 GST_DEBUG("view %p image %" GST_PTR_FORMAT, &view, image);
427 view.releaseImage(gst_egl_image_get_image(image));