aboutsummaryrefslogtreecommitdiff
path: root/src/mesa/drivers/verite/libv3d.c
diff options
context:
space:
mode:
authorRadosław Kujawa <radoslaw.kujawa@c0ff33.net>2026-07-17 14:54:32 +0200
committerRadosław Kujawa <radoslaw.kujawa@c0ff33.net>2026-07-17 14:54:32 +0200
commit2702c90d7a39fca2a3319c5e915fd290cb31fc70 (patch)
tree283c4d851efef7e3a7cd03ba80c87e2bffcdbb2c /src/mesa/drivers/verite/libv3d.c
parent836167945ec2c7ad5acdf0aa17ce35ec1b9428a6 (diff)
Initial import.
Diffstat (limited to 'src/mesa/drivers/verite/libv3d.c')
-rw-r--r--src/mesa/drivers/verite/libv3d.c400
1 files changed, 400 insertions, 0 deletions
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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#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);
+}