d243beb538b2adef4719be6ca5a6b6a5c8cd9021
[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 GTK_CHECK_VERSION(3, 16, 0)
142     if (m_gdkGLContext && m_gdkGLContext.get() == gdk_gl_context_get_current())
143         gdk_gl_context_clear_current();
144 #endif
145 }
146
147 void AcceleratedBackingStoreWayland::tryEnsureGLContext()
148 {
149     if (m_glContextInitialized)
150         return;
151
152     m_glContextInitialized = true;
153
154 #if GTK_CHECK_VERSION(3, 16, 0)
155     GUniqueOutPtr<GError> error;
156     m_gdkGLContext = adoptGRef(gdk_window_create_gl_context(gtk_widget_get_window(m_webPage.viewWidget()), &error.outPtr()));
157     if (m_gdkGLContext) {
158 #if USE(OPENGL_ES)
159         gdk_gl_context_set_use_es(m_gdkGLContext.get(), TRUE);
160 #endif
161         return;
162     }
163
164     g_warning("GDK is not able to create a GL context, falling back to glReadPixels (slow!): %s", error->message);
165 #endif
166
167     m_glContext = GLContext::createOffscreenContext();
168 }
169
170 bool AcceleratedBackingStoreWayland::makeContextCurrent()
171 {
172     tryEnsureGLContext();
173
174 #if GTK_CHECK_VERSION(3, 16, 0)
175     if (m_gdkGLContext) {
176         gdk_gl_context_make_current(m_gdkGLContext.get());
177         return true;
178     }
179 #endif
180
181     return m_glContext ? m_glContext->makeContextCurrent() : false;
182 }
183
184 #if USE(WPE_RENDERER)
185 void AcceleratedBackingStoreWayland::update(const LayerTreeContext& context)
186 {
187     if (m_surfaceID == context.contextID)
188         return;
189
190     m_surfaceID = context.contextID;
191     if (m_pendingImage) {
192         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
193         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_pendingImage);
194         m_pendingImage = nullptr;
195     }
196 }
197
198 int AcceleratedBackingStoreWayland::renderHostFileDescriptor()
199 {
200     return wpe_view_backend_get_renderer_host_fd(wpe_view_backend_exportable_fdo_get_view_backend(m_exportable));
201 }
202
203 void AcceleratedBackingStoreWayland::displayBuffer(struct wpe_fdo_egl_exported_image* image)
204 {
205     if (!m_surfaceID) {
206         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
207         if (image != m_committedImage)
208             wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, image);
209         return;
210     }
211
212     if (!m_viewTexture) {
213         if (!makeContextCurrent())
214             return;
215
216         glGenTextures(1, &m_viewTexture);
217         glBindTexture(GL_TEXTURE_2D, m_viewTexture);
218         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
219         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
220         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
221         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
222     }
223
224     if (m_pendingImage)
225         wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_pendingImage);
226     m_pendingImage = image;
227
228     m_webPage.setViewNeedsDisplay(IntRect(IntPoint::zero(), m_webPage.viewSize()));
229 }
230 #endif
231
232 bool AcceleratedBackingStoreWayland::paint(cairo_t* cr, const IntRect& clipRect)
233 {
234     GLuint texture;
235     IntSize textureSize;
236
237 #if USE(WPE_RENDERER)
238     if (!makeContextCurrent())
239         return false;
240
241     if (m_pendingImage) {
242         wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
243
244         if (m_committedImage)
245             wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_committedImage);
246         m_committedImage = m_pendingImage;
247         m_pendingImage = nullptr;
248     }
249
250     if (!m_committedImage)
251         return true;
252
253     glBindTexture(GL_TEXTURE_2D, m_viewTexture);
254     glImageTargetTexture2D(GL_TEXTURE_2D, wpe_fdo_egl_exported_image_get_egl_image(m_committedImage));
255
256     texture = m_viewTexture;
257     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)) };
258 #else
259     if (!WaylandCompositor::singleton().getTexture(m_webPage, texture, textureSize))
260         return false;
261 #endif
262
263     cairo_save(cr);
264
265 #if GTK_CHECK_VERSION(3, 16, 0)
266     if (m_gdkGLContext) {
267         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());
268         cairo_restore(cr);
269         return true;
270     }
271 #endif
272
273     ASSERT(m_glContext);
274
275     if (!m_surface || cairo_image_surface_get_width(m_surface.get()) != textureSize.width() || cairo_image_surface_get_height(m_surface.get()) != textureSize.height())
276         m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, textureSize.width(), textureSize.height()));
277
278     cairoSurfaceSetDeviceScale(m_surface.get(), m_webPage.deviceScaleFactor(), m_webPage.deviceScaleFactor());
279
280     GLuint fb;
281     glGenFramebuffers(1, &fb);
282     glBindFramebuffer(GL_FRAMEBUFFER, fb);
283     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
284
285     glPixelStorei(GL_PACK_ALIGNMENT, 4);
286
287 #if USE(OPENGL_ES)
288     unsigned char* data = cairo_image_surface_get_data(m_surface.get());
289     if (cairo_image_surface_get_stride(m_surface.get()) == textureSize.width() * 4)
290         glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, data);
291     else {
292         int strideBytes = cairo_image_surface_get_stride(m_surface.get());
293         for (int i = 0; i < textureSize.height(); i++) {
294             unsigned char* dataOffset = data + i * strideBytes;
295             glReadPixels(0, i, textureSize.width(), 1, GL_RGBA, GL_UNSIGNED_BYTE, dataOffset);
296         }
297     }
298
299     // Convert to BGRA.
300     int totalBytes = textureSize.width() * textureSize.height() * 4;
301     for (int i = 0; i < totalBytes; i += 4)
302         std::swap(data[i], data[i + 2]);
303 #else
304     glPixelStorei(GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride(m_surface.get()) / 4);
305     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()));
306     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
307 #endif
308
309     glBindFramebuffer(GL_FRAMEBUFFER, 0);
310     glDeleteFramebuffers(1, &fb);
311
312     // The surface can be modified by the web process at any time, so we mark it
313     // as dirty to ensure we always render the updated contents as soon as possible.
314     cairo_surface_mark_dirty(m_surface.get());
315
316     // The compositor renders the texture flipped for gdk_cairo_draw_from_gl, fix that here.
317     cairo_matrix_t transform;
318     cairo_matrix_init(&transform, 1, 0, 0, -1, 0, textureSize.height() / m_webPage.deviceScaleFactor());
319     cairo_transform(cr, &transform);
320
321     cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
322     cairo_set_source_surface(cr, m_surface.get(), 0, 0);
323     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
324     cairo_fill(cr);
325
326     cairo_restore(cr);
327
328     return true;
329 }
330
331 } // namespace WebKit
332
333 #endif // PLATFORM(WAYLAND) && USE(EGL)