diff options
Diffstat (limited to 'src/mesa/drivers/verite/vrtex.c')
| -rw-r--r-- | src/mesa/drivers/verite/vrtex.c | 501 |
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; +} |
