Diff
Modified: trunk/Tools/CMakeLists.txt (225044 => 225045)
--- trunk/Tools/CMakeLists.txt 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/CMakeLists.txt 2017-11-20 09:47:56 UTC (rev 225045)
@@ -27,6 +27,7 @@
if (DEVELOPER_MODE)
add_subdirectory(ImageDiff)
add_subdirectory(WebKitTestRunner)
+ add_subdirectory(wpe/HeadlessViewBackend)
if (ENABLE_API_TESTS)
add_subdirectory(TestWebKitAPI/glib)
endif ()
Modified: trunk/Tools/ChangeLog (225044 => 225045)
--- trunk/Tools/ChangeLog 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/ChangeLog 2017-11-20 09:47:56 UTC (rev 225045)
@@ -1,5 +1,40 @@
2017-11-20 Carlos Garcia Campos <[email protected]>
+ [WPE] Add env var WPE_USE_HEADLESS_VIEW_BACKEND
+ https://bugs.webkit.org/show_bug.cgi?id=173770
+
+ Reviewed by Michael Catanzaro.
+
+ Move HeadlessViewBackend implementation to a common place and build it as a private static library to be used by
+ WTR, GLib API tests and eventually C API tests too. WTR uses the HeadlessViewBackend unconditionally, but GLib
+ API tests use it only when WPE_USE_HEADLESS_VIEW_BACKEND environment variable is present and not "0".
+
+ * CMakeLists.txt: Include wpe/HeadlessViewBackend directory for developer builds.
+ * TestWebKitAPI/Tests/WebKitGLib/TestAutomationSession.cpp:
+ (testAutomationSessionRequestSession):
+ * TestWebKitAPI/Tests/WebKitGLib/TestCookieManager.cpp:
+ (testCookieManagerEphemeral):
+ * TestWebKitAPI/Tests/WebKitGLib/TestWebKitFaviconDatabase.cpp:
+ (testPrivateBrowsing):
+ * TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebContext.cpp:
+ (testWebContextProxySettings):
+ * TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:
+ (testWebViewWebContext):
+ (testWebViewEphemeral):
+ * TestWebKitAPI/glib/PlatformWPE.cmake:
+ * TestWebKitAPI/glib/WebKitGLib/TestMain.h:
+ (Test::createWebViewBackend):
+ (Test::createWebView):
+ * TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp:
+ (WebViewTest::initializeWebView):
+ * WebKitTestRunner/PlatformWPE.cmake:
+ * wpe/HeadlessViewBackend/CMakeLists.txt: Added.
+ * wpe/HeadlessViewBackend/HeadlessViewBackend.cpp: Renamed from Tools/WebKitTestRunner/wpe/HeadlessViewBackend.cpp.
+ (HeadlessViewBackend::HeadlessViewBackend):
+ * wpe/HeadlessViewBackend/HeadlessViewBackend.h: Renamed from Tools/WebKitTestRunner/wpe/HeadlessViewBackend.h.
+
+2017-11-20 Carlos Garcia Campos <[email protected]>
+
[WPE] webkit_web_view_new() should enable specifying wpe_view_backend object
https://bugs.webkit.org/show_bug.cgi?id=178655
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAutomationSession.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAutomationSession.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAutomationSession.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -313,7 +313,13 @@
g_assert(!test->createTopLevelBrowsingContext(webView.get()));
// And will work with a proper web view.
- webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", test->m_webContext.get(), "is-controlled-by-automation", TRUE, nullptr));
+ webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
+ "web-context", test->m_webContext.get(),
+ "is-controlled-by-automation", TRUE,
+ nullptr));
g_assert(webkit_web_view_is_controlled_by_automation(webView.get()));
g_assert(test->createTopLevelBrowsingContext(webView.get()));
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestCookieManager.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestCookieManager.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestCookieManager.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -340,6 +340,9 @@
g_assert_cmpint(g_strv_length(domains), ==, 0);
auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
"web-context", webkit_web_view_get_context(test->m_webView),
"is-ephemeral", TRUE,
nullptr));
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitFaviconDatabase.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitFaviconDatabase.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitFaviconDatabase.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -184,6 +184,9 @@
static void testPrivateBrowsing(FaviconDatabaseTest* test)
{
auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
"web-context", test->m_webContext.get(),
"is-ephemeral", TRUE,
nullptr));
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebContext.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebContext.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebContext.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -708,6 +708,9 @@
// Proxy settings also affect ephemeral web views.
auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
"web-context", test->m_webContext.get(),
"is-ephemeral", TRUE,
nullptr));
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -51,7 +51,11 @@
g_assert(webkit_web_context_get_default() != test->m_webContext.get());
// Check that a web view created with g_object_new has the default context.
- auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW, nullptr));
+ auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
+ nullptr));
g_assert(webkit_web_view_get_context(webView.get()) == webkit_web_context_get_default());
// Check that a web view created with a related view has the related view context.
@@ -60,7 +64,12 @@
// Check that a web context given as construct parameter is ignored if a related view is also provided.
webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
- "web-context", webkit_web_context_get_default(), "related-view", test->m_webView, nullptr));
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
+ "web-context", webkit_web_context_get_default(),
+ "related-view", test->m_webView,
+ nullptr));
g_assert(webkit_web_view_get_context(webView.get()) == test->m_webContext.get());
}
@@ -182,6 +191,9 @@
// A WebView on a non ephemeral context can be ephemeral.
auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
"web-context", webkit_web_view_get_context(test->m_webView),
"is-ephemeral", TRUE,
nullptr));
Modified: trunk/Tools/TestWebKitAPI/glib/PlatformWPE.cmake (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/glib/PlatformWPE.cmake 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/glib/PlatformWPE.cmake 2017-11-20 09:47:56 UTC (rev 225045)
@@ -9,6 +9,7 @@
${DERIVED_SOURCES_WPE_API_DIR}
${FORWARDING_HEADERS_WPE_DIR}
${FORWARDING_HEADERS_WPE_EXTENSION_DIR}
+ ${TOOLS_DIR}/wpe/HeadlessViewBackend
)
list(APPEND WebKitGLibAPITests_SYSTEM_INCLUDE_DIRECTORIES
@@ -17,4 +18,5 @@
list(APPEND WebKitGLibAPITest_LIBRARIES
${WPE_LIBRARIES}
+ WPEHeadlessViewBackend
)
Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h 2017-11-20 09:47:56 UTC (rev 225045)
@@ -29,6 +29,7 @@
#if PLATFORM(GTK)
#include <webkit2/webkit2.h>
#elif PLATFORM(WPE)
+#include "HeadlessViewBackend.h"
#include <wpe/webkit.h>
#endif
@@ -143,12 +144,25 @@
webkit_web_context_set_web_extensions_initialization_user_data(m_webContext.get(), g_variant_new_uint32(++s_webExtensionID));
}
+#if PLATFORM(WPE)
+ static WebKitWebViewBackend* createWebViewBackend()
+ {
+ const char* useHeadlessViewBackend = g_getenv("WPE_USE_HEADLESS_VIEW_BACKEND");
+ if (!useHeadlessViewBackend || !strcmp(useHeadlessViewBackend, "0"))
+ return nullptr;
+ auto* headlessBackend = new HeadlessViewBackend;
+ return webkit_web_view_backend_new(headlessBackend->backend(), [](gpointer userData) {
+ delete static_cast<HeadlessViewBackend*>(userData);
+ }, headlessBackend);
+ }
+#endif
+
static WebKitWebView* createWebView()
{
#if PLATFORM(GTK)
return WEBKIT_WEB_VIEW(webkit_web_view_new());
#elif PLATFORM(WPE)
- return webkit_web_view_new(nullptr);
+ return webkit_web_view_new(createWebViewBackend());
#endif
}
@@ -157,7 +171,7 @@
#if PLATFORM(GTK)
return WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(context));
#elif PLATFORM(WPE)
- return webkit_web_view_new_with_context(nullptr, context);
+ return webkit_web_view_new_with_context(createWebViewBackend(), context);
#endif
}
@@ -166,7 +180,7 @@
#if PLATFORM(GTK)
return WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(relatedView));
#elif PLATFORM(WPE)
- return webkit_web_view_new_with_related_view(nullptr, relatedView);
+ return webkit_web_view_new_with_related_view(createWebViewBackend(), relatedView);
#endif
}
@@ -175,7 +189,7 @@
#if PLATFORM(GTK)
return WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(contentManager));
#elif PLATFORM(WPE)
- return webkit_web_view_new_with_user_content_manager(nullptr, contentManager);
+ return webkit_web_view_new_with_user_content_manager(createWebViewBackend(), contentManager);
#endif
}
@@ -184,7 +198,7 @@
#if PLATFORM(GTK)
return WEBKIT_WEB_VIEW(webkit_web_view_new_with_settings(settings));
#elif PLATFORM(WPE)
- return webkit_web_view_new_with_settings(nullptr, settings);
+ return webkit_web_view_new_with_settings(createWebViewBackend(), settings);
#endif
}
Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp (225044 => 225045)
--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -48,7 +48,13 @@
void WebViewTest::initializeWebView()
{
g_assert(!m_webView);
- m_webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", m_webContext.get(), "user-content-manager", m_userContentManager.get(), nullptr));
+ m_webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+#if PLATFORM(WPE)
+ "backend", Test::createWebViewBackend(),
+#endif
+ "web-context", m_webContext.get(),
+ "user-content-manager", m_userContentManager.get(),
+ nullptr));
platformInitializeWebView();
assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
Modified: trunk/Tools/WebKitTestRunner/PlatformWPE.cmake (225044 => 225045)
--- trunk/Tools/WebKitTestRunner/PlatformWPE.cmake 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/WebKitTestRunner/PlatformWPE.cmake 2017-11-20 09:47:56 UTC (rev 225045)
@@ -1,5 +1,3 @@
-find_package(LibGBM REQUIRED)
-find_package(WPEBackend-mesa REQUIRED)
find_package(Libxkbcommon 0.4.0 REQUIRED)
add_custom_target(WebKitTestRunner-forwarding-headers
@@ -12,7 +10,6 @@
${WEBKIT_TESTRUNNER_DIR}/cairo/TestInvocationCairo.cpp
${WEBKIT_TESTRUNNER_DIR}/wpe/EventSenderProxyWPE.cpp
- ${WEBKIT_TESTRUNNER_DIR}/wpe/HeadlessViewBackend.cpp
${WEBKIT_TESTRUNNER_DIR}/wpe/PlatformWebViewWPE.cpp
${WEBKIT_TESTRUNNER_DIR}/wpe/TestControllerWPE.cpp
${WEBKIT_TESTRUNNER_DIR}/wpe/main.cpp
@@ -21,22 +18,20 @@
list(APPEND WebKitTestRunner_INCLUDE_DIRECTORIES
${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/wpe
${FORWARDING_HEADERS_DIR}
+ ${TOOLS_DIR}/wpe/HeadlessViewBackend
)
list(APPEND WebKitTestRunner_SYSTEM_INCLUDE_DIRECTORIES
${CAIRO_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
- ${LIBGBM_INCLUDE_DIRS}
${LIBXKBCOMMON_INCLUDE_DIRS}
- ${WPE_MESA_INCLUDE_DIRS}
)
list(APPEND WebKitTestRunner_LIBRARIES
${CAIRO_LIBRARIES}
${GLIB_LIBRARIES}
- ${LIBGBM_LIBRARIES}
${LIBXKBCOMMON_LIBRARIES}
- ${WPE_MESA_LIBRARIES}
+ WPEHeadlessViewBackend
)
list(APPEND WebKitTestRunnerInjectedBundle_SOURCES
Deleted: trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.cpp (225044 => 225045)
--- trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.cpp 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -1,258 +0,0 @@
-/*
- * 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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 "HeadlessViewBackend.h"
-
-#include <cassert>
-#include <fcntl.h>
-#include <unistd.h>
-#include <wtf/glib/RunLoopSourcePriority.h>
-
-// Manually provide the EGL_CAST C++ definition in case eglplatform.h doesn't provide it.
-#ifndef EGL_CAST
-#define EGL_CAST(type, value) (static_cast<type>(value))
-#endif
-
-// FIXME: Deploy good practices and clean up GBM resources at process exit.
-static EGLDisplay getEGLDisplay()
-{
- static EGLDisplay s_display = EGL_NO_DISPLAY;
- if (s_display == EGL_NO_DISPLAY) {
- int fd = open("/dev/dri/renderD128", O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
- if (fd < 0)
- return EGL_NO_DISPLAY;
-
- struct gbm_device* device = gbm_create_device(fd);
- if (!device)
- return EGL_NO_DISPLAY;
-
- EGLDisplay display = eglGetDisplay(device);
- if (display == EGL_NO_DISPLAY)
- return EGL_NO_DISPLAY;
-
- if (!eglInitialize(display, nullptr, nullptr))
- return EGL_NO_DISPLAY;
-
- if (!eglBindAPI(EGL_OPENGL_ES_API))
- return EGL_NO_DISPLAY;
-
- s_display = display;
- }
-
- return s_display;
-}
-
-HeadlessViewBackend::HeadlessViewBackend()
-{
- m_egl.display = getEGLDisplay();
-
- static const EGLint configAttributes[13] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RED_SIZE, 1,
- EGL_GREEN_SIZE, 1,
- EGL_BLUE_SIZE, 1,
- EGL_ALPHA_SIZE, 1,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_NONE
- };
-
- EGLint numConfigs;
- EGLBoolean ret = eglChooseConfig(m_egl.display, configAttributes, &m_egl.config, 1, &numConfigs);
- if (!ret || !numConfigs)
- return;
-
- static const EGLint contextAttributes[3] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
-
- m_egl.context = eglCreateContext(m_egl.display, m_egl.config, EGL_NO_CONTEXT, contextAttributes);
- if (m_egl.context == EGL_NO_CONTEXT)
- return;
-
- if (!eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context))
- return;
-
- m_egl.createImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
- m_egl.destroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
- m_egl.imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
-
- m_exportable = wpe_mesa_view_backend_exportable_dma_buf_create(&s_exportableClient, this);
-
- m_updateSource = g_timeout_source_new(m_frameRate / 1000);
- g_source_set_callback(m_updateSource,
- [](gpointer data) -> gboolean {
- auto& backend = *static_cast<HeadlessViewBackend*>(data);
- backend.performUpdate();
- return TRUE;
- }, this, nullptr);
- g_source_set_priority(m_updateSource, RunLoopSourcePriority::RunLoopDispatcher);
- g_source_attach(m_updateSource, g_main_context_default());
-}
-
-HeadlessViewBackend::~HeadlessViewBackend()
-{
- if (m_updateSource)
- g_source_destroy(m_updateSource);
-
- if (auto image = std::get<0>(m_pendingImage.second))
- m_egl.destroyImage(m_egl.display, image);
- if (auto image = std::get<0>(m_lockedImage.second))
- m_egl.destroyImage(m_egl.display, image);
-
- for (auto it : m_exportMap) {
- int fd = it.second;
- if (fd >= 0)
- close(fd);
- }
-
- if (m_egl.context)
- eglDestroyContext(m_egl.display, m_egl.context);
-
- wpe_mesa_view_backend_exportable_dma_buf_destroy(m_exportable);
-}
-
-struct wpe_view_backend* HeadlessViewBackend::backend() const
-{
- return wpe_mesa_view_backend_exportable_dma_buf_get_view_backend(m_exportable);
-}
-
-cairo_surface_t* HeadlessViewBackend::createSnapshot()
-{
- performUpdate();
-
- EGLImageKHR image = std::get<0>(m_lockedImage.second);
- if (!image)
- return nullptr;
-
- uint32_t width = std::get<1>(m_lockedImage.second);
- uint32_t height = std::get<2>(m_lockedImage.second);
-
- uint8_t* buffer = new uint8_t[4 * width * height];
- bool successfulSnapshot = false;
-
- makeCurrent();
-
- GLuint imageTexture;
- glGenTextures(1, &imageTexture);
- glBindTexture(GL_TEXTURE_2D, imageTexture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, nullptr);
-
- m_egl.imageTargetTexture2DOES(GL_TEXTURE_2D, image);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- GLuint imageFramebuffer;
- glGenFramebuffers(1, &imageFramebuffer);
- glBindFramebuffer(GL_FRAMEBUFFER, imageFramebuffer);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, imageTexture, 0);
-
- glFlush();
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
- glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
- successfulSnapshot = true;
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &imageFramebuffer);
- glDeleteTextures(1, &imageTexture);
-
- if (!successfulSnapshot) {
- delete[] buffer;
- return nullptr;
- }
-
- cairo_surface_t* imageSurface = cairo_image_surface_create_for_data(buffer,
- CAIRO_FORMAT_ARGB32, width, height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
- cairo_surface_mark_dirty(imageSurface);
-
- static cairo_user_data_key_t bufferKey;
- cairo_surface_set_user_data(imageSurface, &bufferKey, buffer,
- [](void* data) {
- auto* buffer = static_cast<uint8_t*>(data);
- delete[] buffer;
- });
-
- return imageSurface;
-}
-
-bool HeadlessViewBackend::makeCurrent()
-{
- return eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context);
-}
-
-void HeadlessViewBackend::performUpdate()
-{
- if (!m_pendingImage.first)
- return;
-
- wpe_mesa_view_backend_exportable_dma_buf_dispatch_frame_complete(m_exportable);
- if (m_lockedImage.first) {
- wpe_mesa_view_backend_exportable_dma_buf_dispatch_release_buffer(m_exportable, m_lockedImage.first);
- m_egl.destroyImage(m_egl.display, std::get<0>(m_lockedImage.second));
- }
-
- m_lockedImage = m_pendingImage;
- m_pendingImage = std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> { };
-}
-
-struct wpe_mesa_view_backend_exportable_dma_buf_client HeadlessViewBackend::s_exportableClient = {
- // export_dma_buf
- [](void* data, struct wpe_mesa_view_backend_exportable_dma_buf_data* imageData)
- {
- auto& backend = *static_cast<HeadlessViewBackend*>(data);
-
- auto it = backend.m_exportMap.end();
- if (imageData->fd >= 0) {
- assert(backend.m_exportMap.find(imageData->handle) == backend.m_exportMap.end());
-
- it = backend.m_exportMap.insert({ imageData->handle, imageData->fd }).first;
- } else {
- assert(backend.m_exportMap.find(imageData->handle) != backend.m_exportMap.end());
- it = backend.m_exportMap.find(imageData->handle);
- }
-
- assert(it != backend.m_exportMap.end());
- int32_t fd = it->second;
-
- backend.makeCurrent();
-
- EGLint attributes[] = {
- EGL_WIDTH, static_cast<EGLint>(imageData->width),
- EGL_HEIGHT, static_cast<EGLint>(imageData->height),
- EGL_LINUX_DRM_FOURCC_EXT, static_cast<EGLint>(imageData->format),
- EGL_DMA_BUF_PLANE0_FD_EXT, fd,
- EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
- EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(imageData->stride),
- EGL_NONE,
- };
- EGLImageKHR image = backend.m_egl.createImage(backend.m_egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attributes);
- backend.m_pendingImage = { imageData->handle, std::make_tuple(image, imageData->width, imageData->height) };
- },
-};
Deleted: trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.h (225044 => 225045)
--- trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.h 2017-11-20 08:16:26 UTC (rev 225044)
+++ trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.h 2017-11-20 09:47:56 UTC (rev 225045)
@@ -1,70 +0,0 @@
-/*
- * 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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.
- */
-
-#pragma once
-
-// This include order is necessary to enforce the GBM EGL platform.
-#include <gbm.h>
-#include <epoxy/egl.h>
-
-#include <cairo.h>
-#include <glib.h>
-#include <unordered_map>
-#include <wpe-mesa/view-backend-exportable-dma-buf.h>
-
-class HeadlessViewBackend {
-public:
- HeadlessViewBackend();
- ~HeadlessViewBackend();
-
- struct wpe_view_backend* backend() const;
-
- cairo_surface_t* createSnapshot();
-
-private:
- bool makeCurrent();
- void performUpdate();
-
- static struct wpe_mesa_view_backend_exportable_dma_buf_client s_exportableClient;
-
- struct {
- EGLDisplay display;
- EGLConfig config;
- EGLContext context;
-
- PFNEGLCREATEIMAGEKHRPROC createImage;
- PFNEGLDESTROYIMAGEKHRPROC destroyImage;
- PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES;
- } m_egl;
-
- struct wpe_mesa_view_backend_exportable_dma_buf* m_exportable;
-
- std::unordered_map<uint32_t, int32_t> m_exportMap;
- std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> m_pendingImage { };
- std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> m_lockedImage { };
-
- GSource* m_updateSource;
- gint64 m_frameRate { G_USEC_PER_SEC / 60 };
-};
Added: trunk/Tools/wpe/HeadlessViewBackend/CMakeLists.txt (0 => 225045)
--- trunk/Tools/wpe/HeadlessViewBackend/CMakeLists.txt (rev 0)
+++ trunk/Tools/wpe/HeadlessViewBackend/CMakeLists.txt 2017-11-20 09:47:56 UTC (rev 225045)
@@ -0,0 +1,24 @@
+find_package(LibGBM REQUIRED)
+find_package(WPEBackend-mesa REQUIRED)
+
+set(WPEHeadlessViewBackend_SOURCES
+ ${TOOLS_DIR}/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp
+)
+
+set(WPEHeadlessViewBackend_SYSTEM_INCLUDE_DIRECTORIES
+ ${CAIRO_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+ ${LIBGBM_INCLUDE_DIRS}
+ ${WPE_MESA_INCLUDE_DIRS}
+)
+
+set(WPEHeadlessViewBackend_LIBRARIES
+ ${CAIRO_LIBRARIES}
+ ${GLIB_LIBRARIES}
+ ${LIBGBM_LIBRARIES}
+ ${WPE_MESA_LIBRARIES}
+)
+
+add_library(WPEHeadlessViewBackend ${WPEHeadlessViewBackend_SOURCES})
+include_directories(SYSTEM ${WPEHeadlessViewBackend_SYSTEM_INCLUDE_DIRECTORIES})
+target_link_libraries(WPEHeadlessViewBackend ${WPEHeadlessViewBackend_LIBRARIES})
Copied: trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp (from rev 225044, trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.cpp) (0 => 225045)
--- trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp (rev 0)
+++ trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp 2017-11-20 09:47:56 UTC (rev 225045)
@@ -0,0 +1,259 @@
+/*
+ * 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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 "HeadlessViewBackend.h"
+
+#include <cassert>
+#include <fcntl.h>
+#include <unistd.h>
+
+// Manually provide the EGL_CAST C++ definition in case eglplatform.h doesn't provide it.
+#ifndef EGL_CAST
+#define EGL_CAST(type, value) (static_cast<type>(value))
+#endif
+
+// Keep this in sync with wtf/glib/RunLoopSourcePriority.h.
+static int kRunLoopSourcePriorityDispatcher = -70;
+
+// FIXME: Deploy good practices and clean up GBM resources at process exit.
+static EGLDisplay getEGLDisplay()
+{
+ static EGLDisplay s_display = EGL_NO_DISPLAY;
+ if (s_display == EGL_NO_DISPLAY) {
+ int fd = open("/dev/dri/renderD128", O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ return EGL_NO_DISPLAY;
+
+ struct gbm_device* device = gbm_create_device(fd);
+ if (!device)
+ return EGL_NO_DISPLAY;
+
+ EGLDisplay display = eglGetDisplay(device);
+ if (display == EGL_NO_DISPLAY)
+ return EGL_NO_DISPLAY;
+
+ if (!eglInitialize(display, nullptr, nullptr))
+ return EGL_NO_DISPLAY;
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API))
+ return EGL_NO_DISPLAY;
+
+ s_display = display;
+ }
+
+ return s_display;
+}
+
+HeadlessViewBackend::HeadlessViewBackend()
+{
+ m_egl.display = getEGLDisplay();
+
+ static const EGLint configAttributes[13] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint numConfigs;
+ EGLBoolean ret = eglChooseConfig(m_egl.display, configAttributes, &m_egl.config, 1, &numConfigs);
+ if (!ret || !numConfigs)
+ return;
+
+ static const EGLint contextAttributes[3] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ m_egl.context = eglCreateContext(m_egl.display, m_egl.config, EGL_NO_CONTEXT, contextAttributes);
+ if (m_egl.context == EGL_NO_CONTEXT)
+ return;
+
+ if (!eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context))
+ return;
+
+ m_egl.createImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+ m_egl.destroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+ m_egl.imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+
+ m_exportable = wpe_mesa_view_backend_exportable_dma_buf_create(&s_exportableClient, this);
+
+ m_updateSource = g_timeout_source_new(m_frameRate / 1000);
+ g_source_set_callback(m_updateSource,
+ [](gpointer data) -> gboolean {
+ auto& backend = *static_cast<HeadlessViewBackend*>(data);
+ backend.performUpdate();
+ return TRUE;
+ }, this, nullptr);
+ g_source_set_priority(m_updateSource, kRunLoopSourcePriorityDispatcher);
+ g_source_attach(m_updateSource, g_main_context_default());
+}
+
+HeadlessViewBackend::~HeadlessViewBackend()
+{
+ if (m_updateSource)
+ g_source_destroy(m_updateSource);
+
+ if (auto image = std::get<0>(m_pendingImage.second))
+ m_egl.destroyImage(m_egl.display, image);
+ if (auto image = std::get<0>(m_lockedImage.second))
+ m_egl.destroyImage(m_egl.display, image);
+
+ for (auto it : m_exportMap) {
+ int fd = it.second;
+ if (fd >= 0)
+ close(fd);
+ }
+
+ if (m_egl.context)
+ eglDestroyContext(m_egl.display, m_egl.context);
+
+ wpe_mesa_view_backend_exportable_dma_buf_destroy(m_exportable);
+}
+
+struct wpe_view_backend* HeadlessViewBackend::backend() const
+{
+ return wpe_mesa_view_backend_exportable_dma_buf_get_view_backend(m_exportable);
+}
+
+cairo_surface_t* HeadlessViewBackend::createSnapshot()
+{
+ performUpdate();
+
+ EGLImageKHR image = std::get<0>(m_lockedImage.second);
+ if (!image)
+ return nullptr;
+
+ uint32_t width = std::get<1>(m_lockedImage.second);
+ uint32_t height = std::get<2>(m_lockedImage.second);
+
+ uint8_t* buffer = new uint8_t[4 * width * height];
+ bool successfulSnapshot = false;
+
+ makeCurrent();
+
+ GLuint imageTexture;
+ glGenTextures(1, &imageTexture);
+ glBindTexture(GL_TEXTURE_2D, imageTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, nullptr);
+
+ m_egl.imageTargetTexture2DOES(GL_TEXTURE_2D, image);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ GLuint imageFramebuffer;
+ glGenFramebuffers(1, &imageFramebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, imageFramebuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, imageTexture, 0);
+
+ glFlush();
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
+ glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer);
+ successfulSnapshot = true;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &imageFramebuffer);
+ glDeleteTextures(1, &imageTexture);
+
+ if (!successfulSnapshot) {
+ delete[] buffer;
+ return nullptr;
+ }
+
+ cairo_surface_t* imageSurface = cairo_image_surface_create_for_data(buffer,
+ CAIRO_FORMAT_ARGB32, width, height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
+ cairo_surface_mark_dirty(imageSurface);
+
+ static cairo_user_data_key_t bufferKey;
+ cairo_surface_set_user_data(imageSurface, &bufferKey, buffer,
+ [](void* data) {
+ auto* buffer = static_cast<uint8_t*>(data);
+ delete[] buffer;
+ });
+
+ return imageSurface;
+}
+
+bool HeadlessViewBackend::makeCurrent()
+{
+ return eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context);
+}
+
+void HeadlessViewBackend::performUpdate()
+{
+ if (!m_pendingImage.first)
+ return;
+
+ wpe_mesa_view_backend_exportable_dma_buf_dispatch_frame_complete(m_exportable);
+ if (m_lockedImage.first) {
+ wpe_mesa_view_backend_exportable_dma_buf_dispatch_release_buffer(m_exportable, m_lockedImage.first);
+ m_egl.destroyImage(m_egl.display, std::get<0>(m_lockedImage.second));
+ }
+
+ m_lockedImage = m_pendingImage;
+ m_pendingImage = std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> { };
+}
+
+struct wpe_mesa_view_backend_exportable_dma_buf_client HeadlessViewBackend::s_exportableClient = {
+ // export_dma_buf
+ [](void* data, struct wpe_mesa_view_backend_exportable_dma_buf_data* imageData)
+ {
+ auto& backend = *static_cast<HeadlessViewBackend*>(data);
+
+ auto it = backend.m_exportMap.end();
+ if (imageData->fd >= 0) {
+ assert(backend.m_exportMap.find(imageData->handle) == backend.m_exportMap.end());
+
+ it = backend.m_exportMap.insert({ imageData->handle, imageData->fd }).first;
+ } else {
+ assert(backend.m_exportMap.find(imageData->handle) != backend.m_exportMap.end());
+ it = backend.m_exportMap.find(imageData->handle);
+ }
+
+ assert(it != backend.m_exportMap.end());
+ int32_t fd = it->second;
+
+ backend.makeCurrent();
+
+ EGLint attributes[] = {
+ EGL_WIDTH, static_cast<EGLint>(imageData->width),
+ EGL_HEIGHT, static_cast<EGLint>(imageData->height),
+ EGL_LINUX_DRM_FOURCC_EXT, static_cast<EGLint>(imageData->format),
+ EGL_DMA_BUF_PLANE0_FD_EXT, fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<EGLint>(imageData->stride),
+ EGL_NONE,
+ };
+ EGLImageKHR image = backend.m_egl.createImage(backend.m_egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attributes);
+ backend.m_pendingImage = { imageData->handle, std::make_tuple(image, imageData->width, imageData->height) };
+ },
+};
Copied: trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h (from rev 225044, trunk/Tools/WebKitTestRunner/wpe/HeadlessViewBackend.h) (0 => 225045)
--- trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h (rev 0)
+++ trunk/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h 2017-11-20 09:47:56 UTC (rev 225045)
@@ -0,0 +1,70 @@
+/*
+ * 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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.
+ */
+
+#pragma once
+
+// This include order is necessary to enforce the GBM EGL platform.
+#include <gbm.h>
+#include <epoxy/egl.h>
+
+#include <cairo.h>
+#include <glib.h>
+#include <unordered_map>
+#include <wpe-mesa/view-backend-exportable-dma-buf.h>
+
+class HeadlessViewBackend {
+public:
+ HeadlessViewBackend();
+ ~HeadlessViewBackend();
+
+ struct wpe_view_backend* backend() const;
+
+ cairo_surface_t* createSnapshot();
+
+private:
+ bool makeCurrent();
+ void performUpdate();
+
+ static struct wpe_mesa_view_backend_exportable_dma_buf_client s_exportableClient;
+
+ struct {
+ EGLDisplay display;
+ EGLConfig config;
+ EGLContext context;
+
+ PFNEGLCREATEIMAGEKHRPROC createImage;
+ PFNEGLDESTROYIMAGEKHRPROC destroyImage;
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES;
+ } m_egl;
+
+ struct wpe_mesa_view_backend_exportable_dma_buf* m_exportable;
+
+ std::unordered_map<uint32_t, int32_t> m_exportMap;
+ std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> m_pendingImage { };
+ std::pair<uint32_t, std::tuple<EGLImageKHR, uint32_t, uint32_t>> m_lockedImage { };
+
+ GSource* m_updateSource;
+ gint64 m_frameRate { G_USEC_PER_SEC / 60 };
+};