winsys: replace flush_frontbuffer stub with real ADDFB+SCANOUT_FLIP
Replaces the previous stub in flush_frontbuffer with the actual
end-to-end path: ADDFB to create the kernel-side framebuffer on
first use, then REDOX_SCANOUT_FLIP to present it.
Three new functions:
redox_drm_bo_get_fb() in redox_drm_bo.{c,h}: Lazily creates a
kernel framebuffer via the existing
DRM_IOCTL_MODE_ADDFB ioctl (Linux 7.1's
drm_mode_addfb_ioctl, drivers/gpu/drm/
drm_ioctl.c). Caches fb_id and stride on
the BO so the second call is a no-op.
redox_drm_bo_flip_to_crtc() in redox_drm_surface.{c,h}: Calls
bo_get_fb to obtain the cached fb_id,
then issues DRM_IOCTL_MODE_SCANOUT_FLIP
and returns the CS seqno.
flush_frontbuffer in redox_drm_winsys.c: Wires (crtc=0,
resource) into the winsys via
bo_flip_to_crtc and updates
cs->last_seqno so fence modules can
poll for completion.
This mirrors the pattern in radeon_drm_bo::bo_get_tiling +
radeon_drm_surface::bo_set_tiling: a per-resource init helper
called lazily on the first use.
The new fields (fb_id, stride) on redox_drm_bo are documented
in the struct comment, which cross-references the kernel-side
DrmAddFbWire in local/recipes/gpu/redox-drm/source/src/scheme.rs.
Limitation (preserved as comment): ctrc_id is hardcoded to 0.
Mesa's pipe_surface lacks a per-surface crtc_id binding; a later
commit will plumb this through the surface struct.
This closes Phase 6+'s 'real flush_frontbuffer' gap. The
remaining 'real fence' work is Phase 6.4 (real eventfd for
fence_eventfd, replacing the synthetic stand-in).
This commit is contained in:
@@ -122,6 +122,8 @@ redox_drm_bo_create(struct redox_drm_winsys *rws,
|
||||
|
||||
bo->base = *templat;
|
||||
bo->gem_handle = handle;
|
||||
bo->fb_id = 0;
|
||||
bo->stride = 0;
|
||||
bo->size = templat->width0;
|
||||
bo->map_ptr = NULL;
|
||||
|
||||
@@ -171,3 +173,58 @@ redox_drm_bo_unmap(struct redox_drm_winsys *rws,
|
||||
munmap(bo->map_ptr, (size_t)bo->size);
|
||||
bo->map_ptr = NULL;
|
||||
}
|
||||
|
||||
/* Wire struct that matches the kernel's DrmAddFbWire. The kernel's
|
||||
* scheme.rs defines this struct as #[repr(C)].
|
||||
*/
|
||||
struct DrmAddFbWire {
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
uint32_t bpp;
|
||||
uint32_t flags;
|
||||
uint32_t pitch;
|
||||
uint64_t size;
|
||||
uint32_t handle;
|
||||
uint32_t fb_id;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
uint32_t
|
||||
redox_drm_bo_get_fb(struct redox_drm_winsys *rws,
|
||||
struct pipe_resource *pr,
|
||||
uint32_t *out_stride)
|
||||
{
|
||||
struct redox_drm_bo *bo = (struct redox_drm_bo *)pr;
|
||||
struct DrmAddFbWire req = {0};
|
||||
int err;
|
||||
|
||||
if (bo->fb_id != 0) {
|
||||
if (out_stride)
|
||||
*out_stride = bo->stride;
|
||||
return bo->fb_id;
|
||||
}
|
||||
|
||||
req.handle = bo->gem_handle;
|
||||
req.width = (uint32_t)bo->base.width0;
|
||||
req.height = (uint32_t)bo->base.height0;
|
||||
req.bpp = 32;
|
||||
/* pitch = width * 4 (32-bit RGBA) — matches drm_mode_addfb's
|
||||
* alignment behaviour when the userland supplies 0. The kernel
|
||||
* recomputes if pitch is 0.
|
||||
*/
|
||||
req.pitch = req.width * 4;
|
||||
|
||||
err = drmIoctl(rws->fd, /* DRM_IOCTL_MODE_ADDFB = 0xB5 (DRM_IOCTL_BASE+21) */
|
||||
0xB5, &req);
|
||||
if (err != 0) {
|
||||
debug_printf("redox: ADDFB failed for handle=%u: errno=%d (%s)\n",
|
||||
req.handle, errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
bo->fb_id = req.fb_id;
|
||||
bo->stride = req.pitch;
|
||||
if (out_stride)
|
||||
*out_stride = bo->stride;
|
||||
return bo->fb_id;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ struct redox_drm_winsys;
|
||||
struct redox_drm_bo {
|
||||
struct pipe_resource base;
|
||||
uint32_t gem_handle;
|
||||
/* Cached framebuffer id from the kernel's ADDFB ioctl. 0 if no
|
||||
* framebuffer has been created yet. The winsys creates the fb on
|
||||
* first use (from flush_frontbuffer) and reuses it across flips.
|
||||
*/
|
||||
uint32_t fb_id;
|
||||
/* Cached stride for ADDFB. 0 if not yet computed. */
|
||||
uint32_t stride;
|
||||
/* If the user has called resource_map, this is the mmap'd pointer.
|
||||
* NULL otherwise. The user must call resource_unmap to release.
|
||||
*/
|
||||
@@ -52,4 +59,17 @@ void
|
||||
redox_drm_bo_unmap(struct redox_drm_winsys *rws,
|
||||
struct pipe_resource *pr);
|
||||
|
||||
/* Create a kernel-side framebuffer object (ADDFB) for the BO. The
|
||||
* first call sets up the cached fb_id and stride on the BO; subsequent
|
||||
* calls return the cached values. On failure returns 0.
|
||||
*
|
||||
* Models Linux 7.1's drm_mode_addfb_ioctl (drivers/gpu/drm/drm_ioctl.c)
|
||||
* and the winsys-side pattern used by radeon_drm_bo in
|
||||
* src/gallium/winsys/radeon/drm/radeon_drm_bo.c.
|
||||
*/
|
||||
uint32_t
|
||||
redox_drm_bo_get_fb(struct redox_drm_winsys *rws,
|
||||
struct pipe_resource *pr,
|
||||
uint32_t *out_stride);
|
||||
|
||||
#endif /* REDOX_DRM_BO_H */
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
* model mirrors drivers/gpu/drm/drm_plane.c::drm_mode_page_flip_ioctl
|
||||
* (Linux 7.1): the userland caller hands the kernel a framebuffer
|
||||
* handle, the kernel programs the display device, and returns the
|
||||
* CS seqno at which the flip will complete.
|
||||
* CS seqno at which the flip completes.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "redox_drm_surface.h"
|
||||
#include "redox_drm_winsys.h"
|
||||
#include "redox_drm_bo.h"
|
||||
|
||||
#include "pipe/p_state.h"
|
||||
|
||||
@@ -65,3 +66,55 @@ redox_drm_destroy_surface(struct pipe_surface *ps)
|
||||
|
||||
FREE(surf);
|
||||
}
|
||||
|
||||
/* Wire struct that matches the kernel's RedoxScanoutFlipWire.
|
||||
* The struct is #[repr(C)] on the kernel side (scheme.rs).
|
||||
*/
|
||||
struct RedoxScanoutFlipWire {
|
||||
uint32_t crtc_id;
|
||||
uint32_t fb_handle;
|
||||
uint32_t flags;
|
||||
uint32_t pad;
|
||||
uint64_t seqno;
|
||||
};
|
||||
|
||||
/* Wire the winsys to the kernel ADDFB and SCANOUT_FLIP ioctls.
|
||||
* On a pipe_resource bound to a framebuffer, this:
|
||||
* 1. Lazily creates the kernel-side framebuffer via ADDFB
|
||||
* (cached on the redox_drm_bo struct).
|
||||
* 2. Issues REDOX_SCANOUT_FLIP and stashes the resulting seqno.
|
||||
*
|
||||
* Models i915_display.c::i9xx_queue_flip + the gallium
|
||||
* radeon_drm_bo approach.
|
||||
*/
|
||||
uint64_t
|
||||
redox_drm_bo_flip_to_crtc(struct redox_drm_winsys *rws,
|
||||
uint32_t crtc_id,
|
||||
struct pipe_resource *fb)
|
||||
{
|
||||
struct redox_drm_bo *bo = (struct redox_drm_bo *)fb;
|
||||
struct RedoxScanoutFlipWire req = {0};
|
||||
int err;
|
||||
|
||||
if (!rws || !bo)
|
||||
return 0;
|
||||
|
||||
uint32_t fb_id = redox_drm_bo_get_fb(rws, fb, NULL);
|
||||
if (!fb_id) {
|
||||
debug_printf("redox: ADDFB failed for resource, cannot flip\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
req.crtc_id = crtc_id;
|
||||
req.fb_handle = fb_id;
|
||||
req.flags = 0;
|
||||
|
||||
err = drmIoctl(rws->fd, REDOX_DRM_IOCTL_SCANOUT_FLIP, &req);
|
||||
if (err != 0) {
|
||||
debug_printf("redox: SCANOUT_FLIP failed for crtc=%u fb=%u: errno=%d (%s)\n",
|
||||
crtc_id, fb_id, errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return req.seqno;
|
||||
}
|
||||
|
||||
@@ -39,4 +39,14 @@ redox_drm_create_surface(struct pipe_screen *screen,
|
||||
void
|
||||
redox_drm_destroy_surface(struct pipe_surface *ps);
|
||||
|
||||
/* Per-resource flush that wires the BO through ADDFB (cached) and
|
||||
* REDOX_SCANOUT_FLIP. Returns the kernel-assigned CS seqno on
|
||||
* success, 0 on failure. The fence is created by the caller using
|
||||
* redox_drm_fence_create(seqno) which polls the same ring.
|
||||
*/
|
||||
uint64_t
|
||||
redox_drm_bo_flip_to_crtc(struct redox_drm_winsys *rws,
|
||||
uint32_t crtc_id,
|
||||
struct pipe_resource *fb);
|
||||
|
||||
#endif /* REDOX_DRM_SURFACE_H */
|
||||
|
||||
@@ -147,15 +147,42 @@ redox_flush_frontbuffer(struct pipe_screen *screen,
|
||||
struct pipe_box *subbox)
|
||||
{
|
||||
struct redox_drm_winsys *rws = (struct redox_drm_winsys *)screen->winsys_priv;
|
||||
uint32_t crtc_id;
|
||||
|
||||
(void) ctx; (void) resource; (void) level; (void) layer;
|
||||
(void) winsys_drawable_handle; (void) nboxes; (void) subbox;
|
||||
(void) ctx; (void) level; (void) layer; (void) nboxes; (void) subbox;
|
||||
|
||||
/* TODO: bind pipe_resource to a framebuffer, then call
|
||||
* REDOX_SCANOUT_FLIP. Mirrors i915_display.c::i9xx_queue_flip.
|
||||
*/
|
||||
if (!rws)
|
||||
if (!rws || !resource)
|
||||
return;
|
||||
|
||||
/* winsys_drawable_handle is the pipe_surface's internal id; we
|
||||
* don't have a pipe_surface->crtc_id mapping yet. For now, the
|
||||
* Mesa frontend is expected to set up the surface on CRTC 0; a
|
||||
* later commit will plumb per-surface crtc_id through pipe_surface.
|
||||
*/
|
||||
crtc_id = 0;
|
||||
|
||||
/* Lazily create the kernel-side framebuffer (ADDFB) and submit
|
||||
* the scanout flip (SCANOUT_FLIP). Returns the CS seqno; the
|
||||
* caller is expected to track this in a fence.
|
||||
*
|
||||
* Models drm_plane.c::drm_mode_page_flip_ioctl + radeon_drm_bo
|
||||
* + i915_display.c::i9xx_queue_flip.
|
||||
*/
|
||||
uint64_t seqno = redox_drm_bo_flip_to_crtc(rws, crtc_id, resource);
|
||||
if (seqno == 0) {
|
||||
/* SCANOUT_FLIP failed; the caller's fence will never signal.
|
||||
* The flush isn't fatal — Mesa will continue normally — but
|
||||
* the user will see a black scanout. Logged for diagnosis.
|
||||
*/
|
||||
debug_printf("redox: SCANOUT_FLIP failed; scanout will be blank\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the winsys's last_submitted_seqno so the fence module
|
||||
* can poll for completion.
|
||||
*/
|
||||
if (rws->cs && seqno > rws->cs->last_seqno)
|
||||
rws->cs->last_seqno = seqno;
|
||||
}
|
||||
|
||||
static struct pipe_fence_handle *
|
||||
|
||||
Reference in New Issue
Block a user