diff options
Diffstat (limited to 'src/mesa/drivers/verite')
| -rw-r--r-- | src/mesa/drivers/verite/.gdbinit | 41 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/README | 5 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/libv3d.c | 400 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/libv3d.h | 138 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/rlgl.c | 982 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/rlgl.h | 194 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/verite3dio.h | 107 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrapi.c | 319 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrdd.c | 382 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrdrv.h | 125 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrperf.h | 161 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrspan.c | 202 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrtex.c | 501 | ||||
| -rw-r--r-- | src/mesa/drivers/verite/vrtris.c | 1085 |
14 files changed, 4642 insertions, 0 deletions
diff --git a/src/mesa/drivers/verite/.gdbinit b/src/mesa/drivers/verite/.gdbinit new file mode 100644 index 0000000..a735046 --- /dev/null +++ b/src/mesa/drivers/verite/.gdbinit @@ -0,0 +1,41 @@ +# veritemesa perf-counter inspection helpers +# +# Requires libGL.a built with VERITE_INSTRUMENT=1 ! +# +# $ gdbserver :2345 ./glquake +timedemo demo1 +# $ gdb glquake +# (gdb) target remote localhost:2345 +# (gdb) break vrMesaSwapBuffers +# (gdb) run +# (gdb) vrp +# (gdb) vrpwatch + +define vrp + printf "VRPERF(live) frames=%llu\n", g_vrp.frames + printf " fallback: tex=%llu blend=%llu afunc=%llu zfunc=%llu\n", \ + g_vrp.fb[0], g_vrp.fb[1], g_vrp.fb[2], g_vrp.fb[3] + printf " stipple=%llu smooth=%llu twoside=%llu spec=%llu\n", \ + g_vrp.fb[4], g_vrp.fb[5], g_vrp.fb[6], g_vrp.fb[7] + printf " unfilled=%llu rmode=%llu texres=%llu alphagrad=%llu\n", \ + g_vrp.fb[8], g_vrp.fb[9], g_vrp.fb[10], g_vrp.fb[11] + printf " texval: first=%llu evict=%llu dirty=%llu bindonly=%llu failv=%llu\n", \ + g_vrp.tv_first, g_vrp.tv_evict, g_vrp.tv_dirty, g_vrp.tv_bindonly, g_vrp.tv_failv + printf " churn: fgcolor=%llu mipbind=%llu filt=%llu fences=%llu\n", \ + g_vrp.fgcolor_writes, g_vrp.mip_binds, g_vrp.filt_changes, g_vrp.fences +end +document vrp +Dump the veritemesa driver perf counters at the current stop. +end + +# Break at each swap, print the counters, and keep going. +define vrpwatch + break vrMesaSwapBuffers + commands + silent + vrp + continue + end +end +document vrpwatch +Install a breakpoint at vrMesaSwapBuffers that prints g_vrp every frame. +end diff --git a/src/mesa/drivers/verite/README b/src/mesa/drivers/verite/README new file mode 100644 index 0000000..38417fa --- /dev/null +++ b/src/mesa/drivers/verite/README @@ -0,0 +1,5 @@ +veritemesa - Mesa 6.2 driver for the Rendition Verite V2200 + +NetBSD, no X, /dev/verite3d kernel interface. + +Build: configs/netbsd-verite 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); +} diff --git a/src/mesa/drivers/verite/libv3d.h b/src/mesa/drivers/verite/libv3d.h new file mode 100644 index 0000000..4ee44b1 --- /dev/null +++ b/src/mesa/drivers/verite/libv3d.h @@ -0,0 +1,138 @@ +/* + * 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 (verite3dio.h), + * shaped after the RRedline command-buffer model. Uses the v2000gl.uc + * command numbers (sync=8, context_init=2, exit=0) and the decoded + * state-class words. + */ +#ifndef LIBV3D_H +#define LIBV3D_H + +#include <stdint.h> +#include "verite3dio.h" + +/* + * Perf counters + */ +struct v3d_stats { + uint64_t words; /* words emitted into slots */ + uint64_t submits, submit_bytes, submit_ns; + uint64_t syncs, sync_ns; + uint64_t flips, flip_ns; + uint64_t uploads, upload_bytes; + uint64_t st_calls, texbinds; +}; + +struct v3d { + int fd; + uint32_t *slot[V3D_RING_SLOTS]; + uint8_t *vram; /* lazy full-VRAM read mapping */ + int cur; /* slot being filled */ + uint32_t n; /* words used in cur */ + /* geometry from the kernel */ + uint32_t ctx_base, memsize, pool_base; + uint32_t width, height, stride, pe_stride; + /* RLGL_STATS / RLGL_NULLDRAW / RLGL_NOSYNC / RLGL_NOFLIP / + * RLGL_TEARFLIP */ + int opt_stats, opt_nulldraw, opt_nosync, opt_noflip; + int opt_tearflip; + struct v3d_stats st; +}; + +/* GL blob FIFO ABI */ +#define V3D_GL_EXIT 0 +#define V3D_GL_CTX_INIT 2 +#define V3D_GL_SYNC 8 +#define V3D_GL_MEM_WRITE 9 +#define V3D_GL_CLEAR_RECT 13 +#define V3D_GL_LINE 16 /* hw Bresenham line (rdraw2) */ +#define V3D_GL_AALINE 20 /* coverage-graded AA line */ +#define V3D_GL_TRIANGLE 24 +#define V3D_GL_TRISTRIP 26 /* hw tri-strip start (cont word 48) */ +#define V3D_GL_TRIFAN 27 /* hw tri-fan start (cont word 49) */ +#define V3D_GL_AAPOINT 50 /* coverage-graded AA point */ +#define V3D_GL_STRIP_CONT 48 /* tristrip_third_vertex continuation */ +#define V3D_GL_FAN_CONT 49 /* trifan continuation (single vtx) */ + +/* PE register indices (state class 1 writes, value pre-shifted) */ +#define V3D_PE_SRCBASE 0 +#define V3D_PE_STRIDE 3 +#define V3D_PE_DSTBASE 4 +#define V3D_PE_DSTFMT 6 +#define V3D_PE_PMASK 7 +#define V3D_PE_SCISSORX 14 +#define V3D_PE_SCISSORY 15 +#define V3D_PE_ZBASE 16 +#define V3D_PE_FGCOLOR 19 +#define V3D_PE_FOGCOLOR 21 /* 0x00RRGGBB */ +#define V3D_PE_SRCFMT 48 +#define V3D_PE_SRCFUNC 49 /* value << 4 */ +#define V3D_PE_SRCFILTER 50 /* value << 7 */ +#define V3D_PE_ALUMODE 64 /* bits 3:0 */ +#define V3D_PE_BLENDSRCFUNC 65 /* value << 4 */ +#define V3D_PE_BLENDDSTFUNC 66 /* value << 8 */ +#define V3D_PE_ZBUFMODE 67 /* value << 12 */ +#define V3D_PE_ZBUFWRMODE 68 /* value << 16 */ +#define V3D_PE_BLENDENABLE 70 /* value << 19 */ +#define V3D_PE_DITHEREN 71 /* value << 20 */ +#define V3D_PE_FOGEN 72 /* value << 21 */ +#define V3D_PE_DSTRDDIS 74 /* value << 23 */ + +uint64_t v3d_now_ns(void); +int v3d_open(struct v3d *, const char *devpath, const char *ucpath); +void v3d_close(struct v3d *); +/* + * VRAM window + */ +const uint8_t *v3d_vram(struct v3d *); +void v3d_emit(struct v3d *, const uint32_t *, uint32_t); +void v3d_emit1(struct v3d *, uint32_t); +void v3d_emitf(struct v3d *, float); + +uint32_t *v3d_reserve(struct v3d *, uint32_t nwords); +void v3d_commit(struct v3d *, uint32_t nwords); +int v3d_flush(struct v3d *); +int v3d_sync(struct v3d *); +int v3d_alloc(struct v3d *, uint32_t, uint32_t, uint32_t *); +int v3d_mode(struct v3d *, uint32_t depth, uint32_t frame_base); +int v3d_flip(struct v3d *, uint32_t frame_base); + +/* state class 1: single PE register write (pre-shifted value) */ +void v3d_st(struct v3d *, uint32_t reg, uint32_t val); +/* state class 4: full texture bind */ +void v3d_texbind(struct v3d *, uint32_t addr, uint32_t srcstride, + uint32_t w, uint32_t h, uint32_t uscale, uint32_t vscale); +/* mem_write upload (chunks as needed) and engine-side clear */ +int v3d_upload(struct v3d *, uint32_t dst, const void *, uint32_t); +void v3d_clear(struct v3d *, uint32_t addr, uint32_t stride, + uint32_t wbytes, uint32_t h, uint32_t val); + +#endif /* LIBV3D_H */ diff --git a/src/mesa/drivers/verite/rlgl.c b/src/mesa/drivers/verite/rlgl.c new file mode 100644 index 0000000..1da14f2 --- /dev/null +++ b/src/mesa/drivers/verite/rlgl.c @@ -0,0 +1,982 @@ +/* + * 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. + */ + +#include <sys/ioctl.h> +#include <stdio.h> +#include <string.h> + +#include "rlgl.h" +#include "rlvec.h" + +int rlgl_force_rebind = 0; + +/* + * Line-bytes -> encoded stride nibble pair + */ +static uint32_t +stride_code(uint32_t linebytes) +{ + static const struct { uint32_t bytes, s0, s1; } tab[] = { + { 16, 0, 1 }, { 32, 0, 2 }, { 64, 0, 3 }, { 128, 0, 4 }, + { 256, 1, 0 }, { 512, 2, 0 }, { 1024, 3, 0 }, + { 2048, 0, 6 }, { 4096, 0, 7 }, + }; + unsigned i; + + for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++) + if (tab[i].bytes == linebytes) + return (tab[i].s1 << 4) | tab[i].s0; + return (uint32_t)-1; +} + +int +rlgl_init(struct rlgl *rl, const char *ucpath) +{ + struct v3d *v = &rl->v; + + memset(rl, 0, sizeof(*rl)); + rlvec_init(); + if (v3d_open(v, "/dev/verite3d", ucpath) != 0) + return -1; + if (v3d_mode(v, 16, 0) != 0) + return -1; + if (v3d_alloc(v, v->stride * v->height, 4096, &rl->fb[0]) != 0 || + v3d_alloc(v, v->stride * v->height, 4096, &rl->fb[1]) != 0 || + v3d_alloc(v, v->stride * v->height, 4096, &rl->zbuf) != 0) + return -1; + /* tiny throwaway surface for the glCopyPixels source-read seed + * (128px * 8 rows * 2B = 2KB, addressed at a 256B stride) */ + if (v3d_alloc(v, 128 * 8 * 2, 4096, &rl->scratch) != 0) + return -1; + + /* constant state */ + v3d_st(v, V3D_PE_DSTFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SCISSORX, v->width); + v3d_st(v, V3D_PE_SCISSORY, v->height); + v3d_st(v, V3D_PE_ALUMODE, 0xC); + v3d_st(v, V3D_PE_DSTRDDIS, 0); + v3d_st(v, V3D_PE_ZBASE, rl->zbuf); + /* + * Zero every mode the GL microcode's context_init leaves + * undefined + */ + v3d_st(v, 69, 0); /* YUV2RGB */ + v3d_st(v, V3D_PE_DITHEREN, 0); /* DitherEnable */ + v3d_st(v, 72, 0); /* FogEnable */ + v3d_st(v, 73, 0); /* DstColorNoPad */ + v3d_st(v, 75, 0); /* DstBGR */ + v3d_st(v, 76, 0); /* TranspReject */ + v3d_st(v, 55, 0); /* SrcBGR */ + v3d_st(v, 81, 0); /* PatEnable */ + v3d_st(v, 83, 0); /* SpecularEn */ + v3d_st(v, 62, 0); /* UClamp (wrap) */ + v3d_st(v, 63, 0); /* VClamp (wrap) */ + v3d_st(v, V3D_PE_PMASK, 0xFFFFFFFF); /* all planes */ + /* dst + Z stride identical, src patched by tex_bind */ + v3d_st(v, V3D_PE_STRIDE, v->pe_stride | + ((v->pe_stride >> 8) << 16)); + v3d_st(v, V3D_PE_DSTBASE, rl->fb[0]); + rl->cur_tex = (uint32_t)-1; + rl->cur_func = -1; + rl->env_mod = RLGL_ENV_REPLACE; + rl->fog_on = 0; + rlgl_depth_test(rl, 0); + rlgl_depth_mask(rl, 1); + rlgl_blend(rl, 0, 0, 0); + rlgl_alpha_test(rl, 0, 0); + rlgl_tex_bind(rl, NULL); + + /* + * Claim the glass: clear both color buffers + Z, then point + * scanout at the FRONT buffer. + */ + v3d_clear(v, rl->fb[0], v->stride, v->width * 2, v->height, 0); + v3d_clear(v, rl->fb[1], v->stride, v->width * 2, v->height, 0); + v3d_clear(v, rl->zbuf, v->stride, v->width * 2, v->height, + 0xFFFFFFFFU); + if (v3d_sync(v) != 0) + return -1; + return v3d_flip(v, rl->fb[rl->back ^ 1]); +} + +void +rlgl_shutdown(struct rlgl *rl) +{ + + rlgl_stats_print(rl); + v3d_close(&rl->v); +} + +int +rlgl_tex_init(struct rlgl_tex *t, int w, int h, uint32_t fmt) +{ + uint32_t code = stride_code((uint32_t)w * 2); + + if (code == (uint32_t)-1 || (w & (w - 1)) || (h & (h - 1))) + return -1; + t->w = (uint16_t)w; + t->h = (uint16_t)h; + t->fmt = fmt; + t->srcstride = code; + t->addr = 0; + return 0; +} + +static uint32_t +tex_align(uint32_t w) +{ + uint32_t a = w * 2; + + return a < 64 ? 64 : a; +} + +int +rlgl_tex_alloc(struct rlgl *rl, struct rlgl_tex *t) +{ + + return v3d_alloc(&rl->v, (uint32_t)t->w * t->h * 2, + tex_align(t->w), &t->addr); +} + +/* + * Allocate a whole mip pyramid as ONE contiguous VRAM block, + * like 3dfx does. + */ +int +rlgl_tex_pyramid(struct rlgl *rl, struct rlgl_tex *levs, int nlev) +{ + uint32_t total = 0, base; + int i; + + for (i = 0; i < nlev; i++) { + uint32_t a = tex_align(levs[i].w); + + total = (total + a - 1) & ~(a - 1); + total += (uint32_t)levs[i].w * (uint32_t)levs[i].h * 2; + } + if (v3d_alloc(&rl->v, total, tex_align(levs[0].w), &base) != 0) + return -1; + total = 0; + for (i = 0; i < nlev; i++) { + uint32_t a = tex_align(levs[i].w); + + total = (total + a - 1) & ~(a - 1); + levs[i].addr = base + total; + total += (uint32_t)levs[i].w * (uint32_t)levs[i].h * 2; + } + return 0; +} + +int +rlgl_tex_create(struct rlgl *rl, struct rlgl_tex *t, int w, int h, + uint32_t fmt) +{ + + if (rlgl_tex_init(t, w, h, fmt) != 0) + return -1; + return rlgl_tex_alloc(rl, t); +} + +int +rlgl_tex_upload_rows(struct rlgl *rl, struct rlgl_tex *t, + const uint16_t *texels, int y, int rows) +{ + static uint32_t buf[512 * 512 / 2]; + uint32_t n = (uint32_t)t->w * (uint32_t)rows; + uint32_t base = (uint32_t)t->w * (uint32_t)y; + uint32_t i; + + if (n / 2 > sizeof(buf) / 4) + return -1; + for (i = 0; i < n; i += 2) + buf[i / 2] = ((uint32_t)texels[base + i] << 16) | + (uint32_t)texels[base + i + 1]; + return v3d_upload(&rl->v, t->addr + base * 2, buf, n * 2); +} + +int +rlgl_tex_upload(struct rlgl *rl, struct rlgl_tex *t, const uint16_t *texels) +{ + + return rlgl_tex_upload_rows(rl, t, texels, 0, t->h); +} + +static void +set_srcfunc(struct rlgl *rl, int func) +{ + + if (rlgl_force_rebind || rl->cur_func != func) { + v3d_st(&rl->v, V3D_PE_SRCFUNC, (uint32_t)func << 4); + rl->cur_func = func; + } +} + +void +rlgl_tex_bind(struct rlgl *rl, const struct rlgl_tex *t) +{ + struct v3d *v = &rl->v; + + if (t == NULL) { + set_srcfunc(rl, 0); /* NOTEXTURE */ + return; + } + if (rlgl_force_rebind || rl->cur_tex != t->addr) { + v3d_texbind(v, t->addr, t->srcstride, t->w, t->h, + 0x10000, 0x10000); + v3d_st(v, V3D_PE_SRCFMT, t->fmt); + rl->cur_tex = t->addr; + } + set_srcfunc(rl, rl->env_mod); +} + +void +rlgl_tex_env(struct rlgl *rl, int modulate) +{ + + rlgl_tex_env_code(rl, modulate ? RLGL_ENV_MODULATE : + RLGL_ENV_REPLACE); +} + +void +rlgl_tex_env_code(struct rlgl *rl, int code) +{ + + rl->env_mod = code; + if (rl->cur_func != 0) /* texturing active: apply now */ + set_srcfunc(rl, code); +} + +void +rlgl_tex_wrap(struct rlgl *rl, int uclamp, int vclamp) +{ + + v3d_st(&rl->v, 62, uclamp ? 1 << 15 : 0); /* UClamp */ + v3d_st(&rl->v, 63, vclamp ? 1 << 16 : 0); /* VClamp */ +} + +void +rlgl_tex_free(struct rlgl *rl, struct rlgl_tex *t) +{ + uint32_t addr = t->addr; + + if (addr == 0) + return; + if (rl->cur_tex == addr) + rl->cur_tex = (uint32_t)-1; + (void)ioctl(rl->v.fd, V3D_FREE, &addr); + t->addr = 0; +} + +void +rlgl_tex_filter(struct rlgl *rl, int bilinear) +{ + + v3d_st(&rl->v, V3D_PE_SRCFILTER, bilinear ? 1 << 7 : 0); +} + +void +rlgl_depth_test(struct rlgl *rl, int enable) +{ + + /* LE keeps coplanar multi-pass (lightmaps) stable */ + rlgl_depth_func(rl, enable, RLGL_Z_LE); +} + +void +rlgl_depth_func(struct rlgl *rl, int enable, int pe_code) +{ + + v3d_st(&rl->v, V3D_PE_ZBUFMODE, enable ? pe_code << 12 : 0); +} + +void +rlgl_depth_mask(struct rlgl *rl, int write) +{ + + v3d_st(&rl->v, V3D_PE_ZBUFWRMODE, write ? 1 << 16 : 0); +} + +void +rlgl_blend(struct rlgl *rl, int enable, uint32_t srcf, uint32_t dstf) +{ + struct v3d *v = &rl->v; + + v3d_st(v, V3D_PE_BLENDENABLE, enable ? 1 << 19 : 0); + if (enable) { + v3d_st(v, V3D_PE_BLENDSRCFUNC, srcf << 4); + v3d_st(v, V3D_PE_BLENDDSTFUNC, dstf << 8); + } +} + +void +rlgl_alpha_test(struct rlgl *rl, int enable, uint32_t thresh8) +{ + struct v3d *v = &rl->v; + + /* TranspReject: reject texel alpha <= AlphaThres */ + v3d_st(v, 76, enable ? 1 << 25 : 0); + if (enable) + v3d_st(v, 29, thresh8); /* AlphaThres */ +} + +/* + * Hardware scissor. + */ +void +rlgl_scissor(struct rlgl *rl, int x, int y, int w, int h) +{ + struct v3d *v = &rl->v; + int W = v->width, H = v->height; + int left = x, right = x + w; + int top = H - (y + h), bottom = H - y; + + if (left < 0) left = 0; if (left > W) left = W; + if (right < 0) right = 0; if (right > W) right = W; + if (top < 0) top = 0; if (top > H) top = H; + if (bottom < 0) bottom = 0; if (bottom > H) bottom = H; + if (right < left) right = left; /* off-screen -> empty box */ + if (bottom < top) bottom = top; + v3d_st(v, V3D_PE_SCISSORX, ((uint32_t)left << 16) | (uint32_t)right); + v3d_st(v, V3D_PE_SCISSORY, ((uint32_t)top << 16) | (uint32_t)bottom); +} + +/* + * Hardware color mask + */ +void +rlgl_color_mask(struct rlgl *rl, int r, int g, int b) +{ + uint32_t m = (r ? 0xF800u : 0) | (g ? 0x07E0u : 0) | (b ? 0x001Fu : 0); + + v3d_st(&rl->v, V3D_PE_PMASK, m | (m << 16)); +} + +/* + * Hardware logic op (glLogicOp). + */ +void +rlgl_logic_op(struct rlgl *rl, int enable, int code) +{ + v3d_st(&rl->v, V3D_PE_ALUMODE, enable ? (uint32_t)(code & 0xF) : 0xC); +} + +/* + * Hardware dither (GL_DITHER). + */ +void +rlgl_dither(struct rlgl *rl, int enable) +{ + v3d_st(&rl->v, V3D_PE_DITHEREN, enable ? (1u << 20) : 0); +} + +/* + * Per-vertex fog (GL_FOG). + */ +void +rlgl_fog(struct rlgl *rl, int enable, uint32_t rgb888) +{ + v3d_st(&rl->v, V3D_PE_FOGCOLOR, rgb888 & 0x00FFFFFFu); + v3d_st(&rl->v, V3D_PE_FOGEN, enable ? (1u << 21) : 0); + rl->fog_on = enable ? 1 : 0; +} + +static int +stride_codes(uint32_t bytes, uint32_t *s0code, uint32_t *s1code) +{ + static const struct { uint32_t code, bytes; } s0[] = { + { 0, 0 }, { 4, 4 }, { 1, 256 }, { 2, 512 }, { 3, 1024 }, + }; + static const struct { uint32_t code, bytes; } s1[] = { + { 0, 0 }, { 1, 16 }, { 2, 32 }, { 3, 64 }, { 4, 128 }, + { 5, 1024 }, { 6, 2048 }, { 7, 4096 }, + }; + unsigned i, j; + + for (i = 0; i < sizeof(s0) / sizeof(s0[0]); i++) + for (j = 0; j < sizeof(s1) / sizeof(s1[0]); j++) + if (s0[i].bytes + s1[j].bytes == bytes) { + *s0code = s0[i].code; + *s1code = s1[j].code; + return 0; + } + return -1; +} + +int +rlgl_copy_rect(struct rlgl *rl, uint32_t src_base, int src_w, + uint32_t dst_base, int dst_w, int sx, int sy, int dx, int dy, + int w, int h) +{ + struct v3d *v = &rl->v; + uint32_t ss0, ss1, ds0, ds1, reg58, reg59, sbase, c[6], b[4]; + static const float seed[3][9] = { + { 255, 255, 255, 0, 0, 100, 0, 0, 1 }, + { 255, 255, 255, 2, 0, 100, 0, 0, 1 }, + { 255, 255, 255, 0, 2, 100, 0, 0, 1 }, + }; + int i, k; + + if (w <= 0 || h <= 0) + return 0; + if (stride_codes((uint32_t)src_w * 2, &ss0, &ss1) != 0 || + stride_codes((uint32_t)dst_w * 2, &ds0, &ds1) != 0) + return -1; + reg58 = (ss1 << 4) | ss0; /* SrcStride subfields */ + reg59 = (ds1 << 12) | (ds0 << 8); /* DstStride subfields */ + sbase = src_base + (uint32_t)sy * ((uint32_t)src_w * 2) + + (uint32_t)sx * 2; + + c[0] = (4 << 12) | V3D_PE_SRCBASE; /* class-4 texture bind */ + c[1] = sbase; + c[2] = reg58; + c[3] = (0x7FFu << 16) | 0x7FFu; /* UMask/VMask big (no wrap) */ + c[4] = 0x10000; /* uscale 1.0 */ + c[5] = 0x10000; /* vscale 1.0 */ + v3d_emit(v, c, 6); + v3d_st(v, V3D_PE_SRCFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SRCFUNC, 1 << 4); /* REPLACE (copy source) */ + v3d_st(v, V3D_PE_SRCFILTER, 0); /* point sample / BitBlt */ + v3d_st(v, V3D_PE_DSTBASE, rl->scratch); + v3d_st(v, 59, 0x0100); /* scratch DstStride 256B */ + v3d_st(v, V3D_PE_DSTFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | 128); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | 8); + v3d_st(v, V3D_PE_ALUMODE, 0xC); + v3d_st(v, V3D_PE_ZBUFMODE, 0); + v3d_st(v, V3D_PE_ZBUFWRMODE, 0); + v3d_emit1(v, (20 << 16) | V3D_GL_TRIANGLE); /* KXYZUVQ */ + for (i = 0; i < 3; i++) + for (k = 0; k < 9; k++) + v3d_emitf(v, seed[i][k]); + + v3d_st(v, V3D_PE_DSTBASE, dst_base); + v3d_st(v, 59, reg59); /* DstStride dual-shift */ + v3d_st(v, V3D_PE_DSTFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | (uint32_t)dst_w); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | (uint32_t)v->height); + v3d_st(v, V3D_PE_PMASK, 0xFFFFFFFF); + v3d_st(v, 62, 0); /* UClamp off */ + v3d_st(v, 63, 0); /* VClamp off */ + v3d_st(v, V3D_PE_ALUMODE, 0xC); + v3d_st(v, V3D_PE_BLENDENABLE, 0); + v3d_st(v, V3D_PE_DSTRDDIS, 0); + v3d_st(v, 76, 0); /* TranspReject off */ + b[0] = 30; /* microcode bitblt */ + b[1] = ((uint32_t)dx << 16) | (uint32_t)dy; + b[2] = ((uint32_t)w << 16) | (uint32_t)h; + b[3] = 0; /* srcX=srcY=0 (base pre-offset) */ + v3d_emit(v, b, 4); + + v3d_st(v, V3D_PE_STRIDE, v->pe_stride | ((v->pe_stride >> 8) << 16)); + v3d_st(v, V3D_PE_DSTBASE, rl->fb[rl->back]); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | v->width); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | v->height); + rl->cur_tex = (uint32_t)-1; + rl->cur_func = -1; + return 0; +} + +int +rlgl_copy_to_tex(struct rlgl *rl, uint32_t fb_base, int fb_h, + uint32_t tex_base, int tex_aw, int sx, int sy_gl, int dx, int dy, + int w, int h) +{ + struct v3d *v = &rl->v; + uint32_t fs0, fs1, ts0, ts1, reg58, reg59, c[6], b[4]; + static const float seed[3][9] = { + { 255, 255, 255, 0, 0, 100, 0, 0, 1 }, + { 255, 255, 255, 2, 0, 100, 0, 0, 1 }, + { 255, 255, 255, 0, 2, 100, 0, 0, 1 }, + }; + int i, k, j; + + if (w <= 0 || h <= 0) + return 0; + if (stride_codes((uint32_t)v->width * 2, &fs0, &fs1) != 0 || + stride_codes((uint32_t)tex_aw * 2, &ts0, &ts1) != 0) + return -1; + reg58 = (fs1 << 4) | fs0; /* fb SrcStride */ + reg59 = (ts1 << 12) | (ts0 << 8); /* texture DstStride */ + + c[0] = (4 << 12) | V3D_PE_SRCBASE; + c[1] = fb_base; + c[2] = reg58; + c[3] = (0x7FFu << 16) | 0x7FFu; + c[4] = 0x10000; + c[5] = 0x10000; + v3d_emit(v, c, 6); + v3d_st(v, V3D_PE_SRCFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SRCFUNC, 1 << 4); /* REPLACE */ + v3d_st(v, V3D_PE_SRCFILTER, 0); /* point / BitBlt */ + v3d_st(v, V3D_PE_DSTBASE, rl->scratch); + v3d_st(v, 59, 0x0100); /* scratch DstStride 256B */ + v3d_st(v, V3D_PE_DSTFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | 128); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | 8); + v3d_st(v, V3D_PE_ALUMODE, 0xC); + v3d_st(v, V3D_PE_ZBUFMODE, 0); + v3d_st(v, V3D_PE_ZBUFWRMODE, 0); + v3d_emit1(v, (20 << 16) | V3D_GL_TRIANGLE); + for (i = 0; i < 3; i++) + for (k = 0; k < 9; k++) + v3d_emitf(v, seed[i][k]); + + /* dest state = the texture surface */ + v3d_st(v, V3D_PE_DSTBASE, tex_base); + v3d_st(v, 59, reg59); + v3d_st(v, V3D_PE_DSTFMT, RLGL_FMT_565); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | (uint32_t)tex_aw); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | (uint32_t)(dy + h)); + v3d_st(v, V3D_PE_PMASK, 0xFFFFFFFF); + v3d_st(v, 62, 0); + v3d_st(v, 63, 0); + v3d_st(v, V3D_PE_ALUMODE, 0xC); + v3d_st(v, V3D_PE_BLENDENABLE, 0); + v3d_st(v, V3D_PE_DSTRDDIS, 0); + v3d_st(v, 76, 0); + + for (j = 0; j < h; j++) { + b[0] = 30; + b[1] = ((uint32_t)dx << 16) | (uint32_t)(dy + j); + b[2] = ((uint32_t)w << 16) | 1u; + b[3] = ((uint32_t)sx << 16) | + (uint32_t)(fb_h - 1 - sy_gl - j); + v3d_emit(v, b, 4); + } + + /* restore persistent draw state (see rlgl_copy_rect) */ + v3d_st(v, V3D_PE_STRIDE, v->pe_stride | ((v->pe_stride >> 8) << 16)); + v3d_st(v, V3D_PE_DSTBASE, rl->fb[rl->back]); + v3d_st(v, V3D_PE_SCISSORX, (0u << 16) | v->width); + v3d_st(v, V3D_PE_SCISSORY, (0u << 16) | v->height); + rl->cur_tex = (uint32_t)-1; + rl->cur_func = -1; + return 0; +} + +void +rlgl_clear(struct rlgl *rl, int color, uint32_t rgb565, int depth) +{ + struct v3d *v = &rl->v; + uint32_t val = rgb565 | (rgb565 << 16); + + /* spec 2.8: clamps must be off for parallel-draw fills */ + rlgl_tex_wrap(rl, 0, 0); + if (color) + v3d_clear(v, rl->fb[rl->back], v->stride, v->width * 2, + v->height, val); + if (depth) + v3d_clear(v, rl->zbuf, v->stride, v->width * 2, + v->height, 0xFFFFFFFFU); +} + +/* + * Blit one row of w 565 pixels to the back buffer at window (x, wy). + */ +int +rlgl_draw_row(struct rlgl *rl, int x, int wy, int w, const uint16_t *row565) +{ + struct v3d *v = &rl->v; + static uint32_t buf[2048 / 2]; + uint32_t fbrow; + int i; + + if ((x & 1) || (w & 1) || w <= 0 || w > 2048 || + wy < 0 || wy >= (int)v->height || x < 0 || x + w > (int)v->width) + return -1; + fbrow = (uint32_t)((int)v->height - 1 - wy); + for (i = 0; i < w; i += 2) + buf[i / 2] = ((uint32_t)row565[i] << 16) | (uint32_t)row565[i + 1]; + return v3d_upload(v, rl->fb[rl->back] + fbrow * v->stride + + (uint32_t)x * 2, buf, (uint32_t)w * 2); +} + +void +rlgl_tri(struct rlgl *rl, const struct rlgl_vtx *a, + const struct rlgl_vtx *b, const struct rlgl_vtx *c) +{ + struct v3d *v = &rl->v; + const struct rlgl_vtx *p[3]; + int i; + + p[0] = a; p[1] = b; p[2] = c; + rl->tris++; + /* + * KXYZUVQ (type 20) normally, with fog on, KFXYZUVQ (type 23) + */ + v3d_emit1(v, ((rl->fog_on ? 23 : 20) << 16) | V3D_GL_TRIANGLE); + for (i = 0; i < 3; i++) { + v3d_emitf(v, p[i]->r); + v3d_emitf(v, p[i]->g); + v3d_emitf(v, p[i]->b); + if (rl->fog_on) + v3d_emitf(v, p[i]->f); + v3d_emitf(v, p[i]->x); + v3d_emitf(v, p[i]->y); + v3d_emitf(v, p[i]->z); + v3d_emitf(v, p[i]->u); + v3d_emitf(v, p[i]->v); + v3d_emitf(v, p[i]->q); + } +} + +/* + * Native hardware lines. + */ +static void +rlgl_emit_line(struct rlgl *rl, uint32_t cmd, const struct rlgl_vtx *a, + const struct rlgl_vtx *b) +{ + struct v3d *v = &rl->v; + const struct rlgl_vtx *p[2]; + int i; + + p[0] = a; p[1] = b; + v3d_emit1(v, (8 << 16) | cmd); /* KXYZ (type 8) */ + for (i = 0; i < 2; i++) { + v3d_emitf(v, p[i]->r); + v3d_emitf(v, p[i]->g); + v3d_emitf(v, p[i]->b); + v3d_emitf(v, p[i]->x); + v3d_emitf(v, p[i]->y); + v3d_emitf(v, p[i]->z); + } +} + +void +rlgl_line(struct rlgl *rl, const struct rlgl_vtx *a, const struct rlgl_vtx *b) +{ + + rl->tris++; + rlgl_emit_line(rl, V3D_GL_LINE, a, b); +} + +void +rlgl_aaline(struct rlgl *rl, const struct rlgl_vtx *a, const struct rlgl_vtx *b) +{ + + rl->tris++; + rlgl_emit_line(rl, V3D_GL_AALINE, a, b); +} + +/* + * Anti-aliased points (GL_POINT_SMOOTH) via microcode aapoint (cmd 50). + */ +#define RLGL_PTTEX_DIM 32 +#define RLGL_PTTEX_USCALE 0x1F8000 /* ~31.5 in 16.16 -> span [0,1] */ + +static int +rlgl_pt_tex_ensure(struct rlgl *rl) +{ + static uint16_t tex[RLGL_PTTEX_DIM * RLGL_PTTEX_DIM]; + const float c = RLGL_PTTEX_DIM / 2.0f - 0.5f; /* texel centre */ + const float rad = RLGL_PTTEX_DIM / 2.0f - 1.5f; /* disc radius */ + int x, y; + + if (rl->pt_tex.addr != 0) + return 0; + if (rlgl_tex_create(rl, &rl->pt_tex, RLGL_PTTEX_DIM, RLGL_PTTEX_DIM, + RLGL_FMT_4444) != 0) + return -1; + for (y = 0; y < RLGL_PTTEX_DIM; y++) + for (x = 0; x < RLGL_PTTEX_DIM; x++) { + float dx = (float)x - c, dy = (float)y - c; + float d2 = dx * dx + dy * dy; /* no sqrt: compare r^2 */ + int a; + + /* + * Radial alpha: opaque core, a several-texel linear + * coverage ramp at the rim + */ + const float ramp = 3.0f; /* rim width, texels */ + if (d2 <= (rad - ramp) * (rad - ramp)) + a = 15; /* opaque core */ + else if (d2 >= rad * rad) + a = 0; /* transparent */ + else { + /* linear in true distance across [rad-ramp,rad] */ + float d = (d2 - (rad - ramp) * (rad - ramp)) / + ((rad * rad) - (rad - ramp) * (rad - ramp)); + a = (int)(15.0f * (1.0f - d) + 0.5f); + if (a < 0) a = 0; + if (a > 15) a = 15; + } + /* ARGB4444: alpha = coverage, RGB = white */ + tex[y * RLGL_PTTEX_DIM + x] = + (uint16_t)((a << 12) | 0x0FFF); + } + return rlgl_tex_upload(rl, &rl->pt_tex, tex); +} + +void +rlgl_aapoint_begin(struct rlgl *rl) +{ + struct v3d *v = &rl->v; + static const float seed[3][9] = { + { 255, 255, 255, 1, 1, 100, 0, 0, 1 }, + { 255, 255, 255, 3, 1, 100, 0, 0, 1 }, + { 255, 255, 255, 1, 3, 100, 0, 0, 1 }, + }; + int i, k; + + if (rlgl_pt_tex_ensure(rl) != 0) + return; + /* bind the coverage texture, uscale/vscale map [0,1] over any size */ + v3d_texbind(v, rl->pt_tex.addr, rl->pt_tex.srcstride, + rl->pt_tex.w, rl->pt_tex.h, RLGL_PTTEX_USCALE, RLGL_PTTEX_USCALE); + v3d_st(v, V3D_PE_SRCFMT, RLGL_FMT_4444); + v3d_st(v, V3D_PE_SRCFILTER, 1 << 7); /* bilinear: smooth the rim */ + rl->cur_tex = rl->pt_tex.addr; + set_srcfunc(rl, RLGL_ENV_MODULATE); /* colour = FGColor x white tex, + * SrcAlpha = TexAlpha (coverage) + * x FGColor-alpha (0xff) */ + v3d_st(v, V3D_PE_ZBUFMODE, 0); + v3d_st(v, V3D_PE_ZBUFWRMODE, 0); + v3d_emit1(v, (20 << 16) | V3D_GL_TRIANGLE); /* KXYZUVQ */ + for (i = 0; i < 3; i++) + for (k = 0; k < 9; k++) + v3d_emitf(v, seed[i][k]); +} + +void +rlgl_aapoint(struct rlgl *rl, const struct rlgl_vtx *a, float size) +{ + struct v3d *v = &rl->v; + int r = (int)(a->r + 0.5f), g = (int)(a->g + 0.5f); + int b = (int)(a->b + 0.5f); + uint32_t fg; + + if (r < 0) r = 0; if (r > 255) r = 255; + if (g < 0) g = 0; if (g > 255) g = 255; + if (b < 0) b = 0; if (b > 255) b = 255; + /* FGColor 0xAARRGGBB: alpha 0xff so MODULATE SrcAlpha = TexAlpha */ + fg = 0xFF000000u | ((uint32_t)r << 16) | ((uint32_t)g << 8) | + (uint32_t)b; + rl->tris++; + v3d_st(v, V3D_PE_FGCOLOR, fg); + v3d_emit1(v, (8 << 16) | V3D_GL_AAPOINT); /* KXYZ */ + v3d_emitf(v, size); /* diameter */ + v3d_emitf(v, a->r); v3d_emitf(v, a->g); v3d_emitf(v, a->b); + v3d_emitf(v, a->x); v3d_emitf(v, a->y); v3d_emitf(v, a->z); +} + +void +rlgl_aapoint_end(struct rlgl *rl) +{ + + /* release the coverage-texture bind: force the next textured draw to + * rebind (which re-selects its own filter/env), and untextured draws + * to re-select NOTEXTURE; drop the bilinear filter we forced. */ + v3d_st(&rl->v, V3D_PE_SRCFILTER, 0); + rl->cur_tex = 0; + set_srcfunc(rl, 0); +} + +/* + * Emit one KXYZUVQ (or KFXYZUVQ when fog_on) vertex record + */ +static void +rlgl_emit_vtx(struct v3d *v, const struct rlgl_vtx *p, int fog_on) +{ + + v3d_emitf(v, p->r); + v3d_emitf(v, p->g); + v3d_emitf(v, p->b); + if (fog_on) + v3d_emitf(v, p->f); + v3d_emitf(v, p->x); + v3d_emitf(v, p->y); + v3d_emitf(v, p->z); + v3d_emitf(v, p->u); + v3d_emitf(v, p->v); + v3d_emitf(v, p->q); +} + +/* + * Native strip/fan common core. + */ +static void +rlgl_emit_run(struct rlgl *rl, uint32_t startcmd, uint32_t contword, + const struct rlgl_vtx *v, int n) +{ + struct v3d *vv = &rl->v; + int i; + + if (n < 3) + return; + v3d_emit1(vv, ((rl->fog_on ? 23 : 20) << 16) | startcmd); + rlgl_emit_vtx(vv, &v[0], rl->fog_on); + rlgl_emit_vtx(vv, &v[1], rl->fog_on); + rlgl_emit_vtx(vv, &v[2], rl->fog_on); + for (i = 3; i < n; i++) { + v3d_emit1(vv, contword); + rlgl_emit_vtx(vv, &v[i], rl->fog_on); + } + rl->tris += (uint64_t)(n - 2); +} + +void +rlgl_tristrip(struct rlgl *rl, const struct rlgl_vtx *v, int n) +{ + + rlgl_emit_run(rl, V3D_GL_TRISTRIP, V3D_GL_STRIP_CONT, v, n); +} + +void +rlgl_trifan(struct rlgl *rl, const struct rlgl_vtx *v, int n) +{ + + rlgl_emit_run(rl, V3D_GL_TRIFAN, V3D_GL_FAN_CONT, v, n); +} + +void +rlgl_tris_idx(struct rlgl *rl, const float *verts9, const uint8_t *idx, + int ntris) +{ + struct v3d *v = &rl->v; + uint32_t *p; + + if (ntris <= 0) + return; + p = v3d_reserve(v, (uint32_t)ntris * 28); + if (p == NULL) + return; + (void)rlvec.emit_tris(p, verts9, idx, ntris, + (20 << 16) | V3D_GL_TRIANGLE); /* KXYZUVQ */ + v3d_commit(v, (uint32_t)ntris * 28); + rl->tris += (uint64_t)ntris; +} + +int +rlgl_finish(struct rlgl *rl) +{ + + return v3d_sync(&rl->v); +} + +int +rlgl_swap(struct rlgl *rl) +{ + struct v3d *v = &rl->v; + + if (!v->opt_nosync) { + if (v3d_sync(v) != 0) + return -1; + } + if (v3d_flip(v, rl->fb[rl->back]) != 0) + return -1; + rl->back ^= 1; + v3d_st(v, V3D_PE_DSTBASE, rl->fb[rl->back]); + rl->frames++; + + if (v->opt_stats) { + uint64_t now = v3d_now_ns(); + + if (rl->last_swap_ns != 0) { + uint64_t d = now - rl->last_swap_ns; + + rl->frame_ns_tot += d; + if (rl->frame_ns_min == 0 || d < rl->frame_ns_min) + rl->frame_ns_min = d; + if (d > rl->frame_ns_max) + rl->frame_ns_max = d; + } + rl->last_swap_ns = now; + /* + * Cumulative snapshot lines: diffing two RLGLSNAPs + * isolates the demo window from load/console frames. + */ + if (v->opt_stats >= 2 && (rl->frames & 127) == 0) + printf("RLGLSNAP frames=%d ns=%llu submit_ns=%llu " + "sync_ns=%llu flip_ns=%llu submits=%llu " + "bytes=%llu tris=%llu uploads=%llu " + "upl_bytes=%llu\n", + rl->frames, + (unsigned long long)rl->frame_ns_tot, + (unsigned long long)v->st.submit_ns, + (unsigned long long)v->st.sync_ns, + (unsigned long long)v->st.flip_ns, + (unsigned long long)v->st.submits, + (unsigned long long)v->st.submit_bytes, + (unsigned long long)rl->tris, + (unsigned long long)v->st.uploads, + (unsigned long long)v->st.upload_bytes); + } + return 0; +} + +void +rlgl_stats_print(struct rlgl *rl) +{ + struct v3d_stats *s = &rl->v.st; + double nf = rl->frames > 0 ? (double)rl->frames : 1.0; + double ms = 1e6; + + if (rl->stats_printed) + return; + rl->stats_printed = 1; + printf("RLGL frames=%d tris=%llu submits=%llu syncs=%llu " + "flips=%llu\n", rl->frames, + (unsigned long long)rl->tris, + (unsigned long long)s->submits, + (unsigned long long)s->syncs, + (unsigned long long)s->flips); + printf("RLGL words=%llu submit_bytes=%llu uploads=%llu " + "upload_bytes=%llu st_calls=%llu texbinds=%llu\n", + (unsigned long long)s->words, + (unsigned long long)s->submit_bytes, + (unsigned long long)s->uploads, + (unsigned long long)s->upload_bytes, + (unsigned long long)s->st_calls, + (unsigned long long)s->texbinds); + printf("RLGL per_frame tris=%.1f submits=%.2f bytes=%.0f " + "upload_bytes=%.0f\n", (double)rl->tris / nf, + (double)s->submits / nf, (double)s->submit_bytes / nf, + (double)s->upload_bytes / nf); + if (rl->v.opt_stats && rl->frames > 1) { + double blocked = (double)(s->submit_ns + s->sync_ns + + s->flip_ns); + double tot = (double)rl->frame_ns_tot; + /* first swap has no predecessor: nf-1 timed frames */ + double tf = (double)(rl->frames - 1); + + printf("RLGL avg_frame_ms=%.2f min=%.2f max=%.2f\n", + tot / tf / ms, + (double)rl->frame_ns_min / ms, + (double)rl->frame_ns_max / ms); + printf("RLGL avg_submit_ms=%.2f avg_sync_ms=%.2f " + "avg_flip_ms=%.2f avg_blocked_ms=%.2f " + "avg_build_ms=%.2f\n", + (double)s->submit_ns / nf / ms, + (double)s->sync_ns / nf / ms, + (double)s->flip_ns / nf / ms, + blocked / nf / ms, + (tot / tf - blocked / nf) / ms); + } +} diff --git a/src/mesa/drivers/verite/rlgl.h b/src/mesa/drivers/verite/rlgl.h new file mode 100644 index 0000000..002134c --- /dev/null +++ b/src/mesa/drivers/verite/rlgl.h @@ -0,0 +1,194 @@ +/* + * 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. + */ + +#ifndef RLGL_H +#define RLGL_H + +#include <stdint.h> +#include "libv3d.h" + +/* texel formats (PE SrcFmt/DstFmt encodings) */ +#define RLGL_FMT_565 4 +#define RLGL_FMT_4444 5 +#define RLGL_FMT_1555 6 + +/* + * blend factors + */ +#define RLGL_BL_ZERO 6 /* Blend0 */ +#define RLGL_BL_ONE 7 /* Blend1 */ +#define RLGL_BL_SRCALPHA 2 +#define RLGL_BL_INVSRCALPHA 3 +#define RLGL_BL_OTHERCOLOR 0 /* dst side: src color; src: dst */ +#define RLGL_BL_INVOTHERCOLOR 1 +#define RLGL_BL_OWNCOLOR 10 /* own color - no GL equivalent */ +#define RLGL_BL_INVOWNCOLOR 11 +/* legacy names (dst-side view), kept for minigl */ +#define RLGL_BL_SRCCOLOR RLGL_BL_OTHERCOLOR +#define RLGL_BL_INVSRCCOLOR RLGL_BL_INVOTHERCOLOR + +struct rlgl_tex { + uint32_t addr; /* VRAM; 0 = not resident */ + uint16_t w, h; /* texels, powers of two */ + uint32_t srcstride; /* encoded (Src1<<4)|Src0 */ + uint32_t fmt; /* RLGL_FMT_* */ +}; + +struct rlgl { + struct v3d v; + uint32_t fb[2], zbuf; + uint32_t scratch; /* glCopyPixels seed scratch surface */ + int back; + struct rlgl_tex pt_tex; /* AA-point radial coverage tex (lazy; + * addr 0 = not yet created) */ + /* redundant-state cache */ + uint32_t cur_tex; + int cur_func; /* PE SrcFunc currently set */ + int env_mod; /* SrcFunc code for textured draws */ + int fog_on; /* emit the KFXYZUVQ fog channel */ + int frames; + /* perf: swap-to-swap frame timing (RLGL_STATS) + counters */ + uint64_t tris; + uint64_t frame_ns_tot, frame_ns_min, frame_ns_max; + uint64_t last_swap_ns; + int stats_printed; +}; + +/* per-vertex layout handed to rlgl_tri: the KXYZUVQ order */ +struct rlgl_vtx { + float r, g, b; /* 0..255 */ + float x, y; /* screen pixels */ + float z; /* 0..65535, smaller = nearer */ + float u, v; /* texels */ + float q; /* 1/w (any consistent scale) */ + float f; /* fog factor 0..255, 255 = no fog (fog_on only) */ +}; + +/* diagnostic: 1 = disable redundant-state caches (VRMESA_NOCACHE probe) */ +extern int rlgl_force_rebind; + +int rlgl_init(struct rlgl *, const char *ucpath); +void rlgl_shutdown(struct rlgl *); + +int rlgl_tex_init(struct rlgl_tex *, int w, int h, uint32_t fmt); +int rlgl_tex_alloc(struct rlgl *, struct rlgl_tex *); +int rlgl_tex_pyramid(struct rlgl *, struct rlgl_tex *levs, int nlev); +int rlgl_tex_create(struct rlgl *, struct rlgl_tex *, int w, int h, + uint32_t fmt); +void rlgl_tex_free(struct rlgl *, struct rlgl_tex *); +int rlgl_tex_upload(struct rlgl *, struct rlgl_tex *, + const uint16_t *texels); +int rlgl_tex_upload_rows(struct rlgl *, struct rlgl_tex *, + const uint16_t *texels, int y, int rows); +void rlgl_tex_bind(struct rlgl *, const struct rlgl_tex *); +void rlgl_tex_env(struct rlgl *, int modulate); +/* PE SrcFunc codes (spec table: 1=replace 2=decal 3=modulate) */ +#define RLGL_ENV_REPLACE 1 +#define RLGL_ENV_DECAL 2 +#define RLGL_ENV_MODULATE 3 +void rlgl_tex_env_code(struct rlgl *, int srcfunc_code); +/* + * Per-axis clamp-to-edge (UClamp b15 / VClamp b16 of SrcMode). + */ +void rlgl_tex_wrap(struct rlgl *, int uclamp, int vclamp); +void rlgl_tex_filter(struct rlgl *, int bilinear); + +/* PE ZBufMode compare codes (silicon-proven: LT ztest, LE GLQuake) */ +#define RLGL_Z_LT 1 +#define RLGL_Z_LE 3 + +void rlgl_depth_test(struct rlgl *, int enable_lt_or_le); +void rlgl_depth_func(struct rlgl *, int enable, int pe_code); +void rlgl_depth_mask(struct rlgl *, int write); +void rlgl_blend(struct rlgl *, int enable, uint32_t srcf, uint32_t dstf); +void rlgl_alpha_test(struct rlgl *, int enable, uint32_t thresh8); +/* GL window-coord box (bottom-left origin); flips to the PE top-left */ +void rlgl_scissor(struct rlgl *, int x, int y, int w, int h); +/* 565 plane write mask; args are glColorMask R/G/B booleans (alpha n/a) */ +void rlgl_color_mask(struct rlgl *, int r, int g, int b); +/* PE raster op (ALUMode 4-bit ROP code); disabled restores COPY */ +void rlgl_logic_op(struct rlgl *, int enable, int code); +/* PE 4x4 ordered dither on 565 writes (GL_DITHER) */ +void rlgl_dither(struct rlgl *, int enable); +/* + * Per-vertex fog (GL_FOG) + */ +void rlgl_fog(struct rlgl *, int enable, uint32_t rgb888); + +void rlgl_clear(struct rlgl *, int color, uint32_t rgb565, + int depth); +/* + * glDrawPixels fast path + */ +int rlgl_draw_row(struct rlgl *, int x, int wy, int w, + const uint16_t *row565); +/* + * Hardware glCopyPixels (GL_COLOR) + */ +int rlgl_copy_rect(struct rlgl *, uint32_t src_base, int src_w, + uint32_t dst_base, int dst_w, int sx, int sy, int dx, int dy, + int w, int h); +/* + * Hardware glCopyTexSubImage + */ +int rlgl_copy_to_tex(struct rlgl *, uint32_t fb_base, int fb_h, + uint32_t tex_base, int tex_aw, int sx, int sy_gl, int dx, int dy, + int w, int h); +void rlgl_tri(struct rlgl *, const struct rlgl_vtx *, + const struct rlgl_vtx *, const struct rlgl_vtx *); +/* + * Native hardware lines from a 2-vertex KXYZ stream (only r,g,b,x,y,z + * used). + */ +void rlgl_line(struct rlgl *, const struct rlgl_vtx *, + const struct rlgl_vtx *); +void rlgl_aaline(struct rlgl *, const struct rlgl_vtx *, + const struct rlgl_vtx *); +/* + * Native anti-aliased points (GL_POINT_SMOOTH) + */ +void rlgl_aapoint_begin(struct rlgl *); +void rlgl_aapoint(struct rlgl *, const struct rlgl_vtx *, float size); +void rlgl_aapoint_end(struct rlgl *); +/* + * Native hardware primitive strips/fans + */ +void rlgl_tristrip(struct rlgl *, const struct rlgl_vtx *v, int n); +void rlgl_trifan(struct rlgl *, const struct rlgl_vtx *v, int n); +/* + * Bulk indexed emit: verts9 = packed 9-float wire records + * (r,g,b,x,y,z,u,v,q), idx = 3*ntris vertex indices into verts9. + */ +void rlgl_tris_idx(struct rlgl *, const float *verts9, + const uint8_t *idx, int ntris); +int rlgl_swap(struct rlgl *); +int rlgl_finish(struct rlgl *); +void rlgl_stats_print(struct rlgl *); + +#endif /* RLGL_H */ diff --git a/src/mesa/drivers/verite/verite3dio.h b/src/mesa/drivers/verite/verite3dio.h new file mode 100644 index 0000000..1337dc3 --- /dev/null +++ b/src/mesa/drivers/verite/verite3dio.h @@ -0,0 +1,107 @@ +/* + * Vendored from src/sys/dev/pci/verite3dio.h + */ + +/* $NetBSD: verite3dio.h,v 1.1 2026/07/15 20:53:22 rkujawa Exp $ */ + +/* + * 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 AUTHOR ``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 AUTHOR 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. + */ + + +/* + * /dev/verite3d - expierimental 3D interface for veritefb(4) + * + * By design, it is completely independent of wsdisplay(4). It supports + * one exclusive client - opening the device stops the 2D microcode. + * Closing the device stops the 3D microcode and restores the console, + * in the state it was left behind. + * + * The client supplies its own RISC microcode (V3D_INIT) and owns + * the FIFO command stream. The kernel moves bytes (DMA) and tracks VRAM, + * but does NOT interpret the command stream. That is responsibility of + * the microcode (on the hardware side) and the userland process. + */ + +#ifndef VERITE3DIO_H +#define VERITE3DIO_H + +#include <sys/ioccom.h> + +#define V3D_RING_SLOTS 4 +#define V3D_SLOT_SIZE 65536 + +#define V3D_VRAM_MMAP_OFF ((off_t)V3D_RING_SLOTS * V3D_SLOT_SIZE) + +/* Load client microcode into the foreign slot and cold-start it. */ +struct v3d_init { + uint64_t vi_ucode; /* in: user pointer to image */ + uint32_t vi_size; /* in: bytes */ + uint32_t vi_ctx_base; /* out: context save area (VRAM) */ + uint32_t vi_memsize; /* out: total VRAM bytes */ + uint32_t vi_pool_base; /* out: first V3D_ALLOC-managed byte */ +}; +#define V3D_INIT _IOWR('V', 0, struct v3d_init) + +struct v3d_submit { + uint32_t vs_slot; /* 0 to V3D_RING_SLOTS-1 */ + uint32_t vs_len; /* bytes, word multiple */ + uint32_t vs_swap; /* descriptor swap code 0..3 */ +}; +#define V3D_SUBMIT _IOW('V', 1, struct v3d_submit) + +/* + * Fence: send one word (the client microcode's sync command) and + * wait for the response word. In/out: word to send / word received. + */ +#define V3D_SYNC _IOWR('V', 2, uint32_t) + +struct v3d_alloc { + uint32_t va_size; /* in: bytes */ + uint32_t va_align; /* in: byte alignment (0 = 4) */ + uint32_t va_addr; /* out: VRAM byte address */ +}; +#define V3D_ALLOC _IOWR('V', 3, struct v3d_alloc) +#define V3D_FREE _IOW('V', 4, uint32_t) + +struct v3d_mode { + uint32_t vm_depth; /* in: 8 or 16; 0 = query only */ + uint32_t vm_frame_base; /* in: scanout base; 0 = keep */ + uint32_t vm_width; /* out */ + uint32_t vm_height; /* out */ + uint32_t vm_stride; /* out: scanout line bytes */ + uint32_t vm_pe_stride; /* out: PE Stride(3) dst encoding */ +}; +#define V3D_MODE _IOWR('V', 5, struct v3d_mode) + +/* + * vsync-waited FRAMEBASE flip. + */ +#define V3D_FLIP_NOWAIT 0x80000000U +#define V3D_FLIP _IOW('V', 6, uint32_t) + +#endif /* VERITE3DIO_H */ diff --git a/src/mesa/drivers/verite/vrapi.c b/src/mesa/drivers/verite/vrapi.c new file mode 100644 index 0000000..b2aa74d --- /dev/null +++ b/src/mesa/drivers/verite/vrapi.c @@ -0,0 +1,319 @@ +/* + * 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. + */ + +/* + * veritemesa public API + */ + +#include <stdlib.h> +#ifdef VERITE_INSTRUMENT +#include <stdio.h> +#endif + +#include "vrdrv.h" + +#include "GL/vrmesa.h" +#include "context.h" +#include "extensions.h" +#include "imports.h" +#include "array_cache/acache.h" +#include "swrast/swrast.h" +#include "swrast_setup/swrast_setup.h" +#include "tnl/tnl.h" +#include "tnl/t_context.h" +#include "tnl/t_pipeline.h" +#include "drivers/common/driverfuncs.h" + +#ifdef VERITE_INSTRUMENT +/* + * The single driver-layer perf accumulator (vrperf.h). + */ +struct vrperf g_vrp; + +/* + * RunPipeline wrapper + */ +static void +vr_run_pipeline(GLcontext *ctx) +{ + + VRP_ZE(VRP_Z_PIPELINE); + _tnl_run_pipeline(ctx); + VRP_ZX(VRP_Z_PIPELINE); +} + +void +vrPerfInit(vrmesa_context *vm) +{ + const char *p = getenv("VRMESA_PROF"); + + g_vrp.level = p ? atoi(p) : 0; + g_vrp.on = g_vrp.level >= 1; + g_vrp.rl = &vm->rl; + g_vrp.tb0 = vrp_tb64(); + g_vrp.ns0 = v3d_now_ns(); + /* + * Bridge the librlgl/libv3d _ns timers into VRPERF + */ + if (g_vrp.on && vm->rl.v.opt_stats == 0) + vm->rl.v.opt_stats = 1; + atexit(vrPerfDump); +} + +void +vrPerfSwap(vrmesa_context *vm) +{ + + (void)vm; + g_vrp.frames++; + /* cumulative snapshot, diff two lines to isolate a demo window */ + if (g_vrp.level >= 2 && (g_vrp.frames & 127) == 0) + printf("VRPERFSNAP frame=%llu texres=%llu zfunc=%llu " + "blend=%llu fgcolor=%llu fences=%llu\n", + (unsigned long long)g_vrp.frames, + (unsigned long long)g_vrp.fb[VRP_FB_TEX_RESIDENCY], + (unsigned long long)g_vrp.fb[VRP_FB_DEPTHFUNC], + (unsigned long long)g_vrp.fb[VRP_FB_BLEND], + (unsigned long long)g_vrp.fgcolor_writes, + (unsigned long long)g_vrp.fences); +} + +void +vrPerfDump(void) +{ + static const char *zname[VRP_Z__N] = { + "pipeline", "render_start", "texvalidate", "tri_emit" + }; + struct rlgl *rl = g_vrp.rl; + double tick_ns, nf; + uint64_t dtb; + int i; + + if (g_vrp.printed || g_vrp.level < 1) + return; + g_vrp.printed = 1; + + dtb = vrp_tb64() - g_vrp.tb0; + tick_ns = dtb ? (double)(v3d_now_ns() - g_vrp.ns0) / (double)dtb : 0.0; + nf = g_vrp.frames > 0 ? (double)g_vrp.frames : 1.0; + + printf("VRPERF frames=%llu\n", (unsigned long long)g_vrp.frames); + printf("VRPERF fallback tex=%llu blend=%llu afunc=%llu zfunc=%llu " + "stipple=%llu smooth=%llu twoside=%llu specular=%llu " + "unfilled=%llu rmode=%llu texres=%llu alphagrad=%llu\n", + (unsigned long long)g_vrp.fb[VRP_FB_TEX_HWOK], + (unsigned long long)g_vrp.fb[VRP_FB_BLEND], + (unsigned long long)g_vrp.fb[VRP_FB_ALPHAFUNC], + (unsigned long long)g_vrp.fb[VRP_FB_DEPTHFUNC], + (unsigned long long)g_vrp.fb[VRP_FB_STIPPLE], + (unsigned long long)g_vrp.fb[VRP_FB_SMOOTH], + (unsigned long long)g_vrp.fb[VRP_FB_TWOSIDE], + (unsigned long long)g_vrp.fb[VRP_FB_SPECULAR], + (unsigned long long)g_vrp.fb[VRP_FB_UNFILLED_SUB], + (unsigned long long)g_vrp.fb[VRP_FB_RENDERMODE], + (unsigned long long)g_vrp.fb[VRP_FB_TEX_RESIDENCY], + (unsigned long long)g_vrp.fb[VRP_FB_TRI_ALPHAGRAD]); + printf("VRPERF texval dirty=%llu nlvl=%llu evict=%llu first=%llu " + "bindonly=%llu creates=%llu failv=%llu failu=%llu\n", + (unsigned long long)g_vrp.tv_dirty, + (unsigned long long)g_vrp.tv_nlevels, + (unsigned long long)g_vrp.tv_evict, + (unsigned long long)g_vrp.tv_first, + (unsigned long long)g_vrp.tv_bindonly, + (unsigned long long)g_vrp.tv_creates, + (unsigned long long)g_vrp.tv_failv, + (unsigned long long)g_vrp.tv_failu); + printf("VRPERF churn fgcolor=%llu mipbind=%llu filt=%llu fences=%llu\n", + (unsigned long long)g_vrp.fgcolor_writes, + (unsigned long long)g_vrp.mip_binds, + (unsigned long long)g_vrp.filt_changes, + (unsigned long long)g_vrp.fences); + printf("VRPERF cpu_ms_per_frame tick_ns=%.3f", tick_ns); + for (i = 0; i < VRP_Z__N; i++) + printf(" %s=%.2f", zname[i], + (double)g_vrp.zone[i] * tick_ns / nf / 1e6); + printf("\n"); + if (rl != NULL) + printf("VRPERF gpu_wait_ms_per_frame submit=%.2f sync=%.2f " + "flip=%.2f | tris=%llu st_calls=%llu texbinds=%llu " + "submit_kb=%llu\n", + (double)rl->v.st.submit_ns / nf / 1e6, + (double)rl->v.st.sync_ns / nf / 1e6, + (double)rl->v.st.flip_ns / nf / 1e6, + (unsigned long long)rl->tris, + (unsigned long long)rl->v.st.st_calls, + (unsigned long long)rl->v.st.texbinds, + (unsigned long long)(rl->v.st.submit_bytes / 1024)); +} +#endif /* VERITE_INSTRUMENT */ + +vrMesaContext +vrMesaCreateContext(const char *ucodepath) +{ + vrmesa_context *vmesa; + GLcontext *ctx; + struct dd_function_table functions; + + if (ucodepath == NULL) + ucodepath = getenv("VERITE_UCODE"); + if (ucodepath == NULL) { + _mesa_printf("vrMesaCreateContext: no microcode " + "(pass a path or set VERITE_UCODE)\n"); + return NULL; + } + + vmesa = CALLOC_STRUCT(vrmesa_context); + if (vmesa == NULL) + return NULL; + + if (rlgl_init(&vmesa->rl, ucodepath) != 0) { + _mesa_free(vmesa); + return NULL; + } + vmesa->fg_alpha = ~0u; /* FGColor alpha cache: unset */ + + if (!_mesa_initialize_visual(&vmesa->glvis, + GL_TRUE, /* rgb */ + GL_TRUE, /* double buffered */ + GL_FALSE, /* stereo */ + 5, 6, 5, 0, /* rgba bits */ + 0, /* index bits */ + 16, /* depth bits */ + 0, /* stencil */ + 0, 0, 0, 0, /* accum */ + 0)) { /* samples */ + rlgl_shutdown(&vmesa->rl); + _mesa_free(vmesa); + return NULL; + } + + _mesa_init_driver_functions(&functions); + vrInitDDFuncs(&functions); + + if (!_mesa_initialize_context(&vmesa->glctx, &vmesa->glvis, NULL, + &functions, (void *)vmesa)) { + rlgl_shutdown(&vmesa->rl); + _mesa_free(vmesa); + return NULL; + } + ctx = &vmesa->glctx; + + vrInitTexConsts(ctx); + + /* hardware depth+color, no software stencil/accum/alpha planes */ + _mesa_initialize_framebuffer(&vmesa->glfb, &vmesa->glvis, + GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + _swrast_CreateContext(ctx); + _ac_CreateContext(ctx); + _tnl_CreateContext(ctx); + _swsetup_CreateContext(ctx); + _swsetup_Wakeup(ctx); + + /* + * The PE fog unit iterates a per-vertex factor (KFXYZUVQ f term) + */ + _tnl_allow_vertex_fog(ctx, GL_TRUE); + _tnl_allow_pixel_fog(ctx, GL_FALSE); + + vrInitSpanFuncs(ctx); + vrInitTriFuncs(ctx); + +#ifdef VERITE_INSTRUMENT + TNL_CONTEXT(ctx)->Driver.RunPipeline = vr_run_pipeline; +#else + TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline; +#endif + + _mesa_enable_sw_extensions(ctx); + + vrPerfInit(vmesa); + + return (vrMesaContext)vmesa; +} + +void +vrMesaDestroyContext(vrMesaContext vrctx) +{ + vrmesa_context *vmesa = (vrmesa_context *)vrctx; + GLcontext *ctx; + + if (vmesa == NULL) + return; + ctx = &vmesa->glctx; + + if (vrMesaGetCurrentContext() == vrctx) + _mesa_make_current2(NULL, NULL, NULL); + + _swsetup_DestroyContext(ctx); + _tnl_DestroyContext(ctx); + _ac_DestroyContext(ctx); + _swrast_DestroyContext(ctx); + + _mesa_free_framebuffer_data(&vmesa->glfb); + _mesa_free_context_data(ctx); + vrPerfDump(); /* while g_vrp.rl is still valid */ + rlgl_shutdown(&vmesa->rl); + _mesa_free(vmesa); +} + +vrMesaContext +vrMesaGetCurrentContext(void) +{ + GET_CURRENT_CONTEXT(ctx); + return (vrMesaContext)ctx; +} + +void +vrMesaMakeCurrent(vrMesaContext vrctx) +{ + vrmesa_context *vmesa = (vrmesa_context *)vrctx; + + if (vmesa == NULL) { + _mesa_make_current2(NULL, NULL, NULL); + return; + } + _mesa_make_current2(&vmesa->glctx, &vmesa->glfb, &vmesa->glfb); +} + +void +vrMesaSwapBuffers(void) +{ + GET_CURRENT_CONTEXT(ctx); + vrmesa_context *vmesa; + + if (ctx == NULL) + return; + vmesa = VRMESA_CONTEXT(ctx); + _mesa_notifySwapBuffers(ctx); + rlgl_swap(&vmesa->rl); + /* rlgl_swap fences the engine before flipping */ + vmesa->engine_dirty = GL_FALSE; + vrPerfSwap(vmesa); +} diff --git a/src/mesa/drivers/verite/vrdd.c b/src/mesa/drivers/verite/vrdd.c new file mode 100644 index 0000000..36daf2b --- /dev/null +++ b/src/mesa/drivers/verite/vrdd.c @@ -0,0 +1,382 @@ +/* + * 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. + */ + +/* + * veritemesa device-driver functions + */ + +#include "vrdrv.h" + +#include "context.h" +#include "swrast/swrast.h" +#include "swrast/s_context.h" /* SWRAST_CONTEXT / _RasterMask */ +#include "swrast_setup/swrast_setup.h" +#include "array_cache/acache.h" +#include "tnl/tnl.h" + +static const GLubyte * +vr_get_string(GLcontext *ctx, GLenum pname) +{ + (void)ctx; + switch (pname) { + case GL_VENDOR: + return (const GLubyte *)"Rendition"; + case GL_RENDERER: + return (const GLubyte *)"Mesa Rendition Verite V2200"; + default: + return NULL; + } +} + +static void +vr_get_buffer_size(GLframebuffer *buffer, GLuint *width, GLuint *height) +{ + GET_CURRENT_CONTEXT(ctx); + vrmesa_context *vmesa; + + (void)buffer; + if (ctx == NULL) + return; + vmesa = VRMESA_CONTEXT(ctx); + *width = vmesa->rl.v.width; + *height = vmesa->rl.v.height; +} + +/* GL_CLEAR..GL_SET (0x1500..0x150F) -> PE ALUMode 4-bit ROP */ +static const GLubyte vr_rop[16] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF, +}; + +static void +vr_update_state(GLcontext *ctx, GLuint new_state) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct rlgl *rl = &vmesa->rl; + + _swrast_InvalidateState(ctx, new_state); + _swsetup_InvalidateState(ctx, new_state); + _ac_InvalidateState(ctx, new_state); + _tnl_InvalidateState(ctx, new_state); + + /* + * PE state used only by the hardware triangle path + */ + if (new_state & _NEW_DEPTH) { + /* GL_ALWAYS = compare off; writes are ZBufWrMode's job */ + rlgl_depth_func(rl, + ctx->Depth.Test && ctx->Depth.Func != GL_ALWAYS, + ctx->Depth.Func == GL_LESS ? RLGL_Z_LT : RLGL_Z_LE); + rlgl_depth_mask(rl, ctx->Depth.Test && ctx->Depth.Mask); + } + if (new_state & _NEW_COLOR) { + GLuint srcf, dstf; + GLboolean na; + + rlgl_color_mask(rl, ctx->Color.ColorMask[0], + ctx->Color.ColorMask[1], ctx->Color.ColorMask[2]); + + /* honor GL_DITHER (default on): PE 4x4 dither on 565 */ + rlgl_dither(rl, ctx->Color.DitherFlag); + + if (ctx->Color._LogicOpEnabled) { + rlgl_logic_op(rl, 1, + vr_rop[ctx->Color.LogicOp - GL_CLEAR]); + rlgl_blend(rl, 0, 0, 0); + } else { + rlgl_logic_op(rl, 0, 0); + if (ctx->Color.BlendEnabled && + vrBlendMap(ctx, &srcf, &dstf, &na)) + rlgl_blend(rl, 1, srcf, dstf); + else + rlgl_blend(rl, 0, 0, 0); + } + + /* TranspReject: reject SrcAlpha <= thresh */ + if (ctx->Color.AlphaEnabled) { + GLint ref = (GLint)(ctx->Color.AlphaRef * + 255.0F + 0.5F); + + switch (ctx->Color.AlphaFunc) { + case GL_GREATER: + rlgl_alpha_test(rl, 1, (uint32_t)ref); + break; + case GL_GEQUAL: + /* pass a >= ref == reject a <= ref-1 */ + if (ref <= 0) + rlgl_alpha_test(rl, 0, 0); + else + rlgl_alpha_test(rl, 1, + (uint32_t)(ref - 1)); + break; + case GL_NEVER: + rlgl_alpha_test(rl, 1, 255); + break; + default: /* ALWAYS or gated shapes */ + rlgl_alpha_test(rl, 0, 0); + break; + } + } else + rlgl_alpha_test(rl, 0, 0); + } + + if (new_state & _NEW_FOG) { + if (ctx->Fog.Enabled) { + GLubyte c[3]; + + UNCLAMPED_FLOAT_TO_UBYTE(c[0], ctx->Fog.Color[0]); + UNCLAMPED_FLOAT_TO_UBYTE(c[1], ctx->Fog.Color[1]); + UNCLAMPED_FLOAT_TO_UBYTE(c[2], ctx->Fog.Color[2]); + rlgl_fog(rl, 1, ((uint32_t)c[0] << 16) | + ((uint32_t)c[1] << 8) | (uint32_t)c[2]); + } else + rlgl_fog(rl, 0, 0); + } + + if (new_state & _NEW_SCISSOR) { + if (ctx->Scissor.Enabled) + rlgl_scissor(rl, ctx->Scissor.X, ctx->Scissor.Y, + ctx->Scissor.Width, ctx->Scissor.Height); + else + rlgl_scissor(rl, 0, 0, rl->v.width, rl->v.height); + } + + vrCheckHwRender(ctx); +} + +static void +vr_clear(GLcontext *ctx, GLbitfield mask, GLboolean all, + GLint x, GLint y, GLint width, GLint height) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLbitfield rest = mask; + int cc = 0, cz = 0; + + /* frame boundary for texture evict-pinning (once per GLQuake frame) */ + vrTexNewFrame(); + + if (all && !ctx->Scissor.Enabled) { + if ((mask & DD_BACK_LEFT_BIT) != 0 && + ctx->Color.ColorMask[0] && ctx->Color.ColorMask[1] && + ctx->Color.ColorMask[2]) { + cc = 1; + rest &= ~DD_BACK_LEFT_BIT; + } + /* engine Z clear writes the max-Z pattern = depth 1.0 */ + if ((mask & DD_DEPTH_BIT) != 0 && ctx->Depth.Mask && + ctx->Depth.Clear == 1.0) { + cz = 1; + rest &= ~DD_DEPTH_BIT; + } + } + + if (cc || cz) { + const GLfloat *c = ctx->Color.ClearColor; + GLuint r = (GLuint)(c[0] * 255.0F + 0.5F); + GLuint g = (GLuint)(c[1] * 255.0F + 0.5F); + GLuint b = (GLuint)(c[2] * 255.0F + 0.5F); + uint32_t rgb565; + + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + rgb565 = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3); + rlgl_clear(&vmesa->rl, cc, rgb565, cz); + vmesa->engine_dirty = GL_TRUE; + } + + if (rest != 0) + _swrast_Clear(ctx, rest, all, x, y, width, height); +} + +/* + * Hardware glDrawPixels. + */ +static void +vr_drawpixels(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, + const GLvoid *pixels) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + static GLushort row565[2048]; + GLint destX, destY, drawW, drawH, skipPix, skipRows, rowLen, r, i, ch; + const GLchan *src; + + if (!ctx->Current.RasterPosValid) + return; + if ((format != GL_RGBA && format != GL_RGB) || type != CHAN_TYPE || + ctx->_ImageTransferState != 0 || !ctx->Visual.rgbMode || + (SWRAST_CONTEXT(ctx)->_RasterMask & ~CLIP_BIT) != 0 || + ctx->Texture._EnabledCoordUnits != 0 || + unpack->Alignment != 1 || unpack->SwapBytes || unpack->LsbFirst || + ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F) + goto slow; + + destX = x; destY = y; drawW = width; drawH = height; + skipPix = unpack->SkipPixels; skipRows = unpack->SkipRows; + rowLen = unpack->RowLength > 0 ? unpack->RowLength : width; + + /* clip to the draw buffer (mirrors swrast fast_draw_pixels) */ + if (destX < ctx->DrawBuffer->_Xmin) { + skipPix += ctx->DrawBuffer->_Xmin - destX; + drawW -= ctx->DrawBuffer->_Xmin - destX; + destX = ctx->DrawBuffer->_Xmin; + } + if (destX + drawW > ctx->DrawBuffer->_Xmax) + drawW -= destX + drawW - ctx->DrawBuffer->_Xmax; + if (drawW <= 0) + return; + if (destY < ctx->DrawBuffer->_Ymin) { + skipRows += ctx->DrawBuffer->_Ymin - destY; + drawH -= ctx->DrawBuffer->_Ymin - destY; + destY = ctx->DrawBuffer->_Ymin; + } + if (destY + drawH > ctx->DrawBuffer->_Ymax) + drawH -= destY + drawH - ctx->DrawBuffer->_Ymax; + if (drawH <= 0) + return; + + /* mem_write needs 32-bit alignment: even x and even width */ + if ((destX & 1) || (drawW & 1) || drawW > 2048) + goto slow; + + ch = (format == GL_RGBA) ? 4 : 3; + src = (const GLchan *)pixels + (skipRows * rowLen + skipPix) * ch; + for (r = 0; r < drawH; r++) { + const GLchan *p = src + (GLint)r * rowLen * ch; + + for (i = 0; i < drawW; i++) { + GLchan cr = p[i * ch], cg = p[i * ch + 1], + cb = p[i * ch + 2]; + + row565[i] = (GLushort)(((cr & 0xf8) << 8) | + ((cg & 0xfc) << 3) | (cb >> 3)); + } + (void)rlgl_draw_row(&vmesa->rl, destX, destY + r, drawW, row565); + } + vmesa->engine_dirty = GL_TRUE; + return; +slow: + _swrast_DrawPixels(ctx, x, y, width, height, format, type, unpack, + pixels); +} + +/* rl.fb[back] is the draw target, the other buffer is scanned out */ +uint32_t +vr_fb_base(struct rlgl *rl, GLenum buffer) +{ + + if (buffer == GL_FRONT || buffer == GL_FRONT_LEFT) + return rl->fb[rl->back ^ 1]; + return rl->fb[rl->back]; /* GL_BACK / default */ +} + +/* + * Hardware glCopyPixels(GL_COLOR). + */ +static void +vr_copypixels(GLcontext *ctx, GLint srcx, GLint srcy, GLsizei width, + GLsizei height, GLint destx, GLint desty, GLenum type) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct rlgl *rl = &vmesa->rl; + GLint fbW = (GLint)rl->v.width, fbH = (GLint)rl->v.height; + uint32_t sbase, dbase; + GLint sy_pe, dy_pe; + GLboolean overlap; + + if (type != GL_COLOR || ctx->_ImageTransferState != 0 || + !ctx->Visual.rgbMode || + (SWRAST_CONTEXT(ctx)->_RasterMask & ~CLIP_BIT) != 0 || + ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F) + goto slow; + + /* both rects must lie fully inside their buffers (swrast handles + * the clipped/border cases correctly) */ + if (width <= 0 || height <= 0 || + srcx < 0 || srcy < 0 || srcx + width > fbW || srcy + height > fbH || + destx < 0 || desty < 0 || destx + width > fbW || + desty + height > fbH) + goto slow; + + sbase = vr_fb_base(rl, ctx->Pixel.ReadBuffer); + dbase = vr_fb_base(rl, ctx->Color.DrawBuffer); + + /* + * The copy hides the true src/dst relationship from the microcode's + * overlap-direction logic + */ + overlap = (sbase == dbase) && + !(destx >= srcx + width || destx + width <= srcx || + desty >= srcy + height || desty + height <= srcy); + if (overlap) + goto slow; + + /* GL window (bottom-left) -> PE surface (top-left) top edge */ + sy_pe = fbH - srcy - height; + dy_pe = fbH - desty - height; + if (rlgl_copy_rect(rl, sbase, fbW, dbase, fbW, srcx, sy_pe, + destx, dy_pe, width, height) != 0) + goto slow; + vmesa->engine_dirty = GL_TRUE; + return; +slow: + _swrast_CopyPixels(ctx, srcx, srcy, destx, desty, width, height, type); +} + +static void +vr_finish(GLcontext *ctx) +{ + + vrFence(VRMESA_CONTEXT(ctx)); +} + +static void +vr_flush(GLcontext *ctx) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + + if (vmesa->engine_dirty) + (void)v3d_flush(&vmesa->rl.v); +} + +void +vrInitDDFuncs(struct dd_function_table *functions) +{ + + functions->GetString = vr_get_string; + functions->GetBufferSize = vr_get_buffer_size; + functions->UpdateState = vr_update_state; + functions->Clear = vr_clear; + functions->Finish = vr_finish; + functions->Flush = vr_flush; + functions->DrawPixels = vr_drawpixels; + functions->CopyPixels = vr_copypixels; + vrInitTexFuncs(functions); +} diff --git a/src/mesa/drivers/verite/vrdrv.h b/src/mesa/drivers/verite/vrdrv.h new file mode 100644 index 0000000..9762e95 --- /dev/null +++ b/src/mesa/drivers/verite/vrdrv.h @@ -0,0 +1,125 @@ +/* + * 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. + */ + +/* + * Mesa driver for the Rendition Verite V2200 over librlgl + * (NetBSD /dev/verite3d) - "veritemesa". + */ + +#ifndef VRDRV_H +#define VRDRV_H + +#include "glheader.h" +#include "mtypes.h" + +#include "rlgl.h" + +#include "vrperf.h" + +typedef struct vrmesa_context { + GLcontext glctx; /* base class -- must be first */ + GLvisual glvis; + GLframebuffer glfb; + + struct rlgl rl; + + GLboolean hw_tris; /* state allows PE triangles */ + GLboolean engine_dirty; /* PE work queued, spans must fence */ + GLboolean span_front; /* swrast SetBuffer chose front */ + + /* hardware texture stage (vrtex.c) */ + struct vr_texture *textures; /* all driver texture data */ + GLuint lru_tick; + GLboolean tex_on; /* this render run is textured */ + GLfloat tex_w, tex_h; /* texel scales for u/v emit */ + GLfloat tex_off; /* 0.5 iff bilinear (GL footprint) */ + GLfloat tex_base_w, tex_base_h; /* level-0 dims (LOD) */ + + /* per-polygon mipmapping (vrtris picks a level per triangle) */ + struct vr_texture *tex_cur; /* validated binding */ + GLboolean tex_mip; /* MIN filter is a mipmap one */ + GLboolean tex_min_bilin; /* LINEAR_MIPMAP_* */ + GLboolean tex_mag_bilin; /* MagFilter == LINEAR */ + GLint cur_level; /* bound level (bind cache) */ + GLint cur_bilin; /* SrcFilter cache (-1 unset) */ + + /* hardware blend: source alpha rides PE FGColor bits 31:24 */ + GLboolean blend_alpha; /* hw blend factors read src alpha */ + GLboolean atest_alpha; /* alpha test needs composed ColorAlpha */ + GLuint fg_alpha; /* cached FGColor alpha (~0 = unset) */ + + /* swsetup's Render.Start, wrapped by vr_render_start */ + void (*ss_render_start)(GLcontext *); + /* swsetup's current tri/quad/line/point funcs (restored on fallback) */ + void (*ss_triangle)(GLcontext *, GLuint, GLuint, GLuint); + void (*ss_quad)(GLcontext *, GLuint, GLuint, GLuint, + GLuint); + void (*ss_line)(GLcontext *, GLuint, GLuint); + void (*ss_points)(GLcontext *, GLuint, GLuint); + + void *ss_prim_verts, *ss_prim_elts; + GLboolean use_strips; /* VRMESA_STRIPS: opt-in native tri-fans */ + GLboolean strips_ok; /* this run's state permits them */ + + void (*ss_build_vertices)(GLcontext *, GLuint, GLuint, + GLuint); + GLboolean use_fastbuild; /* VRMESA_NOFASTBUILD unset (default on) */ + GLboolean fastbuild_ok; /* this run's inputs are fast-pathable */ + GLboolean fb_tex, fb_fog; /* fast build emits texcoord0 / fog */ +} vrmesa_context; + +#define VRMESA_CONTEXT(ctx) ((vrmesa_context *)(ctx)) + +/* vrdd.c */ +extern void vrInitDDFuncs(struct dd_function_table *functions); +/* VRAM base of a GL_FRONT/GL_BACK color buffer (rl.fb[back] = draw) */ +extern uint32_t vr_fb_base(struct rlgl *rl, GLenum buffer); + +/* vrspan.c */ +extern void vrInitSpanFuncs(GLcontext *ctx); +extern void vrFence(vrmesa_context *vmesa); + +/* vrtex.c */ +struct vr_texture; +struct rlgl_tex; +extern void vrInitTexConsts(GLcontext *ctx); +extern void vrInitTexFuncs(struct dd_function_table *functions); +extern GLboolean vrTexHwOk(GLcontext *ctx); +extern GLboolean vrTexValidate(GLcontext *ctx); +extern struct rlgl_tex *vrTexLevel(struct vr_texture *t, GLint level); +extern GLint vrTexNumLevels(struct vr_texture *t); +extern void vrTexNewFrame(void); + +/* vrtris.c */ +extern void vrInitTriFuncs(GLcontext *ctx); +extern void vrCheckHwRender(GLcontext *ctx); +extern GLboolean vrBlendMap(const GLcontext *ctx, GLuint *srcf, + GLuint *dstf, GLboolean *needs_alpha); + +#endif /* VRDRV_H */ diff --git a/src/mesa/drivers/verite/vrperf.h b/src/mesa/drivers/verite/vrperf.h new file mode 100644 index 0000000..27c5487 --- /dev/null +++ b/src/mesa/drivers/verite/vrperf.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +/* + * vrperf - veritemesa driver-layer performance instrumentation. + */ +#ifndef VRPERF_H +#define VRPERF_H + +enum { + VRP_FB_TEX_HWOK, /* texture enabled but not PE-expressible */ + VRP_FB_BLEND, /* blend func not in the PE map */ + VRP_FB_ALPHAFUNC, /* alpha func not GREATER/GEQUAL/NEVER/ALWAYS */ + VRP_FB_DEPTHFUNC, /* depth func not LESS/LEQUAL/ALWAYS */ + VRP_FB_STIPPLE, /* DD_TRI_STIPPLE */ + VRP_FB_SMOOTH, /* DD_TRI_SMOOTH */ + VRP_FB_TWOSIDE, /* DD_TRI_LIGHT_TWOSIDE */ + VRP_FB_SPECULAR, /* DD_SEPARATE_SPECULAR */ + VRP_FB_UNFILLED_SUB, /* unfilled poly + a sub-mode HW can't do */ + VRP_FB_RENDERMODE, /* RenderMode != GL_RENDER */ + VRP_FB_TEX_RESIDENCY, /* gate passed but vrTexValidate could not fit */ + VRP_FB_TRI_ALPHAGRAD, /* per-tri: real alpha gradient -> _swrast_Triangle */ + VRP_FB__N +}; + +enum { + VRP_Z_PIPELINE, /* whole _tnl_run_pipeline (all CPU/frame) */ + VRP_Z_RENDER_START, /* vr_render_start: our validate + texvalidate */ + VRP_Z_TEXVALIDATE, /* vrTexValidate: texture convert/upload CPU */ + VRP_Z_TRI_EMIT, /* per-tri vr_tri (level >= 3 only) */ + VRP_Z__N +}; + +#ifdef VERITE_INSTRUMENT + +#include <stdint.h> + +struct vrperf { + int on; /* VRMESA_PROF >= 1 (gates hot sites) */ + int level; /* full VRMESA_PROF value */ + int printed; /* atexit/destroy double-print guard */ + struct rlgl *rl; /* borrowed: surface librlgl/libv3d stats */ + + uint64_t fb[VRP_FB__N]; /* swrast-fallback histogram */ + uint64_t frames; /* our own swap count */ + + /* state-churn / stall counters */ + uint64_t fgcolor_writes; /* per-tri FGColor writes (vrtris.c) */ + uint64_t mip_binds; /* per-poly level binds */ + uint64_t filt_changes; /* per-poly filter switches */ + uint64_t fences; /* vrFence real PE stalls (vrspan.c) */ + + /* texture-validate classification (the 1->19 fps diagnostic set) */ + uint64_t tv_dirty; /* data changed -> re-upload */ + uint64_t tv_nlevels; /* resident but fewer levels than wanted */ + uint64_t tv_evict; /* an LRU victim was unloaded */ + uint64_t tv_first; /* never-resident first upload */ + uint64_t tv_bindonly; /* resident, no upload (fast path) */ + uint64_t tv_creates; /* new vr_texture objects made */ + uint64_t tv_failv; /* validate failed: could not fit base */ + uint64_t tv_failu; /* validate failed: an upload errored */ + + uint64_t zone[VRP_Z__N]; + uint64_t zone_t0[VRP_Z__N]; + uint64_t tb0, ns0; /* one-time ns calibration anchors */ +}; + +extern struct vrperf g_vrp; + +#if defined(__powerpc__) +static __inline uint32_t +vrp_tb(void) +{ + uint32_t t; + + __asm__ volatile("mftb %0" : "=r"(t)); + return t; +} + +static __inline uint64_t +vrp_tb64(void) +{ + uint32_t hi, lo, hi2; + + do { + __asm__ volatile("mftbu %0" : "=r"(hi)); + __asm__ volatile("mftb %0" : "=r"(lo)); + __asm__ volatile("mftbu %0" : "=r"(hi2)); + } while (hi != hi2); + return ((uint64_t)hi << 32) | lo; +} +#else +static __inline uint32_t vrp_tb(void) { return (uint32_t)v3d_now_ns(); } +static __inline uint64_t vrp_tb64(void) { return v3d_now_ns(); } +#endif + +/* + * Counter tiers. + */ +#define VRP_COUNT(f) (g_vrp.f++) +#define VRP_ADD(f, n) (g_vrp.f += (uint64_t)(n)) +#define VRP_FALLBACK(r) (g_vrp.fb[r]++) +/* gated fallback bump for per-primitive sites (vr_tri alpha gradient) */ +#define VRP_FALLBACK_HOT(r) do { if (g_vrp.on) g_vrp.fb[r]++; } while (0) +#define VRP_HOT(f) do { if (g_vrp.on) g_vrp.f++; } while (0) +#define VRP_ZE(z) do { if (g_vrp.on) g_vrp.zone_t0[z] = vrp_tb(); } while (0) +#define VRP_ZX(z) do { if (g_vrp.on) \ + g_vrp.zone[z] += (uint32_t)(vrp_tb() - g_vrp.zone_t0[z]); } while (0) +#define VRP_ZE3(z) do { if (g_vrp.level >= 3) g_vrp.zone_t0[z] = vrp_tb(); } while (0) +#define VRP_ZX3(z) do { if (g_vrp.level >= 3) \ + g_vrp.zone[z] += (uint32_t)(vrp_tb() - g_vrp.zone_t0[z]); } while (0) + +struct vrmesa_context; +extern void vrPerfInit(struct vrmesa_context *); +extern void vrPerfSwap(struct vrmesa_context *); +extern void vrPerfDump(void); + +#else /* !VERITE_INSTRUMENT */ + +#define VRP_COUNT(f) ((void)0) +#define VRP_ADD(f, n) ((void)0) +#define VRP_FALLBACK(r) ((void)0) +#define VRP_FALLBACK_HOT(r) ((void)0) +#define VRP_HOT(f) ((void)0) +#define VRP_ZE(z) ((void)0) +#define VRP_ZX(z) ((void)0) +#define VRP_ZE3(z) ((void)0) +#define VRP_ZX3(z) ((void)0) +#define vrPerfInit(vm) ((void)0) +#define vrPerfSwap(vm) ((void)0) +#define vrPerfDump() ((void)0) + +#endif /* VERITE_INSTRUMENT */ + +#endif /* VRPERF_H */ diff --git a/src/mesa/drivers/verite/vrspan.c b/src/mesa/drivers/verite/vrspan.c new file mode 100644 index 0000000..82a9f73 --- /dev/null +++ b/src/mesa/drivers/verite/vrspan.c @@ -0,0 +1,202 @@ +/* + * 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. + */ + +/* + * veritemesa swrast spans over the /dev/verite3d VRAM mmap window. + */ + +#include "vrdrv.h" + +#include "imports.h" +#include "swrast/swrast.h" + +void +vrFence(vrmesa_context *vmesa) +{ + + if (vmesa->engine_dirty) { + rlgl_finish(&vmesa->rl); + vmesa->engine_dirty = GL_FALSE; + VRP_COUNT(fences); + } +} + +/* + * Base of the buffer swrast is currently addressing. + */ +static GLubyte * +vr_span_base(vrmesa_context *vmesa) +{ + struct rlgl *rl = &vmesa->rl; + uint32_t fb; + + vrFence(vmesa); + fb = vmesa->span_front ? rl->fb[rl->back ^ 1] : rl->fb[rl->back]; + return (GLubyte *)(uintptr_t)v3d_vram(&rl->v) + fb; +} + +static GLushort * +vr_zrow(vrmesa_context *vmesa, GLint y) +{ + struct rlgl *rl = &vmesa->rl; + + vrFence(vmesa); + return (GLushort *)((GLubyte *)(uintptr_t)v3d_vram(&rl->v) + + rl->zbuf + (size_t)(rl->v.height - 1 - y) * rl->v.stride); +} + +static void +vr_set_buffer(GLcontext *ctx, GLframebuffer *buffer, GLuint bufferBit) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + + (void)buffer; + switch (bufferBit) { + case DD_FRONT_LEFT_BIT: + vmesa->span_front = GL_TRUE; + break; + case DD_BACK_LEFT_BIT: + vmesa->span_front = GL_FALSE; + break; + default: + _mesa_problem(ctx, "bad bufferBit in vr_set_buffer"); + } +} + +#define VR_SWAP16(x) ((GLushort)((((x) & 0xff) << 8) | ((x) >> 8))) + +#define NAME(PREFIX) PREFIX##_565 +#define SPAN_VARS \ + vrmesa_context *vmesa = VRMESA_CONTEXT((GLcontext *)ctx); \ + GLubyte *vrbuf = vr_span_base(vmesa); \ + const GLuint vrstride = vmesa->rl.v.stride; \ + const GLint vrheight = vmesa->rl.v.height; +#define INIT_PIXEL_PTR(P, X, Y) \ + GLushort *P = (GLushort *)(vrbuf + \ + (size_t)(vrheight - 1 - (Y)) * vrstride + (X) * 2) +#define INC_PIXEL_PTR(P) P += 1 +#define STORE_RGB_PIXEL(P, X, Y, R, G, B) \ + *P = VR_SWAP16((((R) & 0xf8) << 8) | (((G) & 0xfc) << 3) | \ + ((B) >> 3)) +#define STORE_RGBA_PIXEL(P, X, Y, R, G, B, A) \ + *P = VR_SWAP16((((R) & 0xf8) << 8) | (((G) & 0xfc) << 3) | \ + ((B) >> 3)) +#define FETCH_RGBA_PIXEL(R, G, B, A, P) \ + do { \ + GLushort px_ = VR_SWAP16(*P); \ + R = (((px_ >> 8) & 0xf8) | ((px_ >> 11) & 0x7)); \ + G = (((px_ >> 3) & 0xfc) | ((px_ >> 5) & 0x3)); \ + B = (((px_ << 3) & 0xf8) | ((px_ ) & 0x7)); \ + A = CHAN_MAX; \ + } while (0) + +#include "swrast/s_spantemp.h" + +static void +vr_write_depth_span(GLcontext *ctx, GLuint n, GLint x, GLint y, + const GLdepth depth[], const GLubyte mask[]) +{ + GLushort *zrow = vr_zrow(VRMESA_CONTEXT(ctx), y) + x; + GLuint i; + + for (i = 0; i < n; i++) + if (mask == NULL || mask[i]) + zrow[i] = VR_SWAP16((GLushort)depth[i]); +} + +static void +vr_write_mono_depth_span(GLcontext *ctx, GLuint n, GLint x, GLint y, + const GLdepth depth, const GLubyte mask[]) +{ + GLushort *zrow = vr_zrow(VRMESA_CONTEXT(ctx), y) + x; + GLuint i; + + for (i = 0; i < n; i++) + if (mask == NULL || mask[i]) + zrow[i] = VR_SWAP16((GLushort)depth); +} + +static void +vr_read_depth_span(GLcontext *ctx, GLuint n, GLint x, GLint y, + GLdepth depth[]) +{ + const GLushort *zrow = vr_zrow(VRMESA_CONTEXT(ctx), y) + x; + GLuint i; + + for (i = 0; i < n; i++) + depth[i] = VR_SWAP16(zrow[i]); +} + +static void +vr_write_depth_pixels(GLcontext *ctx, GLuint n, + const GLint x[], const GLint y[], const GLdepth depth[], + const GLubyte mask[]) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLuint i; + + for (i = 0; i < n; i++) + if (mask == NULL || mask[i]) + vr_zrow(vmesa, y[i])[x[i]] = + VR_SWAP16((GLushort)depth[i]); +} + +static void +vr_read_depth_pixels(GLcontext *ctx, GLuint n, + const GLint x[], const GLint y[], GLdepth depth[]) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLuint i; + + for (i = 0; i < n; i++) + depth[i] = VR_SWAP16(vr_zrow(vmesa, y[i])[x[i]]); +} + +void +vrInitSpanFuncs(GLcontext *ctx) +{ + struct swrast_device_driver *swdd = + _swrast_GetDeviceDriverReference(ctx); + + swdd->SetBuffer = vr_set_buffer; + + swdd->WriteRGBASpan = write_rgba_span_565; + swdd->WriteRGBSpan = write_rgb_span_565; + swdd->WriteMonoRGBASpan = write_monorgba_span_565; + swdd->WriteRGBAPixels = write_rgba_pixels_565; + swdd->WriteMonoRGBAPixels = write_monorgba_pixels_565; + swdd->ReadRGBASpan = read_rgba_span_565; + swdd->ReadRGBAPixels = read_rgba_pixels_565; + + swdd->WriteDepthSpan = vr_write_depth_span; + swdd->WriteMonoDepthSpan = vr_write_mono_depth_span; + swdd->ReadDepthSpan = vr_read_depth_span; + swdd->WriteDepthPixels = vr_write_depth_pixels; + swdd->ReadDepthPixels = vr_read_depth_pixels; +} diff --git a/src/mesa/drivers/verite/vrtex.c b/src/mesa/drivers/verite/vrtex.c new file mode 100644 index 0000000..4f71dd3 --- /dev/null +++ b/src/mesa/drivers/verite/vrtex.c @@ -0,0 +1,501 @@ +/* + * 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. + */ + +/* + * veritemesa hardware texture stage + */ + +#include "vrdrv.h" + +#include "context.h" +#include "imports.h" +#include "texstore.h" +#include "texobj.h" +#include "swrast/swrast.h" + +#define VR_MAX_LEVELS 9 /* 256x256 .. 1x1 */ + +struct vr_texture { + /* hw[0].addr == 0 -> not resident; mip chain in hw[1..] */ + struct rlgl_tex hw[VR_MAX_LEVELS]; + GLint nlevels; /* resident levels (1 = no mips) */ + GLboolean has_alpha; /* uploaded as 4444, else 565 */ + GLboolean dirty; /* core image newer than VRAM */ + GLboolean capped; /* nlevels is all that fit VRAM; + * do not retry for more (anti-thrash) */ + GLuint lru; /* last validate tick */ + GLuint used_frame; /* frame last bound (evict pinning) */ + struct gl_texture_object *obj; + struct vr_texture *next; +}; + +/* + * Frame counter for evict-pinning + */ +static GLuint g_vr_frame = 1; + +void +vrTexNewFrame(void) +{ + + g_vr_frame++; +} + +/* vrtris.c reads the level array for per-triangle mip binds */ +struct rlgl_tex * +vrTexLevel(struct vr_texture *t, GLint level) +{ + + return &t->hw[level]; +} + +GLint +vrTexNumLevels(struct vr_texture *t) +{ + + return t->nlevels; +} + +void +vrInitTexConsts(GLcontext *ctx) +{ + + ctx->Const.MaxTextureUnits = 1; + ctx->Const.MaxTextureImageUnits = 1; + ctx->Const.MaxTextureCoordUnits = 1; + /* 2^(9-1) = 256: mesaconf tex-256 scene proves the PE takes it */ + ctx->Const.MaxTextureLevels = 9; +} + +static struct vr_texture * +vr_tex_data(GLcontext *ctx, struct gl_texture_object *tObj) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct vr_texture *t = tObj->DriverData; + + if (t == NULL) { + t = CALLOC_STRUCT(vr_texture); + if (t == NULL) + return NULL; + t->obj = tObj; + t->dirty = GL_TRUE; + t->next = vmesa->textures; + vmesa->textures = t; + tObj->DriverData = t; + VRP_COUNT(tv_creates); + } + return t; +} + +static void +vr_tex_unload(vrmesa_context *vmesa, struct vr_texture *t) +{ + GLint i; + + /* + * The mip pyramid is ONE contiguous allocation + */ + if (t->hw[0].addr != 0) + rlgl_tex_free(&vmesa->rl, &t->hw[0]); + for (i = 1; i < VR_MAX_LEVELS; i++) + t->hw[i].addr = 0; + t->nlevels = 0; + /* addresses may be recycled: kill the bind cache */ + vmesa->rl.cur_tex = 0; +} + +static void +vr_teximage2d(GLcontext *ctx, GLenum target, GLint level, + GLint internalFormat, GLint width, GLint height, GLint border, + GLenum format, GLenum type, const GLvoid *pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, struct gl_texture_image *texImage) +{ + struct vr_texture *t; + + _mesa_store_teximage2d(ctx, target, level, internalFormat, + width, height, border, format, type, pixels, packing, + texObj, texImage); + t = vr_tex_data(ctx, texObj); + if (t != NULL) + t->dirty = GL_TRUE; +} + +static void +vr_texsubimage2d(GLcontext *ctx, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels, + const struct gl_pixelstore_attrib *packing, + struct gl_texture_object *texObj, struct gl_texture_image *texImage) +{ + struct vr_texture *t; + + _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset, + width, height, format, type, pixels, packing, texObj, + texImage); + t = vr_tex_data(ctx, texObj); + if (t != NULL) + t->dirty = GL_TRUE; /* v1: full re-upload */ +} + +static void +vr_delete_texture(GLcontext *ctx, struct gl_texture_object *tObj) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct vr_texture *t = tObj->DriverData; + + if (t != NULL) { + struct vr_texture **pp; + + vr_tex_unload(vmesa, t); + for (pp = &vmesa->textures; *pp != NULL; + pp = &(*pp)->next) + if (*pp == t) { + *pp = t->next; + break; + } + _mesa_free(t); + tObj->DriverData = NULL; + } + _mesa_delete_texture_object(ctx, tObj); +} + +/* + * Hardware glCopyTexSubImage2D. + */ +static void +vr_copytexsubimage2d(GLcontext *ctx, GLenum target, GLint level, + GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, + GLsizei height) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct rlgl *rl = &vmesa->rl; + struct gl_texture_object *tObj = ctx->Texture.Unit[0].Current2D; + GLint fbW = (GLint)rl->v.width, fbH = (GLint)rl->v.height; + struct vr_texture *t = (tObj != NULL) ? tObj->DriverData : NULL; + GLboolean was_dirty = (t != NULL) ? t->dirty : GL_TRUE; + + /* + * Keep the canonical core image correct in every case. + */ + _swrast_copy_texsubimage2d(ctx, target, level, xoffset, yoffset, + x, y, width, height); + + if (t == NULL) + return; + + if (target != GL_TEXTURE_2D || level != tObj->BaseLevel || + t->hw[0].addr == 0 || t->has_alpha || t->nlevels != 1 || + was_dirty || ctx->_ImageTransferState != 0 || + width <= 0 || height <= 0 || + x < 0 || y < 0 || x + width > fbW || y + height > fbH) { + t->dirty = GL_TRUE; /* re-upload from the updated core */ + return; + } + + if (rlgl_copy_to_tex(rl, vr_fb_base(rl, ctx->Pixel.ReadBuffer), fbH, + t->hw[0].addr, t->hw[0].w, x, y, xoffset, yoffset, + width, height) != 0) { + t->dirty = GL_TRUE; + return; + } + t->dirty = GL_FALSE; /* VRAM matches the core copy */ + vmesa->engine_dirty = GL_TRUE; +} + +void +vrInitTexFuncs(struct dd_function_table *functions) +{ + + functions->TexImage2D = vr_teximage2d; + functions->TexSubImage2D = vr_texsubimage2d; + functions->CopyTexSubImage2D = vr_copytexsubimage2d; + functions->DeleteTexture = vr_delete_texture; +} + +/* + * Can the PE render the CURRENT texture binding? + */ +GLboolean +vrTexHwOk(GLcontext *ctx) +{ + const struct gl_texture_unit *unit = &ctx->Texture.Unit[0]; + const struct gl_texture_object *tObj; + const struct gl_texture_image *img; + + if (ctx->Texture._EnabledUnits != 0x1) + return GL_FALSE; + if (unit->_ReallyEnabled != TEXTURE_2D_BIT) + return GL_FALSE; + tObj = unit->Current2D; + if (tObj == NULL) + return GL_FALSE; + img = tObj->Image[0][tObj->BaseLevel]; + if (img == NULL || img->Border != 0 || + img->Width > 256 || img->Height > 256) + return GL_FALSE; + if (tObj->WrapS != GL_REPEAT && tObj->WrapS != GL_CLAMP_TO_EDGE && + tObj->WrapS != GL_CLAMP) + return GL_FALSE; + if (tObj->WrapT != GL_REPEAT && tObj->WrapT != GL_CLAMP_TO_EDGE && + tObj->WrapT != GL_CLAMP) + return GL_FALSE; + /* + * GL_CLAMP equals CLAMP_TO_EDGE under NEAREST sampling, but + * a LINEAR footprint mixes the border color at the edge + */ + if (tObj->WrapS == GL_CLAMP || tObj->WrapT == GL_CLAMP) { + if (tObj->MagFilter == GL_LINEAR || + tObj->MinFilter == GL_LINEAR || + tObj->MinFilter == GL_LINEAR_MIPMAP_NEAREST || + tObj->MinFilter == GL_LINEAR_MIPMAP_LINEAR) + return GL_FALSE; + } + /* all six MIN filters: mipmap ones ride per-poly selection */ + if (tObj->MagFilter != GL_NEAREST && tObj->MagFilter != GL_LINEAR) + return GL_FALSE; + if (unit->EnvMode != GL_MODULATE && unit->EnvMode != GL_REPLACE && + unit->EnvMode != GL_DECAL) + return GL_FALSE; + return GL_TRUE; +} + +static GLuint +vr_alloc_width(GLuint w) +{ + + if (w < 2) + return 2; + if (w == 4) + return 8; + return w; +} + +static int +vr_tex_upload(vrmesa_context *vmesa, struct vr_texture *t, + const struct gl_texture_image *img, GLint level) +{ + GLuint w = img->Width, h = img->Height; + GLuint aw = vr_alloc_width(w); + GLushort *buf; + GLuint x, y; + int error; + + if (img->FetchTexelc == NULL) + return -1; + buf = _mesa_malloc(aw * h * sizeof(GLushort)); + if (buf == NULL) + return -1; + for (y = 0; y < h; y++) + for (x = 0; x < aw; x++) { + GLchan texel[4]; + + img->FetchTexelc((struct gl_texture_image *)img, + x < w ? x : w - 1, y, 0, texel); + buf[y * aw + x] = t->has_alpha ? (GLushort) + (((texel[3] & 0xf0) << 8) | + ((texel[0] & 0xf0) << 4) | + (texel[1] & 0xf0) | (texel[2] >> 4)) : + (GLushort) + (((texel[0] & 0xf8) << 8) | + ((texel[1] & 0xfc) << 3) | (texel[2] >> 3)); + } + error = rlgl_tex_upload(&vmesa->rl, &t->hw[level], buf); + _mesa_free(buf); + return error; +} + +static GLboolean +vr_min_is_mip(GLenum f) +{ + + return f != GL_NEAREST && f != GL_LINEAR; +} + +static GLboolean +vr_base_has_alpha(const struct gl_texture_image *img) +{ + + switch (img->TexFormat->BaseFormat) { + case GL_RGBA: + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_INTENSITY: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +/* + * Make the current binding resident and bound + */ +GLboolean +vrTexValidate(GLcontext *ctx) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + struct gl_texture_object *tObj = ctx->Texture.Unit[0].Current2D; + const struct gl_texture_image *img = + tObj->Image[0][tObj->BaseLevel]; + struct vr_texture *t = vr_tex_data(ctx, tObj); + + GLint want, i; + GLboolean was_resident; + + VRP_ZE(VRP_Z_TEXVALIDATE); + if (t == NULL) { + VRP_COUNT(tv_failv); + VRP_ZX(VRP_Z_TEXVALIDATE); + return GL_FALSE; + } + t->lru = ++vmesa->lru_tick; + t->used_frame = g_vr_frame; /* pin: not evictable this frame */ + was_resident = (t->hw[0].addr != 0); + + /* levels wanted: full chain for mipmap MIN filters */ + if (vr_min_is_mip(tObj->MinFilter)) { + want = 0; + while (want < VR_MAX_LEVELS - 1 && + tObj->Image[0][tObj->BaseLevel + want] != NULL && + (tObj->Image[0][tObj->BaseLevel + want]->Width > 1 || + tObj->Image[0][tObj->BaseLevel + want]->Height > 1)) + want++; + want++; /* include the 1x1 (or last) level */ + } else + want = 1; + + if (t->hw[0].addr != 0 && + (t->dirty || (t->nlevels < want && !t->capped))) { + if (t->dirty) + VRP_COUNT(tv_dirty); + else + VRP_COUNT(tv_nlevels); + vr_tex_unload(vmesa, t); + } + + if (t->hw[0].addr == 0) { + int nl; + + if (!was_resident) + VRP_COUNT(tv_first); + t->has_alpha = vr_base_has_alpha(img); + + /* + * Allocate the whole pyramid as ONE contiguous block + */ + for (nl = want; nl >= 1; nl--) { + for (i = 0; i < nl; i++) { + const struct gl_texture_image *li = + tObj->Image[0][tObj->BaseLevel + i]; + + if (li == NULL || + rlgl_tex_init(&t->hw[i], + vr_alloc_width(li->Width), li->Height, + t->has_alpha ? RLGL_FMT_4444 : + RLGL_FMT_565) != 0) + break; + } + if (i < nl) /* a level was missing/bad */ + continue; + + while (rlgl_tex_pyramid(&vmesa->rl, t->hw, nl) != 0) { + struct vr_texture *victim = NULL, *it; + + for (it = vmesa->textures; it != NULL; + it = it->next) + if (it != t && + it->hw[0].addr != 0 && + it->used_frame != g_vr_frame && + (victim == NULL || + it->lru < victim->lru)) + victim = it; + if (victim == NULL) + break; /* try fewer levels */ + vr_tex_unload(vmesa, victim); + VRP_COUNT(tv_evict); + } + if (t->hw[0].addr != 0) + break; /* nl levels allocated */ + } + if (t->hw[0].addr == 0) { + VRP_COUNT(tv_failv); + VRP_ZX(VRP_Z_TEXVALIDATE); + return GL_FALSE; /* not even the base fit */ + } + + for (i = 0; i < nl; i++) { + const struct gl_texture_image *li = + tObj->Image[0][tObj->BaseLevel + i]; + + if (vr_tex_upload(vmesa, t, li, i) != 0) { + vr_tex_unload(vmesa, t); + VRP_COUNT(tv_failu); + VRP_ZX(VRP_Z_TEXVALIDATE); + return GL_FALSE; + } + } + t->nlevels = nl; + t->capped = (nl < want); + t->dirty = GL_FALSE; + vmesa->engine_dirty = GL_TRUE; + } else { + VRP_COUNT(tv_bindonly); /* resident, no upload */ + } + + rlgl_tex_env_code(&vmesa->rl, + ctx->Texture.Unit[0].EnvMode == GL_MODULATE ? + RLGL_ENV_MODULATE : + (ctx->Texture.Unit[0].EnvMode == GL_DECAL ? + RLGL_ENV_DECAL : RLGL_ENV_REPLACE)); + rlgl_tex_filter(&vmesa->rl, tObj->MagFilter == GL_LINEAR); + rlgl_tex_bind(&vmesa->rl, &t->hw[0]); + rlgl_tex_wrap(&vmesa->rl, tObj->WrapS != GL_REPEAT, + tObj->WrapT != GL_REPEAT); + + if (_mesa_getenv("VRMESA_DEBUG")) + _mesa_printf("vrTexValidate: obj=%u nlvl=%d addr0=%x " + "min=%x\n", tObj->Name, t->nlevels, t->hw[0].addr, + tObj->MinFilter); + + vmesa->tex_cur = t; + vmesa->tex_mip = vr_min_is_mip(tObj->MinFilter) && t->nlevels > 1; + vmesa->tex_min_bilin = + (tObj->MinFilter == GL_LINEAR_MIPMAP_NEAREST || + tObj->MinFilter == GL_LINEAR_MIPMAP_LINEAR || + tObj->MinFilter == GL_LINEAR); + vmesa->tex_mag_bilin = (tObj->MagFilter == GL_LINEAR); + vmesa->cur_level = 0; + vmesa->cur_bilin = vmesa->tex_mag_bilin; + + vmesa->tex_w = vmesa->tex_base_w = (GLfloat)img->Width; + vmesa->tex_h = vmesa->tex_base_h = (GLfloat)img->Height; + vmesa->tex_off = vmesa->tex_mag_bilin ? 0.5F : 0.0F; + VRP_ZX(VRP_Z_TEXVALIDATE); + return GL_TRUE; +} diff --git a/src/mesa/drivers/verite/vrtris.c b/src/mesa/drivers/verite/vrtris.c new file mode 100644 index 0000000..48c270f --- /dev/null +++ b/src/mesa/drivers/verite/vrtris.c @@ -0,0 +1,1085 @@ +/* + * 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. + */ + +/* + * veritemesa hardware triangles. + */ + +#include <math.h> +#include <stdlib.h> + +#include "vrdrv.h" + +#include "context.h" +#include "imports.h" +#include "macros.h" +#include "colormac.h" +#include "swrast/swrast.h" +#include "swrast_setup/swrast_setup.h" +#include "swrast_setup/ss_context.h" +#include "tnl/tnl.h" +#include "tnl/t_context.h" + +static unsigned long g_vr_nat_polys, g_vr_nat_tris; +static unsigned long g_vr_pt_polys, g_vr_pt_tris; +static unsigned long g_vr_cb_tris; + +static int g_vr_nocache; +static int g_vr_noblendfan; +static float g_vr_zbias; + +/* + * Window-Z finalizer. + */ +#define VR_ZSCALE (65000.0F / 65535.0F) + +static GLfloat +vr_winz(GLfloat mesa_z) +{ + GLfloat z = mesa_z * VR_ZSCALE; + + if (z < 0.0F) + return 0.0F; + if (z > 65000.0F) + return 65000.0F; + return z; +} + +static void +vr_emit_at(vrmesa_context *vmesa, GLfloat wx, GLfloat wy, + const SWvertex *v, const GLchan color[4], struct rlgl_vtx *o) +{ + const GLfloat w = (GLfloat)vmesa->rl.v.width; + const GLfloat h = (GLfloat)vmesa->rl.v.height; + GLfloat x = wx, y; + + o->r = (GLfloat)color[0]; + o->g = (GLfloat)color[1]; + o->b = (GLfloat)color[2]; + + if (x < 0.0F) x = 0.0F; + if (x > w) x = w; + o->x = x; + y = h - wy; + if (y < 0.0F) y = 0.0F; + if (y > h) y = h; + o->y = y; + o->z = vr_winz(v->win[2]); /* rescale 65535->65000, see vr_winz */ + /* + * u/v in texels, q = 1/w + */ + if (vmesa->tex_on) { + o->u = v->texcoord[0][0] * vmesa->tex_w - vmesa->tex_off; + o->v = v->texcoord[0][1] * vmesa->tex_h - vmesa->tex_off; + } else { + o->u = 0.0F; + o->v = 0.0F; + } + o->q = v->win[3]; + /* + * Fog factor for the KFXYZUVQ path. + */ + o->f = CLAMP(v->fog, 0.0F, 1.0F) * 255.0F; +} + +static void +vr_emit_vtx(vrmesa_context *vmesa, const SWvertex *v, + const GLchan color[4], struct rlgl_vtx *o) +{ + + vr_emit_at(vmesa, v->win[0], v->win[1], v, color, o); +} + +/* + * PE blend factor mapping + */ +static GLboolean +vr_blend_factor(GLenum f, GLboolean is_src, GLuint *code, + GLboolean *needs_alpha) +{ + + switch (f) { + case GL_ZERO: *code = RLGL_BL_ZERO; break; + case GL_ONE: *code = RLGL_BL_ONE; break; + case GL_DST_ALPHA: *code = RLGL_BL_ONE; break; + case GL_ONE_MINUS_DST_ALPHA: *code = RLGL_BL_ZERO; break; + case GL_SRC_ALPHA: + *code = RLGL_BL_SRCALPHA; + *needs_alpha = GL_TRUE; + break; + case GL_ONE_MINUS_SRC_ALPHA: + *code = RLGL_BL_INVSRCALPHA; + *needs_alpha = GL_TRUE; + break; + /* + * Color-factor codes are SIDE-RELATIVE + */ + case GL_SRC_COLOR: + *code = is_src ? RLGL_BL_OWNCOLOR : RLGL_BL_OTHERCOLOR; + break; + case GL_ONE_MINUS_SRC_COLOR: + *code = is_src ? RLGL_BL_INVOWNCOLOR : + RLGL_BL_INVOTHERCOLOR; + break; + case GL_DST_COLOR: + *code = is_src ? RLGL_BL_OTHERCOLOR : RLGL_BL_OWNCOLOR; + break; + case GL_ONE_MINUS_DST_COLOR: + *code = is_src ? RLGL_BL_INVOTHERCOLOR : + RLGL_BL_INVOWNCOLOR; + break; + default: + return GL_FALSE; + } + return GL_TRUE; +} + +GLboolean +vrBlendMap(const GLcontext *ctx, GLuint *srcf, GLuint *dstf, + GLboolean *needs_alpha) +{ + + *needs_alpha = GL_FALSE; + if (ctx->Color.BlendEquationRGB != GL_FUNC_ADD) + return GL_FALSE; + if (!vr_blend_factor(ctx->Color.BlendSrcRGB, GL_TRUE, srcf, + needs_alpha)) + return GL_FALSE; + if (!vr_blend_factor(ctx->Color.BlendDstRGB, GL_FALSE, dstf, + needs_alpha)) + return GL_FALSE; + return GL_TRUE; +} + +/* + * Clamp a Mesa-space window Z + */ +static GLfloat +vr_clampz(GLfloat z) +{ + + if (z < 0.0F) + return 0.0F; + if (z > 65535.0F) + return 65535.0F; + return z; +} + +/* + * Polygon offset (glPolygonOffset) computed exactly as swrast does at + * setup + */ +static GLfloat +vr_poly_offset(GLcontext *ctx, const SWvertex *v0, const SWvertex *v1, + const SWvertex *v2) +{ + GLfloat ex = v0->win[0] - v2->win[0], ey = v0->win[1] - v2->win[1]; + GLfloat fx = v1->win[0] - v2->win[0], fy = v1->win[1] - v2->win[1]; + GLfloat cc = ex * fy - ey * fx; + GLfloat off = ctx->Polygon.OffsetUnits; /* * MRD (= 1) */ + + if (cc * cc > 1e-16F) { + GLfloat ez = v0->win[2] - v2->win[2]; + GLfloat fz = v1->win[2] - v2->win[2]; + GLfloat ooa = 1.0F / cc; + GLfloat dzdx = (ey * fz - ez * fy) * ooa; + GLfloat dzdy = (ez * fx - ex * fz) * ooa; + GLfloat m, nz; + + if (dzdx < 0.0F) dzdx = -dzdx; + if (dzdy < 0.0F) dzdy = -dzdy; + m = dzdx > dzdy ? dzdx : dzdy; + off += m * ctx->Polygon.OffsetFactor; + /* clamp so no vertex Z goes below 0 (matches swrast) */ + nz = -v0->win[2]; if (off < nz) off = nz; + nz = -v1->win[2]; if (off < nz) off = nz; + nz = -v2->win[2]; if (off < nz) off = nz; + } + return off; +} + +static void +vr_tri(GLcontext *ctx, GLuint e0, GLuint e1, GLuint e2, GLuint eprov) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + const SWvertex *v0 = &verts[e0], *v1 = &verts[e1], *v2 = &verts[e2]; + struct rlgl_vtx a, b, c; + const GLboolean flat = (ctx->Light.ShadeModel == GL_FLAT); + const GLchan *pc = verts[eprov].color; + + if (vmesa->blend_alpha || vmesa->atest_alpha) { + GLuint a8 = flat ? pc[3] : v0->color[3]; + + if (!flat && (v1->color[3] != a8 || v2->color[3] != a8)) { + VRP_FALLBACK_HOT(VRP_FB_TRI_ALPHAGRAD); + _swrast_Triangle(ctx, v0, v1, v2); + return; + } + if (a8 != vmesa->fg_alpha) { + v3d_st(&vmesa->rl.v, V3D_PE_FGCOLOR, a8 << 24); + vmesa->fg_alpha = a8; + VRP_HOT(fgcolor_writes); + } + } + + if (ctx->Polygon.CullFlag) { + /* window y is flipped on the PE: GL-CCW = negative area */ + GLfloat area = + (v1->win[0] - v0->win[0]) * (v0->win[1] - v2->win[1]) - + (v2->win[0] - v0->win[0]) * (v0->win[1] - v1->win[1]); + GLboolean front = (area < 0.0F) ^ + (ctx->Polygon.FrontFace == GL_CW); + + if (ctx->Polygon.CullFaceMode == GL_FRONT_AND_BACK) + return; + if (ctx->Polygon.CullFaceMode == GL_BACK && !front) + return; + if (ctx->Polygon.CullFaceMode == GL_FRONT && front) + return; + } + + /* + * Per-polygon mipmapping + */ + if (vmesa->tex_on && vmesa->tex_mip) { + const GLfloat bw = vmesa->tex_base_w; + const GLfloat bh = vmesa->tex_base_h; + GLfloat du1 = (v1->texcoord[0][0] - v0->texcoord[0][0]) * bw; + GLfloat dv1 = (v1->texcoord[0][1] - v0->texcoord[0][1]) * bh; + GLfloat du2 = (v2->texcoord[0][0] - v0->texcoord[0][0]) * bw; + GLfloat dv2 = (v2->texcoord[0][1] - v0->texcoord[0][1]) * bh; + GLfloat auv = du1 * dv2 - du2 * dv1; + GLfloat axy = + (v1->win[0] - v0->win[0]) * (v2->win[1] - v0->win[1]) - + (v2->win[0] - v0->win[0]) * (v1->win[1] - v0->win[1]); + GLfloat rho2; + GLint level = 0, nl = vrTexNumLevels(vmesa->tex_cur); + GLint bilin; + GLint lw, lh; + + if (auv < 0.0F) auv = -auv; + if (axy < 0.0F) axy = -axy; + rho2 = (axy > 1e-12F) ? auv / axy : 0.0F; + if (rho2 > 1.0F) { + GLfloat lod = 0.5F * LOG2(rho2); + + level = (GLint)(lod + 0.5F); + if (level >= nl) + level = nl - 1; + } + bilin = (level == 0 && rho2 <= 1.0F) ? + vmesa->tex_mag_bilin : vmesa->tex_min_bilin; + if (g_vr_nocache || level != vmesa->cur_level) { + rlgl_tex_bind(&vmesa->rl, + vrTexLevel(vmesa->tex_cur, level)); + vmesa->cur_level = level; + VRP_HOT(mip_binds); + } + if (g_vr_nocache || bilin != vmesa->cur_bilin) { + rlgl_tex_filter(&vmesa->rl, bilin); + vmesa->cur_bilin = bilin; + VRP_HOT(filt_changes); + } + lw = (GLint)bw >> level; + lh = (GLint)bh >> level; + vmesa->tex_w = (GLfloat)(lw > 0 ? lw : 1); + vmesa->tex_h = (GLfloat)(lh > 0 ? lh : 1); + vmesa->tex_off = bilin ? 0.5F : 0.0F; + } + + VRP_ZE3(VRP_Z_TRI_EMIT); + vr_emit_vtx(vmesa, v0, flat ? pc : v0->color, &a); + vr_emit_vtx(vmesa, v1, flat ? pc : v1->color, &b); + vr_emit_vtx(vmesa, v2, flat ? pc : v2->color, &c); + /* polygon offset: per-poly window-Z bias over the emitted z */ + if (ctx->Polygon.OffsetFill) { + GLfloat off = vr_poly_offset(ctx, v0, v1, v2); + + a.z = vr_winz(v0->win[2] + off); + b.z = vr_winz(v1->win[2] + off); + c.z = vr_winz(v2->win[2] + off); + } + rlgl_tri(&vmesa->rl, &a, &b, &c); + vmesa->engine_dirty = GL_TRUE; + VRP_ZX3(VRP_Z_TRI_EMIT); +} + +static void +vr_triangle(GLcontext *ctx, GLuint e0, GLuint e1, GLuint e2) +{ + + g_vr_cb_tris++; /* clipped path / any Render.Triangle caller */ + vr_tri(ctx, e0, e1, e2, e2); +} + +static void +vr_quad(GLcontext *ctx, GLuint e0, GLuint e1, GLuint e2, GLuint e3) +{ + + vr_tri(ctx, e0, e1, e2, e3); + vr_tri(ctx, e0, e2, e3, e3); +} + +/* longest run collapsed to one native fan/strip, larger falls to per-tri */ +#define VR_MAX_RUN 128 + +static void +vr_strip_stat(void) +{ + + if (_mesa_getenv("VRMESA_STRIPSTAT")) + _mesa_printf("VRSTRIP native_polys=%lu native_tris=%lu " + "pertri_polys=%lu pertri_tris=%lu clipped_tris=%lu\n", + g_vr_nat_polys, g_vr_nat_tris, g_vr_pt_polys, g_vr_pt_tris, + g_vr_cb_tris); +} + +static GLuint +vidx(const GLuint *elt, GLuint j) +{ + + return elt ? elt[j] : j; +} + +static GLboolean +vr_native_run(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count, + GLboolean cull_uniform, GLuint flat_prov) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + const GLchan *fc = (flat_prov != ~0U) ? + verts[vidx(elt, flat_prov)].color : NULL; + struct rlgl_vtx buf[VR_MAX_RUN]; + GLuint n = count - start, i; + + if (n < 3 || n > VR_MAX_RUN) + return GL_FALSE; + if (ctx->Polygon.CullFlag) { + const SWvertex *v0, *v1, *v2; + GLfloat area; + GLboolean front; + + /* + * A tri-strip's winding alternates per triangle, and a + * non-convex tri-fan's can flip + */ + if (!cull_uniform) + return GL_FALSE; + v0 = &verts[vidx(elt, start)]; + v1 = &verts[vidx(elt, start + 1)]; + v2 = &verts[vidx(elt, start + 2)]; + area = (v1->win[0] - v0->win[0]) * (v0->win[1] - v2->win[1]) - + (v2->win[0] - v0->win[0]) * (v0->win[1] - v1->win[1]); + front = (area < 0.0F) ^ (ctx->Polygon.FrontFace == GL_CW); + if (ctx->Polygon.CullFaceMode == GL_FRONT_AND_BACK) + return GL_TRUE; /* whole run culled */ + if (ctx->Polygon.CullFaceMode == GL_BACK && !front) + return GL_TRUE; + if (ctx->Polygon.CullFaceMode == GL_FRONT && front) + return GL_TRUE; + } + for (i = 0; i < n; i++) { + const SWvertex *v = &verts[vidx(elt, start + i)]; + + vr_emit_vtx(vmesa, v, fc ? fc : v->color, &buf[i]); + if (g_vr_zbias != 0.0F) { + buf[i].z -= g_vr_zbias; + if (buf[i].z < 0.0F) + buf[i].z = 0.0F; + } + } + rlgl_trifan(&vmesa->rl, buf, (int)n); + g_vr_nat_polys++; + g_vr_nat_tris += n - 2; + vmesa->engine_dirty = GL_TRUE; + return GL_TRUE; +} + +static void +vr_c_triangles(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + GLuint j; + + for (j = start + 2; j < count; j += 3) + vr_tri(ctx, vidx(elt, j - 2), vidx(elt, j - 1), vidx(elt, j), + vidx(elt, j)); +} + +static void +vr_c_tri_strip(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + GLuint j, parity = 0; + + for (j = start + 2; j < count; j++, parity ^= 1) + vr_tri(ctx, vidx(elt, j - 2 + parity), vidx(elt, j - 1 - parity), + vidx(elt, j), vidx(elt, j)); +} + +static void +vr_c_tri_fan(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLuint j; + + if (vmesa->strips_ok && ctx->Light.ShadeModel == GL_SMOOTH && + vr_native_run(ctx, elt, start, count, GL_FALSE, ~0U)) + return; + g_vr_pt_polys++; + g_vr_pt_tris += count - start - 2; + for (j = start + 2; j < count; j++) + vr_tri(ctx, vidx(elt, start), vidx(elt, j - 1), vidx(elt, j), + vidx(elt, j)); +} + +static void +vr_c_poly(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLboolean flat = (ctx->Light.ShadeModel == GL_FLAT); + GLuint j; + + if (vmesa->strips_ok && vr_native_run(ctx, elt, start, count, + GL_TRUE, flat ? start : ~0U)) /* cull_uniform: convex */ + return; + g_vr_pt_polys++; + g_vr_pt_tris += count - start - 2; + for (j = start + 2; j < count; j++) + vr_tri(ctx, vidx(elt, j - 1), vidx(elt, j), vidx(elt, start), + vidx(elt, start)); +} + +static void +vr_c_quads(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + GLuint j; + + for (j = start + 3; j < count; j += 4) + vr_quad(ctx, vidx(elt, j - 3), vidx(elt, j - 2), + vidx(elt, j - 1), vidx(elt, j)); +} + +static void +vr_c_quad_strip(GLcontext *ctx, const GLuint *elt, GLuint start, GLuint count) +{ + GLuint j; + + for (j = start + 3; j < count; j += 2) + vr_quad(ctx, vidx(elt, j - 1), vidx(elt, j - 3), + vidx(elt, j - 2), vidx(elt, j)); +} + +/* generate the {verts,elts} PrimTab wrappers around each run core */ +#define VR_TAB(core) \ +static void core##_verts(GLcontext *ctx, GLuint s, GLuint c, GLuint f) \ +{ \ + (void)f; \ + core(ctx, NULL, s, c); \ +} \ +static void core##_elts(GLcontext *ctx, GLuint s, GLuint c, GLuint f) \ +{ \ + (void)f; \ + core(ctx, TNL_CONTEXT(ctx)->vb.Elts, s, c); \ +} + +VR_TAB(vr_c_triangles) +VR_TAB(vr_c_tri_strip) +VR_TAB(vr_c_tri_fan) +VR_TAB(vr_c_poly) +VR_TAB(vr_c_quads) +VR_TAB(vr_c_quad_strip) + +static tnl_render_func vr_tab_verts[GL_POLYGON + 2]; +static tnl_render_func vr_tab_elts[GL_POLYGON + 2]; +static GLboolean vr_tab_ready = GL_FALSE; + +static void +vr_build_tables(TNLcontext *tnl) +{ + GLuint i; + + if (vr_tab_ready) + return; + for (i = 0; i < GL_POLYGON + 2; i++) { + vr_tab_verts[i] = tnl->Driver.Render.PrimTabVerts[i]; + vr_tab_elts[i] = tnl->Driver.Render.PrimTabElts[i]; + } + vr_tab_verts[GL_TRIANGLES] = vr_c_triangles_verts; + vr_tab_verts[GL_TRIANGLE_STRIP] = vr_c_tri_strip_verts; + vr_tab_verts[GL_TRIANGLE_FAN] = vr_c_tri_fan_verts; + vr_tab_verts[GL_POLYGON] = vr_c_poly_verts; + vr_tab_verts[GL_QUADS] = vr_c_quads_verts; + vr_tab_verts[GL_QUAD_STRIP] = vr_c_quad_strip_verts; + vr_tab_elts[GL_TRIANGLES] = vr_c_triangles_elts; + vr_tab_elts[GL_TRIANGLE_STRIP] = vr_c_tri_strip_elts; + vr_tab_elts[GL_TRIANGLE_FAN] = vr_c_tri_fan_elts; + vr_tab_elts[GL_POLYGON] = vr_c_poly_elts; + vr_tab_elts[GL_QUADS] = vr_c_quads_elts; + vr_tab_elts[GL_QUAD_STRIP] = vr_c_quad_strip_elts; + vr_tab_ready = GL_TRUE; +} + +/* + * Line as a perpendicular-offset quad (2 tris), width = Line._Width. + */ +static void +vr_line(GLcontext *ctx, GLuint e0, GLuint e1) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + const SWvertex *v0 = &verts[e0], *v1 = &verts[e1]; + const GLboolean flat = (ctx->Light.ShadeModel == GL_FLAT); + const GLchan *c0 = flat ? v1->color : v0->color; + const GLchan *c1 = v1->color; /* provoking = last vertex */ + GLfloat dx = v1->win[0] - v0->win[0]; + GLfloat dy = v1->win[1] - v0->win[1]; + GLfloat len = (GLfloat)sqrt(dx * dx + dy * dy); + GLfloat hw = ctx->Line._Width * 0.5F; + GLfloat px, py; + struct rlgl_vtx a, b, c, d; + + if (len < 1e-4F) + return; /* degenerate */ + if (hw < 0.75F) { + GLfloat adx = dx < 0.0F ? -dx : dx; + GLfloat ady = dy < 0.0F ? -dy : dy; + + if (adx > 0.5F && ady > 0.5F) + hw = 0.75F; + } + px = -dy / len * hw; /* perpendicular * half-width */ + py = dx / len * hw; + vr_emit_at(vmesa, v0->win[0] + px, v0->win[1] + py, v0, c0, &a); + vr_emit_at(vmesa, v0->win[0] - px, v0->win[1] - py, v0, c0, &b); + vr_emit_at(vmesa, v1->win[0] - px, v1->win[1] - py, v1, c1, &c); + vr_emit_at(vmesa, v1->win[0] + px, v1->win[1] + py, v1, c1, &d); + rlgl_tri(&vmesa->rl, &a, &b, &c); + rlgl_tri(&vmesa->rl, &a, &c, &d); + vmesa->engine_dirty = GL_TRUE; +} + +/* + * Native hardware line via the microcode's own line drawers + */ +static void +vr_line_native(GLcontext *ctx, GLuint e0, GLuint e1, GLboolean aa) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + const SWvertex *v0 = &verts[e0], *v1 = &verts[e1]; + const GLboolean flat = (ctx->Light.ShadeModel == GL_FLAT); + const GLchan *c0 = flat ? v1->color : v0->color; + const GLchan *c1 = v1->color; /* provoking = last vertex */ + struct rlgl_vtx a, b; + + vr_emit_vtx(vmesa, v0, c0, &a); + vr_emit_vtx(vmesa, v1, c1, &b); + a.x -= 0.5F; a.y -= 0.5F; + b.x -= 0.5F; b.y -= 0.5F; + if (aa) + rlgl_aaline(&vmesa->rl, &a, &b); + else + rlgl_line(&vmesa->rl, &a, &b); + vmesa->engine_dirty = GL_TRUE; +} + +static void +vr_hwline(GLcontext *ctx, GLuint e0, GLuint e1) +{ + + vr_line_native(ctx, e0, e1, GL_FALSE); +} + +static void +vr_aaline(GLcontext *ctx, GLuint e0, GLuint e1) +{ + + vr_line_native(ctx, e0, e1, GL_TRUE); +} + +/* One point as a centered square (2 tris), half-side hs = Point._Size/2. */ +static void +vr_point_emit(vrmesa_context *vmesa, const SWvertex *v, GLfloat hs) +{ + struct rlgl_vtx a, b, c, d; + GLfloat x = v->win[0], y = v->win[1]; + + vr_emit_at(vmesa, x - hs, y - hs, v, v->color, &a); + vr_emit_at(vmesa, x + hs, y - hs, v, v->color, &b); + vr_emit_at(vmesa, x + hs, y + hs, v, v->color, &c); + vr_emit_at(vmesa, x - hs, y + hs, v, v->color, &d); + rlgl_tri(&vmesa->rl, &a, &b, &c); + rlgl_tri(&vmesa->rl, &a, &c, &d); +} + +static void +vr_points(GLcontext *ctx, GLuint first, GLuint last) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb; + const GLfloat hs = ctx->Point._Size * 0.5F; + GLuint i; + + for (i = first; i < last; i++) { + GLuint j = VB->Elts ? VB->Elts[i] : i; + + if (VB->ClipMask[j]) + continue; + vr_point_emit(vmesa, &verts[j], hs); + } + vmesa->engine_dirty = GL_TRUE; +} + +/* + * Native anti-aliased points (GL_POINT_SMOOTH) + */ +static void +vr_aapoints(GLcontext *ctx, GLuint first, GLuint last) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + const SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb; + const GLfloat sz = ctx->Point._Size; + GLuint i; + + rlgl_aapoint_begin(&vmesa->rl); + for (i = first; i < last; i++) { + GLuint j = VB->Elts ? VB->Elts[i] : i; + struct rlgl_vtx a; + + if (VB->ClipMask[j]) + continue; + vr_emit_vtx(vmesa, &verts[j], verts[j].color, &a); + rlgl_aapoint(&vmesa->rl, &a, sz); + } + rlgl_aapoint_end(&vmesa->rl); + rlgl_depth_func(&vmesa->rl, + ctx->Depth.Test && ctx->Depth.Func != GL_ALWAYS, + ctx->Depth.Func == GL_LESS ? RLGL_Z_LT : RLGL_Z_LE); + /* Z-write only with the test enabled (see vr_update_state) */ + rlgl_depth_mask(&vmesa->rl, ctx->Depth.Test && ctx->Depth.Mask); + vmesa->engine_dirty = GL_TRUE; +} + +/* + * Unfilled triangle (glPolygonMode GL_LINE / GL_POINT). + */ +static void +vr_unfilled_tri(GLcontext *ctx, GLuint e0, GLuint e1, GLuint e2) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + SWvertex *verts = SWSETUP_CONTEXT(ctx)->verts; + SWvertex *v0 = &verts[e0], *v1 = &verts[e1], *v2 = &verts[e2]; + const GLubyte *ef = TNL_CONTEXT(ctx)->vb.EdgeFlag; + GLfloat ex = v0->win[0] - v2->win[0], ey = v0->win[1] - v2->win[1]; + GLfloat fx = v1->win[0] - v2->win[0], fy = v1->win[1] - v2->win[1]; + GLfloat cc = ex * fy - ey * fx; + GLuint facing = (cc < 0.0F) ^ ctx->Polygon._FrontBit; + GLenum mode = facing ? ctx->Polygon.BackMode : ctx->Polygon.FrontMode; + GLchan s0[4], s1[4]; + GLfloat z0 = 0, z1 = 0, z2 = 0; + GLboolean flat, offed = GL_FALSE; + + if (mode == GL_FILL) { /* mixed modes: fill this face */ + vr_tri(ctx, e0, e1, e2, e2); + return; + } + if (ctx->Polygon.CullFlag) { /* whole-face cull, per swsetup */ + if (facing == 1 && ctx->Polygon.CullFaceMode != GL_FRONT) + return; + if (facing == 0 && ctx->Polygon.CullFaceMode != GL_BACK) + return; + } + + flat = (ctx->Light.ShadeModel == GL_FLAT); + if (flat) { + COPY_CHAN4(s0, v0->color); + COPY_CHAN4(s1, v1->color); + COPY_CHAN4(v0->color, v2->color); + COPY_CHAN4(v1->color, v2->color); + } + if ((mode == GL_LINE && ctx->Polygon.OffsetLine) || + (mode == GL_POINT && ctx->Polygon.OffsetPoint)) { + GLfloat off = vr_poly_offset(ctx, v0, v1, v2); + + z0 = v0->win[2]; z1 = v1->win[2]; z2 = v2->win[2]; + v0->win[2] = vr_clampz(z0 + off); + v1->win[2] = vr_clampz(z1 + off); + v2->win[2] = vr_clampz(z2 + off); + offed = GL_TRUE; + } + + if (mode == GL_POINT) { + GLfloat hs = ctx->Point._Size * 0.5F; + + if (ef[e0]) vr_point_emit(vmesa, v0, hs); + if (ef[e1]) vr_point_emit(vmesa, v1, hs); + if (ef[e2]) vr_point_emit(vmesa, v2, hs); + } else { /* GL_LINE */ + if (ef[e0]) vr_line(ctx, e0, e1); + if (ef[e1]) vr_line(ctx, e1, e2); + if (ef[e2]) vr_line(ctx, e2, e0); + } + vmesa->engine_dirty = GL_TRUE; + + if (offed) { + v0->win[2] = z0; + v1->win[2] = z1; + v2->win[2] = z2; + } + if (flat) { + COPY_CHAN4(v0->color, s0); + COPY_CHAN4(v1->color, s1); + } +} + +/* + * Unfilled quad + */ +static void +vr_unfilled_quad(GLcontext *ctx, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +{ + GLubyte *ef = TNL_CONTEXT(ctx)->vb.EdgeFlag; + GLubyte ef1 = ef[v1], ef3 = ef[v3]; + + ef[v1] = 0; + vr_unfilled_tri(ctx, v0, v1, v3); + ef[v1] = ef1; + ef[v3] = 0; + vr_unfilled_tri(ctx, v1, v2, v3); + ef[v3] = ef3; +} + +/* + * State gate for the v1 hardware path. + */ +void +vrCheckHwRender(GLcontext *ctx) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + GLboolean hw = GL_TRUE; + + vmesa->blend_alpha = GL_FALSE; + vmesa->atest_alpha = GL_FALSE; + if (ctx->Texture._EnabledUnits != 0 && !vrTexHwOk(ctx)) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_TEX_HWOK); + } + if (ctx->Color.BlendEnabled) { + GLuint srcf, dstf; + GLboolean na; + + if (!vrBlendMap(ctx, &srcf, &dstf, &na)) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_BLEND); + } else + vmesa->blend_alpha = na; + } + if (ctx->Color.AlphaEnabled) { + /* + * TranspReject rejects SrcAlpha <= AlphaThres, and + * v_TranspReject is enable-only (redline), DUH. + */ + GLenum f = ctx->Color.AlphaFunc; + + if (f != GL_GREATER && f != GL_GEQUAL && + f != GL_NEVER && f != GL_ALWAYS) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_ALPHAFUNC); + } else if (f != GL_ALWAYS && + !(ctx->Texture._EnabledUnits == 0x1 && + ctx->Texture.Unit[0].EnvMode == GL_REPLACE)) + vmesa->atest_alpha = GL_TRUE; + } + /* fog now rides the KFXYZUVQ per-vertex f term + PE FogColor/En */ + if (ctx->Depth.Test && ctx->Depth.Func != GL_LESS && + ctx->Depth.Func != GL_LEQUAL && ctx->Depth.Func != GL_ALWAYS) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_DEPTHFUNC); + } + if (ctx->_TriangleCaps & (DD_TRI_STIPPLE | DD_TRI_SMOOTH | + DD_TRI_LIGHT_TWOSIDE | DD_SEPARATE_SPECULAR)) { + hw = GL_FALSE; /* offset rides a per-tri Z bias now */ + /* split the mask so the histogram shows which cap gated */ + if (ctx->_TriangleCaps & DD_TRI_STIPPLE) + VRP_FALLBACK(VRP_FB_STIPPLE); + if (ctx->_TriangleCaps & DD_TRI_SMOOTH) + VRP_FALLBACK(VRP_FB_SMOOTH); + if (ctx->_TriangleCaps & DD_TRI_LIGHT_TWOSIDE) + VRP_FALLBACK(VRP_FB_TWOSIDE); + if (ctx->_TriangleCaps & DD_SEPARATE_SPECULAR) + VRP_FALLBACK(VRP_FB_SPECULAR); + } + if ((ctx->_TriangleCaps & DD_TRI_UNFILLED) && + (ctx->Line.SmoothFlag || ctx->Line.StippleFlag || + ctx->Point.SmoothFlag || ctx->Point._Attenuated || + ctx->Point.PointSprite)) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_UNFILLED_SUB); + } + if (ctx->RenderMode != GL_RENDER) { + hw = GL_FALSE; + VRP_FALLBACK(VRP_FB_RENDERMODE); + } + + vmesa->hw_tris = hw; +} + +static void +vr_build_vertices(GLcontext *ctx, GLuint start, GLuint end, GLuint newinputs) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + TNLcontext *tnl = TNL_CONTEXT(ctx); + struct vertex_buffer *VB = &tnl->vb; + struct tnl_clipspace *vtx = &tnl->clipspace; + const GLfloat *m = ctx->Viewport._WindowMap.m; + SWvertex *vbuf; + const GLubyte *pin, *cin, *tin = NULL, *fin = NULL; + GLuint pstr, cstr, tstr = 0, fstr = 0, csize, tsize = 0, g; + + if (!vmesa->fastbuild_ok) { + vmesa->ss_build_vertices(ctx, start, end, newinputs); + return; + } + + newinputs |= vtx->new_inputs; + vtx->new_inputs = 0; + if (!newinputs) + return; + + vbuf = (SWvertex *)vtx->vertex_buf; + + pin = (const GLubyte *)VB->NdcPtr->data; + pstr = VB->NdcPtr->stride; + cin = (const GLubyte *)VB->ColorPtr[0]->data; + cstr = VB->ColorPtr[0]->stride; + csize = VB->ColorPtr[0]->size; + if (vmesa->fb_tex) { + tin = (const GLubyte *)VB->TexCoordPtr[0]->data; + tstr = VB->TexCoordPtr[0]->stride; + tsize = VB->TexCoordPtr[0]->size; + } + if (vmesa->fb_fog) { + fin = (const GLubyte *)VB->FogCoordPtr->data; + fstr = VB->FogCoordPtr->stride; + } + + for (g = start; g < end; g++) { + SWvertex *o = &vbuf[g]; + const GLfloat *ndc = (const GLfloat *)(pin + g * pstr); + const GLfloat *col = (const GLfloat *)(cin + g * cstr); + + /* POS: EMIT_4F_VIEWPORT */ + o->win[0] = m[0] * ndc[0] + m[12]; + o->win[1] = m[5] * ndc[1] + m[13]; + o->win[2] = m[10] * ndc[2] + m[14]; + o->win[3] = ndc[3]; + + /* COLOR0: EMIT_4CHAN_4F_RGBA */ + UNCLAMPED_FLOAT_TO_CHAN(o->color[0], col[0]); + UNCLAMPED_FLOAT_TO_CHAN(o->color[1], col[1]); + UNCLAMPED_FLOAT_TO_CHAN(o->color[2], col[2]); + if (csize == 4) + UNCLAMPED_FLOAT_TO_CHAN(o->color[3], col[3]); + else + o->color[3] = CHAN_MAX; + + /* TEX0: EMIT_4F (insert_4f_{4,3,2,1} padding) */ + if (vmesa->fb_tex) { + const GLfloat *t = (const GLfloat *)(tin + g * tstr); + + o->texcoord[0][0] = t[0]; + o->texcoord[0][1] = tsize >= 2 ? t[1] : 0.0F; + o->texcoord[0][2] = tsize >= 3 ? t[2] : 0.0F; + o->texcoord[0][3] = tsize >= 4 ? t[3] : 1.0F; + } + + /* FOG: EMIT_1F */ + if (vmesa->fb_fog) + o->fog = *(const GLfloat *)(fin + g * fstr); + } +} + +static void +vr_render_start(GLcontext *ctx) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + TNLcontext *tnl = TNL_CONTEXT(ctx); + + VRP_ZE(VRP_Z_RENDER_START); + + /* swsetup validation: builds vertices, may re-choose funcs */ + vmesa->ss_render_start(ctx); + + { + const GLuint ri = tnl->render_inputs; + const GLuint allowed = _TNL_BIT_POS | _TNL_BIT_COLOR0 | + _TNL_BIT_FOG | _TNL_BIT_TEX(0); + + vmesa->fb_tex = (ri & _TNL_BIT_TEX(0)) != 0; + vmesa->fb_fog = (ri & _TNL_BIT_FOG) != 0; + vmesa->fastbuild_ok = vmesa->use_fastbuild && + (ri & _TNL_BIT_POS) && (ri & _TNL_BIT_COLOR0) && + (ri & ~allowed) == 0 && + tnl->clipspace.vertex_size == sizeof(SWvertex) && + tnl->vb.NdcPtr != NULL && + (tnl->vb.ColorPtr[0]->size == 3 || + tnl->vb.ColorPtr[0]->size == 4); + } + + if (_mesa_getenv("VRMESA_DEBUG")) + _mesa_printf("vr_render_start: hw=%d texen=%x mip=%d " + "texw=%.0f\n", vmesa->hw_tris, + ctx->Texture._EnabledUnits, vmesa->tex_mip, + vmesa->tex_w); + + if (tnl->Driver.Render.Triangle != vr_triangle && + tnl->Driver.Render.Triangle != vr_unfilled_tri) { + vmesa->ss_triangle = tnl->Driver.Render.Triangle; + vmesa->ss_quad = tnl->Driver.Render.Quad; + } + if (tnl->Driver.Render.Line != vr_line && + tnl->Driver.Render.Line != vr_hwline && + tnl->Driver.Render.Line != vr_aaline) + vmesa->ss_line = tnl->Driver.Render.Line; + if (tnl->Driver.Render.Points != vr_points && + tnl->Driver.Render.Points != vr_aapoints) + vmesa->ss_points = tnl->Driver.Render.Points; + + if (vmesa->hw_tris) { + if (ctx->Texture._EnabledUnits != 0) { + /* gate passed, but residency can still fail */ + if (!vrTexValidate(ctx)) { + vmesa->tex_on = GL_FALSE; + VRP_FALLBACK(VRP_FB_TEX_RESIDENCY); + goto fallback; + } + vmesa->tex_on = GL_TRUE; + } else { + rlgl_tex_bind(&vmesa->rl, NULL); + vmesa->tex_on = GL_FALSE; + } + if (ctx->_TriangleCaps & DD_TRI_UNFILLED) { + tnl->Driver.Render.Triangle = vr_unfilled_tri; + tnl->Driver.Render.Quad = vr_unfilled_quad; + tnl->Driver.Render.PrimTabVerts = vmesa->ss_prim_verts; + tnl->Driver.Render.PrimTabElts = vmesa->ss_prim_elts; + vmesa->strips_ok = GL_FALSE; + } else { + tnl->Driver.Render.Triangle = vr_triangle; + tnl->Driver.Render.Quad = vr_quad; + tnl->Driver.Render.PrimTabVerts = vr_tab_verts; + tnl->Driver.Render.PrimTabElts = vr_tab_elts; + vmesa->strips_ok = vmesa->use_strips && + !vmesa->tex_mip && + !vmesa->blend_alpha && !vmesa->atest_alpha && + !ctx->Polygon.OffsetFill && + !(g_vr_noblendfan && ctx->Color.BlendEnabled); + } + /* + * Line dispatch: + * - stippled -> swrast + * - smooth + narrow + -> vr_aaline + * blending enabled + * - smooth + wide/ -> swrast (HW AA line is ~1px) + * no blend + * - plain + narrow -> vr_hwline + * - plain + wide -> vr_line thin-quad + */ + if (ctx->Line.StippleFlag) + tnl->Driver.Render.Line = vmesa->ss_line; + else if (ctx->Line.SmoothFlag) + tnl->Driver.Render.Line = + (ctx->Color.BlendEnabled && + ctx->Line._Width <= 1.5F) ? + vr_aaline : vmesa->ss_line; + else + tnl->Driver.Render.Line = + (ctx->Line._Width <= 1.5F) ? vr_hwline : vr_line; + /* + * Points: native AA point + */ + if (ctx->Point.SmoothFlag && ctx->Color.BlendEnabled && + ctx->Texture._EnabledUnits == 0 && + !ctx->Point._Attenuated && !ctx->Point.PointSprite) + tnl->Driver.Render.Points = vr_aapoints; + else if (!ctx->Point.SmoothFlag && !ctx->Point._Attenuated && + !ctx->Point.PointSprite) + tnl->Driver.Render.Points = vr_points; + else + tnl->Driver.Render.Points = vmesa->ss_points; + VRP_ZX(VRP_Z_RENDER_START); + return; + } +fallback: + tnl->Driver.Render.Triangle = vmesa->ss_triangle; + tnl->Driver.Render.Quad = vmesa->ss_quad; + tnl->Driver.Render.Line = vmesa->ss_line; + tnl->Driver.Render.Points = vmesa->ss_points; + /* swrast fallback: everything dispatches through the restored funcs */ + tnl->Driver.Render.PrimTabVerts = vmesa->ss_prim_verts; + tnl->Driver.Render.PrimTabElts = vmesa->ss_prim_elts; + vmesa->strips_ok = GL_FALSE; + VRP_ZX(VRP_Z_RENDER_START); +} + +void +vrInitTriFuncs(GLcontext *ctx) +{ + vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); + TNLcontext *tnl = TNL_CONTEXT(ctx); + + /* capture swsetup's Start (installed by _swsetup_Wakeup) */ + vmesa->ss_render_start = tnl->Driver.Render.Start; + tnl->Driver.Render.Start = vr_render_start; + + vmesa->ss_build_vertices = tnl->Driver.Render.BuildVertices; + tnl->Driver.Render.BuildVertices = vr_build_vertices; + vmesa->use_fastbuild = (_mesa_getenv("VRMESA_NOFASTBUILD") == NULL); + + /* + * Capture swsetup's default per-mode render tables, then + * build our batch tables from them. + */ + vmesa->ss_prim_verts = tnl->Driver.Render.PrimTabVerts; + vmesa->ss_prim_elts = tnl->Driver.Render.PrimTabElts; + vr_build_tables(tnl); + vmesa->use_strips = (_mesa_getenv("VRMESA_STRIPS") != NULL); + g_vr_nocache = (_mesa_getenv("VRMESA_NOCACHE") != NULL); + rlgl_force_rebind = g_vr_nocache; + g_vr_noblendfan = (_mesa_getenv("VRMESA_NOBLENDFAN") != NULL); + { + const char *zb = _mesa_getenv("VRMESA_ZBIAS"); + g_vr_zbias = zb ? (float)atoi(zb) : 0.0F; + } + atexit(vr_strip_stat); /* VRMESA_STRIPSTAT fire-rate report */ + + vrCheckHwRender(ctx); +} |
