From 25881c701a56233dd8fc7f92db6884a73949d63d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 23 Aug 2025 21:46:47 +0300 Subject: [PATCH] virgl: Support new resource-layout command Support new vrend command that queries layout of a backing GBM buffer for a giver vrend resource. Use it for querying stride/modifier of a PIPE_SHARED resource, passing this info down to WSI for exported resources. Now venus is able to import vrend resources, making gamescope work in KMS mode on QEMU. Virgl doesn't use stride/modifier info of winsys when it imports classic vrend resources, hence this change only affects venus context when it imports virgl WSI buffers. Based on initial version of resource-layout command from Daniel Stone. Reviewed-by: Gert Wollny Acked-by: Yiwei Zhang Signed-off-by: Dmitry Osipenko Part-of: --- src/gallium/drivers/virgl/virgl_encode.c | 9 ++ src/gallium/drivers/virgl/virgl_encode.h | 4 + src/gallium/drivers/virgl/virgl_resource.c | 108 ++++++++++++++++++++- src/gallium/drivers/virgl/virgl_resource.h | 9 ++ src/virtio/virtio-gpu/virgl_hw.h | 1 + src/virtio/virtio-gpu/virgl_protocol.h | 19 ++++ 6 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/gallium/drivers/virgl/virgl_encode.c b/src/gallium/drivers/virgl/virgl_encode.c index 0561ea0f827..32e5149f9c2 100644 --- a/src/gallium/drivers/virgl/virgl_encode.c +++ b/src/gallium/drivers/virgl/virgl_encode.c @@ -1892,3 +1892,12 @@ int virgl_encode_clear_surface(struct virgl_context *ctx, return 0; } + +void virgl_encoder_get_layout(struct virgl_context *ctx, + struct virgl_resource *out_res, + struct virgl_resource *res) +{ + virgl_encoder_write_cmd_dword(ctx, VIRGL_CMD0(VIRGL_CCMD_GET_PIPE_RESOURCE_LAYOUT, 0, VIRGL_RESOURCE_LAYOUT_SIZE)); + virgl_encoder_write_res(ctx, out_res); + virgl_encoder_write_res(ctx, res); +} diff --git a/src/gallium/drivers/virgl/virgl_encode.h b/src/gallium/drivers/virgl/virgl_encode.h index 54dcb94fa9c..627ba4468c1 100644 --- a/src/gallium/drivers/virgl/virgl_encode.h +++ b/src/gallium/drivers/virgl/virgl_encode.h @@ -341,6 +341,10 @@ int virgl_encode_clear_surface(struct virgl_context *ctx, unsigned width, unsigned height, bool render_condition_enabled); +void virgl_encoder_get_layout(struct virgl_context *ctx, + struct virgl_resource *out_res, + struct virgl_resource *res); + enum virgl_formats pipe_to_virgl_format(enum pipe_format format); enum pipe_format virgl_to_pipe_format(enum virgl_formats format); #endif diff --git a/src/gallium/drivers/virgl/virgl_resource.c b/src/gallium/drivers/virgl/virgl_resource.c index 3ab6cf591c2..9a163ff9c73 100644 --- a/src/gallium/drivers/virgl/virgl_resource.c +++ b/src/gallium/drivers/virgl/virgl_resource.c @@ -642,6 +642,89 @@ static void virgl_resource_layout(struct pipe_resource *pt, metadata->total_size = 0; } +static void virgl_resource_free_gbm_layout(struct virgl_resource *res) +{ + if (!res->metadata.gbm.res) + return; + + res->metadata.gbm.ctx->destroy(res->metadata.gbm.ctx); + pipe_resource_reference((struct pipe_resource **)&res->metadata.gbm.res, NULL); +} + +static void virgl_resource_sync_gbm_layout(struct virgl_resource *res) +{ + struct virgl_screen *vs = virgl_screen(res->b.screen); + + simple_mtx_lock(&res->metadata.gbm.lock); + if (res->metadata.gbm.res) { + vs->vws->resource_wait(vs->vws, res->metadata.gbm.res->hw_res); + + pipe_buffer_read(res->metadata.gbm.ctx, + &res->metadata.gbm.res->b, 0, + sizeof(res->metadata.gbm.layout), + &res->metadata.gbm.layout); + + virgl_resource_free_gbm_layout(res); + } + simple_mtx_unlock(&res->metadata.gbm.lock); +} + +static void +virgl_resource_async_query_gbm_layout(struct pipe_screen *screen, + struct pipe_resource *resource, + uint32_t bind) +{ + struct virgl_resource *res = virgl_resource(resource); + struct virgl_screen *vs = virgl_screen(screen); + struct virgl_resource *out_res; + struct virgl_context *vctx; + struct pipe_context *ctx; + + if (!(bind & PIPE_BIND_SHARED)) + return; + + if (!(vs->caps.caps.v2.capability_bits_v2 & VIRGL_CAP_V2_RESOURCE_LAYOUT)) + return; + + out_res = (struct virgl_resource *) + pipe_buffer_create(&vs->base, PIPE_BIND_CUSTOM, PIPE_USAGE_STAGING, + sizeof(res->metadata.gbm.layout)); + if (!out_res) + return; + + ctx = screen->context_create(screen, NULL, 0); + vctx = virgl_context(ctx); + + virgl_encoder_get_layout(vctx, out_res, res); + ctx->flush(ctx, NULL, 0); + + /* + * Async query must be completed by virgl_resource_sync_gbm_layout(). + * Returned layout will be zeroed if resource isn't backed by GBM buffer. + */ + res->metadata.gbm.res = out_res; + res->metadata.gbm.ctx = ctx; +} + +static size_t virgl_resource_shared_tex_size(struct virgl_resource *res) +{ + size_t aligned_stride = align(res->metadata.stride[0], 256); + struct virgl_resource_metadata metadata = {}; + + /* + * Size of a shared buffer is validated by WSI. WSI retrieves BO size + * from resource's dmabuf with lseek(). When shared buffer is backed + * by a GBM BO on host, WSI validation may fail for a classic resource + * because we will tell WSI to use stride of the host's GBM BO that won't + * match guest BO stride. Mitigate this problem by using stride aligned to + * 256 bytes for estimated buffer size, which is a max possible alignment + * that GPUs are using today. + */ + virgl_resource_layout(&res->b, &metadata, 0, aligned_stride, 0, 0); + + return metadata.total_size; +} + static struct pipe_resource *virgl_resource_create_front(struct pipe_screen *screen, const struct pipe_resource *templ, const void *map_front_private) @@ -657,6 +740,7 @@ static struct pipe_resource *virgl_resource_create_front(struct pipe_screen *scr vbind = pipe_to_virgl_bind(vs, templ->bind); vflags = pipe_to_virgl_flags(vs, templ->flags); virgl_resource_layout(&res->b, &res->metadata, 0, 0, 0, 0); + simple_mtx_init(&res->metadata.gbm.lock, mtx_plain); if ((vs->caps.caps.v2.capability_bits & VIRGL_CAP_APP_TWEAK_SUPPORT) && vs->tweak_gles_emulate_bgra && @@ -674,6 +758,8 @@ static struct pipe_resource *virgl_resource_create_front(struct pipe_screen *scr if (res->use_staging) alloc_size = 1; + else if (templ->bind & PIPE_BIND_SHARED) + alloc_size = virgl_resource_shared_tex_size(res); else alloc_size = res->metadata.total_size; @@ -700,6 +786,7 @@ static struct pipe_resource *virgl_resource_create_front(struct pipe_screen *scr virgl_buffer_init(res); } else { virgl_texture_init(res); + virgl_resource_async_query_gbm_layout(screen, &res->b, templ->bind); } return &res->b; @@ -829,6 +916,7 @@ static struct pipe_resource *virgl_resource_from_handle(struct pipe_screen *scre } virgl_texture_init(res); + virgl_resource_async_query_gbm_layout(screen, &res->b, PIPE_BIND_SHARED); return &res->b; } @@ -848,7 +936,12 @@ virgl_resource_get_param(struct pipe_screen *screen, switch(param) { case PIPE_RESOURCE_PARAM_MODIFIER: - *value = res->metadata.modifier; + virgl_resource_sync_gbm_layout(res); + + if (res->metadata.gbm.layout.planes[0].stride) + *value = res->metadata.gbm.layout.modifier; + else + *value = res->metadata.modifier; return true; default: return false; @@ -987,6 +1080,7 @@ void virgl_resource_destroy(struct pipe_screen *screen, util_range_destroy(&res->valid_buffer_range); vs->vws->resource_reference(vs->vws, &res->hw_res, NULL); + virgl_resource_free_gbm_layout(res); FREE(res); } @@ -998,13 +1092,19 @@ bool virgl_resource_get_handle(struct pipe_screen *screen, { struct virgl_screen *vs = virgl_screen(screen); struct virgl_resource *res = virgl_resource(resource); + int stride; if (res->b.target == PIPE_BUFFER) return false; - return vs->vws->resource_get_handle(vs->vws, res->hw_res, - res->metadata.stride[0], - whandle); + virgl_resource_sync_gbm_layout(res); + + if (res->metadata.gbm.layout.planes[0].stride) + stride = res->metadata.gbm.layout.planes[0].stride; + else + stride = res->metadata.stride[0]; + + return vs->vws->resource_get_handle(vs->vws, res->hw_res, stride, whandle); } void virgl_resource_dirty(struct virgl_resource *res, uint32_t level) diff --git a/src/gallium/drivers/virgl/virgl_resource.h b/src/gallium/drivers/virgl/virgl_resource.h index 3b348e67c3f..76dc5ee37d8 100644 --- a/src/gallium/drivers/virgl/virgl_resource.h +++ b/src/gallium/drivers/virgl/virgl_resource.h @@ -41,6 +41,14 @@ struct winsys_handle; struct virgl_screen; struct virgl_context; +struct virgl_resource_gbm_metadata +{ + struct virgl_resource_layout layout; + struct virgl_resource *res; + struct pipe_context *ctx; + simple_mtx_t lock; +}; + struct virgl_resource_metadata { unsigned long level_offset[VR_MAX_TEXTURE_2D_LEVELS]; @@ -48,6 +56,7 @@ struct virgl_resource_metadata unsigned layer_stride[VR_MAX_TEXTURE_2D_LEVELS]; uint32_t plane, plane_offset, total_size; uint64_t modifier; + struct virgl_resource_gbm_metadata gbm; }; struct virgl_resource { diff --git a/src/virtio/virtio-gpu/virgl_hw.h b/src/virtio/virtio-gpu/virgl_hw.h index a8b1f699533..ec10bdd3447 100644 --- a/src/virtio/virtio-gpu/virgl_hw.h +++ b/src/virtio/virtio-gpu/virgl_hw.h @@ -627,6 +627,7 @@ enum virgl_formats { #define VIRGL_CAP_V2_GROUP_VOTE (1u << 15) #define VIRGL_CAP_V2_MIRROR_CLAMP_TO_EDGE (1u << 16) #define VIRGL_CAP_V2_MIRROR_CLAMP (1u << 17) +#define VIRGL_CAP_V2_RESOURCE_LAYOUT (1u << 18) /* virgl bind flags - these are compatible with mesa 10.5 gallium. * but are fixed, no other should be passed to virgl either. diff --git a/src/virtio/virtio-gpu/virgl_protocol.h b/src/virtio/virtio-gpu/virgl_protocol.h index d24be63bb03..cb80403564d 100644 --- a/src/virtio/virtio-gpu/virgl_protocol.h +++ b/src/virtio/virtio-gpu/virgl_protocol.h @@ -45,6 +45,19 @@ struct virgl_memory_info uint32_t nr_device_memory_evictions; /**< # of evictions (monotonic counter) */ }; +#define VIRGL_RESOURCE_MAX_PLANES 4 +struct virgl_resource_layout +{ + uint64_t modifier; + uint32_t num_planes; + uint32_t reserved1; + struct { + uint64_t offset; + uint32_t stride; + uint32_t size; + } planes[VIRGL_RESOURCE_MAX_PLANES]; +}; + enum virgl_object_type { VIRGL_OBJECT_NULL, VIRGL_OBJECT_BLEND, @@ -130,6 +143,7 @@ enum virgl_context_cmd { VIRGL_CCMD_END_FRAME, VIRGL_CCMD_CLEAR_SURFACE, + VIRGL_CCMD_GET_PIPE_RESOURCE_LAYOUT, VIRGL_MAX_COMMANDS }; @@ -780,4 +794,9 @@ enum vrend_tweak_type { #define VIRGL_CLEAR_SURFACE_WIDTH 9 #define VIRGL_CLEAR_SURFACE_HEIGHT 10 +/* VIRGL_CCMD_GET_PIPE_RESOURCE_LAYOUT */ +#define VIRGL_RESOURCE_LAYOUT_SIZE 2 +#define VIRGL_RESOURCE_LAYOUT_HANDLE_OUT 1 +#define VIRGL_RESOURCE_LAYOUT_HANDLE_TARGET 2 + #endif