/* * 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 #include #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); }