Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/egl/drivers/dri2/platform_x11_dri3.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,22 @@ egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->surf.base);
}

static int
egl_dri3_flush_drawable_with_fence_fd(struct loader_dri3_drawable *draw,
unsigned flags)
{
return loader_dri3_flush_with_fence_fd(
draw, __DRI2_FLUSH_DRAWABLE | __DRI2_FLUSH_INVALIDATE_ANCILLARY,
__DRI2_THROTTLE_SWAPBUFFER);
}

static const struct loader_dri3_vtable egl_dri3_vtable = {
.set_drawable_size = egl_dri3_set_drawable_size,
.in_current_context = egl_dri3_in_current_context,
.get_dri_context = egl_dri3_get_dri_context,
.get_dri_screen = egl_dri3_get_dri_screen,
.flush_drawable = egl_dri3_flush_drawable,
.flush_drawable_with_fence_fd = egl_dri3_flush_drawable_with_fence_fd,
};

static EGLBoolean
Expand Down
4 changes: 4 additions & 0 deletions src/freedreno/drm/freedreno_drmif.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ void fd_bo_del(struct fd_bo *bo);
void fd_bo_del_array(struct fd_bo **bos, int count);
void fd_bo_del_list_nocache(struct list_head *list);
int fd_bo_get_name(struct fd_bo *bo, uint32_t *name);
/* Return the BO's handle in its owning device's namespace, not a handle for
* another device used for scanout. Native KGSL BOs need not be dma-buf
* exportable to have a valid handle. Suballocated BOs return zero.
*/
uint32_t fd_bo_handle(struct fd_bo *bo);
int fd_bo_dmabuf_drm(struct fd_bo *bo);
int fd_bo_dmabuf(struct fd_bo *bo);
Expand Down
15 changes: 12 additions & 3 deletions src/freedreno/drm/kgsl/kgsl_ringbuffer_sp.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ flush_submit_list(struct list_head *submit_list)
DEBUG_MSG("merged %u submits", cmd_idx);
break;
}

list_del(&submit->node);
fd_submit_del(submit);
}

struct kgsl_cmd_syncpoint_fence sync_fence = {
Expand Down Expand Up @@ -109,6 +106,18 @@ flush_submit_list(struct list_head *submit_list)
close(fd_submit->in_fence_fd);

fail:
/* Keep merged submits alive until KGSL has consumed the command list and,
* on success, populated the shared kernel timestamp. Dropping them before
* IOCTL_KGSL_GPU_COMMAND can free BOs that the command stream still uses.
*/
foreach_submit_safe (submit, submit_list) {
if (submit == last_submit(submit_list))
break;

list_del(&submit->node);
fd_submit_del(submit);
}

return ret;
}

Expand Down
13 changes: 13 additions & 0 deletions src/freedreno/drm/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,16 @@ libfreedreno_drm = static_library(
build_by_default : false,
)

if with_tests and freedreno_kmds.contains('kgsl')
test(
'kgsl_bo_handle',
executable(
'kgsl_bo_handle_test',
['tests/kgsl_bo_handle_test.c', freedreno_xml_header_files],
include_directories : libfreedreno_drm_includes,
dependencies : [libfreedreno_drm_deps, idep_libfreedreno_common],
link_with : libfreedreno_drm,
),
suite : ['freedreno'],
)
endif
68 changes: 68 additions & 0 deletions src/freedreno/drm/tests/kgsl_bo_handle_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: MIT */

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#include "freedreno_drmif.h"
#include "kgsl/kgsl_priv.h"

/* Hardware test: allocate one native KGSL BO, without submitting GPU work.
* The removed KMS-handle override tried to export this BO and returned zero.
* A device-local handle must remain valid even when dma-buf export cannot work.
*/
int
main(void)
{
int fd = open("/dev/kgsl-3d0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
fprintf(stderr, "SKIP: /dev/kgsl-3d0 is not accessible\n");
return 77;
}

struct fd_device *dev = fd_device_new(fd);
if (!dev) {
close(fd);
fprintf(stderr, "Failed to create KGSL device\n");
return 1;
}

struct fd_bo *bo = fd_bo_new(dev, 4096, 0, "handle regression test");
int result = 1;
if (!bo) {
fprintf(stderr, "Failed to allocate native KGSL BO\n");
goto out;
}

uint32_t handle = bo->handle;
if (to_kgsl_bo(bo)->bo_type != KGSL_BO_NATIVE || !handle) {
fprintf(stderr, "Expected a native, non-suballocated KGSL BO\n");
goto out_bo;
}

/* The first lookup marks the BO shared; repeat to cover both states. */
if (fd_bo_handle(bo) != handle || fd_bo_handle(bo) != handle) {
fprintf(stderr, "Native handle was replaced during handle lookup\n");
goto out_bo;
}

int dma_fd = fd_bo_dmabuf(bo);
if (dma_fd >= 0)
close(dma_fd);

if (fd_bo_handle(bo) != handle) {
fprintf(stderr, "dma-buf export attempt changed the native handle\n");
goto out_bo;
}

printf("PASS: native KGSL handle survives sharing and %s export\n",
dma_fd < 0 ? "failed" : "successful");
result = 0;

out_bo:
fd_bo_del(bo);
out:
fd_device_del(dev);
close(fd);
return result;
}
8 changes: 8 additions & 0 deletions src/gallium/drivers/freedreno/freedreno_batch.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,14 @@ batch_flush(struct fd_batch *batch, bool last_batch)
if (last_batch && !batch->fence)
batch->fence = fd_pipe_fence_create(batch);

/* flush_resource() can submit the drawable's writer before the frontend's
* context flush reaches fd_context_flush(). Mark that writer's fence for
* native-fd export here so the Present fence still comes from the rendering
* submission rather than a following empty submit.
*/
if (last_batch && batch->ctx->explicit_present_fence && batch->fence)
batch->fence->use_fence_fd = true;

if (batch->fence)
fd_pipe_fence_ref(&batch->ctx->last_fence, batch->fence);

Expand Down
19 changes: 19 additions & 0 deletions src/gallium/drivers/freedreno/freedreno_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep,
fd_bc_dump(ctx, "need fence, last_fence=%p", ctx->last_fence);
batch = fd_context_batch(ctx);
} else if (!batch) {
ctx->explicit_present_fence = false;
return;
}

Expand Down Expand Up @@ -118,6 +119,8 @@ fd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep,
fd_bc_dump(ctx, "%p: remaining:\n", ctx);

out:
ctx->explicit_present_fence = false;

if (fencep)
fd_pipe_fence_ref(fencep, fence);

Expand All @@ -134,6 +137,21 @@ fd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep,
assert(pctx->get_device_reset_status(pctx) == PIPE_NO_RESET);
}

static void
fd_set_context_param(struct pipe_context *pctx, enum pipe_context_param param,
unsigned value)
{
struct fd_context *ctx = fd_context(pctx);

switch (param) {
case PIPE_CONTEXT_PARAM_EXPLICIT_PRESENT_FENCE:
ctx->explicit_present_fence = value;
break;
default:
break;
}
}

static void
fd_texture_barrier(struct pipe_context *pctx, unsigned flags) in_dt
{
Expand Down Expand Up @@ -699,6 +717,7 @@ fd_context_init(struct fd_context *ctx, struct pipe_screen *pscreen,
pctx->screen = pscreen;
pctx->priv = priv;
pctx->flush = fd_context_flush;
pctx->set_context_param = fd_set_context_param;
pctx->emit_string_marker = fd_emit_string_marker;
pctx->set_debug_callback = fd_set_debug_callback;
pctx->create_fence_fd = fd_create_pipe_fence_fd;
Expand Down
5 changes: 5 additions & 0 deletions src/gallium/drivers/freedreno/freedreno_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,11 @@ struct fd_context {
*/
struct pipe_fence_handle *last_fence dt;

/* The next context flush exports the render-completion fence directly to
* the window-system presentation path.
*/
bool explicit_present_fence;

/*
* Counter to keep track of batch's most recent update. Ie. the batch with
* the higher update count is the one that has been drawn/etc to the most
Expand Down
10 changes: 10 additions & 0 deletions src/gallium/drivers/freedreno/freedreno_fence.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ fd_pipe_fence_finish(struct pipe_screen *pscreen, struct pipe_context *pctx,

if (fence->use_fence_fd) {
assert(fence->fence);
/* Locally submitted fences also have a GPU-written completion marker.
* Check it before waiting for the kernel's sync-file notification, as
* fence signaling can lag behind completion of the command stream.
* Imported fences have no userspace sequence number. A wrapped zero
* sequence simply takes the existing sync-file path as well.
*/
if (fence->fence->ufence &&
!fd_pipe_wait_timeout(fence->pipe, fence->fence, 0))
return true;

int ret = sync_wait(fence->fence->fence_fd, timeout / 1000000);
return ret == 0;
}
Expand Down
11 changes: 10 additions & 1 deletion src/gallium/drivers/freedreno/freedreno_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ fd_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
* to the kernel for the fence to be added to the backing GEM
* object.
*/
if (ctx->no_implicit_sync)
if (ctx->no_implicit_sync && !ctx->screen->is_kgsl)
return;

flush_resource(ctx, rsc, PIPE_MAP_READ);
Expand All @@ -703,6 +703,15 @@ fd_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
* way to the kernel:
*/
fd_resource_wait(ctx, rsc, FD_BO_PREP_FLUSH);

/* KGSL does not attach Mesa's tracked render fence to an exported dma-buf.
* If the window-system path is not carrying the next submission's native
* fence explicitly, wait here before handing the resource to an
* implicit-sync consumer. The explicit Present-fence path avoids this
* per-frame CPU stall.
*/
if (ctx->screen->is_kgsl && !ctx->explicit_present_fence)
fd_resource_wait(ctx, rsc, FD_BO_PREP_READ);
}

static void
Expand Down
5 changes: 4 additions & 1 deletion src/gallium/frontends/dri/dri2.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,17 @@ dri2_allocate_textures(struct dri_context *ctx,
/* Flush the texture before unreferencing, so that other clients can
* see what the driver has rendered.
*/
if (i != ST_ATTACHMENT_DEPTH_STENCIL && drawable->textures[i]) {
if (i != ST_ATTACHMENT_DEPTH_STENCIL && drawable->textures[i] &&
!(i == ST_ATTACHMENT_BACK_LEFT && drawable->back_buffer_fenced)) {
struct pipe_context *pipe = ctx->st->pipe;
pipe->flush_resource(pipe, drawable->textures[i]);
}

pipe_resource_reference(&drawable->textures[i], NULL);
}

drawable->back_buffer_fenced = false;

if (drawable->stvis.samples > 1) {
for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
bool del = true;
Expand Down
Loading