diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp new file mode 100644 index 000000000..99e1588b4 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WaylandCompositor.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WaylandCompositor.h" + +#if PLATFORM(WAYLAND) && USE(EGL) + +#include "WebKit2WaylandServerProtocol.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <WebCore/GLContext.h> +#include <WebCore/PlatformDisplayWayland.h> +#include <WebCore/Region.h> +#include <WebCore/UUID.h> +#include <wayland-server-protocol.h> + +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#else +#include <WebCore/OpenGLShims.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +#if !defined(PFNEGLBINDWAYLANDDISPLAYWL) +typedef EGLBoolean (*PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*); +#endif + +#if !defined(PFNEGLUNBINDWAYLANDDISPLAYWL) +typedef EGLBoolean (*PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay, struct wl_display*); +#endif + +#if !defined(PFNEGLQUERYWAYLANDBUFFERWL) +typedef EGLBoolean (*PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay, struct wl_resource*, EGLint attribute, EGLint* value); +#endif + +#if !defined(PFNEGLCREATEIMAGEKHRPROC) +typedef EGLImageKHR (*PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay, EGLContext, EGLenum target, EGLClientBuffer, const EGLint* attribList); +#endif + +#if !defined(PFNEGLDESTROYIMAGEKHRPROC) +typedef EGLBoolean (*PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay, EGLImageKHR); +#endif + +#if !defined(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) +typedef void (*PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES); +#endif + +static PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplay; +static PFNEGLUNBINDWAYLANDDISPLAYWL eglUnbindWaylandDisplay; +static PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBuffer; +static PFNEGLCREATEIMAGEKHRPROC eglCreateImage; +static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImage; +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glImageTargetTexture2D; + +WaylandCompositor& WaylandCompositor::singleton() +{ + static NeverDestroyed<WaylandCompositor> waylandCompositor; + return waylandCompositor; +} + +WaylandCompositor::Buffer* WaylandCompositor::Buffer::getOrCreate(struct wl_resource* resource) +{ + if (struct wl_listener* listener = wl_resource_get_destroy_listener(resource, destroyListenerCallback)) { + WaylandCompositor::Buffer* buffer; + return wl_container_of(listener, buffer, m_destroyListener); + } + + return new WaylandCompositor::Buffer(resource); +} + +WaylandCompositor::Buffer::Buffer(struct wl_resource* resource) + : m_resource(resource) + , m_weakPtrFactory(this) +{ + wl_list_init(&m_destroyListener.link); + m_destroyListener.notify = destroyListenerCallback; + wl_resource_add_destroy_listener(m_resource, &m_destroyListener); +} + +WaylandCompositor::Buffer::~Buffer() +{ + wl_list_remove(&m_destroyListener.link); +} + +void WaylandCompositor::Buffer::destroyListenerCallback(struct wl_listener* listener, void*) +{ + WaylandCompositor::Buffer* buffer; + buffer = wl_container_of(listener, buffer, m_destroyListener); + delete buffer; +} + +void WaylandCompositor::Buffer::use() +{ + m_busyCount++; +} + +void WaylandCompositor::Buffer::unuse() +{ + m_busyCount--; + if (!m_busyCount) + wl_resource_queue_event(m_resource, WL_BUFFER_RELEASE); +} + +EGLImageKHR WaylandCompositor::Buffer::createImage() const +{ + return static_cast<EGLImageKHR*>(eglCreateImage(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, m_resource, nullptr)); +} + +IntSize WaylandCompositor::Buffer::size() const +{ + EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay(); + int width, height; + eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_WIDTH, &width); + eglQueryWaylandBuffer(eglDisplay, m_resource, EGL_HEIGHT, &height); + + return { width, height }; +} + +WaylandCompositor::Surface::Surface() + : m_image(EGL_NO_IMAGE_KHR) +{ + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_2D, m_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +WaylandCompositor::Surface::~Surface() +{ + // Destroy pending frame callbacks. + auto list = WTFMove(m_frameCallbackList); + for (auto* resource : list) + wl_resource_destroy(resource); + + if (m_buffer) + m_buffer->unuse(); + + if (m_image != EGL_NO_IMAGE_KHR) + eglDestroyImage(PlatformDisplay::sharedDisplay().eglDisplay(), m_image); + + glDeleteTextures(1, &m_texture); +} + +void WaylandCompositor::Surface::makePendingBufferCurrent() +{ + if (m_pendingBuffer == m_buffer) + return; + + if (m_buffer) + m_buffer->unuse(); + + if (m_pendingBuffer) + m_pendingBuffer->use(); + + m_buffer = m_pendingBuffer; +} + +void WaylandCompositor::Surface::attachBuffer(struct wl_resource* buffer) +{ + if (m_pendingBuffer) + m_pendingBuffer = nullptr; + + if (buffer) { + auto* compositorBuffer = WaylandCompositor::Buffer::getOrCreate(buffer); + m_pendingBuffer = compositorBuffer->createWeakPtr(); + } +} + +void WaylandCompositor::Surface::requestFrame(struct wl_resource* resource) +{ + wl_resource_set_implementation(resource, nullptr, this, [](struct wl_resource* resource) { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (size_t item = surface->m_frameCallbackList.find(resource) != notFound) + surface->m_frameCallbackList.remove(item); + }); + m_frameCallbackList.append(resource); +} + +bool WaylandCompositor::Surface::prepareTextureForPainting(unsigned& texture, IntSize& textureSize) +{ + if (m_image == EGL_NO_IMAGE_KHR) + return false; + + glBindTexture(GL_TEXTURE_2D, m_texture); + glImageTargetTexture2D(GL_TEXTURE_2D, m_image); + + texture = m_texture; + textureSize = m_imageSize; + return true; +} + +void WaylandCompositor::Surface::commit() +{ + EGLDisplay eglDisplay = PlatformDisplay::sharedDisplay().eglDisplay(); + if (m_image != EGL_NO_IMAGE_KHR) + eglDestroyImage(eglDisplay, m_image); + m_image = m_pendingBuffer->createImage(); + if (m_image == EGL_NO_IMAGE_KHR) + return; + + m_imageSize = m_pendingBuffer->size(); + + makePendingBufferCurrent(); + if (m_webPage) + m_webPage->setViewNeedsDisplay(IntRect(IntPoint::zero(), m_webPage->viewSize())); + + // From a Wayland point-of-view frame callbacks should be fired where the + // compositor knows it has *used* the committed contents, so firing them here + // can be surprising but we don't need them as a throttling mechanism because + // rendering synchronization is handled elsewhere by WebKit. + auto list = WTFMove(m_frameCallbackList); + for (auto* resource : list) { + wl_callback_send_done(resource, 0); + wl_resource_destroy(resource); + } +} + +static const struct wl_surface_interface surfaceInterface = { + // destroyCallback + [](struct wl_client*, struct wl_resource* resource) + { + wl_resource_destroy(resource); + }, + // attachCallback + [](struct wl_client* client, struct wl_resource* resource, struct wl_resource* buffer, int32_t sx, int32_t sy) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + + EGLint format; + if (!eglQueryWaylandBuffer(PlatformDisplay::sharedDisplay().eglDisplay(), buffer, EGL_TEXTURE_FORMAT, &format) + || (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA)) + return; + + surface->attachBuffer(buffer); + }, + // damageCallback + [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, + // frameCallback + [](struct wl_client* client, struct wl_resource* resource, uint32_t id) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + + if (struct wl_resource* callbackResource = wl_resource_create(client, &wl_callback_interface, 1, id)) + surface->requestFrame(callbackResource); + else + wl_client_post_no_memory(client); + }, + // setOpaqueRegionCallback + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + // setInputRegionCallback + [](struct wl_client*, struct wl_resource*, struct wl_resource*) { }, + // commitCallback + [](struct wl_client* client, struct wl_resource* resource) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + if (!surface) + return; + surface->commit(); + }, + // setBufferTransformCallback + [](struct wl_client*, struct wl_resource*, int32_t) { }, + // setBufferScaleCallback + [](struct wl_client*, struct wl_resource*, int32_t) { }, +#if WAYLAND_VERSION_MAJOR > 1 || (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR >= 10) + // damageBufferCallback + [](struct wl_client*, struct wl_resource*, int32_t, int32_t, int32_t, int32_t) { }, +#endif +}; + +static const struct wl_compositor_interface compositorInterface = { + // createSurfaceCallback + [](struct wl_client* client, struct wl_resource* resource, uint32_t id) + { + if (struct wl_resource* surfaceResource = wl_resource_create(client, &wl_surface_interface, 1, id)) { + wl_resource_set_implementation(surfaceResource, &surfaceInterface, new WaylandCompositor::Surface(), + [](struct wl_resource* resource) { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(resource)); + delete surface; + }); + } else + wl_client_post_no_memory(client); + }, + // createRegionCallback + [](struct wl_client*, struct wl_resource*, uint32_t) { } +}; + +static const struct wl_webkitgtk_interface webkitgtkInterface = { + // bindSurfaceToPageCallback + [](struct wl_client*, struct wl_resource* resource, struct wl_resource* surfaceResource, uint32_t pageID) + { + auto* surface = static_cast<WaylandCompositor::Surface*>(wl_resource_get_user_data(surfaceResource)); + if (!surface) + return; + + auto* compositor = static_cast<WaylandCompositor*>(wl_resource_get_user_data(resource)); + compositor->bindSurfaceToWebPage(surface, pageID); + } +}; + +bool WaylandCompositor::initializeEGL() +{ + if (PlatformDisplay::sharedDisplay().eglCheckVersion(1, 5)) { + eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImage")); + eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImage")); + } else { + const char* extensions = eglQueryString(PlatformDisplay::sharedDisplay().eglDisplay(), EGL_EXTENSIONS); + if (GLContext::isExtensionSupported(extensions, "EGL_KHR_image_base")) { + eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + } + } + if (!eglCreateImage || !eglDestroyImage) { + WTFLogAlways("WaylandCompositor requires eglCreateImage and eglDestroyImage."); + return false; + } + + glImageTargetTexture2D = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + if (!glImageTargetTexture2D) { + WTFLogAlways("WaylandCompositor requires glEGLImageTargetTexture2D."); + return false; + } + + eglQueryWaylandBuffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL")); + if (!eglQueryWaylandBuffer) { + WTFLogAlways("WaylandCompositor requires eglQueryWaylandBuffer."); + return false; + } + + eglBindWaylandDisplay = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL")); + eglUnbindWaylandDisplay = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL")); + if (!eglBindWaylandDisplay || !eglUnbindWaylandDisplay) { + WTFLogAlways("WaylandCompositor requires eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL."); + return false; + } + + m_eglContext = GLContext::createOffscreenContext(); + if (!m_eglContext) + return false; + + if (!m_eglContext->makeContextCurrent()) + return false; + + return true; +} + +typedef struct { + GSource source; + gpointer fdTag; + struct wl_display* display; +} WaylandLoopSource; + +static const unsigned waylandLoopSourceCondition = G_IO_IN | G_IO_HUP | G_IO_ERR; + +static GSourceFuncs waylandLoopSourceFunctions = { + // prepare + [](GSource *source, int *timeout) -> gboolean + { + *timeout = -1; + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source); + wl_display_flush_clients(wlLoopSource->display); + return FALSE; + }, + nullptr, // check + // dispatch + [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean + { + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source); + unsigned events = g_source_query_unix_fd(source, wlLoopSource->fdTag) & waylandLoopSourceCondition; + if (events & G_IO_HUP || events & G_IO_ERR) { + WTFLogAlways("Wayland Display Event Source: lost connection to nested Wayland compositor"); + return G_SOURCE_REMOVE; + } + + if (events & G_IO_IN) + wl_event_loop_dispatch(wl_display_get_event_loop(wlLoopSource->display), 0); + return G_SOURCE_CONTINUE; + }, + nullptr, // finalize + nullptr, // closure_callback + nullptr, // closure_marshall +}; + +static GRefPtr<GSource> createWaylandLoopSource(struct wl_display* display) +{ + GRefPtr<GSource> source = adoptGRef(g_source_new(&waylandLoopSourceFunctions, sizeof(WaylandLoopSource))); + g_source_set_name(source.get(), "Nested Wayland compositor display event source"); + g_source_set_priority(source.get(), G_PRIORITY_DEFAULT + 1); + + auto* wlLoopSource = reinterpret_cast<WaylandLoopSource*>(source.get()); + wlLoopSource->display = display; + wlLoopSource->fdTag = g_source_add_unix_fd(source.get(), wl_event_loop_get_fd(wl_display_get_event_loop(display)), static_cast<GIOCondition>(waylandLoopSourceCondition)); + g_source_attach(source.get(), nullptr); + + return source; +} + +WaylandCompositor::WaylandCompositor() +{ + WlUniquePtr<struct wl_display> display(wl_display_create()); + if (!display) { + WTFLogAlways("Nested Wayland compositor could not create display object"); + return; + } + + String displayName = "webkitgtk-wayland-compositor-" + createCanonicalUUIDString(); + if (wl_display_add_socket(display.get(), displayName.utf8().data()) == -1) { + WTFLogAlways("Nested Wayland compositor could not create display socket"); + return; + } + + WlUniquePtr<struct wl_global> compositorGlobal(wl_global_create(display.get(), &wl_compositor_interface, wl_compositor_interface.version, this, + [](struct wl_client* client, void* data, uint32_t version, uint32_t id) { + if (struct wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, std::min(static_cast<int>(version), 3), id)) + wl_resource_set_implementation(resource, &compositorInterface, static_cast<WaylandCompositor*>(data), nullptr); + else + wl_client_post_no_memory(client); + })); + if (!compositorGlobal) { + WTFLogAlways("Nested Wayland compositor could not register compositor global"); + return; + } + + WlUniquePtr<struct wl_global> webkitgtkGlobal(wl_global_create(display.get(), &wl_webkitgtk_interface, 1, this, + [](struct wl_client* client, void* data, uint32_t version, uint32_t id) { + if (struct wl_resource* resource = wl_resource_create(client, &wl_webkitgtk_interface, 1, id)) + wl_resource_set_implementation(resource, &webkitgtkInterface, static_cast<WaylandCompositor*>(data), nullptr); + else + wl_client_post_no_memory(client); + })); + if (!webkitgtkGlobal) { + WTFLogAlways("Nested Wayland compositor could not register webkitgtk global"); + return; + } + + if (!initializeEGL()) { + WTFLogAlways("Nested Wayland compositor could not initialize EGL"); + return; + } + + if (!eglBindWaylandDisplay(PlatformDisplay::sharedDisplay().eglDisplay(), display.get())) { + WTFLogAlways("Nested Wayland compositor could not bind nested display"); + return; + } + + m_displayName = WTFMove(displayName); + m_display = WTFMove(display); + m_compositorGlobal = WTFMove(compositorGlobal); + m_webkitgtkGlobal = WTFMove(webkitgtkGlobal); + m_eventSource = createWaylandLoopSource(m_display.get()); +} + +bool WaylandCompositor::getTexture(WebPageProxy& webPage, unsigned& texture, IntSize& textureSize) +{ + if (auto* surface = m_pageMap.get(&webPage)) + return surface->prepareTextureForPainting(texture, textureSize); + return false; +} + +void WaylandCompositor::bindSurfaceToWebPage(WaylandCompositor::Surface* surface, uint64_t pageID) +{ + WebPageProxy* webPage = nullptr; + for (auto* page : m_pageMap.keys()) { + if (page->pageID() == pageID) { + webPage = page; + break; + } + } + if (!webPage) + return; + + surface->setWebPage(webPage); + m_pageMap.set(webPage, surface); +} + +void WaylandCompositor::registerWebPage(WebPageProxy& webPage) +{ + m_pageMap.add(&webPage, nullptr); +} + +void WaylandCompositor::unregisterWebPage(WebPageProxy& webPage) +{ + if (auto* surface = m_pageMap.take(&webPage)) + surface->setWebPage(nullptr); +} + +} // namespace WebKit + +#endif // PLATFORM(WAYLAND) && USE(EGL) |