From 2ce59d54aa110a2c1fc1105855f628c5512f8dac Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Thu, 12 Feb 2026 17:43:39 -0800 Subject: Introduce the texture register. Step towards the deferred pipeline --- src/swgfx.c | 89 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/swgfx.c b/src/swgfx.c index 78d5434..8663b62 100644 --- a/src/swgfx.c +++ b/src/swgfx.c @@ -19,7 +19,8 @@ Coordinate systems: #include #include - +static constexpr sgTextureId DefaultTextureId = SWGFX_MAX_TEXTURES; +static constexpr size_t SWGFX_TEXTURE_REGISTER_SIZE = SWGFX_MAX_TEXTURES + 1; static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; @@ -31,14 +32,21 @@ typedef struct sgMat4 { R val[4][4]; // (col, row) } sgMat4; +typedef struct sgTexture { + const sgImage* image; + sgTextureFilter filter; +} sgTexture; + typedef struct swgfx { - sgVec2i dims; // Colour buffer dimensions. - sgPixel* colour; // Colour buffer. - R* depth; // Depth buffer. - sgViewport_t viewport; - sgMat4 model; // Model matrix. - sgMat4 view; // View matrix. - sgMat4 proj; // Projection matrix. + sgVec2i dims; // Colour buffer dimensions. + sgPixel* colour; // Colour buffer. + R* depth; // Depth buffer. + sgTextureId* textureId; // Texture ID buffer. + sgVec2* texcoords; // Texture coords buffer. + sgViewport_t viewport; + sgMat4 model; // Model matrix. + sgMat4 view; // View matrix. + sgMat4 proj; // Projection matrix. // Pre-multiplied matrices. // The model matrix changes once per object, more frequently than view or // projection. View and projection are expected to change infrequently, maybe @@ -46,12 +54,12 @@ typedef struct swgfx { // Make it so that changing the model matrix only requires one matrix // multiplication (mvp = model * viewProj) and not two (mvp = model * view * projection) // before rendering the model's triangles. - sgMat4 viewProj; // View-projection matrix. - sgMat4 mvp; // Model-view-projection matrix. - const sgImage* texture; // User-specified texture. - sgTextureFilter textureFilter; // Filter method for the texture. - sgImage defaultTexture; // A default for when no texture is provided. - sgPixel defaultPixel; // The single-pixel of the default texture. + sgMat4 viewProj; // View-projection matrix. + sgMat4 mvp; // Model-view-projection matrix. + sgTexture* textureRegister; // Indexed by texture id. + sgTextureId activeTexture; + sgPixel defaultPixel; // The single-pixel of the default texture. + sgImage defaultImage; // Image for the default texture. sgCounters counters; } swgfx; @@ -435,6 +443,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) { // Clip to screen space. pmin = Clip(gfx, pmin); pmax = Clip(gfx, pmax); + const sgTexture* texture = &gfx->textureRegister[gfx->activeTexture]; // Draw. for (int y = pmin.y; y <= pmax.y; ++y) { for (int x = pmin.x; x <= pmax.x; ++x) { @@ -449,7 +458,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) { if ((bar.x >= 0) && (bar.y >= 0) && (bar.z >= 0)) { assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); - const sgPixel colour = Sample(gfx->texture, gfx->textureFilter, uv); + const sgPixel colour = Sample(texture->image, texture->filter, uv); SetPixel(gfx, (sgVec2i){x,y}, colour); } } @@ -641,6 +650,7 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) { const sgVec3 one_over_zs = (sgVec3){1.f / p0.w, 1.f / p1.w, 1.f/ p2.w}; const sgVec3 u_over_zs = (sgVec3){tri->p0.uv.x / p0.w, tri->p1.uv.x / p1.w, tri->p2.uv.x / p2.w}; const sgVec3 v_over_zs = (sgVec3){tri->p0.uv.y / p0.w, tri->p1.uv.y / p1.w, tri->p2.uv.y / p2.w}; + const sgTexture* texture = &gfx->textureRegister[gfx->activeTexture]; // Draw. for (int y = pmin.y; y <= pmax.y; ++y) { for (int x = pmin.x; x <= pmax.x; ++x) { @@ -663,7 +673,7 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) { R* depth = Depth(gfx, x, y); if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { *depth = p_depth; - const sgPixel colour = Sample(gfx->texture, gfx->textureFilter, uv); + const sgPixel colour = Sample(texture->image, texture->filter, uv); // TODO: When doing lighting, need to tone-map here and apply inverse // gamma here. //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; @@ -719,25 +729,37 @@ static void* Alloc(void** ppMem, size_t count, size_t size) { } size_t sgMem(int width, int height) { + const int N = width * height; return Align(sizeof(swgfx)) + - Align(width * height * sizeof(sgPixel)) + - Align(width * height * sizeof(R)) + + Align(N * sizeof(sgPixel)) + // Colour buffer. + Align(N * sizeof(R)) + // Depth buffer. + Align(N * sizeof(sgTextureId)) + // Texture ID buffer. + Align(N * sizeof(sgVec2)) + // Texture coords buffer. + Align(SWGFX_TEXTURE_REGISTER_SIZE * sizeof(sgTexture)) + // Texture register. (SG_ALIGN - 1); // To make room to align allocations within the buffer. } swgfx* sgNew(int width, int height, void* mem) { - void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem(). - swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); - gfx->dims = (sgVec2i){width, height}; - gfx->colour = SG_ALLOC(&aligned, width * height, sgPixel); - gfx->depth = SG_ALLOC(&aligned, width * height, R); - gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; - gfx->defaultTexture = (sgImage){ + const int N = width * height; + void* aligned = AlignPtr(mem); // Uses the extra room we made in sgMem(). + swgfx* gfx = SG_ALLOC(&aligned, 1, swgfx); + gfx->dims = (sgVec2i){width, height}; + gfx->colour = SG_ALLOC(&aligned, N, sgPixel); + gfx->depth = SG_ALLOC(&aligned, N, R); + gfx->textureId = SG_ALLOC(&aligned, N, sgTextureId); + gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); + gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); + gfx->activeTexture = DefaultTextureId; + gfx->defaultPixel = (sgPixel){255, 255, 255, 255}; + gfx->defaultImage = (sgImage){ .width = 1, .height = 1, .pixels = &gfx->defaultPixel, }; - gfx->texture = &gfx->defaultTexture; + gfx->textureRegister[DefaultTextureId] = (sgTexture){ + .image = &gfx->defaultImage, + .filter = sgNearest, + }; return gfx; } @@ -831,11 +853,18 @@ void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { gfx->viewport = (sgViewport_t){x0, y0, width, height}; } -void sgTexture(swgfx* gfx, const sgImage* texture, sgTextureFilter filter) { +void sgTextureRegister(swgfx* gfx, sgTextureId id, const sgImage* image, sgTextureFilter filter) { assert(gfx); - assert(texture); - gfx->texture = texture; - gfx->textureFilter = filter; + assert(id < SWGFX_MAX_TEXTURES); + assert(id != DefaultTextureId); + assert(image); + gfx->textureRegister[id] = (sgTexture){image, filter}; +} + +void sgTextureActivate(swgfx* gfx, sgTextureId id) { + assert(gfx); + assert(id < SWGFX_MAX_TEXTURES); + gfx->activeTexture = id; } void sgClear(swgfx* gfx) { -- cgit v1.2.3