From 2702c90d7a39fca2a3319c5e915fd290cb31fc70 Mon Sep 17 00:00:00 2001 From: Radosław Kujawa Date: Fri, 17 Jul 2026 14:54:32 +0200 Subject: Initial import. --- src/mesa/drivers/verite/libv3d.c | 400 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 src/mesa/drivers/verite/libv3d.c (limited to 'src/mesa/drivers/verite/libv3d.c') diff --git a/src/mesa/drivers/verite/libv3d.c b/src/mesa/drivers/verite/libv3d.c new file mode 100644 index 0000000..b09f960 --- /dev/null +++ b/src/mesa/drivers/verite/libv3d.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Radoslaw Kujawa. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* libv3d - thin userland layer over /dev/verite3d. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libv3d.h" + +uint64_t +v3d_now_ns(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; +} + +/* 0 when timing is off: the accumulators just stay zero */ +static uint64_t +v3d_t0(const struct v3d *v) +{ + + return v->opt_stats ? v3d_now_ns() : 0; +} + +int +v3d_open(struct v3d *v, const char *devpath, const char *ucpath) +{ + struct v3d_init vi; + struct v3d_mode vm; + static uint8_t uc[1024 * 1024]; + FILE *f; + size_t sz; + uint32_t w; + int s; + + memset(v, 0, sizeof(*v)); + if (getenv("RLGL_STATS") != NULL) + v->opt_stats = atoi(getenv("RLGL_STATS")); + v->opt_nulldraw = getenv("RLGL_NULLDRAW") != NULL; + v->opt_nosync = getenv("RLGL_NOSYNC") != NULL; + v->opt_noflip = getenv("RLGL_NOFLIP") != NULL; + v->opt_tearflip = getenv("RLGL_TEARFLIP") != NULL; + if (v->opt_nulldraw || v->opt_nosync || v->opt_noflip || + v->opt_tearflip) + fprintf(stderr, "libv3d: EXPERIMENT MODE nulldraw=%d " + "nosync=%d noflip=%d tearflip=%d\n", v->opt_nulldraw, + v->opt_nosync, v->opt_noflip, v->opt_tearflip); + v->fd = open(devpath, O_RDWR); + if (v->fd == -1) { + perror(devpath); + return -1; + } + + f = fopen(ucpath, "rb"); + if (f == NULL) { + perror(ucpath); + goto fail; + } + sz = fread(uc, 1, sizeof(uc), f); + fclose(f); + + vi.vi_ucode = (uint64_t)(uintptr_t)uc; + vi.vi_size = (uint32_t)sz; + if (ioctl(v->fd, V3D_INIT, &vi) == -1) { + perror("V3D_INIT"); + goto fail; + } + v->ctx_base = vi.vi_ctx_base; + v->memsize = vi.vi_memsize; + v->pool_base = vi.vi_pool_base; + + for (s = 0; s < V3D_RING_SLOTS; s++) { + v->slot[s] = mmap(NULL, V3D_SLOT_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, v->fd, + (off_t)s * V3D_SLOT_SIZE); + if (v->slot[s] == MAP_FAILED) { + perror("mmap slot"); + goto fail; + } + } + + /* GL context up; fence it */ + usleep(10000); + v3d_emit1(v, V3D_GL_CTX_INIT); + if (v3d_sync(v) != 0) + goto fail; + + /* mode query fills geometry */ + memset(&vm, 0, sizeof(vm)); + if (ioctl(v->fd, V3D_MODE, &vm) == -1) + goto fail; + v->width = vm.vm_width; + v->height = vm.vm_height; + v->stride = vm.vm_stride; + v->pe_stride = vm.vm_pe_stride; + (void)w; + return 0; +fail: + close(v->fd); + v->fd = -1; + return -1; +} + +void +v3d_close(struct v3d *v) +{ + + if (v->fd == -1) + return; + /* park politely */ + v3d_emit1(v, V3D_GL_EXIT); + (void)v3d_flush(v); + if (v->vram != NULL) { + munmap(v->vram, v->memsize); + v->vram = NULL; + } + close(v->fd); + v->fd = -1; +} + +const uint8_t * +v3d_vram(struct v3d *v) +{ + + if (v->vram == NULL) { + /* RW for span writes (Mesa swrast), RO fallback */ + v->vram = mmap(NULL, v->memsize, PROT_READ | PROT_WRITE, + MAP_SHARED, v->fd, V3D_VRAM_MMAP_OFF); + if (v->vram == MAP_FAILED) + v->vram = mmap(NULL, v->memsize, PROT_READ, + MAP_SHARED, v->fd, V3D_VRAM_MMAP_OFF); + if (v->vram == MAP_FAILED) { + perror("mmap vram"); + v->vram = NULL; + } + } + return v->vram; +} + +void +v3d_emit(struct v3d *v, const uint32_t *w, uint32_t n) +{ + + if (v->n + n > V3D_SLOT_SIZE / 4) { + if (v3d_flush(v) != 0) + return; + } + memcpy(v->slot[v->cur] + v->n, w, n * 4); + v->n += n; + v->st.words += n; +} + +void +v3d_emit1(struct v3d *v, uint32_t w) +{ + + v3d_emit(v, &w, 1); +} + +uint32_t * +v3d_reserve(struct v3d *v, uint32_t nwords) +{ + + if (nwords > V3D_SLOT_SIZE / 4) + return NULL; + if (v->n + nwords > V3D_SLOT_SIZE / 4) { + if (v3d_flush(v) != 0) + return NULL; + } + return v->slot[v->cur] + v->n; +} + +void +v3d_commit(struct v3d *v, uint32_t nwords) +{ + + v->n += nwords; + v->st.words += nwords; +} + +void +v3d_emitf(struct v3d *v, float f) +{ + union { float f; uint32_t u; } u; + + u.f = f; + v3d_emit1(v, u.u); +} + +int +v3d_flush(struct v3d *v) +{ + struct v3d_submit vs; + uint64_t t0; + + if (v->n == 0) + return 0; + vs.vs_slot = (uint32_t)v->cur; + vs.vs_len = v->n * 4; + vs.vs_swap = 1; /* XXX: host-native words */ + v->n = 0; + v->cur = (v->cur + 1) % V3D_RING_SLOTS; + v->st.submits++; + v->st.submit_bytes += vs.vs_len; + if (v->opt_nulldraw) + return 0; + t0 = v3d_t0(v); + if (ioctl(v->fd, V3D_SUBMIT, &vs) == -1) { + perror("V3D_SUBMIT"); + return -1; + } + if (t0 != 0) + v->st.submit_ns += v3d_now_ns() - t0; + return 0; +} + +int +v3d_sync(struct v3d *v) +{ + uint32_t word = V3D_GL_SYNC; + uint64_t t0; + + if (v3d_flush(v) != 0) + return -1; + v->st.syncs++; + if (v->opt_nulldraw) + return 0; + t0 = v3d_t0(v); + if (ioctl(v->fd, V3D_SYNC, &word) == -1) { + perror("V3D_SYNC"); + return -1; + } + if (t0 != 0) + v->st.sync_ns += v3d_now_ns() - t0; + return 0; +} + +int +v3d_alloc(struct v3d *v, uint32_t size, uint32_t align, uint32_t *addr) +{ + struct v3d_alloc va; + + va.va_size = size; + va.va_align = align; + if (ioctl(v->fd, V3D_ALLOC, &va) == -1) + return -1; + *addr = va.va_addr; + return 0; +} + +int +v3d_mode(struct v3d *v, uint32_t depth, uint32_t frame_base) +{ + struct v3d_mode vm; + + memset(&vm, 0, sizeof(vm)); + vm.vm_depth = depth; + vm.vm_frame_base = frame_base; + if (ioctl(v->fd, V3D_MODE, &vm) == -1) + return -1; + v->width = vm.vm_width; + v->height = vm.vm_height; + v->stride = vm.vm_stride; + v->pe_stride = vm.vm_pe_stride; + return 0; +} + +int +v3d_flip(struct v3d *v, uint32_t frame_base) +{ + uint64_t t0; + int r; + + if (v3d_flush(v) != 0) + return -1; + v->st.flips++; + if (v->opt_nulldraw || v->opt_noflip) + return 0; + if (v->opt_tearflip) + frame_base |= V3D_FLIP_NOWAIT; + t0 = v3d_t0(v); + r = ioctl(v->fd, V3D_FLIP, &frame_base); + if (t0 != 0) + v->st.flip_ns += v3d_now_ns() - t0; + return r; +} + +void +v3d_st(struct v3d *v, uint32_t reg, uint32_t val) +{ + uint32_t w[2]; + + w[0] = (1 << 12) | reg; + w[1] = val; + v->st.st_calls++; + v3d_emit(v, w, 2); +} + +void +v3d_texbind(struct v3d *v, uint32_t addr, uint32_t srcstride, + uint32_t w, uint32_t h, uint32_t uscale, uint32_t vscale) +{ + uint32_t c[6]; + + c[0] = (4 << 12) | V3D_PE_SRCBASE; + c[1] = addr; + c[2] = srcstride; + c[3] = ((h - 1) << 16) | (w - 1); + c[4] = uscale; + c[5] = vscale; + v->st.texbinds++; + v3d_emit(v, c, 6); +} + +/* below this many payload bytes of slot room, flush for a fresh slot */ +#define V3D_UPLOAD_MIN 4096 + +int +v3d_upload(struct v3d *v, uint32_t dst, const void *data, uint32_t bytes) +{ + const uint32_t *p = data; + uint32_t chunk, room, hdr[3]; + + v->st.uploads++; + v->st.upload_bytes += bytes; + while (bytes > 0) { + /* + * Batch into the current slot alongside whatever is + * already queued + */ + room = (V3D_SLOT_SIZE / 4 - v->n) * 4; + if (room < V3D_UPLOAD_MIN + 3 * 4) { + if (v3d_flush(v) != 0) + return -1; + room = V3D_SLOT_SIZE; + } + chunk = bytes; + if (chunk > room - 3 * 4) + chunk = room - 3 * 4; + hdr[0] = V3D_GL_MEM_WRITE; + hdr[1] = dst; + hdr[2] = chunk; + v3d_emit(v, hdr, 3); + v3d_emit(v, p, chunk / 4); + p += chunk / 4; + dst += chunk; + bytes -= chunk; + } + return 0; +} + +void +v3d_clear(struct v3d *v, uint32_t addr, uint32_t stride, uint32_t wbytes, + uint32_t h, uint32_t val) +{ + uint32_t c[6]; + + c[0] = V3D_GL_CLEAR_RECT; + c[1] = addr; + c[2] = stride; + c[3] = wbytes; + c[4] = h; + c[5] = val; + v3d_emit(v, c, 6); +} -- cgit