[WPE][GTK] Bump minimum versions of GLib, GTK, libsoup, ATK, GStreamer, and Cairo
[WebKit-https.git] / Source / WebKit / UIProcess / gtk / AcceleratedBackingStoreWayland.cpp
1 /*
2  * Copyright (C) 2016, 2019 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 "AcceleratedBackingStoreWayland.h"
28
29 #if PLATFORM(WAYLAND) && USE(EGL)
30
31 #include "LayerTreeContext.h"
32 #include "WebPageProxy.h"
33 // These includes need to be in this order because wayland-egl.h defines WL_EGL_PLATFORM
34 // and eglplatform.h, included by egl.h, checks that to decide whether it's Wayland platform.
35 #include <gdk/gdkwayland.h>
36 #include <EGL/egl.h>
37 #include <EGL/eglext.h>
38 #include <WebCore/CairoUtilities.h>
39 #include <WebCore/GLContext.h>
40
41 #if USE(OPENGL_ES)
42 #include <GLES2/gl2.h>
43 #include <GLES2/gl2ext.h>
44 #include <WebCore/Extensions3DOpenGLES.h>
45 #else
46 #include <WebCore/Extensions3DOpenGL.h>
47 #include <WebCore/OpenGLShims.h>
48 #endif
49
50 #if USE(WPE_RENDERER)
51 #include <wpe/fdo-egl.h>
52 #else
53 #include "WaylandCompositor.h"
54 #endif
55
56 #if USE(WPE_RENDERER)
57 #if !defined(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
58 typedef void (*PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES);
59 #endif
60
61 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glImageTargetTexture2D;
62 #endif
63
64 namespace WebKit {
65 using namespace WebCore;
66
67 std::unique_ptr<AcceleratedBackingStoreWayland> AcceleratedBackingStoreWayland::create(WebPageProxy& webPage)
68 {
69 #if USE(WPE_RENDERER)
70     if (!glImageTargetTexture2D) {
71         if (!wpe_fdo_initialize_for_egl_display(PlatformDisplay::sharedDisplay().eglDisplay()))
72             return nullptr;
73
74         std::unique_ptr<WebCore::GLContext> eglContext = GLContext::createOffscreenContext();
75         if (!eglContext)
76             return nullptr;
77
78         if (!eglContext->makeContextCurrent())
79             return nullptr;
80
81 #if USE(OPENGL_ES)
82         std::unique_ptr<Extensions3DOpenGLES> glExtensions = std::make_unique<Extensions3DOpenGLES>(nullptr,  false);
83 #else
84         std::unique_ptr<Extensions3DOpenGL> glExtensions = std::make_unique<Extensions3DOpenGL>(nullptr, GLContext::current()->version() >= 320);
85 #endif
86         if (glExtensions->supports("GL_OES_EGL_image") || glExtensions->supports("GL_OES_EGL_image_external"))
87             glImageTargetTexture2D = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
88     }
89
90     if (!glImageTargetTexture2D) {
91         WTFLogAlways("AcceleratedBackingStoreWPE requires glEGLImageTargetTexture2D.");
92         return nullptr;
93     }
94 #else
95     if (!WaylandCompositor::singleton().isRunning())
96         return nullptr;
97 #endif
98     return std::unique_ptr<AcceleratedBackingStoreWayland>(new AcceleratedBackingStoreWayland(webPage));
99 }
100
101 AcceleratedBackingStoreWayland::AcceleratedBackingStoreWayland(WebPageProxy& webPage)
102     : AcceleratedBackingStore(webPage)
103 {
104 #if USE(WPE_RENDERER)
105     static struct wpe_view_backend_exportable_fdo_egl_client exportableClient = {
106         // export_egl_image
107         nullptr,
108         // export_fdo_egl_image
109         [](void* data, struct wpe_fdo_egl_exported_image* image)
110         {
111             static_cast<AcceleratedBackingStoreWayland*>(data)->displayBuffer(image);
112         },
113         // padding
114         nullptr, nullptr, nullptr
115     };
116
117     auto viewSize = webPage.viewSize();
118     m_exportable = wpe_view_backend_exportable_fdo_egl_create(&exportableClient, this, viewSize.width(), viewSize.height());
119     wpe_view_backend_initialize(wpe_view_backend_exportable_fdo_get_view_backend(m_exportable));
120 #else
121     WaylandCompositor::singleton().registerWebPage(m_webPage);
122 #endif
123 }
124
125 AcceleratedBackingStoreWayland::~AcceleratedBackingStoreWayland()
126 {
127 #if USE(WPE_RENDERER)
128     if (m_pendingImage)
129         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_pendingImage);
130     if (m_committedImage)
131         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_committedImage);
132     if (m_viewTexture) {
133         if (makeContextCurrent())
134             glDeleteTextures(1, &m_viewTexture);
135     }
136     wpe_view_backend_exportable_fdo_destroy(m_exportable);
137 #else
138     WaylandCompositor::singleton().unregisterWebPage(m_webPage);
139 #endif
140
141     if (m_gdkGLContext && m_gdkGLContext.get() == gdk_gl_context_get_current())
142         gdk_gl_context_clear_current();
143 }
144
145 void AcceleratedBackingStoreWayland::tryEnsureGLContext()
146 {
147     if (m_glContextInitialized)
148         return;
149
150     m_glContextInitialized = true;
151
152     GUniqueOutPtr<GError> error;
153     m_gdkGLContext = adoptGRef(gdk_window_create_gl_context(gtk_widget_get_window(m_webPage.viewWidget()), &error.outPtr()));
154     if (m_gdkGLContext) {
155 #if USE(OPENGL_ES)
156         gdk_gl_context_set_use_es(m_gdkGLContext.get(), TRUE);
157 #endif
158         return;
159     }
160
161     g_warning("GDK is not able to create a GL context, falling back to glReadPixels (slow!): %s", error->message);
162
163     m_glContext = GLContext::createOffscreenContext();
164 }
165
166 bool AcceleratedBackingStoreWayland::makeContextCurrent()
167 {
168     tryEnsureGLContext();
169
170     if (m_gdkGLContext) {
171         gdk_gl_context_make_current(m_gdkGLContext.get());
172         return true;
173     }
174     return m_glContext ? m_glContext->makeContextCurrent() : false;
175 }
176
177 #if USE(WPE_RENDERER)
178 void AcceleratedBackingStoreWayland::update(const LayerTreeContext& context)
179 {
180     if (m_surfaceID == context.contextID)
181         return;
182
183     m_surfaceID = context.contextID;
184     if (m_pendingImage) {
185         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
186         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_pendingImage);
187         m_pendingImage = nullptr;
188     }
189 }
190
191 int AcceleratedBackingStoreWayland::renderHostFileDescriptor()
192 {
193     return wpe_view_backend_get_renderer_host_fd(wpe_view_backend_exportable_fdo_get_view_backend(m_exportable));
194 }
195
196 void AcceleratedBackingStoreWayland::displayBuffer(struct wpe_fdo_egl_exported_image* image)
197 {
198     if (!m_surfaceID) {
199         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
200         if (image != m_committedImage)
201             wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, image);
202         return;
203     }
204
205     if (!m_viewTexture) {
206         if (!makeContextCurrent())
207             return;
208
209         glGenTextures(1, &m_viewTexture);
210         glBindTexture(GL_TEXTURE_2D, m_viewTexture);
211         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
212         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
213         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
214         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
215     }
216
217     if (m_pendingImage)
218         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_pendingImage);
219     m_pendingImage = image;
220
221     m_webPage.setViewNeedsDisplay(IntRect(IntPoint::zero(), m_webPage.viewSize()));
222 }
223 #endif
224
225 bool AcceleratedBackingStoreWayland::paint(cairo_t* cr, const IntRect& clipRect)
226 {
227     GLuint texture;
228     IntSize textureSize;
229
230 #if USE(WPE_RENDERER)
231     if (!makeContextCurrent())
232         return false;
233
234     if (m_pendingImage) {
235         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
236
237         if (m_committedImage)
238             wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_committedImage);
239         m_committedImage = m_pendingImage;
240         m_pendingImage = nullptr;
241     }
242
243     if (!m_committedImage)
244         return true;
245
246     glBindTexture(GL_TEXTURE_2D, m_viewTexture);
247     glImageTargetTexture2D(GL_TEXTURE_2D, wpe_fdo_egl_exported_image_get_egl_image(m_committedImage));
248
249     texture = m_viewTexture;
250     textureSize = { static_cast<int>(wpe_fdo_egl_exported_image_get_width(m_committedImage)), static_cast<int>(wpe_fdo_egl_exported_image_get_height(m_committedImage)) };
251 #else
252     if (!WaylandCompositor::singleton().getTexture(m_webPage, texture, textureSize))
253         return false;
254 #endif
255
256     cairo_save(cr);
257
258     if (m_gdkGLContext) {
259         gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(m_webPage.viewWidget()), texture, GL_TEXTURE, m_webPage.deviceScaleFactor(), 0, 0, textureSize.width(), textureSize.height());
260         cairo_restore(cr);
261         return true;
262     }
263
264     ASSERT(m_glContext);
265
266     if (!m_surface || cairo_image_surface_get_width(m_surface.get()) != textureSize.width() || cairo_image_surface_get_height(m_surface.get()) != textureSize.height())
267         m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, textureSize.width(), textureSize.height()));
268
269     cairoSurfaceSetDeviceScale(m_surface.get(), m_webPage.deviceScaleFactor(), m_webPage.deviceScaleFactor());
270
271     GLuint fb;
272     glGenFramebuffers(1, &fb);
273     glBindFramebuffer(GL_FRAMEBUFFER, fb);
274     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
275
276     glPixelStorei(GL_PACK_ALIGNMENT, 4);
277
278 #if USE(OPENGL_ES)
279     unsigned char* data = cairo_image_surface_get_data(m_surface.get());
280     if (cairo_image_surface_get_stride(m_surface.get()) == textureSize.width() * 4)
281         glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, data);
282     else {
283         int strideBytes = cairo_image_surface_get_stride(m_surface.get());
284         for (int i = 0; i < textureSize.height(); i++) {
285             unsigned char* dataOffset = data + i * strideBytes;
286             glReadPixels(0, i, textureSize.width(), 1, GL_RGBA, GL_UNSIGNED_BYTE, dataOffset);
287         }
288     }
289
290     // Convert to BGRA.
291     int totalBytes = textureSize.width() * textureSize.height() * 4;
292     for (int i = 0; i < totalBytes; i += 4)
293         std::swap(data[i], data[i + 2]);
294 #else
295     glPixelStorei(GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride(m_surface.get()) / 4);
296     glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(m_surface.get()));
297     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
298 #endif
299
300     glBindFramebuffer(GL_FRAMEBUFFER, 0);
301     glDeleteFramebuffers(1, &fb);
302
303     // The surface can be modified by the web process at any time, so we mark it
304     // as dirty to ensure we always render the updated contents as soon as possible.
305     cairo_surface_mark_dirty(m_surface.get());
306
307     // The compositor renders the texture flipped for gdk_cairo_draw_from_gl, fix that here.
308     cairo_matrix_t transform;
309     cairo_matrix_init(&transform, 1, 0, 0, -1, 0, textureSize.height() / m_webPage.deviceScaleFactor());
310     cairo_transform(cr, &transform);
311
312     cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
313     cairo_set_source_surface(cr, m_surface.get(), 0, 0);
314     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
315     cairo_fill(cr);
316
317     cairo_restore(cr);
318
319     return true;
320 }
321
322 } // namespace WebKit
323
324 #endif // PLATFORM(WAYLAND) && USE(EGL)