aboutsummaryrefslogtreecommitdiff
path: root/src/mesa/drivers/verite/vrtex.c
diff options
context:
space:
mode:
authorRadosław Kujawa <radoslaw.kujawa@c0ff33.net>2026-07-17 14:54:32 +0200
committerRadosław Kujawa <radoslaw.kujawa@c0ff33.net>2026-07-17 14:54:32 +0200
commit2702c90d7a39fca2a3319c5e915fd290cb31fc70 (patch)
tree283c4d851efef7e3a7cd03ba80c87e2bffcdbb2c /src/mesa/drivers/verite/vrtex.c
parent836167945ec2c7ad5acdf0aa17ce35ec1b9428a6 (diff)
Initial import.
Diffstat (limited to 'src/mesa/drivers/verite/vrtex.c')
-rw-r--r--src/mesa/drivers/verite/vrtex.c501
1 files changed, 501 insertions, 0 deletions
diff --git a/src/mesa/drivers/verite/vrtex.c b/src/mesa/drivers/verite/vrtex.c
new file mode 100644
index 0000000..4f71dd3
--- /dev/null
+++ b/src/mesa/drivers/verite/vrtex.c
@@ -0,0 +1,501 @@
+/*
+ * 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 texture stage
+ */
+
+#include "vrdrv.h"
+
+#include "context.h"
+#include "imports.h"
+#include "texstore.h"
+#include "texobj.h"
+#include "swrast/swrast.h"
+
+#define VR_MAX_LEVELS 9 /* 256x256 .. 1x1 */
+
+struct vr_texture {
+ /* hw[0].addr == 0 -> not resident; mip chain in hw[1..] */
+ struct rlgl_tex hw[VR_MAX_LEVELS];
+ GLint nlevels; /* resident levels (1 = no mips) */
+ GLboolean has_alpha; /* uploaded as 4444, else 565 */
+ GLboolean dirty; /* core image newer than VRAM */
+ GLboolean capped; /* nlevels is all that fit VRAM;
+ * do not retry for more (anti-thrash) */
+ GLuint lru; /* last validate tick */
+ GLuint used_frame; /* frame last bound (evict pinning) */
+ struct gl_texture_object *obj;
+ struct vr_texture *next;
+};
+
+/*
+ * Frame counter for evict-pinning
+ */
+static GLuint g_vr_frame = 1;
+
+void
+vrTexNewFrame(void)
+{
+
+ g_vr_frame++;
+}
+
+/* vrtris.c reads the level array for per-triangle mip binds */
+struct rlgl_tex *
+vrTexLevel(struct vr_texture *t, GLint level)
+{
+
+ return &t->hw[level];
+}
+
+GLint
+vrTexNumLevels(struct vr_texture *t)
+{
+
+ return t->nlevels;
+}
+
+void
+vrInitTexConsts(GLcontext *ctx)
+{
+
+ ctx->Const.MaxTextureUnits = 1;
+ ctx->Const.MaxTextureImageUnits = 1;
+ ctx->Const.MaxTextureCoordUnits = 1;
+ /* 2^(9-1) = 256: mesaconf tex-256 scene proves the PE takes it */
+ ctx->Const.MaxTextureLevels = 9;
+}
+
+static struct vr_texture *
+vr_tex_data(GLcontext *ctx, struct gl_texture_object *tObj)
+{
+ vrmesa_context *vmesa = VRMESA_CONTEXT(ctx);
+ struct vr_texture *t = tObj->DriverData;
+
+ if (t == NULL) {
+ t = CALLOC_STRUCT(vr_texture);
+ if (t == NULL)
+ return NULL;
+ t->obj = tObj;
+ t->dirty = GL_TRUE;
+ t->next = vmesa->textures;
+ vmesa->textures = t;
+ tObj->DriverData = t;
+ VRP_COUNT(tv_creates);
+ }
+ return t;
+}
+
+static void
+vr_tex_unload(vrmesa_context *vmesa, struct vr_texture *t)
+{
+ GLint i;
+
+ /*
+ * The mip pyramid is ONE contiguous allocation
+ */
+ if (t->hw[0].addr != 0)
+ rlgl_tex_free(&vmesa->rl, &t->hw[0]);
+ for (i = 1; i < VR_MAX_LEVELS; i++)
+ t->hw[i].addr = 0;
+ t->nlevels = 0;
+ /* addresses may be recycled: kill the bind cache */
+ vmesa->rl.cur_tex = 0;
+}
+
+static void
+vr_teximage2d(GLcontext *ctx, GLenum target, GLint level,
+ GLint internalFormat, GLint width, GLint height, GLint border,
+ GLenum format, GLenum type, const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj, struct gl_texture_image *texImage)
+{
+ struct vr_texture *t;
+
+ _mesa_store_teximage2d(ctx, target, level, internalFormat,
+ width, height, border, format, type, pixels, packing,
+ texObj, texImage);
+ t = vr_tex_data(ctx, texObj);
+ if (t != NULL)
+ t->dirty = GL_TRUE;
+}
+
+static void
+vr_texsubimage2d(GLcontext *ctx, GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, const GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing,
+ struct gl_texture_object *texObj, struct gl_texture_image *texImage)
+{
+ struct vr_texture *t;
+
+ _mesa_store_texsubimage2d(ctx, target, level, xoffset, yoffset,
+ width, height, format, type, pixels, packing, texObj,
+ texImage);
+ t = vr_tex_data(ctx, texObj);
+ if (t != NULL)
+ t->dirty = GL_TRUE; /* v1: full re-upload */
+}
+
+static void
+vr_delete_texture(GLcontext *ctx, struct gl_texture_object *tObj)
+{
+ vrmesa_context *vmesa = VRMESA_CONTEXT(ctx);
+ struct vr_texture *t = tObj->DriverData;
+
+ if (t != NULL) {
+ struct vr_texture **pp;
+
+ vr_tex_unload(vmesa, t);
+ for (pp = &vmesa->textures; *pp != NULL;
+ pp = &(*pp)->next)
+ if (*pp == t) {
+ *pp = t->next;
+ break;
+ }
+ _mesa_free(t);
+ tObj->DriverData = NULL;
+ }
+ _mesa_delete_texture_object(ctx, tObj);
+}
+
+/*
+ * Hardware glCopyTexSubImage2D.
+ */
+static void
+vr_copytexsubimage2d(GLcontext *ctx, GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
+ GLsizei height)
+{
+ vrmesa_context *vmesa = VRMESA_CONTEXT(ctx);
+ struct rlgl *rl = &vmesa->rl;
+ struct gl_texture_object *tObj = ctx->Texture.Unit[0].Current2D;
+ GLint fbW = (GLint)rl->v.width, fbH = (GLint)rl->v.height;
+ struct vr_texture *t = (tObj != NULL) ? tObj->DriverData : NULL;
+ GLboolean was_dirty = (t != NULL) ? t->dirty : GL_TRUE;
+
+ /*
+ * Keep the canonical core image correct in every case.
+ */
+ _swrast_copy_texsubimage2d(ctx, target, level, xoffset, yoffset,
+ x, y, width, height);
+
+ if (t == NULL)
+ return;
+
+ if (target != GL_TEXTURE_2D || level != tObj->BaseLevel ||
+ t->hw[0].addr == 0 || t->has_alpha || t->nlevels != 1 ||
+ was_dirty || ctx->_ImageTransferState != 0 ||
+ width <= 0 || height <= 0 ||
+ x < 0 || y < 0 || x + width > fbW || y + height > fbH) {
+ t->dirty = GL_TRUE; /* re-upload from the updated core */
+ return;
+ }
+
+ if (rlgl_copy_to_tex(rl, vr_fb_base(rl, ctx->Pixel.ReadBuffer), fbH,
+ t->hw[0].addr, t->hw[0].w, x, y, xoffset, yoffset,
+ width, height) != 0) {
+ t->dirty = GL_TRUE;
+ return;
+ }
+ t->dirty = GL_FALSE; /* VRAM matches the core copy */
+ vmesa->engine_dirty = GL_TRUE;
+}
+
+void
+vrInitTexFuncs(struct dd_function_table *functions)
+{
+
+ functions->TexImage2D = vr_teximage2d;
+ functions->TexSubImage2D = vr_texsubimage2d;
+ functions->CopyTexSubImage2D = vr_copytexsubimage2d;
+ functions->DeleteTexture = vr_delete_texture;
+}
+
+/*
+ * Can the PE render the CURRENT texture binding?
+ */
+GLboolean
+vrTexHwOk(GLcontext *ctx)
+{
+ const struct gl_texture_unit *unit = &ctx->Texture.Unit[0];
+ const struct gl_texture_object *tObj;
+ const struct gl_texture_image *img;
+
+ if (ctx->Texture._EnabledUnits != 0x1)
+ return GL_FALSE;
+ if (unit->_ReallyEnabled != TEXTURE_2D_BIT)
+ return GL_FALSE;
+ tObj = unit->Current2D;
+ if (tObj == NULL)
+ return GL_FALSE;
+ img = tObj->Image[0][tObj->BaseLevel];
+ if (img == NULL || img->Border != 0 ||
+ img->Width > 256 || img->Height > 256)
+ return GL_FALSE;
+ if (tObj->WrapS != GL_REPEAT && tObj->WrapS != GL_CLAMP_TO_EDGE &&
+ tObj->WrapS != GL_CLAMP)
+ return GL_FALSE;
+ if (tObj->WrapT != GL_REPEAT && tObj->WrapT != GL_CLAMP_TO_EDGE &&
+ tObj->WrapT != GL_CLAMP)
+ return GL_FALSE;
+ /*
+ * GL_CLAMP equals CLAMP_TO_EDGE under NEAREST sampling, but
+ * a LINEAR footprint mixes the border color at the edge
+ */
+ if (tObj->WrapS == GL_CLAMP || tObj->WrapT == GL_CLAMP) {
+ if (tObj->MagFilter == GL_LINEAR ||
+ tObj->MinFilter == GL_LINEAR ||
+ tObj->MinFilter == GL_LINEAR_MIPMAP_NEAREST ||
+ tObj->MinFilter == GL_LINEAR_MIPMAP_LINEAR)
+ return GL_FALSE;
+ }
+ /* all six MIN filters: mipmap ones ride per-poly selection */
+ if (tObj->MagFilter != GL_NEAREST && tObj->MagFilter != GL_LINEAR)
+ return GL_FALSE;
+ if (unit->EnvMode != GL_MODULATE && unit->EnvMode != GL_REPLACE &&
+ unit->EnvMode != GL_DECAL)
+ return GL_FALSE;
+ return GL_TRUE;
+}
+
+static GLuint
+vr_alloc_width(GLuint w)
+{
+
+ if (w < 2)
+ return 2;
+ if (w == 4)
+ return 8;
+ return w;
+}
+
+static int
+vr_tex_upload(vrmesa_context *vmesa, struct vr_texture *t,
+ const struct gl_texture_image *img, GLint level)
+{
+ GLuint w = img->Width, h = img->Height;
+ GLuint aw = vr_alloc_width(w);
+ GLushort *buf;
+ GLuint x, y;
+ int error;
+
+ if (img->FetchTexelc == NULL)
+ return -1;
+ buf = _mesa_malloc(aw * h * sizeof(GLushort));
+ if (buf == NULL)
+ return -1;
+ for (y = 0; y < h; y++)
+ for (x = 0; x < aw; x++) {
+ GLchan texel[4];
+
+ img->FetchTexelc((struct gl_texture_image *)img,
+ x < w ? x : w - 1, y, 0, texel);
+ buf[y * aw + x] = t->has_alpha ? (GLushort)
+ (((texel[3] & 0xf0) << 8) |
+ ((texel[0] & 0xf0) << 4) |
+ (texel[1] & 0xf0) | (texel[2] >> 4)) :
+ (GLushort)
+ (((texel[0] & 0xf8) << 8) |
+ ((texel[1] & 0xfc) << 3) | (texel[2] >> 3));
+ }
+ error = rlgl_tex_upload(&vmesa->rl, &t->hw[level], buf);
+ _mesa_free(buf);
+ return error;
+}
+
+static GLboolean
+vr_min_is_mip(GLenum f)
+{
+
+ return f != GL_NEAREST && f != GL_LINEAR;
+}
+
+static GLboolean
+vr_base_has_alpha(const struct gl_texture_image *img)
+{
+
+ switch (img->TexFormat->BaseFormat) {
+ case GL_RGBA:
+ case GL_ALPHA:
+ case GL_LUMINANCE_ALPHA:
+ case GL_INTENSITY:
+ return GL_TRUE;
+ default:
+ return GL_FALSE;
+ }
+}
+
+/*
+ * Make the current binding resident and bound
+ */
+GLboolean
+vrTexValidate(GLcontext *ctx)
+{
+ vrmesa_context *vmesa = VRMESA_CONTEXT(ctx);
+ struct gl_texture_object *tObj = ctx->Texture.Unit[0].Current2D;
+ const struct gl_texture_image *img =
+ tObj->Image[0][tObj->BaseLevel];
+ struct vr_texture *t = vr_tex_data(ctx, tObj);
+
+ GLint want, i;
+ GLboolean was_resident;
+
+ VRP_ZE(VRP_Z_TEXVALIDATE);
+ if (t == NULL) {
+ VRP_COUNT(tv_failv);
+ VRP_ZX(VRP_Z_TEXVALIDATE);
+ return GL_FALSE;
+ }
+ t->lru = ++vmesa->lru_tick;
+ t->used_frame = g_vr_frame; /* pin: not evictable this frame */
+ was_resident = (t->hw[0].addr != 0);
+
+ /* levels wanted: full chain for mipmap MIN filters */
+ if (vr_min_is_mip(tObj->MinFilter)) {
+ want = 0;
+ while (want < VR_MAX_LEVELS - 1 &&
+ tObj->Image[0][tObj->BaseLevel + want] != NULL &&
+ (tObj->Image[0][tObj->BaseLevel + want]->Width > 1 ||
+ tObj->Image[0][tObj->BaseLevel + want]->Height > 1))
+ want++;
+ want++; /* include the 1x1 (or last) level */
+ } else
+ want = 1;
+
+ if (t->hw[0].addr != 0 &&
+ (t->dirty || (t->nlevels < want && !t->capped))) {
+ if (t->dirty)
+ VRP_COUNT(tv_dirty);
+ else
+ VRP_COUNT(tv_nlevels);
+ vr_tex_unload(vmesa, t);
+ }
+
+ if (t->hw[0].addr == 0) {
+ int nl;
+
+ if (!was_resident)
+ VRP_COUNT(tv_first);
+ t->has_alpha = vr_base_has_alpha(img);
+
+ /*
+ * Allocate the whole pyramid as ONE contiguous block
+ */
+ for (nl = want; nl >= 1; nl--) {
+ for (i = 0; i < nl; i++) {
+ const struct gl_texture_image *li =
+ tObj->Image[0][tObj->BaseLevel + i];
+
+ if (li == NULL ||
+ rlgl_tex_init(&t->hw[i],
+ vr_alloc_width(li->Width), li->Height,
+ t->has_alpha ? RLGL_FMT_4444 :
+ RLGL_FMT_565) != 0)
+ break;
+ }
+ if (i < nl) /* a level was missing/bad */
+ continue;
+
+ while (rlgl_tex_pyramid(&vmesa->rl, t->hw, nl) != 0) {
+ struct vr_texture *victim = NULL, *it;
+
+ for (it = vmesa->textures; it != NULL;
+ it = it->next)
+ if (it != t &&
+ it->hw[0].addr != 0 &&
+ it->used_frame != g_vr_frame &&
+ (victim == NULL ||
+ it->lru < victim->lru))
+ victim = it;
+ if (victim == NULL)
+ break; /* try fewer levels */
+ vr_tex_unload(vmesa, victim);
+ VRP_COUNT(tv_evict);
+ }
+ if (t->hw[0].addr != 0)
+ break; /* nl levels allocated */
+ }
+ if (t->hw[0].addr == 0) {
+ VRP_COUNT(tv_failv);
+ VRP_ZX(VRP_Z_TEXVALIDATE);
+ return GL_FALSE; /* not even the base fit */
+ }
+
+ for (i = 0; i < nl; i++) {
+ const struct gl_texture_image *li =
+ tObj->Image[0][tObj->BaseLevel + i];
+
+ if (vr_tex_upload(vmesa, t, li, i) != 0) {
+ vr_tex_unload(vmesa, t);
+ VRP_COUNT(tv_failu);
+ VRP_ZX(VRP_Z_TEXVALIDATE);
+ return GL_FALSE;
+ }
+ }
+ t->nlevels = nl;
+ t->capped = (nl < want);
+ t->dirty = GL_FALSE;
+ vmesa->engine_dirty = GL_TRUE;
+ } else {
+ VRP_COUNT(tv_bindonly); /* resident, no upload */
+ }
+
+ rlgl_tex_env_code(&vmesa->rl,
+ ctx->Texture.Unit[0].EnvMode == GL_MODULATE ?
+ RLGL_ENV_MODULATE :
+ (ctx->Texture.Unit[0].EnvMode == GL_DECAL ?
+ RLGL_ENV_DECAL : RLGL_ENV_REPLACE));
+ rlgl_tex_filter(&vmesa->rl, tObj->MagFilter == GL_LINEAR);
+ rlgl_tex_bind(&vmesa->rl, &t->hw[0]);
+ rlgl_tex_wrap(&vmesa->rl, tObj->WrapS != GL_REPEAT,
+ tObj->WrapT != GL_REPEAT);
+
+ if (_mesa_getenv("VRMESA_DEBUG"))
+ _mesa_printf("vrTexValidate: obj=%u nlvl=%d addr0=%x "
+ "min=%x\n", tObj->Name, t->nlevels, t->hw[0].addr,
+ tObj->MinFilter);
+
+ vmesa->tex_cur = t;
+ vmesa->tex_mip = vr_min_is_mip(tObj->MinFilter) && t->nlevels > 1;
+ vmesa->tex_min_bilin =
+ (tObj->MinFilter == GL_LINEAR_MIPMAP_NEAREST ||
+ tObj->MinFilter == GL_LINEAR_MIPMAP_LINEAR ||
+ tObj->MinFilter == GL_LINEAR);
+ vmesa->tex_mag_bilin = (tObj->MagFilter == GL_LINEAR);
+ vmesa->cur_level = 0;
+ vmesa->cur_bilin = vmesa->tex_mag_bilin;
+
+ vmesa->tex_w = vmesa->tex_base_w = (GLfloat)img->Width;
+ vmesa->tex_h = vmesa->tex_base_h = (GLfloat)img->Height;
+ vmesa->tex_off = vmesa->tex_mag_bilin ? 0.5F : 0.0F;
+ VRP_ZX(VRP_Z_TEXVALIDATE);
+ return GL_TRUE;
+}