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/vrtris.c | |
| parent | 836167945ec2c7ad5acdf0aa17ce35ec1b9428a6 (diff) | |
Initial import.
Diffstat (limited to 'src/mesa/drivers/verite/vrtris.c')
| -rw-r--r-- | src/mesa/drivers/verite/vrtris.c | 1085 |
1 files changed, 1085 insertions, 0 deletions
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); +} |
