/* * Copyright (c) 2026 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Radoslaw Kujawa. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * veritemesa public API */ #include #ifdef VERITE_INSTRUMENT #include #endif #include "vrdrv.h" #include "GL/vrmesa.h" #include "context.h" #include "extensions.h" #include "imports.h" #include "array_cache/acache.h" #include "swrast/swrast.h" #include "swrast_setup/swrast_setup.h" #include "tnl/tnl.h" #include "tnl/t_context.h" #include "tnl/t_pipeline.h" #include "drivers/common/driverfuncs.h" #ifdef VERITE_INSTRUMENT /* * The single driver-layer perf accumulator (vrperf.h). */ struct vrperf g_vrp; /* * RunPipeline wrapper */ static void vr_run_pipeline(GLcontext *ctx) { VRP_ZE(VRP_Z_PIPELINE); _tnl_run_pipeline(ctx); VRP_ZX(VRP_Z_PIPELINE); } void vrPerfInit(vrmesa_context *vm) { const char *p = getenv("VRMESA_PROF"); g_vrp.level = p ? atoi(p) : 0; g_vrp.on = g_vrp.level >= 1; g_vrp.rl = &vm->rl; g_vrp.tb0 = vrp_tb64(); g_vrp.ns0 = v3d_now_ns(); /* * Bridge the librlgl/libv3d _ns timers into VRPERF */ if (g_vrp.on && vm->rl.v.opt_stats == 0) vm->rl.v.opt_stats = 1; atexit(vrPerfDump); } void vrPerfSwap(vrmesa_context *vm) { (void)vm; g_vrp.frames++; /* cumulative snapshot, diff two lines to isolate a demo window */ if (g_vrp.level >= 2 && (g_vrp.frames & 127) == 0) printf("VRPERFSNAP frame=%llu texres=%llu zfunc=%llu " "blend=%llu fgcolor=%llu fences=%llu\n", (unsigned long long)g_vrp.frames, (unsigned long long)g_vrp.fb[VRP_FB_TEX_RESIDENCY], (unsigned long long)g_vrp.fb[VRP_FB_DEPTHFUNC], (unsigned long long)g_vrp.fb[VRP_FB_BLEND], (unsigned long long)g_vrp.fgcolor_writes, (unsigned long long)g_vrp.fences); } void vrPerfDump(void) { static const char *zname[VRP_Z__N] = { "pipeline", "render_start", "texvalidate", "tri_emit" }; struct rlgl *rl = g_vrp.rl; double tick_ns, nf; uint64_t dtb; int i; if (g_vrp.printed || g_vrp.level < 1) return; g_vrp.printed = 1; dtb = vrp_tb64() - g_vrp.tb0; tick_ns = dtb ? (double)(v3d_now_ns() - g_vrp.ns0) / (double)dtb : 0.0; nf = g_vrp.frames > 0 ? (double)g_vrp.frames : 1.0; printf("VRPERF frames=%llu\n", (unsigned long long)g_vrp.frames); printf("VRPERF fallback tex=%llu blend=%llu afunc=%llu zfunc=%llu " "stipple=%llu smooth=%llu twoside=%llu specular=%llu " "unfilled=%llu rmode=%llu texres=%llu alphagrad=%llu\n", (unsigned long long)g_vrp.fb[VRP_FB_TEX_HWOK], (unsigned long long)g_vrp.fb[VRP_FB_BLEND], (unsigned long long)g_vrp.fb[VRP_FB_ALPHAFUNC], (unsigned long long)g_vrp.fb[VRP_FB_DEPTHFUNC], (unsigned long long)g_vrp.fb[VRP_FB_STIPPLE], (unsigned long long)g_vrp.fb[VRP_FB_SMOOTH], (unsigned long long)g_vrp.fb[VRP_FB_TWOSIDE], (unsigned long long)g_vrp.fb[VRP_FB_SPECULAR], (unsigned long long)g_vrp.fb[VRP_FB_UNFILLED_SUB], (unsigned long long)g_vrp.fb[VRP_FB_RENDERMODE], (unsigned long long)g_vrp.fb[VRP_FB_TEX_RESIDENCY], (unsigned long long)g_vrp.fb[VRP_FB_TRI_ALPHAGRAD]); printf("VRPERF texval dirty=%llu nlvl=%llu evict=%llu first=%llu " "bindonly=%llu creates=%llu failv=%llu failu=%llu\n", (unsigned long long)g_vrp.tv_dirty, (unsigned long long)g_vrp.tv_nlevels, (unsigned long long)g_vrp.tv_evict, (unsigned long long)g_vrp.tv_first, (unsigned long long)g_vrp.tv_bindonly, (unsigned long long)g_vrp.tv_creates, (unsigned long long)g_vrp.tv_failv, (unsigned long long)g_vrp.tv_failu); printf("VRPERF churn fgcolor=%llu mipbind=%llu filt=%llu fences=%llu\n", (unsigned long long)g_vrp.fgcolor_writes, (unsigned long long)g_vrp.mip_binds, (unsigned long long)g_vrp.filt_changes, (unsigned long long)g_vrp.fences); printf("VRPERF cpu_ms_per_frame tick_ns=%.3f", tick_ns); for (i = 0; i < VRP_Z__N; i++) printf(" %s=%.2f", zname[i], (double)g_vrp.zone[i] * tick_ns / nf / 1e6); printf("\n"); if (rl != NULL) printf("VRPERF gpu_wait_ms_per_frame submit=%.2f sync=%.2f " "flip=%.2f | tris=%llu st_calls=%llu texbinds=%llu " "submit_kb=%llu\n", (double)rl->v.st.submit_ns / nf / 1e6, (double)rl->v.st.sync_ns / nf / 1e6, (double)rl->v.st.flip_ns / nf / 1e6, (unsigned long long)rl->tris, (unsigned long long)rl->v.st.st_calls, (unsigned long long)rl->v.st.texbinds, (unsigned long long)(rl->v.st.submit_bytes / 1024)); } #endif /* VERITE_INSTRUMENT */ vrMesaContext vrMesaCreateContext(const char *ucodepath) { vrmesa_context *vmesa; GLcontext *ctx; struct dd_function_table functions; if (ucodepath == NULL) ucodepath = getenv("VERITE_UCODE"); if (ucodepath == NULL) { _mesa_printf("vrMesaCreateContext: no microcode " "(pass a path or set VERITE_UCODE)\n"); return NULL; } vmesa = CALLOC_STRUCT(vrmesa_context); if (vmesa == NULL) return NULL; if (rlgl_init(&vmesa->rl, ucodepath) != 0) { _mesa_free(vmesa); return NULL; } vmesa->fg_alpha = ~0u; /* FGColor alpha cache: unset */ if (!_mesa_initialize_visual(&vmesa->glvis, GL_TRUE, /* rgb */ GL_TRUE, /* double buffered */ GL_FALSE, /* stereo */ 5, 6, 5, 0, /* rgba bits */ 0, /* index bits */ 16, /* depth bits */ 0, /* stencil */ 0, 0, 0, 0, /* accum */ 0)) { /* samples */ rlgl_shutdown(&vmesa->rl); _mesa_free(vmesa); return NULL; } _mesa_init_driver_functions(&functions); vrInitDDFuncs(&functions); if (!_mesa_initialize_context(&vmesa->glctx, &vmesa->glvis, NULL, &functions, (void *)vmesa)) { rlgl_shutdown(&vmesa->rl); _mesa_free(vmesa); return NULL; } ctx = &vmesa->glctx; vrInitTexConsts(ctx); /* hardware depth+color, no software stencil/accum/alpha planes */ _mesa_initialize_framebuffer(&vmesa->glfb, &vmesa->glvis, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); _swrast_CreateContext(ctx); _ac_CreateContext(ctx); _tnl_CreateContext(ctx); _swsetup_CreateContext(ctx); _swsetup_Wakeup(ctx); /* * The PE fog unit iterates a per-vertex factor (KFXYZUVQ f term) */ _tnl_allow_vertex_fog(ctx, GL_TRUE); _tnl_allow_pixel_fog(ctx, GL_FALSE); vrInitSpanFuncs(ctx); vrInitTriFuncs(ctx); #ifdef VERITE_INSTRUMENT TNL_CONTEXT(ctx)->Driver.RunPipeline = vr_run_pipeline; #else TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline; #endif _mesa_enable_sw_extensions(ctx); vrPerfInit(vmesa); return (vrMesaContext)vmesa; } void vrMesaDestroyContext(vrMesaContext vrctx) { vrmesa_context *vmesa = (vrmesa_context *)vrctx; GLcontext *ctx; if (vmesa == NULL) return; ctx = &vmesa->glctx; if (vrMesaGetCurrentContext() == vrctx) _mesa_make_current2(NULL, NULL, NULL); _swsetup_DestroyContext(ctx); _tnl_DestroyContext(ctx); _ac_DestroyContext(ctx); _swrast_DestroyContext(ctx); _mesa_free_framebuffer_data(&vmesa->glfb); _mesa_free_context_data(ctx); vrPerfDump(); /* while g_vrp.rl is still valid */ rlgl_shutdown(&vmesa->rl); _mesa_free(vmesa); } vrMesaContext vrMesaGetCurrentContext(void) { GET_CURRENT_CONTEXT(ctx); return (vrMesaContext)ctx; } void vrMesaMakeCurrent(vrMesaContext vrctx) { vrmesa_context *vmesa = (vrmesa_context *)vrctx; if (vmesa == NULL) { _mesa_make_current2(NULL, NULL, NULL); return; } _mesa_make_current2(&vmesa->glctx, &vmesa->glfb, &vmesa->glfb); } void vrMesaSwapBuffers(void) { GET_CURRENT_CONTEXT(ctx); vrmesa_context *vmesa; if (ctx == NULL) return; vmesa = VRMESA_CONTEXT(ctx); _mesa_notifySwapBuffers(ctx); rlgl_swap(&vmesa->rl); /* rlgl_swap fences the engine before flipping */ vmesa->engine_dirty = GL_FALSE; vrPerfSwap(vmesa); }