diff options
| author | Radosław Kujawa <radoslaw.kujawa@c0ff33.net> | 2026-07-17 14:54:32 +0200 |
|---|---|---|
| committer | Radosław Kujawa <radoslaw.kujawa@c0ff33.net> | 2026-07-17 14:54:32 +0200 |
| commit | 2702c90d7a39fca2a3319c5e915fd290cb31fc70 (patch) | |
| tree | 283c4d851efef7e3a7cd03ba80c87e2bffcdbb2c /src/mesa/drivers/verite/rlgl.c | |
| parent | 836167945ec2c7ad5acdf0aa17ce35ec1b9428a6 (diff) | |
Initial import.
Diffstat (limited to 'src/mesa/drivers/verite/rlgl.c')
| -rw-r--r-- | src/mesa/drivers/verite/rlgl.c | 982 |
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); + } +} |
