/* * 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 device-driver functions */ #include "vrdrv.h" #include "context.h" #include "swrast/swrast.h" #include "swrast/s_context.h" /* SWRAST_CONTEXT / _RasterMask */ #include "swrast_setup/swrast_setup.h" #include "array_cache/acache.h" #include "tnl/tnl.h" static const GLubyte * vr_get_string(GLcontext *ctx, GLenum pname) { (void)ctx; switch (pname) { case GL_VENDOR: return (const GLubyte *)"Rendition"; case GL_RENDERER: return (const GLubyte *)"Mesa Rendition Verite V2200"; default: return NULL; } } static void vr_get_buffer_size(GLframebuffer *buffer, GLuint *width, GLuint *height) { GET_CURRENT_CONTEXT(ctx); vrmesa_context *vmesa; (void)buffer; if (ctx == NULL) return; vmesa = VRMESA_CONTEXT(ctx); *width = vmesa->rl.v.width; *height = vmesa->rl.v.height; } /* GL_CLEAR..GL_SET (0x1500..0x150F) -> PE ALUMode 4-bit ROP */ static const GLubyte vr_rop[16] = { 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF, }; static void vr_update_state(GLcontext *ctx, GLuint new_state) { vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); struct rlgl *rl = &vmesa->rl; _swrast_InvalidateState(ctx, new_state); _swsetup_InvalidateState(ctx, new_state); _ac_InvalidateState(ctx, new_state); _tnl_InvalidateState(ctx, new_state); /* * PE state used only by the hardware triangle path */ if (new_state & _NEW_DEPTH) { /* GL_ALWAYS = compare off; writes are ZBufWrMode's job */ rlgl_depth_func(rl, ctx->Depth.Test && ctx->Depth.Func != GL_ALWAYS, ctx->Depth.Func == GL_LESS ? RLGL_Z_LT : RLGL_Z_LE); rlgl_depth_mask(rl, ctx->Depth.Test && ctx->Depth.Mask); } if (new_state & _NEW_COLOR) { GLuint srcf, dstf; GLboolean na; rlgl_color_mask(rl, ctx->Color.ColorMask[0], ctx->Color.ColorMask[1], ctx->Color.ColorMask[2]); /* honor GL_DITHER (default on): PE 4x4 dither on 565 */ rlgl_dither(rl, ctx->Color.DitherFlag); if (ctx->Color._LogicOpEnabled) { rlgl_logic_op(rl, 1, vr_rop[ctx->Color.LogicOp - GL_CLEAR]); rlgl_blend(rl, 0, 0, 0); } else { rlgl_logic_op(rl, 0, 0); if (ctx->Color.BlendEnabled && vrBlendMap(ctx, &srcf, &dstf, &na)) rlgl_blend(rl, 1, srcf, dstf); else rlgl_blend(rl, 0, 0, 0); } /* TranspReject: reject SrcAlpha <= thresh */ if (ctx->Color.AlphaEnabled) { GLint ref = (GLint)(ctx->Color.AlphaRef * 255.0F + 0.5F); switch (ctx->Color.AlphaFunc) { case GL_GREATER: rlgl_alpha_test(rl, 1, (uint32_t)ref); break; case GL_GEQUAL: /* pass a >= ref == reject a <= ref-1 */ if (ref <= 0) rlgl_alpha_test(rl, 0, 0); else rlgl_alpha_test(rl, 1, (uint32_t)(ref - 1)); break; case GL_NEVER: rlgl_alpha_test(rl, 1, 255); break; default: /* ALWAYS or gated shapes */ rlgl_alpha_test(rl, 0, 0); break; } } else rlgl_alpha_test(rl, 0, 0); } if (new_state & _NEW_FOG) { if (ctx->Fog.Enabled) { GLubyte c[3]; UNCLAMPED_FLOAT_TO_UBYTE(c[0], ctx->Fog.Color[0]); UNCLAMPED_FLOAT_TO_UBYTE(c[1], ctx->Fog.Color[1]); UNCLAMPED_FLOAT_TO_UBYTE(c[2], ctx->Fog.Color[2]); rlgl_fog(rl, 1, ((uint32_t)c[0] << 16) | ((uint32_t)c[1] << 8) | (uint32_t)c[2]); } else rlgl_fog(rl, 0, 0); } if (new_state & _NEW_SCISSOR) { if (ctx->Scissor.Enabled) rlgl_scissor(rl, ctx->Scissor.X, ctx->Scissor.Y, ctx->Scissor.Width, ctx->Scissor.Height); else rlgl_scissor(rl, 0, 0, rl->v.width, rl->v.height); } vrCheckHwRender(ctx); } static void vr_clear(GLcontext *ctx, GLbitfield mask, GLboolean all, GLint x, GLint y, GLint width, GLint height) { vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); GLbitfield rest = mask; int cc = 0, cz = 0; /* frame boundary for texture evict-pinning (once per GLQuake frame) */ vrTexNewFrame(); if (all && !ctx->Scissor.Enabled) { if ((mask & DD_BACK_LEFT_BIT) != 0 && ctx->Color.ColorMask[0] && ctx->Color.ColorMask[1] && ctx->Color.ColorMask[2]) { cc = 1; rest &= ~DD_BACK_LEFT_BIT; } /* engine Z clear writes the max-Z pattern = depth 1.0 */ if ((mask & DD_DEPTH_BIT) != 0 && ctx->Depth.Mask && ctx->Depth.Clear == 1.0) { cz = 1; rest &= ~DD_DEPTH_BIT; } } if (cc || cz) { const GLfloat *c = ctx->Color.ClearColor; GLuint r = (GLuint)(c[0] * 255.0F + 0.5F); GLuint g = (GLuint)(c[1] * 255.0F + 0.5F); GLuint b = (GLuint)(c[2] * 255.0F + 0.5F); uint32_t rgb565; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; rgb565 = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3); rlgl_clear(&vmesa->rl, cc, rgb565, cz); vmesa->engine_dirty = GL_TRUE; } if (rest != 0) _swrast_Clear(ctx, rest, all, x, y, width, height); } /* * Hardware glDrawPixels. */ static void vr_drawpixels(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels) { vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); static GLushort row565[2048]; GLint destX, destY, drawW, drawH, skipPix, skipRows, rowLen, r, i, ch; const GLchan *src; if (!ctx->Current.RasterPosValid) return; if ((format != GL_RGBA && format != GL_RGB) || type != CHAN_TYPE || ctx->_ImageTransferState != 0 || !ctx->Visual.rgbMode || (SWRAST_CONTEXT(ctx)->_RasterMask & ~CLIP_BIT) != 0 || ctx->Texture._EnabledCoordUnits != 0 || unpack->Alignment != 1 || unpack->SwapBytes || unpack->LsbFirst || ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F) goto slow; destX = x; destY = y; drawW = width; drawH = height; skipPix = unpack->SkipPixels; skipRows = unpack->SkipRows; rowLen = unpack->RowLength > 0 ? unpack->RowLength : width; /* clip to the draw buffer (mirrors swrast fast_draw_pixels) */ if (destX < ctx->DrawBuffer->_Xmin) { skipPix += ctx->DrawBuffer->_Xmin - destX; drawW -= ctx->DrawBuffer->_Xmin - destX; destX = ctx->DrawBuffer->_Xmin; } if (destX + drawW > ctx->DrawBuffer->_Xmax) drawW -= destX + drawW - ctx->DrawBuffer->_Xmax; if (drawW <= 0) return; if (destY < ctx->DrawBuffer->_Ymin) { skipRows += ctx->DrawBuffer->_Ymin - destY; drawH -= ctx->DrawBuffer->_Ymin - destY; destY = ctx->DrawBuffer->_Ymin; } if (destY + drawH > ctx->DrawBuffer->_Ymax) drawH -= destY + drawH - ctx->DrawBuffer->_Ymax; if (drawH <= 0) return; /* mem_write needs 32-bit alignment: even x and even width */ if ((destX & 1) || (drawW & 1) || drawW > 2048) goto slow; ch = (format == GL_RGBA) ? 4 : 3; src = (const GLchan *)pixels + (skipRows * rowLen + skipPix) * ch; for (r = 0; r < drawH; r++) { const GLchan *p = src + (GLint)r * rowLen * ch; for (i = 0; i < drawW; i++) { GLchan cr = p[i * ch], cg = p[i * ch + 1], cb = p[i * ch + 2]; row565[i] = (GLushort)(((cr & 0xf8) << 8) | ((cg & 0xfc) << 3) | (cb >> 3)); } (void)rlgl_draw_row(&vmesa->rl, destX, destY + r, drawW, row565); } vmesa->engine_dirty = GL_TRUE; return; slow: _swrast_DrawPixels(ctx, x, y, width, height, format, type, unpack, pixels); } /* rl.fb[back] is the draw target, the other buffer is scanned out */ uint32_t vr_fb_base(struct rlgl *rl, GLenum buffer) { if (buffer == GL_FRONT || buffer == GL_FRONT_LEFT) return rl->fb[rl->back ^ 1]; return rl->fb[rl->back]; /* GL_BACK / default */ } /* * Hardware glCopyPixels(GL_COLOR). */ static void vr_copypixels(GLcontext *ctx, GLint srcx, GLint srcy, GLsizei width, GLsizei height, GLint destx, GLint desty, GLenum type) { vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); struct rlgl *rl = &vmesa->rl; GLint fbW = (GLint)rl->v.width, fbH = (GLint)rl->v.height; uint32_t sbase, dbase; GLint sy_pe, dy_pe; GLboolean overlap; if (type != GL_COLOR || ctx->_ImageTransferState != 0 || !ctx->Visual.rgbMode || (SWRAST_CONTEXT(ctx)->_RasterMask & ~CLIP_BIT) != 0 || ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F) goto slow; /* both rects must lie fully inside their buffers (swrast handles * the clipped/border cases correctly) */ if (width <= 0 || height <= 0 || srcx < 0 || srcy < 0 || srcx + width > fbW || srcy + height > fbH || destx < 0 || desty < 0 || destx + width > fbW || desty + height > fbH) goto slow; sbase = vr_fb_base(rl, ctx->Pixel.ReadBuffer); dbase = vr_fb_base(rl, ctx->Color.DrawBuffer); /* * The copy hides the true src/dst relationship from the microcode's * overlap-direction logic */ overlap = (sbase == dbase) && !(destx >= srcx + width || destx + width <= srcx || desty >= srcy + height || desty + height <= srcy); if (overlap) goto slow; /* GL window (bottom-left) -> PE surface (top-left) top edge */ sy_pe = fbH - srcy - height; dy_pe = fbH - desty - height; if (rlgl_copy_rect(rl, sbase, fbW, dbase, fbW, srcx, sy_pe, destx, dy_pe, width, height) != 0) goto slow; vmesa->engine_dirty = GL_TRUE; return; slow: _swrast_CopyPixels(ctx, srcx, srcy, destx, desty, width, height, type); } static void vr_finish(GLcontext *ctx) { vrFence(VRMESA_CONTEXT(ctx)); } static void vr_flush(GLcontext *ctx) { vrmesa_context *vmesa = VRMESA_CONTEXT(ctx); if (vmesa->engine_dirty) (void)v3d_flush(&vmesa->rl.v); } void vrInitDDFuncs(struct dd_function_table *functions) { functions->GetString = vr_get_string; functions->GetBufferSize = vr_get_buffer_size; functions->UpdateState = vr_update_state; functions->Clear = vr_clear; functions->Finish = vr_finish; functions->Flush = vr_flush; functions->DrawPixels = vr_drawpixels; functions->CopyPixels = vr_copypixels; vrInitTexFuncs(functions); }