aboutsummaryrefslogtreecommitdiff
path: root/src/mesa/drivers/verite/rlgl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/drivers/verite/rlgl.c')
-rw-r--r--src/mesa/drivers/verite/rlgl.c982
1 files changed, 982 insertions, 0 deletions
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);
+ }
+}