[GTK] Configures but fails to link with ENABLE_OPENGL=OFF
[WebKit-https.git] / Source / WebKit2 / UIProcess / gtk / WaylandCompositor.cpp
1 /*
2  * Copyright (C) 2016 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WaylandCompositor.h"
28
29 #if PLATFORM(WAYLAND) && USE(EGL)
30
31 #include "WebKit2WaylandServerProtocol.h"
32 #include <EGL/egl.h>
33 #include <EGL/eglext.h>
34 #include <WebCore/GLContext.h>
35 #include <WebCore/PlatformDisplayWayland.h>
36 #include <WebCore/Region.h>
37 #include <WebCore/UUID.h>
38 #include <wayland-server-protocol.h>
39
40 #if USE(OPENGL_ES_2)
41 #include <GLES2/gl2.h>
42 #include <GLES2/gl2ext.h>
43 #else
44 #include <WebCore/OpenGLShims.h>
45 #endif
46
47 using namespace WebCore;
48
49 namespace WebKit {
50
51 #if !defined(PFNEGLBINDWAYLANDDISPLAYWL)
52 typedef EGLBoolean (*PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*);
53 #endif
54
55 #if !defined(PFNEGLUNBINDWAYLANDDISPLAYWL)
56 typedef EGLBoolean (*PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*);
57 #endif
58
59 #if !defined(PFNEGLQUERYWAYLANDBUFFERWL)
60 typedef EGLBoolean (*PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay, struct wl_resource*, EGLint attribute, EGLint* value);
61 #endif
62
63 #if !defined(PFNEGLCREATEIMAGEKHRPROC)
64 typedef EGLImageKHR (*PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay, EGLContext, EGLenum target, EGLClientBuffer, const EGLint* attribList);
65 #endif
66
67 #if !defined(PFNEGLDESTROYIMAGEKHRPROC)
68 typedef EGLBoolean (*PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay, EGLImageKHR);
69 #endif
70
71 #if !defined(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
72 typedef void (*PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES);
73 #endif
74
75 static PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplay;
76 static PFNEGLUNBINDWAYLANDDISPLAYWL eglUnbindWaylandDisplay;
77 static PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBuffer;
78 static PFNEGLCREATEIMAGEKHRPROC eglCreateImage;
79 static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImage;
80 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glImageTargetTexture2D;
81
82 WaylandCompositor& WaylandCompositor::singleton()
83 {
84     static NeverDestroyed<WaylandCompositor> waylandCompositor;
85     return waylandCompositor;
86 }
87
88 WaylandCompositor::Buffer* WaylandCompositor::Buffer::getOrCreate(struct wl_resource* resource)
89 {
90     if (struct wl_listener* listener = wl_resource_get_destroy_listener(resource, destroyListenerCallback)) {
91         WaylandCompositor::Buffer* buffer;
92         return wl_container_of(listener, buffer, m_destroyListener);
93     }
94
95     return new WaylandCompositor::Buffer(resource);
96 }
97
98 WaylandCompositor::Buffer::Buffer(struct wl_resource* resource)
99     : m_resource(resource)
100     , m_weakPtrFactory(this)
101 {
102     wl_list_init(&m_destroyListener.link);
103     m_destroyListener.notify = destroyListenerCallback;
104     wl_resource_add_destroy_listener(m_resource, &m_destroyListener);
105 }
106
107 WaylandCompositor::Buffer::~Buffer()
108 {
109     wl_list_remove(&m_destroyListener.link);
110 }
111
112 void WaylandCompositor::Buffer::destroyListenerCallback(struct wl_listener* listener, void*)
113 {
114     WaylandCompositor::Buffer* buffer;
115     buffer = wl_container_of(listener, buffer, m_destroyListener);
116     delete buffer;
117 }
118
119 void WaylandCompositor::Buffer::use()
120 {
121     m_busyCount++;
122 }
123
124 void WaylandCompositor::Buffer::unuse()
125 {
126     m_busyCount--;
127     if (!m_busyCount)
128         wl_resource_queue_event(m_resource, WL_BUFFER_RELEASE);
129 }
130
131 EGLImageKHR WaylandCompositor::Buffer::createImage() const
132 {
133     return static_cast<EGLImageKHR*>(eglCreateImage(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, m_resource, nullptr));
134 }
135
136 IntSize WaylandCompositor::Buffer::size() const
137 {
138     EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay();
139     int width, height;
140     eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_WIDTH, &width);
141     eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_HEIGHT, &height);
142
143     return { width, height };
144 }
145
146 WaylandCompositor::Surface::Surface()
147     : m_image(EGL_NO_IMAGE_KHR)
148 {
149     glGenTextures(1, &m_texture);
150     glBindTexture(GL_TEXTURE_2D, m_texture);
151     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
152     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
153     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
154     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
155 }
156
157 WaylandCompositor::Surface::~Surface()
158 {
159     // Destroy pending frame callbacks.
160     auto list = WTFMove(m_frameCallbackList);
161     for (auto* resource : list)
162         wl_resource_destroy(resource);
163
164     if (m_buffer)
165         m_buffer->unuse();
166
167     if (m_image != EGL_NO_IMAGE_KHR)
168         eglDestroyImage(PlatformDisplay::sharedDisplay().eglDisplay(), m_image);
169
170     glDeleteTextures(1, &m_texture);
171 }
172
173 void WaylandCompositor::Surface::makePendingBufferCurrent()
174 {
175     if (m_pendingBuffer == m_buffer)
176         return;
177
178     if (m_buffer)
179         m_buffer->unuse();
180
181     if (m_pendingBuffer)
182         m_pendingBuffer->use();
183
184     m_buffer = m_pendingBuffer;
185 }
186
187 void WaylandCompositor::Surface::attachBuffer(struct wl_resource* buffer)
188 {
189     if (m_pendingBuffer)
190         m_pendingBuffer = nullptr;
191
192     if (buffer) {
193         auto* compositorBuffer = WaylandCompositor::Buffer::getOrCreate(buffer);
194         m_pendingBuffer = compositorBuffer->createWeakPtr();
195     }
196 }
197
198 void WaylandCompositor::Surface::requestFrame(struct wl_resource* resource)
199 {
200     wl_resource_set_implementation(resource, nullptr, this, [](struct wl_resource* resource) {
201         auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource));
202         if (size_t item = surface->m_frameCallbackList.find(resource) != notFound)
203             surface->m_frameCallbackList.remove(item);
204     });
205     m_frameCallbackList.append(resource);
206 }
207
208 bool WaylandCompositor::Surface::prepareTextureForPainting(unsigned& texture, IntSize& textureSize)
209 {
210     if (m_image == EGL_NO_IMAGE_KHR)
211         return false;
212
213     glBindTexture(GL_TEXTURE_2D, m_texture);
214     glImageTargetTexture2D(GL_TEXTURE_2D, m_image);
215
216     texture = m_texture;
217     textureSize = m_buffer->size();
218     return true;
219 }
220
221 void WaylandCompositor::Surface::commit()
222 {
223     EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay();
224     if (m_image != EGL_NO_IMAGE_KHR)
225         eglDestroyImage(eglDisplay, m_image);
226     m_image = m_pendingBuffer->createImage();
227     if (m_image == EGL_NO_IMAGE_KHR)
228         return;
229
230     makePendingBufferCurrent();
231     if (m_webPage)
232         m_webPage->setViewNeedsDisplay(IntRect(IntPoint::zero(), m_webPage->viewSize()));
233
234     // From a Wayland point-of-view frame callbacks should be fired where the
235     // compositor knows it has *used* the committed contents, so firing them here
236     // can be surprising but we don't need them as a throttling mechanism because
237     // rendering synchronization is handled elsewhere by WebKit.
238     auto list = WTFMove(m_frameCallbackList);
239     for (auto* resource : list) {
240         wl_callback_send_done(resource, 0);
241         wl_resource_destroy(resource);
242     }
243 }
244
245 static const struct wl_surface_interface surfaceInterface = {
246     // destroyCallback
247     [](struct wl_client*, struct wl_resource* resource)
248     {
249         wl_resource_destroy(resource);
250     },
251     // attachCallback
252     [](struct wl_client* client, struct wl_resource* resource, struct wl_resource* buffer, int32_t sx, int32_t sy)
253     {
254         auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource));
255         if (!surface)
256             return;
257
258         EGLint format;
259         if (!eglQueryWaylandBuffer(PlatformDisplay::sharedDisplay().eglDisplay(), buffer, EGL_TEXTURE_FORMAT, &format)
260             || (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA))
261             return;
262
263         surface->attachBuffer(buffer);
264     },
265     // damageCallback
266     [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { },
267     // frameCallback
268     [](struct wl_client* client, struct wl_resource* resource, uint32_t id)
269     {
270         auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource));
271         if (!surface)
272             return;
273
274         if (struct wl_resource* callbackResource = wl_resource_create(client, &wl_callback_interface, 1, id))
275             surface->requestFrame(callbackResource);
276         else
277             wl_client_post_no_memory(client);
278     },
279     // setOpaqueRegionCallback
280     [](struct wl_client*, struct wl_resource*, struct wl_resource*) { },
281     // setInputRegionCallback
282     [](struct wl_client*, struct wl_resource*, struct wl_resource*) { },
283     // commitCallback
284     [](struct wl_client* client, struct wl_resource* resource)
285     {
286         auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource));
287         if (!surface)
288             return;
289         surface->commit();
290     },
291     // setBufferTransformCallback
292     [](struct wl_client*, struct wl_resource*, int32_t) { },
293     // setBufferScaleCallback
294     [](struct wl_client*, struct wl_resource*, int32_t) { },
295 #if WAYLAND_VERSION_MAJOR > 1 || (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR >= 10)
296     // damageBufferCallback
297     [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { },
298 #endif
299 };
300
301 static const struct wl_compositor_interface compositorInterface = {
302     // createSurfaceCallback
303     [](struct wl_client* client, struct wl_resource* resource, uint32_t id)
304     {
305         if (struct wl_resource* surfaceResource = wl_resource_create(client, &wl_surface_interface, 1, id)) {
306             wl_resource_set_implementation(surfaceResource, &surfaceInterface, new WaylandCompositor::Surface(),
307                 [](struct wl_resource* resource) {
308                     auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource));
309                     delete surface;
310                 });
311         } else
312             wl_client_post_no_memory(client);
313     },
314     // createRegionCallback
315     [](struct wl_client*, struct wl_resource*, uint32_t) { }
316 };
317
318 static const struct wl_webkitgtk_interface webkitgtkInterface = {
319     // bindSurfaceToPageCallback
320     [](struct wl_client*, struct wl_resource* resource, struct wl_resource* surfaceResource, uint32_t pageID)
321     {
322         auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(surfaceResource));
323         if (!surface)
324             return;
325
326         auto* compositor = static_cast<WaylandCompositor*>(wl_resource_get_user_data(resource));
327         compositor->bindSurfaceToWebPage(surface, pageID);
328     }
329 };
330
331 bool WaylandCompositor::initializeEGL()
332 {
333     if (PlatformDisplay::sharedDisplay().eglCheckVersion(1, 5)) {
334         eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImage"));
335         eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImage"));
336     } else {
337         const char* extensions = eglQueryString(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_EXTENSIONS);
338         if (GLContext::isExtensionSupported(extensions, "EGL_KHR_image_base")) {
339             eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
340             eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
341         }
342     }
343     if (!eglCreateImage || !eglDestroyImage) {
344         WTFLogAlways("WaylandCompositor requires eglCreateImage and eglDestroyImage.");
345         return false;
346     }
347
348     glImageTargetTexture2D = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
349     if (!glImageTargetTexture2D) {
350         WTFLogAlways("WaylandCompositor requires glEGLImageTargetTexture2D.");
351         return false;
352     }
353
354     eglQueryWaylandBuffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL"));
355     if (!eglQueryWaylandBuffer) {
356         WTFLogAlways("WaylandCompositor requires eglQueryWaylandBuffer.");
357         return false;
358     }
359
360     eglBindWaylandDisplay = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
361     eglUnbindWaylandDisplay = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
362     if (!eglBindWaylandDisplay || !eglUnbindWaylandDisplay) {
363         WTFLogAlways("WaylandCompositor requires eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
364         return false;
365     }
366
367     m_eglContext = GLContext::createOffscreenContext();
368     if (!m_eglContext)
369         return false;
370
371     if (!m_eglContext->makeContextCurrent())
372         return false;
373
374     return true;
375 }
376
377 typedef struct {
378     GSource source;
379     gpointer fdTag;
380     struct wl_display* display;
381 } WaylandLoopSource;
382
383 static const unsigned waylandLoopSourceCondition = G_IO_IN | G_IO_HUP | G_IO_ERR;
384
385 static GSourceFuncs waylandLoopSourceFunctions = {
386     // prepare
387     [](GSource *source, int *timeout) -> gboolean
388     {
389         *timeout = -1;
390         auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source);
391         wl_display_flush_clients(wlLoopSource->display);
392         return FALSE;
393     },
394     nullptr, // check
395     // dispatch
396     [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
397     {
398         auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source);
399         unsigned events = g_source_query_unix_fd(source, wlLoopSource->fdTag) & waylandLoopSourceCondition;
400         if (events & G_IO_HUP || events & G_IO_ERR) {
401             WTFLogAlways("Wayland Display Event Source: lost connection to nested Wayland compositor");
402             return G_SOURCE_REMOVE;
403         }
404
405         if (events & G_IO_IN)
406             wl_event_loop_dispatch(wl_display_get_event_loop(wlLoopSource->display), 0);
407         return G_SOURCE_CONTINUE;
408     },
409     nullptr, // finalize
410     nullptr, // closure_callback
411     nullptr, // closure_marshall
412 };
413
414 static GRefPtr<GSource> createWaylandLoopSource(struct wl_display* display)
415 {
416     GRefPtr<GSource> source = adoptGRef(g_source_new(&waylandLoopSourceFunctions, sizeof(WaylandLoopSource)));
417     g_source_set_name(source.get(), "Nested Wayland compositor display event source");
418     g_source_set_priority(source.get(), G_PRIORITY_DEFAULT + 1);
419
420     auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source.get());
421     wlLoopSource->display = display;
422     wlLoopSource->fdTag = g_source_add_unix_fd(source.get(), wl_event_loop_get_fd(wl_display_get_event_loop(display)), static_cast<GIOCondition>(waylandLoopSourceCondition));
423     g_source_attach(source.get(), nullptr);
424
425     return source;
426 }
427
428 WaylandCompositor::WaylandCompositor()
429 {
430     WlUniquePtr<struct wl_display> display(wl_display_create());
431     if (!display) {
432         WTFLogAlways("Nested Wayland compositor could not create display object");
433         return;
434     }
435
436     String displayName = "webkitgtk-wayland-compositor-" + createCanonicalUUIDString();
437     if (wl_display_add_socket(display.get(), displayName.utf8().data()) == -1) {
438         WTFLogAlways("Nested Wayland compositor could not create display socket");
439         return;
440     }
441
442     WlUniquePtr<struct wl_global> compositorGlobal(wl_global_create(display.get(), &wl_compositor_interface, wl_compositor_interface.version, this,
443         [](struct wl_client* client, void* data, uint32_t version, uint32_t id) {
444             if (struct wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, std::min(static_cast<int>(version), 3), id))
445                 wl_resource_set_implementation(resource, &compositorInterface, static_cast<WaylandCompositor*>(data), nullptr);
446             else
447                 wl_client_post_no_memory(client);
448         }));
449     if (!compositorGlobal) {
450         WTFLogAlways("Nested Wayland compositor could not register compositor global");
451         return;
452     }
453
454     WlUniquePtr<struct wl_global> webkitgtkGlobal(wl_global_create(display.get(), &wl_webkitgtk_interface, 1, this,
455         [](struct wl_client* client, void* data, uint32_t version, uint32_t id) {
456             if (struct wl_resource* resource = wl_resource_create(client, &wl_webkitgtk_interface, 1, id))
457                 wl_resource_set_implementation(resource, &webkitgtkInterface, static_cast<WaylandCompositor*>(data), nullptr);
458             else
459                 wl_client_post_no_memory(client);
460         }));
461     if (!webkitgtkGlobal) {
462         WTFLogAlways("Nested Wayland compositor could not register webkitgtk global");
463         return;
464     }
465
466     if (!initializeEGL()) {
467         WTFLogAlways("Nested Wayland compositor could not initialize EGL");
468         return;
469     }
470
471     if (!eglBindWaylandDisplay(PlatformDisplay::sharedDisplay().eglDisplay(), display.get())) {
472         WTFLogAlways("Nested Wayland compositor could not bind nested display");
473         return;
474     }
475
476     m_displayName = WTFMove(displayName);
477     m_display = WTFMove(display);
478     m_compositorGlobal = WTFMove(compositorGlobal);
479     m_webkitgtkGlobal = WTFMove(webkitgtkGlobal);
480     m_eventSource = createWaylandLoopSource(m_display.get());
481 }
482
483 bool WaylandCompositor::getTexture(WebPageProxy& webPage, unsigned& texture, IntSize& textureSize)
484 {
485     if (auto* surface = m_pageMap.get(&webPage))
486         return surface->prepareTextureForPainting(texture, textureSize);
487     return false;
488 }
489
490 void WaylandCompositor::bindSurfaceToWebPage(WaylandCompositor::Surface* surface, uint64_t pageID)
491 {
492     WebPageProxy* webPage = nullptr;
493     for (auto* page : m_pageMap.keys()) {
494         if (page->pageID() == pageID) {
495             webPage = page;
496             break;
497         }
498     }
499     if (!webPage)
500         return;
501
502     surface->setWebPage(webPage);
503     m_pageMap.set(webPage, surface);
504 }
505
506 void WaylandCompositor::registerWebPage(WebPageProxy& webPage)
507 {
508     m_pageMap.add(&webPage, nullptr);
509 }
510
511 void WaylandCompositor::unregisterWebPage(WebPageProxy& webPage)
512 {
513     if (auto* surface = m_pageMap.take(&webPage))
514         surface->setWebPage(nullptr);
515 }
516
517 } // namespace WebKit
518
519 #endif // PLATFORM(WAYLAND) && USE(EGL)