Files
dmabuf-modifier-triage/probes/expbuf_probe.c
T
marfrit 89a4b81654 iter1 phase 2: hypothesis 3 ruled out by EXPBUF lseek probe
Probe `/tmp/expbuf_probe.c` (snapshot at probes/expbuf_probe.c) opens
/dev/video1, sets OUTPUT format H264_SLICE 1920x1088, REQBUFS 4 capture
buffers, EXPBUF on plane 0 of buffer 0, lseek(fd, 0, SEEK_END).

On ohm (kernel besser-7.0, hantro-vpu / rk3568-vpu-dec):
  CAPTURE: NV12 1920x1088 num_planes=1 sizeimage=3655712
  EXPBUF fd lseek(SEEK_END) = 3657728  (page-rounded from 3655712)

Kernel exports the dma_buf at full sizeimage; offset 2,088,960
(plane 1 base in ffmpeg's drm-frame-descriptor) is well inside.
Hantro is innocent.

Side observation: sizeimage = 3,655,712 > naive NV12's 3,133,440.
The 522,272-byte excess is trailing padding (likely Rockchip
per-frame MV / context metadata) past the UV plane. Y and UV layout
fit cleanly within [0, 3,133,440), exactly where mpv/ffmpeg expect.

Remaining hypothesis space: H1 (panfrost EGL non-zero plane offset),
H2 (KWin wl_dmabuf import), H4 (kwin-fourier residual, low conf).

Next probe queued: H2 source-read of KWin 6.6.4 wl_dmabuf import
path. ~30 min, no hardware needed. If that turns up nothing,
write the EGL importer harness for H1.

Posted to dmabuf-modifier-triage#1 comment 255.
2026-05-08 21:11:09 +00:00

125 lines
4.7 KiB
C

/*
* iter1 hypothesis 3 probe — lseek(SEEK_END) on a hantro CAPTURE EXPBUF fd
*
* Question: does hantro export the dma_buf with size = full sizeimage
* (3,655,712 = 1920*1088*1.5) or capped to Y-plane only (2,088,960 = 1920*1088)?
*
* Decides hypothesis 3 from ~/src/dmabuf-modifier-triage/phase2_iter1_findings.md.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/videodev2.h>
#define DEV "/dev/video1"
#define WIDTH 1920
#define HEIGHT 1088
static int xioctl(int fd, unsigned long req, void *arg, const char *name)
{
int r = ioctl(fd, req, arg);
if (r < 0)
fprintf(stderr, "ioctl %s: %s (errno=%d)\n", name, strerror(errno), errno);
return r;
}
int main(void)
{
int fd = open(DEV, O_RDWR);
if (fd < 0) { perror("open " DEV); return 1; }
struct v4l2_capability cap = {0};
if (xioctl(fd, VIDIOC_QUERYCAP, &cap, "QUERYCAP") < 0) return 2;
printf("driver: %s\n", cap.driver);
printf("card: %s\n", cap.card);
printf("bus_info: %s\n", cap.bus_info);
printf("caps: 0x%08x (M2M_MPLANE=%d)\n",
cap.device_caps,
!!(cap.device_caps & V4L2_CAP_VIDEO_M2M_MPLANE));
/* Set OUTPUT (compressed bitstream) format first.
* Hantro derives CAPTURE size from OUTPUT pixel format + dimensions.
*/
struct v4l2_format ofmt = {0};
ofmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
ofmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264_SLICE;
ofmt.fmt.pix_mp.width = WIDTH;
ofmt.fmt.pix_mp.height = HEIGHT;
ofmt.fmt.pix_mp.num_planes = 1;
ofmt.fmt.pix_mp.plane_fmt[0].sizeimage = 1024*1024; /* compressed buf size */
if (xioctl(fd, VIDIOC_S_FMT, &ofmt, "S_FMT(OUTPUT)") < 0) return 3;
/* Now CAPTURE format. Driver should default to NV12 1920x1088. */
struct v4l2_format cfmt = {0};
cfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
if (xioctl(fd, VIDIOC_G_FMT, &cfmt, "G_FMT(CAPTURE)") < 0) return 4;
printf("CAPTURE: pixfmt=0x%08x (%c%c%c%c) %ux%u num_planes=%u\n",
cfmt.fmt.pix_mp.pixelformat,
cfmt.fmt.pix_mp.pixelformat & 0xff,
(cfmt.fmt.pix_mp.pixelformat >> 8) & 0xff,
(cfmt.fmt.pix_mp.pixelformat >> 16) & 0xff,
(cfmt.fmt.pix_mp.pixelformat >> 24) & 0xff,
cfmt.fmt.pix_mp.width, cfmt.fmt.pix_mp.height,
cfmt.fmt.pix_mp.num_planes);
for (unsigned i = 0; i < cfmt.fmt.pix_mp.num_planes; ++i) {
printf(" plane[%u]: sizeimage=%u bytesperline=%u\n", i,
cfmt.fmt.pix_mp.plane_fmt[i].sizeimage,
cfmt.fmt.pix_mp.plane_fmt[i].bytesperline);
}
/* Try setting NV12 explicitly in case driver wants confirmation. */
cfmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
if (xioctl(fd, VIDIOC_S_FMT, &cfmt, "S_FMT(CAPTURE NV12)") < 0) return 5;
/* REQBUFS CAPTURE — DMABUF memory so we can EXPBUF later. */
struct v4l2_requestbuffers rb = {0};
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
rb.memory = V4L2_MEMORY_MMAP;
rb.count = 4;
if (xioctl(fd, VIDIOC_REQBUFS, &rb, "REQBUFS(CAPTURE)") < 0) return 6;
printf("REQBUFS allocated %u buffers\n", rb.count);
/* QUERYBUF buffer 0 to learn its plane layout. */
struct v4l2_buffer qb = {0};
struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
qb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
qb.memory = V4L2_MEMORY_MMAP;
qb.index = 0;
qb.length = VIDEO_MAX_PLANES;
qb.m.planes = planes;
if (xioctl(fd, VIDIOC_QUERYBUF, &qb, "QUERYBUF") < 0) return 7;
printf("QUERYBUF buf 0: %u planes\n", qb.length);
for (unsigned i = 0; i < qb.length; ++i) {
printf(" plane[%u]: length=%u data_offset=%u m.mem_offset=0x%x\n",
i, planes[i].length, planes[i].data_offset, planes[i].m.mem_offset);
}
/* THE PROBE: VIDIOC_EXPBUF + lseek(SEEK_END). */
for (unsigned i = 0; i < qb.length; ++i) {
struct v4l2_exportbuffer eb = {0};
eb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
eb.index = 0;
eb.plane = i;
eb.flags = O_CLOEXEC | O_RDWR;
if (xioctl(fd, VIDIOC_EXPBUF, &eb, "EXPBUF") < 0) {
fprintf(stderr, " (plane %u EXPBUF failed)\n", i);
continue;
}
off_t end = lseek(eb.fd, 0, SEEK_END);
printf("\n *** plane[%u] EXPBUF fd=%d lseek(SEEK_END)=%lld ***\n",
i, eb.fd, (long long)end);
printf(" Y-plane-only would be: %d\n", WIDTH * HEIGHT);
printf(" full sizeimage would be: %d\n", WIDTH * HEIGHT * 3 / 2);
printf(" QUERYBUF reported: %u\n", planes[i].length);
close(eb.fd);
}
close(fd);
return 0;
}