mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-13 03:51:10 +00:00
Support Android hardware-buffer presentation over Wayland
This commit is contained in:
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
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user