zink: add renderonly scanouts handling

Add support for allocating scanouts via renderonly, to allow Zink usage
with render-only GPUs paired with display-only scanout device.

Signed-off-by: Frank Binns <frank.binns@imgtec.com>
[Icenowy: split patch, forward port, require modifiers]

Signed-off-by: Icenowy Zheng <uwu@icenowy.me>
Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
Reviewed-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Reviewed-By: Daniel Stone <daniels@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38810>
This commit is contained in:
Frank Binns 2026-01-17 21:06:40 +08:00 committed by Marge Bot
parent 74b8fb330e
commit 83e92f2b5f
5 changed files with 138 additions and 11 deletions

View file

@ -421,7 +421,7 @@ static struct pipe_screen *
pipe_zink_create_screen(int fd, const struct pipe_screen_config *config)
{
struct pipe_screen *screen;
screen = zink_drm_create_screen(fd, config);
screen = zink_drm_create_screen(fd, config, NULL);
return screen ? debug_screen_wrap(screen) : NULL;
}

View file

@ -25,6 +25,7 @@
#define ZINK_PUBLIC_H
struct pipe_screen;
struct renderonly;
struct sw_winsys;
struct pipe_screen_config;
@ -32,7 +33,7 @@ struct pipe_screen *
zink_create_screen(struct sw_winsys *winsys, const struct pipe_screen_config *config);
struct pipe_screen *
zink_drm_create_screen(int fd, const struct pipe_screen_config *config);
zink_drm_create_screen(int fd, const struct pipe_screen_config *config, struct renderonly *ro);
struct pipe_screen *
zink_win32_create_screen(uint64_t adapter_luid);
#endif

View file

@ -255,6 +255,12 @@ zink_resource_destroy(struct pipe_screen *pscreen,
struct zink_resource *res = zink_resource(pres);
/* prevent double-free when unrefing internal surfaces */
res->base.b.reference.count = 999;
#ifdef HAVE_LIBDRM
if (res->ro_scanout)
renderonly_scanout_destroy(res->ro_scanout, screen->ro);
#endif
if (pres->target == PIPE_BUFFER) {
util_range_destroy(&res->valid_buffer_range);
util_idalloc_mt_free(&screen->buffer_ids, res->base.buffer_id_unique);
@ -1610,8 +1616,52 @@ resource_create(struct pipe_screen *pscreen,
if (templ2.flags & PIPE_RESOURCE_FLAG_SPARSE &&
(util_res_sample_count(templ) == 1 || screen->info.feats.features.shaderStorageImageMultisample))
templ2.bind |= PIPE_BIND_SHADER_IMAGE;
#ifdef HAVE_LIBDRM
if (!whandle && screen->ro && (templ2.bind & PIPE_BIND_SCANOUT)) {
struct winsys_handle handle;
assert(screen->info.have_EXT_image_drm_format_modifier);
/* Only LINEAR is considered the appropriate modifier for scaning out
* now, so sort out it if it's present, and fail if a modifier list is
* present but does not include LINEAR.
*/
for (int i = 0; i < modifiers_count; i++) {
if (res->modifiers[i] == DRM_FORMAT_MOD_LINEAR) {
res->modifiers[0] = DRM_FORMAT_MOD_LINEAR;
res->modifiers_count = 1;
break;
}
}
if (modifiers_count > 0 && res->modifiers[0] != DRM_FORMAT_MOD_LINEAR) {
/* Failed to find LINEAR in the modifier list */
free(res->modifiers);
FREE_CL(res);
return NULL;
}
res->ro_scanout =
renderonly_scanout_for_resource(&templ2, screen->ro, &handle);
if (!res->ro_scanout) {
free(res->modifiers);
FREE_CL(res);
return NULL;
}
assert(handle.type == WINSYS_HANDLE_TYPE_FD);
assert(handle.modifier == DRM_FORMAT_MOD_LINEAR);
whandle = &handle;
}
#endif
res->obj = resource_object_create(screen, &templ2, whandle, &linear, res->modifiers, res->modifiers_count, loader_private, user_mem);
if (!res->obj) {
#ifdef HAVE_LIBDRM
if (res->ro_scanout)
renderonly_scanout_destroy(res->ro_scanout, screen->ro);
#endif
free(res->modifiers);
FREE_CL(res);
return NULL;
@ -1852,6 +1902,12 @@ zink_resource_get_param(struct pipe_screen *pscreen, struct pipe_context *pctx,
break;
case PIPE_RESOURCE_PARAM_STRIDE: {
#ifdef HAVE_LIBDRM
if (res->ro_scanout) {
*value = res->ro_scanout->stride;
break;
}
#endif
VkImageSubresource sub_res = {0};
VkSubresourceLayout sub_res_layout = {0};
@ -1864,6 +1920,12 @@ zink_resource_get_param(struct pipe_screen *pscreen, struct pipe_context *pctx,
}
case PIPE_RESOURCE_PARAM_OFFSET: {
#ifdef HAVE_LIBDRM
if (res->ro_scanout) {
*value = 0;
break;
}
#endif
VkImageSubresource isr = {
aspect,
level,
@ -1941,6 +2003,10 @@ zink_resource_get_handle(struct pipe_screen *pscreen,
{
if (tex->target == PIPE_BUFFER)
tc_buffer_disable_cpu_storage(tex);
#ifdef HAVE_LIBDRM
if (whandle->type == WINSYS_HANDLE_TYPE_KMS && zink_screen(pscreen)->ro)
return renderonly_get_handle(zink_resource(tex)->ro_scanout, whandle);
#endif
if (whandle->type == WINSYS_HANDLE_TYPE_FD || whandle->type == WINSYS_HANDLE_TYPE_KMS) {
#ifdef ZINK_USE_DMABUF
while (whandle->plane && tex->next && !zink_resource_is_aux_plane(tex->next)) {
@ -2036,8 +2102,10 @@ zink_resource_from_handle(struct pipe_screen *pscreen,
unsigned usage)
{
#ifdef ZINK_USE_DMABUF
struct zink_screen *screen = zink_screen(pscreen);
if (whandle->modifier != DRM_FORMAT_MOD_INVALID &&
!zink_screen(pscreen)->info.have_EXT_image_drm_format_modifier)
!screen->info.have_EXT_image_drm_format_modifier)
return NULL;
struct pipe_resource templ2 = *templ;
@ -2070,6 +2138,19 @@ zink_resource_from_handle(struct pipe_screen *pscreen,
res->obj->immutable_handle = true;
res->internal_format = whandle->format;
}
#ifdef HAVE_LIBDRM
if (screen->ro) {
struct zink_resource *res = zink_resource(pres);
/* Make sure that renderonly has a handle to our buffer in the display's
* fd, so that a later renderonly_get_handle() returns correct handles
* or GEM names.
*/
res->ro_scanout = renderonly_create_gpu_import_for_resource(pres, screen->ro, NULL);
}
#endif
return pres;
#else
return NULL;

View file

@ -169,6 +169,14 @@ zink_get_device_vendor(struct pipe_screen *pscreen)
return zink_screen(pscreen)->vendor_name;
}
static int
zink_get_screen_fd(struct pipe_screen *pscreen)
{
struct zink_screen *screen = zink_screen(pscreen);
return screen->drm_fd;
}
static const char *
zink_get_name(struct pipe_screen *pscreen)
{
@ -1662,6 +1670,11 @@ zink_destroy_screen(struct pipe_screen *pscreen)
if (screen->loader_lib)
util_dl_close(screen->loader_lib);
#ifdef HAVE_LIBDRM
if (screen->ro)
screen->ro->destroy(screen->ro);
#endif
if (screen->drm_fd != -1)
close(screen->drm_fd);
@ -3613,6 +3626,7 @@ zink_internal_create_screen(const struct pipe_screen_config *config, int64_t dev
for (unsigned i = 0; i < ARRAY_SIZE(screen->base.nir_options); i++)
screen->base.nir_options[i] = &screen->nir_options;
screen->base.get_screen_fd = zink_get_screen_fd;
screen->base.get_name = zink_get_name;
if (screen->instance_info->have_KHR_external_memory_capabilities) {
screen->base.get_device_uuid = zink_get_device_uuid;
@ -3869,25 +3883,44 @@ free_device:
}
struct pipe_screen *
zink_drm_create_screen(int fd, const struct pipe_screen_config *config)
zink_drm_create_screen(int fd, const struct pipe_screen_config *config, struct renderonly *ro)
{
int64_t dev_major, dev_minor;
struct zink_screen *ret;
struct zink_screen *ret = NULL;
if (zink_render_rdev(fd, &dev_major, &dev_minor))
return NULL;
ret = zink_internal_create_screen(config, dev_major, dev_minor, 0);
if (ret)
ret->drm_fd = os_dupfd_cloexec(fd);
if (ret && !ret->info.have_KHR_external_memory_fd) {
debug_printf("ZINK: KHR_external_memory_fd required!\n");
zink_destroy_screen(&ret->base);
if (!ret)
return NULL;
ret->drm_fd = os_dupfd_cloexec(fd);
#ifdef HAVE_LIBDRM
ret->ro = ro;
#else
assert(!ro);
#endif
if (!ret->info.have_KHR_external_memory_fd) {
debug_printf("ZINK: KHR_external_memory_fd required!\n");
goto fail;
}
/*
* Renderonly device may allocate buffers with arbitrary stride that
* can only be supported with EXT_image_drm_format_modifier.
*/
if (ro && !ret->info.have_EXT_image_drm_format_modifier) {
debug_printf("ZINK: EXT_image_drm_format_modifier required for renderonly GPUs!\n");
goto fail;
}
return &ret->base;
fail:
zink_destroy_screen(&ret->base);
return NULL;
}
struct pipe_screen *

View file

@ -40,6 +40,11 @@
#include "util/pb_slab.h"
#include "util/blob.h"
#ifdef HAVE_LIBDRM
#include "renderonly/renderonly.h"
#endif
#include "util/disk_cache.h"
#include "util/hash_table.h"
#include "util/list.h"
@ -1323,6 +1328,10 @@ struct zink_resource {
uint8_t modifiers_count;
uint64_t *modifiers;
#ifdef HAVE_LIBDRM
struct renderonly_scanout *ro_scanout;
#endif
};
static inline struct zink_resource *
@ -1398,6 +1407,9 @@ struct zink_screen {
bool device_lost;
int drm_fd;
#ifdef HAVE_LIBDRM
struct renderonly *ro;
#endif
struct slab_parent_pool transfer_pool;
struct disk_cache *disk_cache;