From 5608ec8002be8370e78c9dbb1e07cee4cfb18b58 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 1 Oct 2013 13:19:20 +0200 Subject: [PATCH 02/16] wayland: Add support for the Wayland winsys * Adds EGL_WL_bind_wayland_display extension * Adds wayland-egl library * Adds wl_dispmanx_buffer protocol extension TODO: Check that platform_get_dimensions() returning swapchain_count == 1 is correct TODO: Remove the requirement of passing a valid DispmanX element handle to the SwapBuffers and CreateSurface RPC calls. This will remove the need to open a DispmanX display from the clients. TODO: wl_dispmanx_server_buffer should probably be defined in a private header that can be included from EGL and vc_* instead of in vc_vchi_dispmanx.h --- .gitignore | 1 + CMakeLists.txt | 11 + README.md | 4 + buildme | 10 +- .../linux/apps/raspicam/CMakeLists.txt | 2 +- interface/khronos/CMakeLists.txt | 53 ++++- interface/khronos/common/khrn_client.c | 15 ++ interface/khronos/common/khrn_client.h | 10 + interface/khronos/common/khrn_client_mangle.h | 3 + interface/khronos/common/khrn_client_platform.h | 8 + interface/khronos/common/khrn_client_unmangle.h | 3 + .../common/linux/khrn_client_platform_linux.c | 115 ++++++++-- interface/khronos/common/linux/khrn_wayland.c | 215 ++++++++++++++++++ .../common/linux/khrn_wayland.h} | 46 +--- interface/khronos/egl/egl_client.c | 92 +++++--- interface/khronos/egl/egl_client_get_proc.c | 11 + interface/khronos/egl/egl_client_surface.c | 42 +++- interface/khronos/egl/egl_client_surface.h | 38 +++- interface/khronos/egl/egl_int_impl.h | 2 +- interface/khronos/ext/egl_wayland.c | 246 +++++++++++++++++++++ interface/khronos/include/EGL/eglext.h | 23 ++ interface/khronos/wayland-egl/wayland-egl-priv.h | 53 +++++ interface/khronos/wayland-egl/wayland-egl.c | 59 +++++ interface/khronos/wayland-egl/wayland-egl.pc.in | 10 + interface/vmcs_host/CMakeLists.txt | 21 +- interface/vmcs_host/vc_dispmanx.h | 10 + interface/vmcs_host/vc_vchi_dispmanx.c | 42 ++++ interface/vmcs_host/vc_vchi_dispmanx.h | 15 ++ interface/wayland/dispmanx.xml | 123 +++++++++++ makefiles/cmake/Wayland.cmake | 72 ++++++ 30 files changed, 1257 insertions(+), 98 deletions(-) create mode 100644 interface/khronos/common/linux/khrn_wayland.c copy interface/{vmcs_host/vc_vchi_dispmanx.h => khronos/common/linux/khrn_wayland.h} (56%) create mode 100644 interface/khronos/ext/egl_wayland.c create mode 100644 interface/khronos/wayland-egl/wayland-egl-priv.h create mode 100644 interface/khronos/wayland-egl/wayland-egl.c create mode 100644 interface/khronos/wayland-egl/wayland-egl.pc.in create mode 100644 interface/wayland/dispmanx.xml create mode 100644 makefiles/cmake/Wayland.cmake Index: git/.gitignore =================================================================== --- git.orig/.gitignore +++ git/.gitignore @@ -30,3 +30,4 @@ build/ *.pts *.ppm *.mkv +*~ Index: git/CMakeLists.txt =================================================================== --- git.orig/CMakeLists.txt +++ git/CMakeLists.txt @@ -24,6 +24,17 @@ include(makefiles/cmake/global_settings. include(makefiles/cmake/arm-linux.cmake) include(makefiles/cmake/vmcs.cmake) +if (BUILD_WAYLAND) + include(makefiles/cmake/Wayland.cmake) + + # Find Wayland libraries + find_package(PkgConfig) + pkg_check_modules(WAYLAND_CLIENT wayland-client REQUIRED) + pkg_check_modules(WAYLAND_SERVER wayland-server REQUIRED) + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBUILD_WAYLAND") +endif() + enable_language(ASM) # Global include paths Index: git/README.md =================================================================== --- git.orig/README.md +++ git/README.md @@ -6,3 +6,7 @@ Use buildme to build. It requires cmake https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian Note that this repository does not contain the source for the edid_parser and vcdbg binaries due to licensing restrictions. + +To build support for the Wayland winsys in EGL, execute the buildme script like this: + +$ BUILD_WAYLAND=1 ./buildme. Index: git/buildme =================================================================== --- git.orig/buildme +++ git/buildme @@ -8,6 +8,10 @@ fi BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; +if [ -n "$BUILD_WAYLAND" ]; then + WAYLAND_VARS="-DBUILD_WAYLAND=TRUE" +fi + if [ "armv6l" = `arch` ] || [ "armv7l" = `arch` ]; then # Native compile on the Raspberry Pi mkdir -p build/raspberry/$BUILDSUBDIR @@ -32,9 +36,13 @@ elif [ "$1" = "--native" ]; then make -j `nproc` $* else # Cross compile on a more capable machine + if [ -n "$BUILD_WAYLAND" ]; then + # Use wayland-scanner from the build platform + WAYLAND_VARS+=" -DWAYLAND_SCANNER_EXECUTABLE:FILEPATH=/usr/bin/wayland-scanner" + fi mkdir -p build/arm-linux/$BUILDSUBDIR pushd build/arm-linux/$BUILDSUBDIR - cmake -DCMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + cmake -DCMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE $WAYLAND_VARS ../../.. make -j `nproc` if [ "$1" != "" ]; then Index: git/host_applications/linux/apps/raspicam/CMakeLists.txt =================================================================== --- git.orig/host_applications/linux/apps/raspicam/CMakeLists.txt +++ git/host_applications/linux/apps/raspicam/CMakeLists.txt @@ -28,7 +28,7 @@ add_executable(raspividyuv ${COMMON_SOU set (MMAL_LIBS mmal_core mmal_util mmal_vc_client) -target_link_libraries(raspistill ${MMAL_LIBS} vcos bcm_host brcmGLESv2 brcmEGL m) +target_link_libraries(raspistill ${MMAL_LIBS} vcos bcm_host brcmGLESv2 brcmEGL m ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_CLIENT_LIBRARIES}) target_link_libraries(raspiyuv ${MMAL_LIBS} vcos bcm_host) target_link_libraries(raspivid ${MMAL_LIBS} vcos bcm_host) target_link_libraries(raspividyuv ${MMAL_LIBS} vcos bcm_host) Index: git/interface/khronos/CMakeLists.txt =================================================================== --- git.orig/interface/khronos/CMakeLists.txt +++ git/interface/khronos/CMakeLists.txt @@ -6,6 +6,12 @@ # have quite a few circular dependencies, and so the only way # to make it work seems to be to have everything static. +if (BUILD_WAYLAND) +include_directories( + ${WAYLAND_SERVER_INCLUDE_DIRS} +) +endif () + set(EGL_SOURCE egl/egl_client_config.c egl/egl_client_context.c @@ -55,12 +61,55 @@ set(CLIENT_SOURCE common/khrn_int_hash_asm.s common/khrn_client_cache.c) +set(EGL_LIBS + khrn_client + vchiq_arm + vcos + bcm_host) + +if (BUILD_WAYLAND) + set(EGL_SOURCE + ${EGL_SOURCE} + ext/egl_wayland.c + common/linux/khrn_wayland.c) + + set(EGL_LIBS + ${EGL_LIBS} + wayland-client + wayland-server) + + set(WAYLAND_EGL_SOURCE + wayland-egl/wayland-egl.c) + + wayland_add_protocol_server( + EGL_SOURCE + ../../interface/wayland/dispmanx.xml + dispmanx + ) + + wayland_add_protocol_client( + EGL_SOURCE + ../../interface/wayland/dispmanx.xml + dispmanx + ) + + add_library(wayland-egl ${SHARED} ${WAYLAND_EGL_SOURCE}) + install(TARGETS wayland-egl DESTINATION lib) + + configure_file ("wayland-egl/wayland-egl.pc.in" "wayland-egl/wayland-egl.pc" @ONLY) + install (FILES "${CMAKE_CURRENT_BINARY_DIR}/wayland-egl/wayland-egl.pc" + DESTINATION lib/pkgconfig) +endif () + add_library(EGL ${SHARED} ${EGL_SOURCE}) add_library(GLESv2 ${SHARED} ${GLES_SOURCE}) add_library(OpenVG ${SHARED} ${VG_SOURCE}) add_library(WFC ${SHARED} ${WFC_SOURCE}) add_library(khrn_client ${CLIENT_SOURCE}) +set_target_properties(EGL PROPERTIES SOVERSION 1 VERSION 1.0.0) +set_target_properties(GLESv2 PROPERTIES SOVERSION 2 VERSION 2.0.0) + # TODO do we need EGL_static and GLESv2_static now that khrn_static exists? add_library(EGL_static STATIC ${EGL_SOURCE}) add_library(GLESv2_static STATIC ${GLES_SOURCE}) @@ -72,8 +121,7 @@ include_directories (../../host_applicat set(VCSM_LIBS vcsm) add_definitions(-DKHRONOS_HAVE_VCSM) endif() - -target_link_libraries(EGL khrn_client vchiq_arm vcos bcm_host ${VCSM_LIBS} -lm) +target_link_libraries(EGL ${EGL_LIBS} ${VCSM_LIBS} -lm) target_link_libraries(GLESv2 EGL khrn_client vcos) target_link_libraries(WFC EGL) target_link_libraries(OpenVG EGL) @@ -87,7 +135,7 @@ add_library(brcmGLESv2 ${SHARED} ${GLES_ add_library(brcmOpenVG ${SHARED} ${VG_SOURCE}) add_library(brcmWFC ${SHARED} ${WFC_SOURCE}) -target_link_libraries(brcmEGL khrn_client vchiq_arm vcos bcm_host ${VCSM_LIBS} -lm) +target_link_libraries(brcmEGL ${EGL_LIBS} ${VCSM_LIBS} -lm) target_link_libraries(brcmGLESv2 brcmEGL khrn_client vcos) target_link_libraries(brcmWFC brcmEGL) target_link_libraries(brcmOpenVG brcmEGL) Index: git/interface/khronos/common/khrn_client.c =================================================================== --- git.orig/interface/khronos/common/khrn_client.c +++ git/interface/khronos/common/khrn_client.c @@ -54,6 +54,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #include "applications/vmcs/khronos/khronos_server.h" #endif +#ifdef BUILD_WAYLAND +#include "interface/khronos/common/linux/khrn_wayland.h" +#endif + VCOS_LOG_CAT_T khrn_client_log = VCOS_LOG_INIT("khrn_client", VCOS_LOG_WARN); /* @@ -142,6 +146,10 @@ void client_try_unload_server(CLIENT_PRO bool client_process_state_init(CLIENT_PROCESS_STATE_T *process) { if (!process->inited) { +#ifdef BUILD_WAYLAND + process->wl_global = NULL; +#endif + if (!khrn_pointer_map_init(&process->contexts, 64)) return false; @@ -194,6 +202,13 @@ bool client_process_state_init(CLIENT_PR } #endif +#ifdef BUILD_WAYLAND + struct wl_display *wl_display = khrn_platform_get_wl_display(); + if (wl_display) + if (!init_process_wayland(process)) + return false; +#endif + process->inited = true; } Index: git/interface/khronos/common/khrn_client.h =================================================================== --- git.orig/interface/khronos/common/khrn_client.h +++ git/interface/khronos/common/khrn_client.h @@ -310,6 +310,16 @@ struct CLIENT_PROCESS_STATE { #ifdef RPC_LIBRARY KHRONOS_SERVER_CONNECTION_T khrn_connection; #endif + +#ifdef BUILD_WAYLAND + /* Client-side Wayland state */ + struct wl_registry *wl_registry; + struct wl_dispmanx *wl_dispmanx; + struct wl_event_queue *wl_queue; + + /* Compositor-side Wayland state */ + struct wl_global *wl_global; +#endif }; extern bool client_process_state_init(CLIENT_PROCESS_STATE_T *process); Index: git/interface/khronos/common/khrn_client_mangle.h =================================================================== --- git.orig/interface/khronos/common/khrn_client_mangle.h +++ git/interface/khronos/common/khrn_client_mangle.h @@ -83,6 +83,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #define eglReleaseGlobalImageBRCM mangled_eglReleaseGlobalImageBRCM #define eglInitGlobalImageBRCM mangled_eglInitGlobalImageBRCM #define eglTermGlobalImageBRCM mangled_eglTermGlobalImageBRCM +#define eglBindWaylandDisplayWL mangled_eglBindWaylandDisplayWL +#define eglUnbindWaylandDisplayWL mangled_eglUnbindWaylandDisplayWL +#define eglQueryWaylandBufferWL mangled_eglQueryWaylandBufferWL /* OpenGL ES 1.1 and 2.0 functions */ Index: git/interface/khronos/common/khrn_client_platform.h =================================================================== --- git.orig/interface/khronos/common/khrn_client_platform.h +++ git/interface/khronos/common/khrn_client_platform.h @@ -48,6 +48,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #include "interface/khronos/common/vcos/khrn_client_platform_filler_vcos.h" #endif +#ifdef BUILD_WAYLAND +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -328,4 +332,8 @@ typedef struct void *platform_wfc_bounce_thread(void *param); +#ifdef BUILD_WAYLAND +struct wl_display *khrn_platform_get_wl_display(); +#endif + #endif // KHRN_CLIENT_PLATFORM_H Index: git/interface/khronos/common/khrn_client_unmangle.h =================================================================== --- git.orig/interface/khronos/common/khrn_client_unmangle.h +++ git/interface/khronos/common/khrn_client_unmangle.h @@ -83,6 +83,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #undef eglReleaseGlobalImageBRCM #undef eglInitGlobalImageBRCM #undef eglTermGlobalImageBRCM +#undef eglBindWaylandDisplayWL +#undef eglUnbindWaylandDisplayWL +#undef eglQueryWaylandBufferWL /* OpenGL ES 1.1 and 2.0 functions */ Index: git/interface/khronos/common/linux/khrn_client_platform_linux.c =================================================================== --- git.orig/interface/khronos/common/linux/khrn_client_platform_linux.c +++ git/interface/khronos/common/linux/khrn_client_platform_linux.c @@ -37,6 +37,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #include "X11/Xlib.h" #endif +#ifdef BUILD_WAYLAND +#include +#include "interface/khronos/wayland-egl/wayland-egl-priv.h" +#endif + extern VCOS_LOG_CAT_T khrn_client_log; extern void vc_vchi_khronos_init(); @@ -464,14 +469,37 @@ EGLDisplay khrn_platform_set_display_id( return EGL_NO_DISPLAY; } #else + +#ifdef BUILD_WAYLAND +static struct wl_display *hacky_display = NULL; +#endif + EGLDisplay khrn_platform_set_display_id(EGLNativeDisplayType display_id) { if (display_id == EGL_DEFAULT_DISPLAY) return (EGLDisplay)1; - else - return EGL_NO_DISPLAY; + else { +#ifdef BUILD_WAYLAND + void *first_pointer = *(void **) display_id; + + /* wl_display is a wl_proxy, which is a wl_object. + * wl_object's first element points to the interfacetype. */ + if (first_pointer == &wl_display_interface) { + hacky_display = (struct wl_display*)display_id; + return (EGLDisplay)1; + } else +#endif + return EGL_NO_DISPLAY; + } +} + +#ifdef BUILD_WAYLAND +struct wl_display *khrn_platform_get_wl_display() +{ + return hacky_display; } #endif +#endif #ifdef WANT_X static void dump_hierarchy(Window w, Window thisw, Window look, int level) @@ -805,22 +833,81 @@ static EGL_DISPMANX_WINDOW_T *check_defa void platform_get_dimensions(EGLDisplay dpy, EGLNativeWindowType win, uint32_t *width, uint32_t *height, uint32_t *swapchain_count) { - EGL_DISPMANX_WINDOW_T *dwin = check_default(win); - vcos_assert(dwin); - vcos_assert(dwin->width < 1<<16); // sanity check - vcos_assert(dwin->height < 1<<16); // sanity check - *width = dwin->width; - *height = dwin->height; - *swapchain_count = 0; +#ifdef BUILD_WAYLAND + if(khrn_platform_get_wl_display()) { + struct wl_egl_window *wl_egl_window = (struct wl_egl_window*)win; + *width = wl_egl_window->width; + *height = wl_egl_window->height; + /* This seems to be used for sync'ing with the VC on buffer creation, but + we are managing them on the CPU side */ + *swapchain_count = 1; + } else { +#endif + EGL_DISPMANX_WINDOW_T *dwin = check_default(win); + vcos_assert(dwin); + vcos_assert(dwin->width < 1<<16); // sanity check + vcos_assert(dwin->height < 1<<16); // sanity check + *width = dwin->width; + *height = dwin->height; + *swapchain_count = 0; +#ifdef BUILD_WAYLAND + } +#endif +} + +#ifdef BUILD_WAYLAND +static DISPMANX_ELEMENT_HANDLE_T create_dummy_element() +{ + DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0); + DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0); + DISPMANX_ELEMENT_HANDLE_T element; + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect; + VC_RECT_T dst_rect; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = 1 << 16; + src_rect.height = 1 << 16; + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = 1; + dst_rect.height = 1; + + element = vc_dispmanx_element_add(update, display, 0/*layer*/, &dst_rect, + 0/*src*/, &src_rect, + DISPMANX_PROTECTION_NONE, &alpha, + 0/*clamp*/, 0/*transform*/); + + vc_dispmanx_update_submit_sync(update); + + vc_dispmanx_display_close(display); + + return element; } +#endif uint32_t platform_get_handle(EGLDisplay dpy, EGLNativeWindowType win) { - EGL_DISPMANX_WINDOW_T *dwin = check_default(win); - vcos_assert(dwin); - vcos_assert(dwin->width < 1<<16); // sanity check - vcos_assert(dwin->height < 1<<16); // sanity check - return dwin->element; +#ifdef BUILD_WAYLAND + if(khrn_platform_get_wl_display()) { + struct wl_egl_window *wl_egl_window = (struct wl_egl_window*)win; + + if (wl_egl_window->dummy_element == PLATFORM_WIN_NONE) + wl_egl_window->dummy_element = create_dummy_element(); + + return wl_egl_window->dummy_element; + } else { +#endif + EGL_DISPMANX_WINDOW_T *dwin = check_default(win); + vcos_assert(dwin); + vcos_assert(dwin->width < 1<<16); // sanity check + vcos_assert(dwin->height < 1<<16); // sanity check + return dwin->element; +#ifdef BUILD_WAYLAND + } +#endif } #endif Index: git/interface/khronos/common/linux/khrn_wayland.c =================================================================== --- /dev/null +++ git/interface/khronos/common/linux/khrn_wayland.c @@ -0,0 +1,215 @@ +/* +Copyright (c) 2013, Raspberry Pi Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER 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. +*/ + +#define VCOS_LOG_CATEGORY (&khrn_client_log) + +#include "interface/khronos/common/linux/khrn_wayland.h" +#include "interface/khronos/wayland-dispmanx-client-protocol.h" +#include "interface/khronos/wayland-egl/wayland-egl-priv.h" + +extern VCOS_LOG_CAT_T khrn_client_log; + +static void handle_dispmanx_format(void *data, struct wl_dispmanx *dispmanx, + uint32_t format) +{ +} + +static void handle_dispmanx_allocated(void *data, struct wl_dispmanx *dispmanx, + struct wl_buffer *wl_buffer, + uint32_t resource_handle) +{ + struct wl_dispmanx_client_buffer *buffer = wl_buffer_get_user_data(wl_buffer); + + buffer->pending_allocation = 0; + buffer->resource = resource_handle; +} + +static const struct wl_dispmanx_listener dispmanx_listener = { + handle_dispmanx_format, + handle_dispmanx_allocated, +}; + +static void +sync_callback(void *data, struct wl_callback *callback, uint32_t serial) +{ + int *done = data; + + *done = 1; + + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +static int +roundtrip(CLIENT_PROCESS_STATE_T *process) +{ + struct wl_display *wl_display = khrn_platform_get_wl_display(); + struct wl_callback *callback; + int done = 0, ret = 0; + + callback = wl_display_sync(wl_display); + wl_callback_add_listener(callback, &sync_listener, &done); + wl_proxy_set_queue((struct wl_proxy *) callback, process->wl_queue); + while (ret != -1 && !done) + ret = wl_display_dispatch_queue(wl_display, process->wl_queue); + + if (!done) + wl_callback_destroy(callback); + + return ret; +} + +int do_wl_roundtrip() +{ + CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE(); + return roundtrip(process); +} + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct wl_display *wl_display = khrn_platform_get_wl_display(); + CLIENT_PROCESS_STATE_T *process = (CLIENT_PROCESS_STATE_T *)data; + + if (strcmp(interface, "wl_dispmanx") == 0) { + process->wl_dispmanx = wl_registry_bind(registry, name, + &wl_dispmanx_interface, 1); + + wl_proxy_set_queue((struct wl_proxy *) process->wl_dispmanx, + process->wl_queue); + wl_dispmanx_add_listener(process->wl_dispmanx, &dispmanx_listener, wl_display); + roundtrip(process); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +int +init_process_wayland(CLIENT_PROCESS_STATE_T *process) +{ + struct wl_display *wl_display = khrn_platform_get_wl_display(); + + process->wl_queue = wl_display_create_queue(wl_display); + if (!process->wl_queue) { + vcos_log_error("wl_display_create_queue failed\n"); + return false; + } + wl_display_dispatch_pending(wl_display); + + process->wl_registry = wl_display_get_registry(wl_display); + if (!process->wl_registry) { + vcos_log_error("wl_display_get_registry failed\n"); + return false; + } + + wl_proxy_set_queue((struct wl_proxy *) process->wl_registry, + process->wl_queue); + + wl_registry_add_listener(process->wl_registry, ®istry_listener, process); + + if (roundtrip(process) < 0 || process->wl_dispmanx == NULL) { + vcos_log_error("failed to get wl_dispmanx\n"); + return false; + } + + return true; +} + +#ifndef ALIGN_UP +#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1)) +#endif + +static void handle_buffer_release(void *data, struct wl_buffer *buffer_wl) +{ + struct wl_dispmanx_client_buffer *wl_dispmanx_client_buffer = data; + wl_dispmanx_client_buffer->in_use = 0; +} + +static const struct wl_buffer_listener buffer_listener = { + handle_buffer_release +}; + +struct wl_dispmanx_client_buffer * +allocate_wl_buffer(struct wl_egl_window *window, KHRN_IMAGE_FORMAT_T color) +{ + CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE(); + struct wl_dispmanx_client_buffer *wl_dispmanx_client_buffer; + struct wl_buffer *wl_buffer; + uint32_t stride = ALIGN_UP(window->width * 4, 16); + uint32_t buffer_height = ALIGN_UP(window->height, 16); + enum wl_dispmanx_format color_format; + int ret = 0; + + switch (color) { + case ABGR_8888: + color_format = WL_DISPMANX_FORMAT_ABGR8888; + break; + case XBGR_8888: + color_format = WL_DISPMANX_FORMAT_XBGR8888; + break; + case RGB_565: + color_format = WL_DISPMANX_FORMAT_RGB565; + break; + default: + vcos_log_error("unknown KHRN_IMAGE_FORMAT_T 0x%x\n", color); + return NULL; + } + + wl_buffer = wl_dispmanx_create_buffer(process->wl_dispmanx, window->width, + window->height, stride, buffer_height, + color_format); + if (wl_buffer == NULL) + return NULL; + + wl_dispmanx_client_buffer = calloc(1, sizeof(struct wl_dispmanx_client_buffer)); + wl_dispmanx_client_buffer->wl_buffer = wl_buffer; + wl_dispmanx_client_buffer->in_use = 0; + wl_dispmanx_client_buffer->pending_allocation = 1; + wl_dispmanx_client_buffer->width = window->width; + wl_dispmanx_client_buffer->height = window->height; + + wl_proxy_set_queue((struct wl_proxy *) wl_buffer, process->wl_queue); + wl_buffer_add_listener(wl_buffer, &buffer_listener, wl_dispmanx_client_buffer); + + while (ret != -1 && wl_dispmanx_client_buffer->pending_allocation) + ret = do_wl_roundtrip(); + + return wl_dispmanx_client_buffer; +} Index: git/interface/vmcs_host/vc_vchi_dispmanx.h =================================================================== --- git.orig/interface/vmcs_host/vc_vchi_dispmanx.h +++ git/interface/vmcs_host/vc_vchi_dispmanx.h @@ -66,4 +66,19 @@ typedef struct { #define ELEMENT_CHANGE_MASK_RESOURCE (1<<4) #define ELEMENT_CHANGE_TRANSFORM (1<<5) +#ifdef BUILD_WAYLAND +/* XXX: This should be in a private header that can be included from EGL and vc_* */ +#include +#include "interface/vmcs_host/wayland-dispmanx-server-protocol.h" +struct wl_dispmanx_server_buffer { + struct wl_resource *resource; + struct wl_dispmanx *dispmanx; + enum wl_dispmanx_format format; + DISPMANX_RESOURCE_HANDLE_T handle; + int32_t width; + int32_t height; + int in_use; +}; +#endif + #endif Index: git/interface/khronos/common/linux/khrn_wayland.h =================================================================== --- /dev/null +++ git/interface/khronos/common/linux/khrn_wayland.h @@ -0,0 +1,33 @@ +/* +Copyright (c) 2013, Raspberry Pi Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER 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 "interface/khronos/common/khrn_client.h" + +int init_process_wayland(CLIENT_PROCESS_STATE_T *process); +int do_wl_roundtrip(); + +struct wl_dispmanx_client_buffer *allocate_wl_buffer(struct wl_egl_window *window, KHRN_IMAGE_FORMAT_T color); Index: git/interface/khronos/egl/egl_client.c =================================================================== --- git.orig/interface/khronos/egl/egl_client.c +++ git/interface/khronos/egl/egl_client.c @@ -153,6 +153,10 @@ by an attribute value" #include #include +#ifdef BUILD_WAYLAND +#include "interface/khronos/wayland-egl/wayland-egl-priv.h" +#include "interface/khronos/common/linux/khrn_wayland.h" +#endif #include "interface/khronos/egl/egl_client_cr.c" @@ -162,17 +166,6 @@ static void egl_current_release(CLIENT_P void egl_gl_flush_callback(bool wait); void egl_vg_flush_callback(bool wait); -#include "interface/vmcs_host/vc_dispmanx_types.h" -/**HACKHACK - give us the ability to inject a DispmanX - * resource handle into the CreateWindowSurface and - * SwapBuffers calls */ -static DISPMANX_RESOURCE_HANDLE_T next_resource_handle; - -EGLAPI EGLBoolean EGLAPIENTRY eglSetNextResourceHandle(DISPMANX_RESOURCE_HANDLE_T handle) -{ - next_resource_handle = handle; -} - /* TODO: do an RPC call to make sure the Khronos vll is loaded (and that it stays loaded until eglTerminate) Also affects global image (and possibly others?) @@ -451,6 +444,9 @@ EGLAPI const char EGLAPIENTRY * eglQuery "EGL_KHR_fence_sync " #endif #endif +#if EGL_WL_bind_wayland_display + "EGL_WL_bind_wayland_display " +#endif ; break; case EGL_VENDOR: @@ -655,8 +651,7 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreateW false, EGL_NO_TEXTURE, EGL_NO_TEXTURE, - 0, 0, - next_resource_handle); + 0, 0); if (surface) { if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { @@ -901,7 +896,7 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreateP mipmap_texture, texture_format, texture_target, - 0, 0, 0); + 0, 0); if (surface) { if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { @@ -1043,7 +1038,7 @@ EGLAPI EGLSurface EGLAPIENTRY eglCreateP false, EGL_NO_TEXTURE, EGL_NO_TEXTURE, - pixmap, ((server_handle[0] == 0) && (server_handle[1] == (uint32_t)(-1))) ? NULL : server_handle, 0); + pixmap, ((server_handle[0] == 0) && (server_handle[1] == (uint32_t)(-1))) ? NULL : server_handle); if (surface) { if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { @@ -2245,6 +2240,9 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuf CLIENT_THREAD_STATE_T *thread; CLIENT_PROCESS_STATE_T *process; EGLBoolean result; +#ifdef BUILD_WAYLAND + struct wl_display *wl_display = khrn_platform_get_wl_display(); +#endif vcos_log_trace("eglSwapBuffers start. dpy=%d. surf=%d.", (int)dpy, (int)surf); @@ -2315,18 +2313,58 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuf vcos_log_trace("eglSwapBuffers server call"); - if (next_resource_handle) - RPC_CALL7(eglIntSwapBuffers_impl, - thread, - EGLINTSWAPBUFFERS_ID_V2, - RPC_UINT(surface->serverbuffer), - RPC_UINT(surface->width), - RPC_UINT(surface->height), - RPC_UINT(surface->internal_handle), - RPC_UINT(surface->swap_behavior == EGL_BUFFER_PRESERVED ? 1 : 0), - RPC_UINT(khrn_platform_get_window_position(surface->win)), - RPC_INT(next_resource_handle)); - else +#ifdef BUILD_WAYLAND + if (wl_display) { + struct wl_egl_window *wl_egl_window = surface->wl_egl_window; + struct wl_dispmanx_client_buffer *buffer_temp; + uint32_t configid; + KHRN_IMAGE_FORMAT_T color; + int ret = 0; + + buffer_temp = surface->front_wl_buffer; + surface->front_wl_buffer = surface->back_wl_buffer; + surface->back_wl_buffer = buffer_temp; + + configid = egl_config_to_id(surface->config); + color = egl_config_get_color_format(configid); + + if (surface->back_wl_buffer == NULL) + surface->back_wl_buffer = allocate_wl_buffer(wl_egl_window, color); + else if (surface->back_wl_buffer->width != width || + surface->back_wl_buffer->height != height) { + + struct wl_dispmanx_client_buffer *buffer; + + wl_buffer_destroy(surface->back_wl_buffer->wl_buffer); + free(surface->back_wl_buffer); + + buffer = allocate_wl_buffer(wl_egl_window, color); + surface->back_wl_buffer = buffer; + } + + RPC_CALL7(eglIntSwapBuffers_impl, + thread, + EGLINTSWAPBUFFERS_ID_V2, + RPC_UINT(surface->serverbuffer), + RPC_UINT(surface->width), + RPC_UINT(surface->height), + RPC_UINT(surface->internal_handle), + RPC_UINT(surface->swap_behavior == EGL_BUFFER_PRESERVED ? 1 : 0), + RPC_UINT(khrn_platform_get_window_position(surface->win)), + RPC_INT(surface->back_wl_buffer->resource)); + + surface->front_wl_buffer->in_use = 1; + wl_surface_attach(wl_egl_window->wl_surface, + surface->front_wl_buffer->wl_buffer, + 0, 0); + wl_surface_damage(wl_egl_window->wl_surface, 0, 0, + surface->width, surface->height); + wl_surface_commit(wl_egl_window->wl_surface); + + while(ret != -1 && surface->back_wl_buffer->in_use) + ret = wl_display_dispatch_queue(wl_display, process->wl_queue); + } else +#endif RPC_CALL6(eglIntSwapBuffers_impl, thread, EGLINTSWAPBUFFERS_ID, Index: git/interface/khronos/egl/egl_client_get_proc.c =================================================================== --- git.orig/interface/khronos/egl/egl_client_get_proc.c +++ git/interface/khronos/egl/egl_client_get_proc.c @@ -254,6 +254,17 @@ EGLAPI void EGLAPIENTRY (* eglGetProcAdd return (void(*)(void))eglQueryGlobalImageBRCM; #endif +#ifdef BUILD_WAYLAND +#if EGL_WL_bind_wayland_display + if (!strcmp(procname, "eglBindWaylandDisplayWL")) + return (void(*)(void))eglBindWaylandDisplayWL; + if (!strcmp(procname, "eglUnbindWaylandDisplayWL")) + return (void(*)(void))eglUnbindWaylandDisplayWL; + if (!strcmp(procname, "eglQueryWaylandBufferWL")) + return (void(*)(void))eglQueryWaylandBufferWL; +#endif +#endif + return (void(*)(void)) NULL; } Index: git/interface/khronos/egl/egl_client_surface.c =================================================================== --- git.orig/interface/khronos/egl/egl_client_surface.c +++ git/interface/khronos/egl/egl_client_surface.c @@ -46,6 +46,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #include "interface/khronos/egl/egl_int_impl.h" #endif +#ifdef BUILD_WAYLAND +#include "interface/khronos/wayland-egl/wayland-egl-priv.h" +#include "interface/khronos/common/linux/khrn_wayland.h" +#endif + #include @@ -314,8 +319,7 @@ EGL_SURFACE_T *egl_surface_create( EGLenum texture_format, EGLenum texture_target, EGLNativePixmapType pixmap, - const uint32_t *pixmap_server_handle, - DISPMANX_RESOURCE_HANDLE_T next_resource_handle) + const uint32_t *pixmap_server_handle) { KHRN_IMAGE_FORMAT_T color; KHRN_IMAGE_FORMAT_T depth; @@ -326,6 +330,10 @@ EGL_SURFACE_T *egl_surface_create( EGLint config_depth_bits; EGLint config_stencil_bits; CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); +#ifdef BUILD_WAYLAND + struct wl_display *wl_display = khrn_platform_get_wl_display(); + DISPMANX_RESOURCE_HANDLE_T resource; +#endif EGL_SURFACE_T *surface = egl_surface_pool_alloc(); @@ -390,6 +398,18 @@ EGL_SURFACE_T *egl_surface_create( vcos_assert(color != IMAGE_FORMAT_INVALID); +#ifdef BUILD_WAYLAND + if (type == WINDOW && wl_display) { + surface->wl_egl_window = (struct wl_egl_window*)win; + surface->back_wl_buffer = allocate_wl_buffer( + surface->wl_egl_window, color); + resource = surface->back_wl_buffer->resource; + } else { + surface->wl_egl_window = NULL; + resource = DISPMANX_NO_HANDLE; + } +#endif + #ifdef KHRONOS_EGL_PLATFORM_OPENWFC // Create stream for this window if(type != PBUFFER) @@ -474,7 +494,8 @@ EGL_SURFACE_T *egl_surface_create( #endif uint32_t results[3]; - if (next_resource_handle) +#ifdef BUILD_WAYLAND + if (resource != DISPMANX_NO_HANDLE) RPC_CALL16_OUT_CTRL(eglIntCreateSurface_impl, thread, EGLINTCREATESURFACE_ID_V2, @@ -492,9 +513,10 @@ EGL_SURFACE_T *egl_surface_create( RPC_UINT(config_stencil_bits), RPC_UINT(sem_name), RPC_UINT(type), - RPC_INT(next_resource_handle), + RPC_INT(resource), results); else +#endif RPC_CALL15_OUT_CTRL(eglIntCreateSurface_impl, thread, EGLINTCREATESURFACE_ID, @@ -663,6 +685,18 @@ void egl_surface_free(EGL_SURFACE_T *sur if( surface->type == WINDOW ) { vcos_log_trace("egl_surface_free: calling platform_destroy_winhandle..."); platform_destroy_winhandle( surface->win, surface->internal_handle ); + +#ifdef BUILD_WAYLAND + if (surface->back_wl_buffer) { + wl_buffer_destroy(surface->back_wl_buffer->wl_buffer); + free(surface->back_wl_buffer); + } + + if (surface->front_wl_buffer) { + wl_buffer_destroy(surface->front_wl_buffer->wl_buffer); + free(surface->front_wl_buffer); + } +#endif } /* return value ignored -- read performed to ensure blocking. we want this to * block so clients can safely destroy the surface's window as soon as the Index: git/interface/khronos/egl/egl_client_surface.h =================================================================== --- git.orig/interface/khronos/egl/egl_client_surface.h +++ git/interface/khronos/egl/egl_client_surface.h @@ -288,6 +288,41 @@ typedef struct { type == PIXMAP */ bool server_owned; + +#ifdef BUILD_WAYLAND + /* + wl_egl_window + + Validity: + type == WINDOW + + Invariant: + wayland EGL window + */ + struct wl_egl_window *wl_egl_window; + + /* + front_wl_buffer + + Validity: + type == WINDOW + + Invariant: + client-side information about the wl_buffer in the front + */ + struct wl_dispmanx_client_buffer *front_wl_buffer; + + /* + back_wl_buffer + + Validity: + type == WINDOW + + Invariant: + client-side information about the wl_buffer in the back + */ + struct wl_dispmanx_client_buffer *back_wl_buffer; +#endif } EGL_SURFACE_T; extern bool egl_surface_check_attribs( @@ -322,8 +357,7 @@ extern EGL_SURFACE_T *egl_surface_create EGLenum texture_format, EGLenum texture_target, EGLNativePixmapType pixmap, - const uint32_t *pixmap_server_handle, - DISPMANX_RESOURCE_HANDLE_T next_resource_handle); + const uint32_t *pixmap_server_handle); extern EGL_SURFACE_T *egl_surface_from_vg_image( VGImage vg_handle, EGLSurface name, Index: git/interface/khronos/egl/egl_int_impl.h =================================================================== --- git.orig/interface/khronos/egl/egl_int_impl.h +++ git/interface/khronos/egl/egl_int_impl.h @@ -57,7 +57,7 @@ FN(int, eglIntCreateSurface_impl, ( uint32_t sem, uint32_t type, uint32_t *results, - DISPMANX_RESOURCE_HANDLE_T next_resource_handle)) + DISPMANX_RESOURCE_HANDLE_T resource_handle)) FN(int, eglIntCreatePbufferFromVGImage_impl, ( VGImage vg_handle, Index: git/interface/khronos/ext/egl_wayland.c =================================================================== --- /dev/null +++ git/interface/khronos/ext/egl_wayland.c @@ -0,0 +1,246 @@ +/* +Copyright (c) 2013, Raspberry Pi Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER 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 "interface/khronos/common/khrn_client_mangle.h" +#include "interface/khronos/common/khrn_client_rpc.h" + +#include "interface/khronos/ext/egl_khr_sync_client.h" +#include "interface/khronos/include/EGL/egl.h" +#include "interface/khronos/include/EGL/eglext.h" + +#include "interface/vmcs_host/vc_vchi_dispmanx.h" + +#include +#include "interface/khronos/wayland-dispmanx-server-protocol.h" + +static void +destroy_buffer(struct wl_resource *resource) +{ + struct wl_dispmanx_server_buffer *buffer = wl_resource_get_user_data(resource); + + if(!buffer->in_use) + vc_dispmanx_resource_delete(buffer->handle); + + free(buffer); +} + +static void +buffer_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_buffer_interface dispmanx_buffer_interface = { + buffer_destroy +}; + +static VC_IMAGE_TYPE_T +get_vc_format(enum wl_dispmanx_format format) +{ + /* XXX: The app is likely to have been premultiplying in its shaders, + * but the VC scanout hardware on the RPi cannot mix premultiplied alpha + * channel with the element's alpha. + */ + switch (format) { + case WL_DISPMANX_FORMAT_ABGR8888: + return VC_IMAGE_RGBA32; + case WL_DISPMANX_FORMAT_XBGR8888: + return VC_IMAGE_BGRX8888; + case WL_DISPMANX_FORMAT_RGB565: + return VC_IMAGE_RGB565; + default: + /* invalid format */ + return VC_IMAGE_MIN; + } +} + +static void +dispmanx_create_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int32_t width, int32_t height, + uint32_t stride, uint32_t buffer_height, uint32_t format) +{ + struct wl_dispmanx_server_buffer *buffer; + VC_IMAGE_TYPE_T vc_format = get_vc_format(format); + uint32_t dummy; + + if(vc_format == VC_IMAGE_MIN) { + wl_resource_post_error(resource, + WL_DISPMANX_ERROR_INVALID_FORMAT, + "invalid format"); + return; + } + + buffer = calloc(1, sizeof *buffer); + if (buffer == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + buffer->handle = vc_dispmanx_resource_create(vc_format, + width | (stride << 16), + height | (buffer_height << 16), + &dummy); + if(buffer->handle == DISPMANX_NO_HANDLE) { + wl_resource_post_error(resource, + WL_DISPMANX_ERROR_ALLOC_FAILED, + "allocation failed"); + free(buffer); + return; + } + + buffer->width = width; + buffer->height = height; + buffer->format = format; + + buffer->resource = wl_resource_create(resource->client, &wl_buffer_interface, + 1, id); + if (!buffer->resource) { + wl_resource_post_no_memory(resource); + vc_dispmanx_resource_delete(buffer->handle); + free(buffer); + return; + } + + wl_resource_set_implementation(buffer->resource, + (void (**)(void)) &dispmanx_buffer_interface, + buffer, destroy_buffer); + + wl_dispmanx_send_buffer_allocated(resource, buffer->resource, + buffer->handle); +} + +static const struct wl_dispmanx_interface dispmanx_interface = { + dispmanx_create_buffer, +}; + +static void +bind_dispmanx(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(client, &wl_dispmanx_interface, 1, id); + wl_resource_set_implementation(resource, &dispmanx_interface, NULL, NULL); + + wl_resource_post_event(resource, WL_DISPMANX_FORMAT, + WL_DISPMANX_FORMAT_ARGB8888); + + wl_resource_post_event(resource, WL_DISPMANX_FORMAT, + WL_DISPMANX_FORMAT_XRGB8888); + + wl_resource_post_event(resource, WL_DISPMANX_FORMAT, + WL_DISPMANX_FORMAT_ABGR8888); + + wl_resource_post_event(resource, WL_DISPMANX_FORMAT, + WL_DISPMANX_FORMAT_XBGR8888); + + wl_resource_post_event(resource, WL_DISPMANX_FORMAT, + WL_DISPMANX_FORMAT_RGB565); +} + +EGLBoolean EGLAPIENTRY +eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display) +{ + CLIENT_THREAD_STATE_T *thread; + CLIENT_PROCESS_STATE_T *process; + + if (!CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) + return EGL_FALSE; + + if (process->wl_global != NULL) + goto error; + + process->wl_global = wl_global_create(display, &wl_dispmanx_interface, 1, + NULL, bind_dispmanx); + if (process->wl_global == NULL) + goto error; + + return EGL_TRUE; + +error: + CLIENT_UNLOCK(); + return EGL_FALSE; +} + +EGLBoolean EGLAPIENTRY +eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display) +{ + CLIENT_THREAD_STATE_T *thread; + CLIENT_PROCESS_STATE_T *process; + + if (!CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) + return EGL_FALSE; + + wl_global_destroy(process->wl_global); + process->wl_global = NULL; + + CLIENT_UNLOCK(); + + return EGL_TRUE; +} + +static int +get_egl_format(enum wl_dispmanx_format format) +{ + switch (format) { + case WL_DISPMANX_FORMAT_ABGR8888: + return EGL_TEXTURE_RGBA; + case WL_DISPMANX_FORMAT_XBGR8888: + return EGL_TEXTURE_RGB; + case WL_DISPMANX_FORMAT_RGB565: + return EGL_TEXTURE_RGB; + default: + /* invalid format */ + return EGL_NO_TEXTURE; + } +} + +EGLBoolean EGLAPIENTRY +eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *_buffer, + EGLint attribute, EGLint *value) +{ + struct wl_dispmanx_server_buffer *buffer = wl_resource_get_user_data(_buffer); + + if (wl_resource_instance_of(_buffer, &wl_dispmanx_interface, + &dispmanx_buffer_interface)) + return EGL_FALSE; + + switch (attribute) { + case EGL_TEXTURE_FORMAT: + *value = get_egl_format(buffer->format); + if (*value == EGL_NO_TEXTURE) + return EGL_FALSE; + return EGL_TRUE; + case EGL_WIDTH: + *value = buffer->width; + return EGL_TRUE; + case EGL_HEIGHT: + *value = buffer->height; + return EGL_TRUE; + } + + return EGL_FALSE; +} Index: git/interface/khronos/include/EGL/eglext.h =================================================================== --- git.orig/interface/khronos/include/EGL/eglext.h +++ git/interface/khronos/include/EGL/eglext.h @@ -191,6 +191,29 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLG #endif +#ifndef EGL_WL_bind_wayland_display +#define EGL_WL_bind_wayland_display 1 + +#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */ +#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */ +#define EGL_TEXTURE_Y_U_V_WL 0x31D7 +#define EGL_TEXTURE_Y_UV_WL 0x31D8 +#define EGL_TEXTURE_Y_XUXV_WL 0x31D9 + +struct wl_display; +struct wl_resource; +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display); +EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); +#endif +typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); + +#endif + + #ifdef __cplusplus } #endif Index: git/interface/khronos/wayland-egl/wayland-egl-priv.h =================================================================== --- /dev/null +++ git/interface/khronos/wayland-egl/wayland-egl-priv.h @@ -0,0 +1,53 @@ +/* Copied from Mesa */ + +#ifndef _WAYLAND_EGL_PRIV_H +#define _WAYLAND_EGL_PRIV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* GCC visibility */ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define WL_EGL_EXPORT __attribute__ ((visibility("default"))) +#else +#define WL_EGL_EXPORT +#endif + +#include "interface/vmcs_host/vc_dispmanx.h" +#include "interface/khronos/egl/egl_client_surface.h" + +#include + +struct wl_dispmanx_client_buffer { + struct wl_buffer *wl_buffer; + DISPMANX_RESOURCE_HANDLE_T resource; + + int pending_allocation; + int in_use; + int width; + int height; +}; + +struct wl_egl_window { + struct wl_surface *wl_surface; + + int width; + int height; + int dx; + int dy; + + int attached_width; + int attached_height; + + /* XXX: The VC side seems to expect a valid element handle to be + passed to eglIntCreateSurface_impl and/or eglIntSwapBuffers_impl, + even for host-managed surfaces. */ + DISPMANX_ELEMENT_HANDLE_T dummy_element; +}; + +#ifdef __cplusplus +} +#endif + +#endif Index: git/interface/khronos/wayland-egl/wayland-egl.c =================================================================== --- /dev/null +++ git/interface/khronos/wayland-egl/wayland-egl.c @@ -0,0 +1,59 @@ +/* Copied from Mesa */ + +#include + +#include +#include +#include "wayland-egl-priv.h" + +WL_EGL_EXPORT void +wl_egl_window_resize(struct wl_egl_window *egl_window, + int width, int height, + int dx, int dy) +{ + if (egl_window->width == width && + egl_window->height == height && + egl_window->dx == dx && + egl_window->dy == dy) + return; + + egl_window->width = width; + egl_window->height = height; + egl_window->dx = dx; + egl_window->dy = dy; +} + +WL_EGL_EXPORT struct wl_egl_window * +wl_egl_window_create(struct wl_surface *surface, + int width, int height) +{ + struct wl_egl_window *egl_window; + + egl_window = calloc(1, sizeof *egl_window); + if (!egl_window) + return NULL; + + egl_window->wl_surface = surface; + wl_egl_window_resize(egl_window, width, height, 0, 0); + egl_window->attached_width = 0; + egl_window->attached_height = 0; + egl_window->dummy_element = PLATFORM_WIN_NONE; + + return egl_window; +} + +WL_EGL_EXPORT void +wl_egl_window_destroy(struct wl_egl_window *egl_window) +{ + free(egl_window); +} + +WL_EGL_EXPORT void +wl_egl_window_get_attached_size(struct wl_egl_window *egl_window, + int *width, int *height) +{ + if (width) + *width = egl_window->attached_width; + if (height) + *height = egl_window->attached_height; +} Index: git/interface/khronos/wayland-egl/wayland-egl.pc.in =================================================================== --- /dev/null +++ git/interface/khronos/wayland-egl/wayland-egl.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: wayland-egl +Description: VideoCore wayland-egl library +Version: @PROJECT_APIVER@ +Libs: -L${libdir} -lwayland-egl +Cflags: -I${includedir} Index: git/interface/vmcs_host/CMakeLists.txt =================================================================== --- git.orig/interface/vmcs_host/CMakeLists.txt +++ git/interface/vmcs_host/CMakeLists.txt @@ -9,13 +9,24 @@ add_definitions(-fno-strict-aliasing) include_directories(${VMCS_TARGET}/vcfiled) -add_library(vchostif - ${VMCS_TARGET}/vcfilesys.c ${VMCS_TARGET}/vcmisc.c - vc_vchi_gencmd.c vc_vchi_filesys.c vc_vchi_gpuserv.c - vc_vchi_tvservice.c vc_vchi_cecservice.c - vc_vchi_dispmanx.c vc_service_common.c) +set(VCHOSTIF_SOURCE + ${VMCS_TARGET}/vcfilesys.c ${VMCS_TARGET}/vcmisc.c + vc_vchi_gencmd.c vc_vchi_filesys.c vc_vchi_gpuserv.c + vc_vchi_tvservice.c vc_vchi_cecservice.c + vc_vchi_dispmanx.c vc_service_common.c) # ${VMCS_TARGET}/vmcs_main.c # vc_vchi_haud.c + +if (BUILD_WAYLAND) +wayland_add_protocol_server( + VCHOSTIF_SOURCE + ../../interface/wayland/dispmanx.xml + dispmanx +) +endif () + +add_library(vchostif ${VCHOSTIF_SOURCE}) + #add_library(bufman vc_vchi_bufman.c ) # OpenMAX/IL component service Index: git/interface/vmcs_host/vc_dispmanx.h =================================================================== --- git.orig/interface/vmcs_host/vc_dispmanx.h +++ git/interface/vmcs_host/vc_dispmanx.h @@ -39,6 +39,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #ifdef __cplusplus extern "C" { #endif + +#ifdef BUILD_WAYLAND +struct wl_resource; +#endif + // Same function as above, to aid migration of code. VCHPRE_ int VCHPOST_ vc_dispman_init( void ); // Stop the service from being used @@ -135,6 +140,11 @@ VCHPRE_ int VCHPOST_ vc_dispmanx_resourc // Start triggering callbacks synced to vsync VCHPRE_ int VCHPOST_ vc_dispmanx_vsync_callback( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ); +#ifdef BUILD_WAYLAND +VCHPRE_ DISPMANX_RESOURCE_HANDLE_T VCHPOST_ vc_dispmanx_get_handle_from_wl_buffer( struct wl_resource *_buffer ); + +VCHPRE_ void VCHPOST_ vc_dispmanx_set_wl_buffer_in_use( struct wl_resource *_buffer, int in_use ); +#endif #ifdef __cplusplus } #endif Index: git/interface/vmcs_host/vc_vchi_dispmanx.c =================================================================== --- git.orig/interface/vmcs_host/vc_vchi_dispmanx.c +++ git/interface/vmcs_host/vc_vchi_dispmanx.c @@ -1319,3 +1319,45 @@ static void *dispmanx_notify_func( void } return 0; } + + +#ifdef BUILD_WAYLAND +/*********************************************************** + * Name: vc_dispmanx_get_handle_from_wl_buffer + * + * Arguments: + * struct wl_resource *_buffer + * + * Description: Return the handle of the resource associated to this Wayland buffer + * + * Returns: A resource handle + * + ***********************************************************/ +VCHPRE_ DISPMANX_RESOURCE_HANDLE_T VCHPOST_ vc_dispmanx_get_handle_from_wl_buffer( struct wl_resource *_buffer ) +{ + struct wl_dispmanx_server_buffer *buffer = (struct wl_dispmanx_server_buffer*)_buffer->data; + if (!buffer) + return DISPMANX_NO_HANDLE; + + return buffer->handle; +} + +/*********************************************************** + * Name: vc_dispmanx_set_wl_buffer_in_use + * + * Arguments: + * struct wl_resource *_buffer + * int in_use + * + * Description: Mark this Wayland buffer as being in use by the compositor + * + ***********************************************************/ +VCHPRE_ void VCHPOST_ vc_dispmanx_set_wl_buffer_in_use( struct wl_resource *_buffer, int in_use ) +{ + struct wl_dispmanx_server_buffer *buffer = (struct wl_dispmanx_server_buffer*)_buffer->data; + if (!buffer) + return; + + buffer->in_use = in_use; +} +#endif Index: git/interface/wayland/dispmanx.xml =================================================================== --- /dev/null +++ git/interface/wayland/dispmanx.xml @@ -0,0 +1,123 @@ + + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + Copyright © 2013 Raspberry Pi Foundation + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that\n the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: git/makefiles/cmake/Wayland.cmake =================================================================== --- /dev/null +++ git/makefiles/cmake/Wayland.cmake @@ -0,0 +1,72 @@ +#============================================================================= +# Copyright (C) 2012-2013 Pier Luigi Fiorini +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * 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. +# +# * Neither the name of Pier Luigi Fiorini nor the names of his +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT +# HOLDER 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. +#============================================================================= + +find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) + +# wayland_add_protocol_client(outfiles inputfile basename) +function(WAYLAND_ADD_PROTOCOL_CLIENT _sources _protocol _basename) + if(NOT WAYLAND_SCANNER_EXECUTABLE) + message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.") + endif() + + get_filename_component(_infile ${_protocol} ABSOLUTE) + set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h") + set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-protocol.c") + + add_custom_command(OUTPUT "${_client_header}" + COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header < ${_infile} > ${_client_header} + DEPENDS ${_infile} VERBATIM) + + add_custom_command(OUTPUT "${_code}" + COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code} + DEPENDS ${_infile} VERBATIM) + + list(APPEND ${_sources} "${_client_header}" "${_code}") + set(${_sources} ${${_sources}} PARENT_SCOPE) +endfunction() + +# wayland_add_protocol_server(outfiles inputfile basename) +function(WAYLAND_ADD_PROTOCOL_SERVER _sources _protocol _basename) + if(NOT WAYLAND_SCANNER_EXECUTABLE) + message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.") + endif() + + get_filename_component(_infile ${_protocol} ABSOLUTE) + set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.h") + + add_custom_command(OUTPUT "${_server_header}" + COMMAND ${WAYLAND_SCANNER_EXECUTABLE} server-header < ${_infile} > ${_server_header} + DEPENDS ${_infile} VERBATIM) + + list(APPEND ${_sources} "${_server_header}") + set(${_sources} ${${_sources}} PARENT_SCOPE) +endfunction()