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 "AcceleratedBackingStoreWayland.h"
28
29#if PLATFORM(WAYLAND) && USE(EGL)
30
31#include "WaylandCompositor.h"
32#include "WebPageProxy.h"
33#include <WebCore/CairoUtilities.h>
34#include <WebCore/GLContext.h>
35
36#if USE(OPENGL_ES)
37#include <GLES2/gl2.h>
38#else
39#include <WebCore/OpenGLShims.h>
40#endif
41
42namespace WebKit {
43using namespace WebCore;
44
45std::unique_ptr<AcceleratedBackingStoreWayland> AcceleratedBackingStoreWayland::create(WebPageProxy& webPage)
46{
47 if (!WaylandCompositor::singleton().isRunning())
48 return nullptr;
49 return std::unique_ptr<AcceleratedBackingStoreWayland>(new AcceleratedBackingStoreWayland(webPage));
50}
51
52AcceleratedBackingStoreWayland::AcceleratedBackingStoreWayland(WebPageProxy& webPage)
53 : AcceleratedBackingStore(webPage)
54{
55 WaylandCompositor::singleton().registerWebPage(m_webPage);
56}
57
58AcceleratedBackingStoreWayland::~AcceleratedBackingStoreWayland()
59{
60 WaylandCompositor::singleton().unregisterWebPage(m_webPage);
61
62#if GTK_CHECK_VERSION(3, 16, 0)
63 if (m_gdkGLContext && m_gdkGLContext.get() == gdk_gl_context_get_current())
64 gdk_gl_context_clear_current();
65#endif
66}
67
68void AcceleratedBackingStoreWayland::tryEnsureGLContext()
69{
70 if (m_glContextInitialized)
71 return;
72
73 m_glContextInitialized = true;
74
75#if GTK_CHECK_VERSION(3, 16, 0)
76 GUniqueOutPtr<GError> error;
77 m_gdkGLContext = adoptGRef(gdk_window_create_gl_context(gtk_widget_get_window(m_webPage.viewWidget()), &error.outPtr()));
78 if (m_gdkGLContext) {
79#if USE(OPENGL_ES)
80 gdk_gl_context_set_use_es(m_gdkGLContext.get(), TRUE);
81#endif
82 return;
83 }
84
85 g_warning("GDK is not able to create a GL context, falling back to glReadPixels (slow!): %s", error->message);
86#endif
87
88 m_glContext = GLContext::createOffscreenContext();
89}
90
91bool AcceleratedBackingStoreWayland::makeContextCurrent()
92{
93 tryEnsureGLContext();
94
95#if GTK_CHECK_VERSION(3, 16, 0)
96 if (m_gdkGLContext) {
97 gdk_gl_context_make_current(m_gdkGLContext.get());
98 return true;
99 }
100#endif
101
102 return m_glContext ? m_glContext->makeContextCurrent() : false;
103}
104
105bool AcceleratedBackingStoreWayland::paint(cairo_t* cr, const IntRect& clipRect)
106{
107 GLuint texture;
108 IntSize textureSize;
109 if (!WaylandCompositor::singleton().getTexture(m_webPage, texture, textureSize))
110 return false;
111
112 cairo_save(cr);
113
114#if GTK_CHECK_VERSION(3, 16, 0)
115 if (m_gdkGLContext) {
116 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());
117 cairo_restore(cr);
118 return true;
119 }
120#endif
121
122 ASSERT(m_glContext);
123
124 if (!m_surface || cairo_image_surface_get_width(m_surface.get()) != textureSize.width() || cairo_image_surface_get_height(m_surface.get()) != textureSize.height())
125 m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, textureSize.width(), textureSize.height()));
126
127 cairoSurfaceSetDeviceScale(m_surface.get(), m_webPage.deviceScaleFactor(), m_webPage.deviceScaleFactor());
128
129 GLuint fb;
130 glGenFramebuffers(1, &fb);
131 glBindFramebuffer(GL_FRAMEBUFFER, fb);
132 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
133
134 glPixelStorei(GL_PACK_ALIGNMENT, 4);
135
136#if USE(OPENGL_ES)
137 unsigned char* data = cairo_image_surface_get_data(m_surface.get());
138 if (cairo_image_surface_get_stride(m_surface.get()) == textureSize.width() * 4)
139 glReadPixels(0, 0, textureSize.width(), textureSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, data);
140 else {
141 int strideBytes = cairo_image_surface_get_stride(m_surface.get());
142 for (int i = 0; i < textureSize.height(); i++) {
143 unsigned char* dataOffset = data + i * strideBytes;
144 glReadPixels(0, i, textureSize.width(), 1, GL_RGBA, GL_UNSIGNED_BYTE, dataOffset);
145 }
146 }
147
148 // Convert to BGRA.
149 int totalBytes = textureSize.width() * textureSize.height() * 4;
150 for (int i = 0; i < totalBytes; i += 4)
151 std::swap(data[i], data[i + 2]);
152#else
153 glPixelStorei(GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride(m_surface.get()) / 4);
154 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()));
155 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
156#endif
157
158 glBindFramebuffer(GL_FRAMEBUFFER, 0);
159 glDeleteFramebuffers(1, &fb);
160
161 // The surface can be modified by the web process at any time, so we mark it
162 // as dirty to ensure we always render the updated contents as soon as possible.
163 cairo_surface_mark_dirty(m_surface.get());
164
165 // The compositor renders the texture flipped for gdk_cairo_draw_from_gl, fix that here.
166 cairo_matrix_t transform;
167 cairo_matrix_init(&transform, 1, 0, 0, -1, 0, textureSize.height() / m_webPage.deviceScaleFactor());
168 cairo_transform(cr, &transform);
169
170 cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
171 cairo_set_source_surface(cr, m_surface.get(), 0, 0);
172 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
173 cairo_fill(cr);
174
175 cairo_restore(cr);
176
177 return true;
178}
179
180} // namespace WebKit
181
182#endif // PLATFORM(WAYLAND) && USE(EGL)
183