rusticl/gl: flush and wait on gl objects inside clEnqueueAcquireGLObjects

This fixes flakes in the gl/gles OpenCL CTS tests on radeonsi and zink.

Cc: mesa-stable
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36249>
This commit is contained in:
Karol Herbst 2025-07-20 18:21:53 +02:00 committed by Marge Bot
parent f6b844e239
commit 45c28a20a4
3 changed files with 93 additions and 70 deletions

View file

@ -3184,13 +3184,27 @@ fn enqueue_acquire_gl_objects(
return Err(CL_INVALID_GL_OBJECT);
}
// We need to flush on the applications thread:
//
// If an OpenGL context is bound to the current thread, then any OpenGL commands which
// 1. affect or access the contents of a memory object listed in the mem_objects list, and
// 2. were issued on that OpenGL context prior to the call to clEnqueueAcquireGLObjects
// will complete before execution of any OpenCL commands following the clEnqueueAcquireGLObjects
// which affect or access any of those memory objects. If a non-NULL event object is returned,
// it will report completion only after completion of such OpenGL commands.
let fence_fd = q.context.flush_gl_mem_objects(&objs)?;
create_and_queue(
q,
CL_COMMAND_ACQUIRE_GL_OBJECTS,
evs,
event,
false,
Box::new(move |_, ctx| copy_cube_to_slice(ctx, &objs)),
Box::new(move |_, ctx| {
if let Some(fence_fd) = fence_fd {
ctx.import_fence(&fence_fd).gpu_wait(ctx);
}
copy_cube_to_slice(ctx, &objs)
}),
)
}

View file

@ -10,6 +10,7 @@ use crate::core::util::*;
use crate::impl_cl_type_trait;
use mesa_rust::pipe::context::RWFlags;
use mesa_rust::pipe::fence::FenceFd;
use mesa_rust::pipe::resource::*;
use mesa_rust::pipe::screen::ResourceType;
use mesa_rust_gen::*;
@ -658,6 +659,11 @@ impl Context {
Ok(res)
}
pub fn flush_gl_mem_objects(&self, mem_objects: &[Mem]) -> CLResult<Option<FenceFd>> {
let gl_ctx = self.gl_ctx_manager.as_ref().ok_or(CL_INVALID_CONTEXT)?;
gl_ctx.flush(mem_objects)
}
}
impl Drop for Context {

View file

@ -206,6 +206,60 @@ impl GLCtxManager {
}
}
fn do_flush(&self, exports_in: &mut [mesa_glinterop_export_in]) -> CLResult<Option<FenceFd>> {
let mut fd = -1;
let mut flush_out = mesa_glinterop_flush_out {
version: 1,
fence_fd: &mut fd,
..Default::default()
};
let err = match self.gl_ctx {
GLCtx::EGL(disp, ctx) => {
let egl_flush_objects_func = self
.xplat_manager
.MesaGLInteropEGLFlushObjects()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
unsafe {
egl_flush_objects_func(
disp.cast(),
ctx.cast(),
exports_in.len() as u32,
exports_in.as_mut_ptr(),
&mut flush_out,
)
}
}
GLCtx::GLX(disp, ctx) => {
let glx_flush_objects_func = self
.xplat_manager
.MesaGLInteropGLXFlushObjects()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
unsafe {
glx_flush_objects_func(
disp.cast(),
ctx.cast(),
exports_in.len() as u32,
exports_in.as_mut_ptr(),
&mut flush_out,
)
}
}
};
if err != 0 {
return Err(interop_to_cl_error(err));
}
if fd != -1 {
Ok(Some(FenceFd { fd: fd }))
} else {
Ok(None)
}
}
pub fn export_object(
&self,
cl_ctx: &Arc<Context>,
@ -229,13 +283,12 @@ impl GLCtxManager {
..Default::default()
};
let mut fd = -1;
let mut flush_out = mesa_glinterop_flush_out {
version: 1,
fence_fd: &mut fd,
..Default::default()
};
if let Some(fence_fd) = self.do_flush(&mut [export_in])? {
cl_ctx.devs.iter().for_each(|dev| {
let fence = dev.helper_ctx().import_fence(&fence_fd);
fence.wait();
});
}
let err = unsafe {
match &self.gl_ctx {
@ -244,74 +297,14 @@ impl GLCtxManager {
.MesaGLInteropEGLExportObject()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
let egl_flush_objects_func = xplat_manager
.MesaGLInteropEGLFlushObjects()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
let err_flush = egl_flush_objects_func(
disp.cast(),
ctx.cast(),
1,
&mut export_in,
&mut flush_out,
);
if err_flush != 0 {
err_flush
} else {
if fd != -1 {
// TODO: use fence_server_sync in ctx inside the queue thread
let fence_fd = FenceFd { fd };
cl_ctx.devs.iter().for_each(|dev| {
let fence = dev.helper_ctx().import_fence(&fence_fd);
fence.wait();
});
}
egl_export_object_func(
disp.cast(),
ctx.cast(),
&mut export_in,
&mut export_out,
)
}
egl_export_object_func(disp.cast(), ctx.cast(), &mut export_in, &mut export_out)
}
GLCtx::GLX(disp, ctx) => {
let glx_export_object_func = xplat_manager
.MesaGLInteropGLXExportObject()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
let glx_flush_objects_func = xplat_manager
.MesaGLInteropGLXFlushObjects()?
.ok_or(CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR)?;
let err_flush = glx_flush_objects_func(
disp.cast(),
ctx.cast(),
1,
&mut export_in,
&mut flush_out,
);
if err_flush != 0 {
err_flush
} else {
if fd != -1 {
// TODO: use fence_server_sync in ctx inside the queue thread
let fence_fd = FenceFd { fd };
cl_ctx.devs.iter().for_each(|dev| {
let fence = dev.helper_ctx().import_fence(&fence_fd);
fence.wait();
});
}
glx_export_object_func(
disp.cast(),
ctx.cast(),
&mut export_in,
&mut export_out,
)
}
glx_export_object_func(disp.cast(), ctx.cast(), &mut export_in, &mut export_out)
}
}
};
@ -331,6 +324,16 @@ impl GLCtxManager {
export_out: export_out,
})
}
pub fn flush(&self, mem_objects: &[Mem]) -> CLResult<Option<FenceFd>> {
let mut exports_in = mem_objects
.iter()
.filter_map(|m| m.gl_obj.as_ref())
.map(|gl_obj| gl_obj.props)
.collect::<Vec<_>>();
self.do_flush(&mut exports_in)
}
}
#[derive(Clone)]