Support Android hardware-buffer presentation over Wayland

This commit is contained in:
wallentx committed 2026-10-11 04:00:42 -05:00
1 parent e5ba165027
commit 6fb6cb83ab
12 files changed
+1135 -35

No files matched your search

@@ -14,6 +14,7 @@ add_library(droiddeckwayland SHARED
src/waylandcomp_jni.c
src/droiddeck_ext.c
src/wl_dmabuf.c
src/wl_android_buffer.c
src/wl_clipboard.c
src/wl_text_input.c
src/wl_toplevel_icon.c
@@ -24,6 +25,7 @@ add_library(droiddeckwayland SHARED
generated/color-management-v1-protocol.c
generated/xdg-shell-protocol.c
generated/linux-dmabuf-v1-protocol.c
generated/wayland-android-protocol.c
generated/viewporter-protocol.c
generated/banner-desktop-v1-protocol.c
generated/presentation-time-protocol.c
@@ -0,0 +1,104 @@
/* Generated by wayland-scanner 1.26.0 */
/*
* Copyright © 2012 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that 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.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface android_wlegl_handle_interface;
extern const struct wl_interface android_wlegl_server_buffer_handle_interface;
extern const struct wl_interface wl_buffer_interface;
static const struct wl_interface *android_types[] = {
NULL,
NULL,
&android_wlegl_handle_interface,
NULL,
NULL,
&wl_buffer_interface,
NULL,
NULL,
NULL,
NULL,
NULL,
&android_wlegl_handle_interface,
&android_wlegl_server_buffer_handle_interface,
NULL,
NULL,
NULL,
NULL,
&wl_buffer_interface,
NULL,
NULL,
};
static const struct wl_message android_wlegl_requests[] = {
{ "create_handle", "nia", android_types + 2 },
{ "create_buffer", "niiiiio", android_types + 5 },
{ "get_server_buffer_handle", "2niiii", android_types + 12 },
};
WL_PRIVATE const struct wl_interface android_wlegl_interface = {
"android_wlegl", 3,
3, android_wlegl_requests,
0, NULL,
};
static const struct wl_message android_wlegl_handle_requests[] = {
{ "add_fd", "h", android_types + 0 },
{ "destroy", "", android_types + 0 },
};
WL_PRIVATE const struct wl_interface android_wlegl_handle_interface = {
"android_wlegl_handle", 1,
2, android_wlegl_handle_requests,
0, NULL,
};
static const struct wl_message android_wlegl_server_buffer_handle_events[] = {
{ "buffer_fd", "h", android_types + 0 },
{ "buffer_ints", "a", android_types + 0 },
{ "buffer", "nii", android_types + 17 },
{ "linear_layout", "3uu", android_types + 0 },
};
WL_PRIVATE const struct wl_interface android_wlegl_server_buffer_handle_interface = {
"android_wlegl_server_buffer_handle", 3,
0, NULL,
4, android_wlegl_server_buffer_handle_events,
};
@@ -0,0 +1,341 @@
/* Generated by wayland-scanner 1.26.0 */
#ifndef ANDROID_SERVER_PROTOCOL_H
#define ANDROID_SERVER_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-server.h"
#ifdef __cplusplus
extern "C" {
#endif
struct wl_client;
struct wl_resource;
/**
* @page page_android The android protocol
* @section page_ifaces_android Interfaces
* - @subpage page_iface_android_wlegl - Android EGL graphics buffer support
* - @subpage page_iface_android_wlegl_handle - An Android native_handle_t object
* - @subpage page_iface_android_wlegl_server_buffer_handle - a server allocated buffer
* @section page_copyright_android Copyright
* <pre>
*
* Copyright © 2012 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that 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.
* </pre>
*/
struct android_wlegl;
struct android_wlegl_handle;
struct android_wlegl_server_buffer_handle;
struct wl_buffer;
#ifndef ANDROID_WLEGL_INTERFACE
#define ANDROID_WLEGL_INTERFACE
/**
* @page page_iface_android_wlegl android_wlegl
* @section page_iface_android_wlegl_desc Description
*
* Interface used in the Android wrapper libEGL to share
* graphics buffers between the server and the client.
* @section page_iface_android_wlegl_api API
* See @ref iface_android_wlegl.
*/
/**
* @defgroup iface_android_wlegl The android_wlegl interface
*
* Interface used in the Android wrapper libEGL to share
* graphics buffers between the server and the client.
*/
extern const struct wl_interface android_wlegl_interface;
#endif
#ifndef ANDROID_WLEGL_HANDLE_INTERFACE
#define ANDROID_WLEGL_HANDLE_INTERFACE
/**
* @page page_iface_android_wlegl_handle android_wlegl_handle
* @section page_iface_android_wlegl_handle_desc Description
*
* The Android native_handle_t is a semi-opaque object, that
* contains an EGL implementation specific number of int32
* values and file descriptors.
*
* We cannot send a variable size array of file descriptors
* over the Wayland protocol, so we send them one by one.
* @section page_iface_android_wlegl_handle_api API
* See @ref iface_android_wlegl_handle.
*/
/**
* @defgroup iface_android_wlegl_handle The android_wlegl_handle interface
*
* The Android native_handle_t is a semi-opaque object, that
* contains an EGL implementation specific number of int32
* values and file descriptors.
*
* We cannot send a variable size array of file descriptors
* over the Wayland protocol, so we send them one by one.
*/
extern const struct wl_interface android_wlegl_handle_interface;
#endif
#ifndef ANDROID_WLEGL_SERVER_BUFFER_HANDLE_INTERFACE
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_INTERFACE
/**
* @page page_iface_android_wlegl_server_buffer_handle android_wlegl_server_buffer_handle
* @section page_iface_android_wlegl_server_buffer_handle_desc Description
*
* On creation a server_side_buffer object will immediately send
* the "buffer_fd" and "buffer_ints" events needed by the client to
* reference the gralloc buffer, followed by the "buffer" event carrying
* the wl_buffer object.
* @section page_iface_android_wlegl_server_buffer_handle_api API
* See @ref iface_android_wlegl_server_buffer_handle.
*/
/**
* @defgroup iface_android_wlegl_server_buffer_handle The android_wlegl_server_buffer_handle interface
*
* On creation a server_side_buffer object will immediately send
* the "buffer_fd" and "buffer_ints" events needed by the client to
* reference the gralloc buffer, followed by the "buffer" event carrying
* the wl_buffer object.
*/
extern const struct wl_interface android_wlegl_server_buffer_handle_interface;
#endif
#ifndef ANDROID_WLEGL_ERROR_ENUM
#define ANDROID_WLEGL_ERROR_ENUM
enum android_wlegl_error {
ANDROID_WLEGL_ERROR_BAD_HANDLE = 0,
ANDROID_WLEGL_ERROR_BAD_VALUE = 1,
};
#endif /* ANDROID_WLEGL_ERROR_ENUM */
#ifndef ANDROID_WLEGL_ERROR_ENUM_IS_VALID
#define ANDROID_WLEGL_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_android_wlegl
* Validate a android_wlegl error value.
*
* @return true on success, false on error.
* @ref android_wlegl_error
*/
static inline bool
android_wlegl_error_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case ANDROID_WLEGL_ERROR_BAD_HANDLE:
return version >= 1;
case ANDROID_WLEGL_ERROR_BAD_VALUE:
return version >= 1;
default:
return false;
}
}
#endif /* ANDROID_WLEGL_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_android_wlegl
* @struct android_wlegl_interface
*/
struct android_wlegl_interface {
/**
* Create an Android native_handle_t object
*
* This creator method initialises the native_handle_t object
* with everything except the file descriptors, which have to be
* submitted separately.
* @param ints an array of int32_t
*/
void (*create_handle)(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
int32_t num_fds,
struct wl_array *ints);
/**
* Create a wl_buffer from the native handle
*
* Pass the Android native_handle_t to the server and attach it
* to the new wl_buffer object.
*
* The android_wlegl_handle object must be destroyed immediately
* after this request.
*/
void (*create_buffer)(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
int32_t width,
int32_t height,
int32_t stride,
int32_t format,
int32_t usage,
struct wl_resource *native_handle);
/**
* @since 2
*/
void (*get_server_buffer_handle)(struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
int32_t width,
int32_t height,
int32_t format,
int32_t usage);
};
/**
* @ingroup iface_android_wlegl
*/
#define ANDROID_WLEGL_CREATE_HANDLE_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl
*/
#define ANDROID_WLEGL_CREATE_BUFFER_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl
*/
#define ANDROID_WLEGL_GET_SERVER_BUFFER_HANDLE_SINCE_VERSION 2
#ifndef ANDROID_WLEGL_HANDLE_ERROR_ENUM
#define ANDROID_WLEGL_HANDLE_ERROR_ENUM
enum android_wlegl_handle_error {
ANDROID_WLEGL_HANDLE_ERROR_TOO_MANY_FDS = 0,
};
#endif /* ANDROID_WLEGL_HANDLE_ERROR_ENUM */
#ifndef ANDROID_WLEGL_HANDLE_ERROR_ENUM_IS_VALID
#define ANDROID_WLEGL_HANDLE_ERROR_ENUM_IS_VALID
/**
* @ingroup iface_android_wlegl_handle
* Validate a android_wlegl_handle error value.
*
* @return true on success, false on error.
* @ref android_wlegl_handle_error
*/
static inline bool
android_wlegl_handle_error_is_valid(uint32_t value, uint32_t version) {
switch (value) {
case ANDROID_WLEGL_HANDLE_ERROR_TOO_MANY_FDS:
return version >= 1;
default:
return false;
}
}
#endif /* ANDROID_WLEGL_HANDLE_ERROR_ENUM_IS_VALID */
/**
* @ingroup iface_android_wlegl_handle
* @struct android_wlegl_handle_interface
*/
struct android_wlegl_handle_interface {
/**
*/
void (*add_fd)(struct wl_client *client,
struct wl_resource *resource,
int32_t fd);
/**
*/
void (*destroy)(struct wl_client *client,
struct wl_resource *resource);
};
/**
* @ingroup iface_android_wlegl_handle
*/
#define ANDROID_WLEGL_HANDLE_ADD_FD_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl_handle
*/
#define ANDROID_WLEGL_HANDLE_DESTROY_SINCE_VERSION 1
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_FD 0
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_INTS 1
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER 2
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_LINEAR_LAYOUT 3
/**
* @ingroup iface_android_wlegl_server_buffer_handle
*/
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_FD_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl_server_buffer_handle
*/
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_INTS_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl_server_buffer_handle
*/
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_SINCE_VERSION 1
/**
* @ingroup iface_android_wlegl_server_buffer_handle
*/
#define ANDROID_WLEGL_SERVER_BUFFER_HANDLE_LINEAR_LAYOUT_SINCE_VERSION 3
/**
* @ingroup iface_android_wlegl_server_buffer_handle
* Sends an buffer_fd event to the client owning the resource.
* @param resource_ The client's resource
*/
static inline void
android_wlegl_server_buffer_handle_send_buffer_fd(struct wl_resource *resource_, int32_t fd)
{
wl_resource_post_event(resource_, ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_FD, fd);
}
/**
* @ingroup iface_android_wlegl_server_buffer_handle
* Sends an buffer_ints event to the client owning the resource.
* @param resource_ The client's resource
*/
static inline void
android_wlegl_server_buffer_handle_send_buffer_ints(struct wl_resource *resource_, struct wl_array *ints)
{
wl_resource_post_event(resource_, ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER_INTS, ints);
}
/**
* @ingroup iface_android_wlegl_server_buffer_handle
* Sends an buffer event to the client owning the resource.
* @param resource_ The client's resource
*/
static inline void
android_wlegl_server_buffer_handle_send_buffer(struct wl_resource *resource_, struct wl_resource *buffer, int32_t format, int32_t stride)
{
wl_resource_post_event(resource_, ANDROID_WLEGL_SERVER_BUFFER_HANDLE_BUFFER, buffer, format, stride);
}
/**
* @ingroup iface_android_wlegl_server_buffer_handle
* Sends an linear_layout event to the client owning the resource.
* @param resource_ The client's resource
*/
static inline void
android_wlegl_server_buffer_handle_send_linear_layout(struct wl_resource *resource_, uint32_t drm_format, uint32_t stride)
{
wl_resource_post_event(resource_, ANDROID_WLEGL_SERVER_BUFFER_HANDLE_LINEAR_LAYOUT, drm_format, stride);
}
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="android">
<copyright>
Copyright © 2012 Collabora, Ltd.
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that 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.
</copyright>
<interface name="android_wlegl" version="3">
<description summary="Android EGL graphics buffer support">
Interface used in the Android wrapper libEGL to share
graphics buffers between the server and the client.
</description>
<enum name="error">
<entry name="bad_handle" value="0" />
<entry name="bad_value" value="1" />
</enum>
<request name="create_handle">
<description summary="Create an Android native_handle_t object">
This creator method initialises the native_handle_t object
with everything except the file descriptors, which have to be
submitted separately.
</description>
<arg name="id" type="new_id" interface="android_wlegl_handle" />
<arg name="num_fds" type="int" />
<arg name="ints" type="array" summary="an array of int32_t" />
</request>
<request name="create_buffer">
<description summary="Create a wl_buffer from the native handle">
Pass the Android native_handle_t to the server and attach it
to the new wl_buffer object.
The android_wlegl_handle object must be destroyed immediately
after this request.
</description>
<arg name="id" type="new_id" interface="wl_buffer" />
<arg name="width" type="int" />
<arg name="height" type="int" />
<arg name="stride" type="int" />
<arg name="format" type="int" />
<arg name="usage" type="int" />
<arg name="native_handle" type="object" interface="android_wlegl_handle" />
</request>
<request name="get_server_buffer_handle" since="2">
<arg name="id" type="new_id" interface="android_wlegl_server_buffer_handle"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="format" type="int"/>
<arg name="usage" type="int"/>
</request>
</interface>
<interface name="android_wlegl_handle" version="1">
<description summary="An Android native_handle_t object">
The Android native_handle_t is a semi-opaque object, that
contains an EGL implementation specific number of int32
values and file descriptors.
We cannot send a variable size array of file descriptors
over the Wayland protocol, so we send them one by one.
</description>
<enum name="error">
<entry name="too_many_fds" value="0" />
</enum>
<request name="add_fd">
<arg name="fd" type="fd" />
</request>
<request name="destroy" type="destructor" />
</interface>
<interface name="android_wlegl_server_buffer_handle" version="3">
<description summary="a server allocated buffer">
On creation a server_side_buffer object will immediately send
the "buffer_fd" and "buffer_ints" events needed by the client to
reference the gralloc buffer, followed by the "buffer" event carrying
the wl_buffer object.
</description>
<event name="buffer_fd">
<arg name="fd" type="fd"/>
</event>
<event name="buffer_ints">
<arg name="ints" type="array"/>
</event>
<event name="buffer">
<description summary="the wl_buffer">
This event will be sent after the ints and all the fds have been sent
</description>
<arg name="buffer" type="new_id" interface="wl_buffer"/>
<arg name="format" type="int"/>
<arg name="stride" type="int"/>
</event>
<event name="linear_layout" since="3">
<description summary="verified single-plane linear allocation">
Optional metadata sent before the buffer event. The first buffer_fd
contains one uncompressed 32-bit RGB plane at byte offset zero.
drm_format is its DRM FourCC and stride is its row pitch in bytes.
The server has verified the layout and allocation bounds using
Android mapper metadata, not inferred them from opaque handle ints.
Absence of this event means the layout is unspecified.
</description>
<arg name="drm_format" type="uint"/>
<arg name="stride" type="uint"/>
</event>
</interface>
</protocol>
@@ -3534,6 +3534,7 @@ int droiddeck_wayland_run(void) {
wl_global_create(display, &wl_output_interface, 4, NULL, bind_output);
wl_global_create(display, &xdg_wm_base_interface, 1, NULL, bind_xdg_wm_base);
wl_global_create(display, &zwp_linux_dmabuf_v1_interface, 4, NULL, bind_dmabuf);
droiddeck_android_wlegl_init(display);
/* gamescope's Wayland backend refuses a seat older than 8. */
wl_global_create(display, &wl_seat_interface, 9, NULL, bind_seat);
wl_global_create(display, &banner_desktop_v1_interface, 1, NULL, bind_desktop);
@@ -77,3 +77,6 @@ extern volatile int g_no_render_node; /* DROIDDECK_WAYLAND_NO_RENDER_NODE */
/* ---- wl_dmabuf.c */
COMPOSITOR_INTERNAL extern dev_t g_main_device; /* the render node clients are told to allocate on */
COMPOSITOR_INTERNAL void bind_dmabuf(struct wl_client *c, void *data, uint32_t ver, uint32_t id);
/* ---- wl_android_buffer.c */
COMPOSITOR_INTERNAL void droiddeck_android_wlegl_init(struct wl_display *display);
@@ -0,0 +1,47 @@
#ifndef VK_EXTERNAL_IMAGE_SYNC_H
#define VK_EXTERNAL_IMAGE_SYNC_H
#include <vulkan/vulkan.h>
struct vkp_external_client_barrier_pair {
VkImageMemoryBarrier acquire;
VkImageMemoryBarrier release;
};
static inline int vkp_is_external_client_image(int dmabuf, int blit_dst) {
return dmabuf && !blit_dst;
}
/* Client-owned dma-bufs, including AHardwareBuffers, live in GENERAL while
* FOREIGN owns them. Layer pool dma-bufs are destinations with a separate
* acquire/release lifecycle and must never take this source-image path. */
static inline int vkp_external_client_barrier_pair(
VkImage image, uint32_t queue_family, int dmabuf, int blit_dst,
struct vkp_external_client_barrier_pair *pair) {
if (!vkp_is_external_client_image(dmabuf, blit_dst) || !pair) return 0;
const VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
pair->acquire = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
.dstQueueFamilyIndex = queue_family,
.image = image,
.subresourceRange = range,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
};
pair->release = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = queue_family,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
.image = image,
.subresourceRange = range,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
};
return 1;
}
#endif
+154 -35
View File
@@ -3,6 +3,7 @@
#define _POSIX_C_SOURCE 200809L
#include "vk_present.h"
#include "vk_loader.h"
#include "vk_external_image_sync.h"
#include "sc_layer.h"
#include "effects_chain.h"
#include "framegen_bridge.h"
@@ -35,6 +36,7 @@ struct vkp_image {
int in_general; /* shm images: moved from PREINITIALIZED to GENERAL */
VkFormat fmt;
int sampled; /* created with SAMPLED usage: the alpha pass can read it */
AHardwareBuffer *android_buffer;
};
static ANativeWindow *g_window; /* the window frames go to; compositor thread only */
@@ -44,6 +46,7 @@ static VkInstance g_inst;
static VkPhysicalDevice g_pd;
static VkDevice g_dev;
static VkQueue g_queue;
static PFN_vkGetAndroidHardwareBufferPropertiesANDROID g_ahb_properties;
static uint32_t g_qfam;
static VkCommandPool g_pool;
/* One command buffer + acquire/render-done semaphore pair per PRESENT. A scene frame is one
@@ -464,7 +467,7 @@ static int dev_init(void) {
g_vk.GetPhysicalDeviceMemoryProperties(g_pd, &g_memprops);
/* Verify the dmabuf-import extensions are present, and log any that are missing. */
const char *dev_exts[7] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, "VK_KHR_external_memory_fd",
const char *dev_exts[9] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, "VK_KHR_external_memory_fd",
"VK_EXT_external_memory_dma_buf", "VK_EXT_image_drm_format_modifier",
"VK_KHR_image_format_list", NULL, NULL};
uint32_t n_dev_exts = 5;
@@ -485,6 +488,13 @@ static int dev_init(void) {
* back to waiting for them on the CPU without it. */
const int want_sem_fd = has_ext(exts, ne, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
if (want_sem_fd) dev_exts[n_dev_exts++] = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME;
const int want_ahb = !g_library_name &&
has_ext(exts, ne, VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME) &&
has_ext(exts, ne, VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
if (want_ahb) {
dev_exts[n_dev_exts++] = VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME;
dev_exts[n_dev_exts++] = VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME;
}
free(exts);
float prio = 1.0f;
@@ -511,6 +521,9 @@ static int dev_init(void) {
}
vk_loader_load_device(g_dev);
g_vk.GetDeviceQueue(g_dev, g_qfam, 0, &g_queue);
if (want_ahb)
g_ahb_properties = (PFN_vkGetAndroidHardwareBufferPropertiesANDROID)
g_vk.GetDeviceProcAddr(g_dev, "vkGetAndroidHardwareBufferPropertiesANDROID");
if (want_sem_fd) {
g_import_sem_fd = (PFN_vkImportSemaphoreFdKHR)g_vk.GetDeviceProcAddr(g_dev, "vkImportSemaphoreFdKHR");
VkSemaphoreCreateInfo wsci = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
@@ -949,7 +962,98 @@ struct vkp_image *vkp_image_from_dmabuf(int fd, uint32_t drm_format, uint64_t mo
return vkp_image_import_dmabuf(fd, drm_format, modifier, w, h, stride, offset, 0);
}
int vkp_image_is_dmabuf(const struct vkp_image *img) { return img && img->dmabuf && !img->blit_dst; }
/* Return client sources to FOREIGN after their final read. The consuming paths
* wait for our fence before Wayland can release an ordinary dma-buf or an
* android_wlegl AHardwareBuffer. Producer-fence synchronization is separate from
* these image-layout and ownership transfers. */
static void release_external_client_image(VkCommandBuffer cmd, struct vkp_image *img) {
struct vkp_external_client_barrier_pair pair;
if (!img || !vkp_external_client_barrier_pair(
img->image, g_qfam, img->dmabuf, img->blit_dst, &pair))
return;
g_vk.CmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &pair.release);
}
static void release_external_client_draws(VkCommandBuffer cmd, const struct vkp_draw *draws, int count) {
for (int i = 0; i < count; i++) {
int seen = 0;
for (int j = 0; j < i; j++) if (draws[j].img == draws[i].img) { seen = 1; break; }
if (!seen) release_external_client_image(cmd, draws[i].img);
}
}
int vkp_image_is_dmabuf(const struct vkp_image *img) {
return img && vkp_is_external_client_image(img->dmabuf, img->blit_dst);
}
int vkp_android_buffer_supported(void) {
return dev_init() == 0 && g_ahb_properties != NULL;
}
struct vkp_image *vkp_image_from_android_buffer(AHardwareBuffer *buffer) {
if (!buffer || !vkp_android_buffer_supported()) return NULL;
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
if (!desc.width || !desc.height || desc.layers != 1 ||
(desc.usage & (AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT | AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)) ||
!(desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE)) return NULL;
VkAndroidHardwareBufferFormatPropertiesANDROID format = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID};
VkAndroidHardwareBufferPropertiesANDROID props = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID, .pNext = &format};
VkResult result = g_ahb_properties(g_dev, buffer, &props);
if (result != VK_SUCCESS ||
(format.format != VK_FORMAT_R8G8B8A8_UNORM && format.format != VK_FORMAT_R8G8B8A8_SRGB &&
format.format != VK_FORMAT_B8G8R8A8_UNORM && format.format != VK_FORMAT_B8G8R8A8_SRGB) ||
!props.memoryTypeBits ||
!(format.formatFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
LOGE("android_wlegl: unsupported Android buffer import (%s, format %d)",
vk_result_name(result), format.format);
return NULL;
}
struct vkp_image *img = calloc(1, sizeof(*img));
if (!img) return NULL;
img->w = (int)desc.width; img->h = (int)desc.height;
img->fmt = format.format; img->dmabuf = 1;
const VkFormatFeatureFlags sampled = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
img->sampled = (format.formatFeatures & sampled) == sampled;
VkExternalMemoryImageCreateInfo external = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID};
VkImageCreateInfo create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = &external,
.imageType = VK_IMAGE_TYPE_2D, .format = img->fmt,
.extent = {desc.width, desc.height, 1}, .mipLevels = 1, .arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | (img->sampled ? VK_IMAGE_USAGE_SAMPLED_BIT : 0),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED};
result = g_vk.CreateImage(g_dev, &create, NULL, &img->image);
if (result != VK_SUCCESS) goto fail;
/* AHB image requirements are not queryable before binding. The AHB query supplies them. */
uint32_t memory_type = 0;
while (!(props.memoryTypeBits & (1u << memory_type))) memory_type++;
VkImportAndroidHardwareBufferInfoANDROID import = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, .buffer = buffer};
VkMemoryDedicatedAllocateInfo dedicated = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &import, .image = img->image};
VkMemoryAllocateInfo allocate = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &dedicated,
.allocationSize = props.allocationSize, .memoryTypeIndex = memory_type};
result = g_vk.AllocateMemory(g_dev, &allocate, NULL, &img->mem);
if (result != VK_SUCCESS) goto fail;
result = g_vk.BindImageMemory(g_dev, img->image, img->mem, 0);
if (result != VK_SUCCESS) goto fail;
AHardwareBuffer_acquire(buffer);
img->android_buffer = buffer;
return img;
fail:
LOGE("android_wlegl: Vulkan buffer import failed: %s", vk_result_name(result));
vkp_image_destroy(img);
return NULL;
}
struct vkp_image *vkp_image_import_dmabuf(int fd, uint32_t drm_format, uint64_t modifier, int w, int h,
uint32_t stride, uint32_t offset, int as_blit_dst) {
@@ -1106,6 +1210,7 @@ void vkp_image_destroy(struct vkp_image *img) {
if (img->image) g_vk.DestroyImage(g_dev, img->image, NULL);
if (img->mem) g_vk.FreeMemory(g_dev, img->mem, NULL);
}
if (img->android_buffer) AHardwareBuffer_release(img->android_buffer);
free(img);
}
@@ -1155,11 +1260,14 @@ static int draw_to_scene_blit(const struct vkp_draw *d, int scene_w, int scene_h
static void record_draw(VkCommandBuffer cmd, const struct vkp_draw *d, const VkImageBlit *blit, VkImage target,
VkFormat target_fmt, int target_w, int target_h, VkFilter filter) {
const struct vkp_image *im = d->img;
const int external_client = vkp_image_is_dmabuf(im);
if (d->blend && im->sampled &&
blendp_draw(cmd, im->image, im->fmt, im->w, im->h, !im->dmabuf, target, target_fmt, target_w, target_h,
blendp_draw(cmd, im->image, im->fmt, im->w, im->h, !external_client,
target, target_fmt, target_w, target_h,
blit) == 0)
return;
g_vk.CmdBlitImage(cmd, im->image, im->dmabuf ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL,
g_vk.CmdBlitImage(cmd, im->image,
external_client ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL,
target, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, filter);
}
@@ -1623,13 +1731,11 @@ static int render_impl(int scene_w, int scene_h, const struct vkp_draw *draws, i
int seen = 0;
for (int j = 0; j < i; j++) if (draws[j].img == im) { seen = 1; break; }
if (seen || !im) continue;
if (im->dmabuf) {
bars[nb++] = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = g_qfam,
.image = im->image, .subresourceRange = range, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT};
} else if (!im->in_general) {
struct vkp_external_client_barrier_pair pair;
if (vkp_external_client_barrier_pair(
im->image, g_qfam, im->dmabuf, im->blit_dst, &pair)) {
bars[nb++] = pair.acquire;
} else if (!im->blit_dst && !im->in_general) {
bars[nb++] = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
@@ -1696,6 +1802,7 @@ static int render_impl(int scene_w, int scene_h, const struct vkp_draw *draws, i
drawn++;
}
release_external_client_draws(cmd, draws, n);
int ngen = 0;
VkImage gens[VKP_FG_MAX_GENERATIONS] = {VK_NULL_HANDLE};
VkImage result = scene_img;
@@ -1901,7 +2008,7 @@ int vkp_map_draw(const struct vkp_draw *d, int out[8]) {
* owned by the "foreign" queue family (the game's driver / the display) between our uses, so each
* use acquires them and the destination is released back for the display to read. */
int vkp_blit_image(struct vkp_image *src, struct vkp_image *dst, int wait_fd) {
if (!src || !dst || !dst->blit_dst || g_dev_state == -2 || dev_init() != 0) {
if (!src || src->blit_dst || !dst || !dst->blit_dst || g_dev_state == -2 || dev_init() != 0) {
if (wait_fd >= 0) close(wait_fd);
return -1;
}
@@ -1914,17 +2021,21 @@ int vkp_blit_image(struct vkp_image *src, struct vkp_image *dst, int wait_fd) {
VkCommandBufferBeginInfo bi = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
g_vk.BeginCommandBuffer(g_cmd, &bi);
struct vkp_external_client_barrier_pair src_pair;
const int src_external = vkp_external_client_barrier_pair(
src->image, g_qfam, src->dmabuf, src->blit_dst, &src_pair);
VkImageMemoryBarrier acq[2] = {
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
src_external ? src_pair.acquire : (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = src->dmabuf ? VK_QUEUE_FAMILY_FOREIGN_EXT : VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = src->dmabuf ? g_qfam : VK_QUEUE_FAMILY_IGNORED,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src->image, .subresourceRange = range, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT},
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = g_qfam,
.image = dst->image, .subresourceRange = range, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT}};
if (!src->dmabuf) { /* shm image: host-written, GENERAL */
if (!src_external) { /* shm image: host-written, GENERAL */
acq[0].oldLayout = src->in_general ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_PREINITIALIZED;
acq[0].newLayout = VK_IMAGE_LAYOUT_GENERAL;
acq[0].srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
@@ -1938,8 +2049,9 @@ int vkp_blit_image(struct vkp_image *src, struct vkp_image *dst, int wait_fd) {
.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
.dstOffsets = {{0, 0, 0}, {bw, bh, 1}}};
g_vk.CmdBlitImage(g_cmd, src->image,
src->dmabuf ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL,
src_external ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL,
dst->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST);
release_external_client_image(g_cmd, src);
VkImageMemoryBarrier rel = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = g_qfam,
@@ -1974,7 +2086,7 @@ int vkp_blit_image(struct vkp_image *src, struct vkp_image *dst, int wait_fd) {
static struct vkp_image *g_rb; /* the readback image, kept for the next shape */
int vkp_image_readback(struct vkp_image *src, uint32_t *out, int max_px) {
if (!src || !out || src->w <= 0 || src->h <= 0 || src->w * src->h > max_px ||
if (!src || src->blit_dst || !out || src->w <= 0 || src->h <= 0 || src->w * src->h > max_px ||
g_dev_state == -2 || dev_init() != 0)
return -1;
if (g_rb && (g_rb->w != src->w || g_rb->h != src->h)) { vkp_image_destroy(g_rb); g_rb = NULL; }
@@ -2015,11 +2127,15 @@ int vkp_image_readback(struct vkp_image *src, uint32_t *out, int max_px) {
VkCommandBufferBeginInfo bi = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
g_vk.BeginCommandBuffer(cmd, &bi);
struct vkp_external_client_barrier_pair src_pair;
const int src_external = vkp_external_client_barrier_pair(
src->image, g_qfam, src->dmabuf, src->blit_dst, &src_pair);
VkImageMemoryBarrier acq[2] = {
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
src_external ? src_pair.acquire : (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = src->dmabuf ? VK_QUEUE_FAMILY_FOREIGN_EXT : VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = src->dmabuf ? g_qfam : VK_QUEUE_FAMILY_IGNORED,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src->image, .subresourceRange = range, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT},
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
@@ -2037,16 +2153,20 @@ int vkp_image_readback(struct vkp_image *src, uint32_t *out, int max_px) {
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = g_rb->image, .subresourceRange = range,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_HOST_READ_BIT},
/* the client's buffer goes back to it */
{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_HOST_READ_BIT}};
uint32_t done_count = 1;
if (!src_external) {
done[done_count++] = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = src->dmabuf ? g_qfam : VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = src->dmabuf ? VK_QUEUE_FAMILY_FOREIGN_EXT : VK_QUEUE_FAMILY_IGNORED,
.image = src->image, .subresourceRange = range, .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT}};
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src->image, .subresourceRange = range, .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT};
}
g_vk.CmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, NULL, 0, NULL, 2, done);
0, 0, NULL, 0, NULL, done_count, done);
release_external_client_image(cmd, src);
g_vk.EndCommandBuffer(cmd);
VkSubmitInfo si = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &cmd};
g_vk.ResetFences(g_dev, 1, &g_fence);
@@ -2149,13 +2269,11 @@ static int pass_begin_impl(int scene_w, int scene_h, const struct vkp_draw *draw
int seen = 0;
for (int j = 0; j < i; j++) if (draws[j].img == im) { seen = 1; break; }
if (seen || !im) continue;
if (im->dmabuf) {
bars[nb++] = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = g_qfam,
.image = im->image, .subresourceRange = range, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT};
} else if (!im->in_general) {
struct vkp_external_client_barrier_pair pair;
if (vkp_external_client_barrier_pair(
im->image, g_qfam, im->dmabuf, im->blit_dst, &pair)) {
bars[nb++] = pair.acquire;
} else if (!im->blit_dst && !im->in_general) {
bars[nb++] = (VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
@@ -2196,6 +2314,7 @@ static int pass_begin_impl(int scene_w, int scene_h, const struct vkp_draw *draw
if (vkp_effects_active()) vkp_effects_set_formats(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM);
}
release_external_client_draws(cmd, draws, n);
int mapped_w = (int)(scene_w * g_map.kx + 0.5f), mapped_h = (int)(scene_h * g_map.ky + 0.5f);
g_pass.rw = scene_w; g_pass.rh = scene_h;
g_pass.result = vkp_effects_run(cmd, scene_img, scene_w, scene_h, mapped_w, mapped_h,
@@ -2,6 +2,7 @@
#define VK_PRESENT_H
#include <stdint.h>
#include <android/native_window.h>
#include <android/hardware_buffer.h>
/*
* Android-surface render backend for the embedded Wayland compositor.
* Owns a Turnip VkDevice + a swapchain on the SurfaceView's ANativeWindow. Each
@@ -39,6 +40,11 @@ void vkp_output_size(int *w, int *h);
struct vkp_image;
/* android_wlegl clients share complete Android buffer handles, not guessed DMA-BUF layouts.
* The image retains its own AHB reference. Call only from the compositor thread. */
int vkp_android_buffer_supported(void);
struct vkp_image *vkp_image_from_android_buffer(AHardwareBuffer *buffer);
/* DRM format modifiers this backend knows the memory layout of (both single-plane on Adreno):
* LINEAR, and QCOM_COMPRESSED = UBWC (drm_fourcc.h: fourcc_mod_code(QCOM = 0x05, 1)). */
#define VKP_MOD_LINEAR 0x0000000000000000ULL
@@ -0,0 +1,212 @@
/* android_wlegl buffer import for the existing libhybris Wayland WSI.
* Protocol: Collabora/libhybris, vendored with its license in protocols/wayland-android.xml.
* Native handles are cloned through Android; opaque vendor integers are never interpreted. */
#include "compositor_internal.h"
#include "droiddeck_ext.h"
#include "wayland-android-server-protocol.h"
#include <android/hardware_buffer.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Stable native_handle_t ABI, unavailable in the public NDK. */
struct android_native_handle {
int version, num_fds, num_ints;
int data[];
};
struct wire_handle {
struct android_native_handle *handle;
int received;
};
enum { MAX_HANDLE_FDS = 64, MAX_HANDLE_INTS = 256, MAX_BUFFER_SIDE = 8192 };
/* VNDK ABI (not exposed by the NDK): frameworks/native/libs/nativewindow/include/
* vndk/hardware_buffer.h, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE = 3.
* Method 1 is not accepted by AHardwareBuffer_createFromHandle. */
enum { CREATE_FROM_HANDLE_CLONE = 3 };
static int (*import_handle)(const AHardwareBuffer_Desc *, const struct android_native_handle *,
int32_t, AHardwareBuffer **);
static const struct android_native_handle *(*native_handle)(const AHardwareBuffer *);
static int valid_description(int32_t width, int32_t height, int32_t stride, int32_t format) {
return width > 0 && height > 0 && width <= MAX_BUFFER_SIDE && height <= MAX_BUFFER_SIDE &&
stride >= width && stride <= MAX_BUFFER_SIDE * 4 &&
(format == 1 || format == 2 || format == 5);
}
static void handle_resource_destroy(struct wl_resource *resource) {
struct wire_handle *wire = wl_resource_get_user_data(resource);
if (!wire) return;
for (int i = 0; i < wire->received; i++) close(wire->handle->data[i]);
free(wire->handle);
free(wire);
}
static void handle_add_fd(struct wl_client *client, struct wl_resource *resource, int32_t fd) {
struct wire_handle *wire = wl_resource_get_user_data(resource);
if (wire->received >= wire->handle->num_fds) {
close(fd);
wl_resource_post_error(resource, ANDROID_WLEGL_HANDLE_ERROR_TOO_MANY_FDS,
"more native handle descriptors than declared");
return;
}
wire->handle->data[wire->received++] = fd;
}
static void handle_destroy(struct wl_client *client, struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct android_wlegl_handle_interface handle_impl = {
.add_fd = handle_add_fd, .destroy = handle_destroy,
};
static void create_handle(struct wl_client *client, struct wl_resource *resource,
uint32_t id, int32_t fds, struct wl_array *ints) {
if (fds < 0 || fds > MAX_HANDLE_FDS || ints->size % sizeof(int32_t) ||
ints->size > MAX_HANDLE_INTS * sizeof(int32_t)) {
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_VALUE, "invalid native handle size");
return;
}
struct wire_handle *wire = calloc(1, sizeof(*wire));
if (!wire) { wl_client_post_no_memory(client); return; }
wire->handle = calloc(1, sizeof(*wire->handle) + (size_t)fds * sizeof(int32_t) + ints->size);
if (!wire->handle) { free(wire); wl_client_post_no_memory(client); return; }
wire->handle->version = sizeof(*wire->handle);
wire->handle->num_fds = fds;
wire->handle->num_ints = (int)(ints->size / sizeof(int32_t));
if (ints->size) memcpy(wire->handle->data + fds, ints->data, ints->size);
struct wl_resource *handle = wl_resource_create(client, &android_wlegl_handle_interface, 1, id);
if (!handle) {
free(wire->handle); free(wire); wl_client_post_no_memory(client); return;
}
wl_resource_set_implementation(handle, &handle_impl, wire, handle_resource_destroy);
}
static void buffer_resource_destroy(struct wl_resource *resource) {
dmabuf_buffer_unref(wl_resource_get_user_data(resource));
}
static struct wl_resource *create_android_buffer(struct wl_client *client, uint32_t id,
AHardwareBuffer *hardware) {
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(hardware, &desc);
struct vkp_image *image = vkp_image_from_android_buffer(hardware);
if (!image) return NULL;
struct dmabuf_buffer *buffer = calloc(1, sizeof(*buffer));
if (!buffer) { vkp_image_destroy(image); wl_client_post_no_memory(client); return NULL; }
buffer->refs = 1;
buffer->width = (int32_t)desc.width; buffer->height = (int32_t)desc.height;
buffer->format = desc.format == 5 ? DRM_ARGB8888 : desc.format == 2 ? DRM_XBGR8888 : DRM_ABGR8888;
buffer->modifier = MOD_INVALID; /* No DMA-BUF layout was inferred from the native handle. */
buffer->img = image;
for (int i = 0; i < MAX_PLANES; i++) buffer->fd[i] = -1;
struct wl_resource *result = wl_resource_create(client, &wl_buffer_interface, 1, id);
if (!result) { dmabuf_buffer_unref(buffer); wl_client_post_no_memory(client); return NULL; }
wl_resource_set_implementation(result, &dbuf_buffer_impl, buffer, buffer_resource_destroy);
struct client_info *info = client_info_of(client);
if (info) info->dmabuf_buffers++;
return result;
}
static void create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id,
int32_t width, int32_t height, int32_t stride, int32_t format,
int32_t usage, struct wl_resource *handle_resource) {
if (!valid_description(width, height, stride, format) ||
!wl_resource_instance_of(handle_resource, &android_wlegl_handle_interface, &handle_impl)) {
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_VALUE, "invalid Android buffer description");
return;
}
struct wire_handle *wire = wl_resource_get_user_data(handle_resource);
if (wire->received != wire->handle->num_fds || !wire->received) {
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_HANDLE, "incomplete native handle");
return;
}
AHardwareBuffer_Desc desc = {
.width = (uint32_t)width, .height = (uint32_t)height, .layers = 1,
.format = (uint32_t)format, .usage = (uint32_t)usage, .stride = (uint32_t)stride};
AHardwareBuffer *hardware = NULL;
int status = import_handle(&desc, wire->handle, CREATE_FROM_HANDLE_CLONE, &hardware);
if (status != 0 || !hardware) {
droiddeck_log("display", "android_wlegl import failed: status=%d %dx%d stride=%d format=%d usage=0x%x fds=%d ints=%d",
status, width, height, stride, format, (uint32_t)usage,
wire->handle->num_fds, wire->handle->num_ints);
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_HANDLE,
"Android rejected the native handle (status %d)", status);
return;
}
struct wl_resource *buffer = create_android_buffer(client, id, hardware);
AHardwareBuffer_release(hardware);
if (!buffer) wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_HANDLE, "Vulkan cannot import this buffer");
}
static void get_server_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id,
int32_t width, int32_t height, int32_t format, int32_t usage) {
if (!format) format = 1;
if (!valid_description(width, height, width, format)) {
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_VALUE, "invalid buffer allocation request");
return;
}
AHardwareBuffer_Desc desc = {
.width = (uint32_t)width, .height = (uint32_t)height, .layers = 1,
.format = (uint32_t)format,
.usage = (uint32_t)usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT};
AHardwareBuffer *hardware = NULL;
if (AHardwareBuffer_allocate(&desc, &hardware) != 0 || !hardware) {
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_VALUE, "Android buffer allocation failed");
return;
}
AHardwareBuffer_describe(hardware, &desc);
const struct android_native_handle *handle = native_handle(hardware);
if (!handle || handle->num_fds < 1 || handle->num_fds > MAX_HANDLE_FDS ||
handle->num_ints < 0 || handle->num_ints > MAX_HANDLE_INTS) {
AHardwareBuffer_release(hardware);
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_HANDLE, "invalid allocated native handle");
return;
}
struct wl_resource *buffer = create_android_buffer(client, 0, hardware);
if (!buffer) {
AHardwareBuffer_release(hardware);
wl_resource_post_error(resource, ANDROID_WLEGL_ERROR_BAD_HANDLE, "Vulkan cannot import allocated buffer");
return;
}
struct wl_resource *reply = wl_resource_create(client, &android_wlegl_server_buffer_handle_interface,
wl_resource_get_version(resource), id);
if (!reply) {
wl_resource_destroy(buffer); AHardwareBuffer_release(hardware);
wl_client_post_no_memory(client); return;
}
struct wl_array ints = {.size = (size_t)handle->num_ints * sizeof(int32_t),
.alloc = 0, .data = (void *)(handle->data + handle->num_fds)};
android_wlegl_server_buffer_handle_send_buffer_ints(reply, &ints);
for (int i = 0; i < handle->num_fds; i++)
android_wlegl_server_buffer_handle_send_buffer_fd(reply, handle->data[i]);
/* No linear_layout event: Android's opaque allocation need not be linear. */
android_wlegl_server_buffer_handle_send_buffer(reply, buffer, (int32_t)desc.format, (int32_t)desc.stride);
wl_resource_destroy(reply);
AHardwareBuffer_release(hardware);
}
static const struct android_wlegl_interface wlegl_impl = {
.create_handle = create_handle, .create_buffer = create_buffer,
.get_server_buffer_handle = get_server_buffer,
};
static void bind_android_wlegl(struct wl_client *client, void *data, uint32_t version, uint32_t id) {
struct wl_resource *resource = wl_resource_create(client, &android_wlegl_interface,
version < 3 ? version : 3, id);
if (!resource) { wl_client_post_no_memory(client); return; }
wl_resource_set_implementation(resource, &wlegl_impl, NULL, NULL);
}
void droiddeck_android_wlegl_init(struct wl_display *display) {
if (!vkp_android_buffer_supported()) return;
void *library = dlopen("libnativewindow.so", RTLD_NOW | RTLD_LOCAL);
if (!library) return;
import_handle = dlsym(library, "AHardwareBuffer_createFromHandle");
native_handle = dlsym(library, "AHardwareBuffer_getNativeHandle");
if (!import_handle || !native_handle) { dlclose(library); return; }
/* Keep the platform entry points valid for the compositor's lifetime. */
if (wl_global_create(display, &android_wlegl_interface, 3, NULL, bind_android_wlegl))
droiddeck_log("display", "android_wlegl v3: Android buffer import available");
}
@@ -0,0 +1,63 @@
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
def vulkan_include_args():
candidates = []
for name in ("ANDROID_NDK_HOME", "ANDROID_NDK_ROOT"):
if os.environ.get(name):
candidates.append(Path(os.environ[name]))
android_home = os.environ.get("ANDROID_HOME")
if android_home:
ndk_root = Path(android_home) / "ndk"
version = os.environ.get("NDK_VERSION")
if version:
candidates.append(ndk_root / version)
if ndk_root.is_dir():
candidates.extend(sorted(ndk_root.iterdir(), reverse=True))
for ndk in candidates:
include = ndk / "toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include"
if (include / "vulkan/vulkan.h").is_file():
return ["-idirafter", str(include)]
return []
class VkExternalImageSyncTest(unittest.TestCase):
def test_client_barrier_pair(self):
compiler = shutil.which(os.environ.get("CC", "cc"))
if not compiler:
self.fail("A C compiler is required for the Vulkan image-sync regression test")
tests = Path(__file__).resolve().parent
source = tests / "vk_external_image_sync_test.c"
includes = tests.parents[1] / "app/src/main/cpp/waylandcomp/src"
with tempfile.TemporaryDirectory(prefix="droiddeck-vk-sync-") as tmp:
program = Path(tmp) / "vk-external-image-sync"
command = [
compiler,
"-std=c11",
"-Wall",
"-Wextra",
"-Werror",
"-UNDEBUG",
"-I",
str(includes),
*vulkan_include_args(),
str(source),
"-o",
str(program),
]
built = subprocess.run(command, capture_output=True, text=True)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
ran = subprocess.run([str(program)], capture_output=True, text=True, timeout=30)
self.assertEqual(ran.returncode, 0, ran.stdout + ran.stderr)
if __name__ == "__main__":
unittest.main()
+63
View File
@@ -0,0 +1,63 @@
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "vk_external_image_sync.h"
static void check_external_pair(const struct vkp_external_client_barrier_pair *pair,
VkImage image, uint32_t queue_family) {
assert(pair->acquire.sType == VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER);
assert(pair->acquire.oldLayout == VK_IMAGE_LAYOUT_GENERAL);
assert(pair->acquire.newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
assert(pair->acquire.srcQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT);
assert(pair->acquire.dstQueueFamilyIndex == queue_family);
assert(pair->acquire.image == image);
assert(pair->acquire.srcAccessMask == 0);
assert(pair->acquire.dstAccessMask == VK_ACCESS_TRANSFER_READ_BIT);
assert(pair->acquire.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT);
assert(pair->acquire.subresourceRange.baseMipLevel == 0);
assert(pair->acquire.subresourceRange.levelCount == 1);
assert(pair->acquire.subresourceRange.baseArrayLayer == 0);
assert(pair->acquire.subresourceRange.layerCount == 1);
assert(pair->release.sType == VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER);
assert(pair->release.oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
assert(pair->release.newLayout == VK_IMAGE_LAYOUT_GENERAL);
assert(pair->release.srcQueueFamilyIndex == queue_family);
assert(pair->release.dstQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT);
assert(pair->release.image == image);
assert(pair->release.srcAccessMask == VK_ACCESS_MEMORY_READ_BIT);
assert(pair->release.dstAccessMask == 0);
assert(pair->release.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT);
assert(pair->release.subresourceRange.levelCount == 1);
assert(pair->release.subresourceRange.layerCount == 1);
}
int main(void) {
const VkImage image = (VkImage)(uintptr_t)0x1234;
const VkImage ahb_image = (VkImage)(uintptr_t)0x5678;
const uint32_t queue_family = 7;
struct vkp_external_client_barrier_pair generic, ahb;
assert(vkp_external_client_barrier_pair(image, queue_family, 1, 0, &generic));
check_external_pair(&generic, image, queue_family);
/* AHardwareBuffer imports deliberately use the same client flags and lifecycle. */
assert(vkp_external_client_barrier_pair(ahb_image, queue_family, 1, 0, &ahb));
check_external_pair(&ahb, ahb_image, queue_family);
unsigned char untouched[sizeof(generic)];
memset(&generic, 0xa5, sizeof(generic));
memcpy(untouched, &generic, sizeof(generic));
assert(!vkp_external_client_barrier_pair(image, queue_family, 0, 0, &generic));
assert(memcmp(&generic, untouched, sizeof(generic)) == 0);
assert(!vkp_is_external_client_image(0, 0));
memset(&generic, 0x5a, sizeof(generic));
memcpy(untouched, &generic, sizeof(generic));
assert(!vkp_external_client_barrier_pair(image, queue_family, 1, 1, &generic));
assert(memcmp(&generic, untouched, sizeof(generic)) == 0);
assert(!vkp_is_external_client_image(1, 1));
assert(vkp_is_external_client_image(1, 0));
return 0;
}