diff options
| author | 3gg <3gg@shellblade.net> | 2026-02-12 18:24:14 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2026-02-12 18:24:14 -0800 |
| commit | 5f7855b6aa0e3338625eb7d30e8ccc5b74050bbf (patch) | |
| tree | fc6ae60f612de4193a96768250997487a524881e /src | |
| parent | 2ce59d54aa110a2c1fc1105855f628c5512f8dac (diff) | |
Deferred pipeline
Diffstat (limited to 'src')
| -rw-r--r-- | src/swgfx.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/src/swgfx.c b/src/swgfx.c index 8663b62..d41b69d 100644 --- a/src/swgfx.c +++ b/src/swgfx.c | |||
| @@ -22,6 +22,8 @@ Coordinate systems: | |||
| 22 | static constexpr sgTextureId DefaultTextureId = SWGFX_MAX_TEXTURES; | 22 | static constexpr sgTextureId DefaultTextureId = SWGFX_MAX_TEXTURES; |
| 23 | static constexpr size_t SWGFX_TEXTURE_REGISTER_SIZE = SWGFX_MAX_TEXTURES + 1; | 23 | static constexpr size_t SWGFX_TEXTURE_REGISTER_SIZE = SWGFX_MAX_TEXTURES + 1; |
| 24 | 24 | ||
| 25 | static constexpr R DepthClearValue = 1.0f; | ||
| 26 | |||
| 25 | static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; | 27 | static constexpr sgVec3 Up3 = (sgVec3){0,1,0}; |
| 26 | 28 | ||
| 27 | typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; | 29 | typedef struct sgViewport_t { int x0, y0, width, height; } sgViewport_t; |
| @@ -41,7 +43,7 @@ typedef struct swgfx { | |||
| 41 | sgVec2i dims; // Colour buffer dimensions. | 43 | sgVec2i dims; // Colour buffer dimensions. |
| 42 | sgPixel* colour; // Colour buffer. | 44 | sgPixel* colour; // Colour buffer. |
| 43 | R* depth; // Depth buffer. | 45 | R* depth; // Depth buffer. |
| 44 | sgTextureId* textureId; // Texture ID buffer. | 46 | sgTextureId* texture; // Texture ID buffer. |
| 45 | sgVec2* texcoords; // Texture coords buffer. | 47 | sgVec2* texcoords; // Texture coords buffer. |
| 46 | sgViewport_t viewport; | 48 | sgViewport_t viewport; |
| 47 | sgMat4 model; // Model matrix. | 49 | sgMat4 model; // Model matrix. |
| @@ -307,14 +309,14 @@ static inline sgPixel* Pixel(swgfx* gfx, int x, int y) { | |||
| 307 | return gfx->colour + (y * gfx->dims.x) + x; | 309 | return gfx->colour + (y * gfx->dims.x) + x; |
| 308 | } | 310 | } |
| 309 | 311 | ||
| 310 | static inline R* Depth(swgfx* gfx, int x, int y) { | 312 | static inline const R* Depth(swgfx* gfx, int x, int y) { |
| 311 | assert(gfx); | 313 | assert(gfx); |
| 312 | assert(gfx->depth); | 314 | assert(gfx->depth); |
| 313 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); | 315 | assert(InBounds(gfx->dims.x, gfx->dims.y, x, y)); |
| 314 | return gfx->depth + (y * gfx->dims.x) + x; | 316 | return gfx->depth + (y * gfx->dims.x) + x; |
| 315 | } | 317 | } |
| 316 | 318 | ||
| 317 | static inline void SetPixel(swgfx* gfx, const sgVec2i p, sgPixel colour) { | 319 | static inline void SetPixelColour(swgfx* gfx, const sgVec2i p, sgPixel colour) { |
| 318 | assert(gfx); | 320 | assert(gfx); |
| 319 | *Pixel(gfx, p.x, p.y) = colour; | 321 | *Pixel(gfx, p.x, p.y) = colour; |
| 320 | #if SWGFX_PROFILING | 322 | #if SWGFX_PROFILING |
| @@ -322,9 +324,14 @@ static inline void SetPixel(swgfx* gfx, const sgVec2i p, sgPixel colour) { | |||
| 322 | #endif // SWGFX_PROFILING | 324 | #endif // SWGFX_PROFILING |
| 323 | } | 325 | } |
| 324 | 326 | ||
| 325 | static inline void SetDepth(swgfx* gfx, const sgVec2i p, R depth) { | 327 | static inline void SetPixelDeferred(swgfx* gfx, const sgVec2i p, R depth, sgTextureId texid, sgVec2 uv) { |
| 326 | assert(gfx); | 328 | assert(gfx); |
| 327 | *Depth(gfx, p.x, p.y) = depth; | 329 | gfx->depth[(p.y * gfx->dims.x) + p.x] = depth; |
| 330 | gfx->texture[(p.y * gfx->dims.x) + p.x] = texid; | ||
| 331 | gfx->texcoords[(p.y * gfx->dims.x) + p.x] = uv; | ||
| 332 | #if SWGFX_PROFILING | ||
| 333 | gfx->counters.pixels++; | ||
| 334 | #endif // SWGFX_PROFILING | ||
| 328 | } | 335 | } |
| 329 | 336 | ||
| 330 | static inline sgPixel ReadTexture(const sgImage* texture, sgVec2i xy) { | 337 | static inline sgPixel ReadTexture(const sgImage* texture, sgVec2i xy) { |
| @@ -459,7 +466,7 @@ static void DrawTriangle2(swgfx* gfx, const sgTri2* const tri) { | |||
| 459 | assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); | 466 | assert((bar.x + bar.y + bar.z - 1e7) <= 1.f); |
| 460 | const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); | 467 | const sgVec2 uv = BarycentricInterp2(bar, tri->p0.uv, tri->p1.uv, tri->p2.uv); |
| 461 | const sgPixel colour = Sample(texture->image, texture->filter, uv); | 468 | const sgPixel colour = Sample(texture->image, texture->filter, uv); |
| 462 | SetPixel(gfx, (sgVec2i){x,y}, colour); | 469 | SetPixelColour(gfx, (sgVec2i){x,y}, colour); |
| 463 | } | 470 | } |
| 464 | } | 471 | } |
| 465 | } | 472 | } |
| @@ -650,7 +657,6 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) { | |||
| 650 | const sgVec3 one_over_zs = (sgVec3){1.f / p0.w, 1.f / p1.w, 1.f/ p2.w}; | 657 | const sgVec3 one_over_zs = (sgVec3){1.f / p0.w, 1.f / p1.w, 1.f/ p2.w}; |
| 651 | const sgVec3 u_over_zs = (sgVec3){tri->p0.uv.x / p0.w, tri->p1.uv.x / p1.w, tri->p2.uv.x / p2.w}; | 658 | const sgVec3 u_over_zs = (sgVec3){tri->p0.uv.x / p0.w, tri->p1.uv.x / p1.w, tri->p2.uv.x / p2.w}; |
| 652 | const sgVec3 v_over_zs = (sgVec3){tri->p0.uv.y / p0.w, tri->p1.uv.y / p1.w, tri->p2.uv.y / p2.w}; | 659 | const sgVec3 v_over_zs = (sgVec3){tri->p0.uv.y / p0.w, tri->p1.uv.y / p1.w, tri->p2.uv.y / p2.w}; |
| 653 | const sgTexture* texture = &gfx->textureRegister[gfx->activeTexture]; | ||
| 654 | // Draw. | 660 | // Draw. |
| 655 | for (int y = pmin.y; y <= pmax.y; ++y) { | 661 | for (int y = pmin.y; y <= pmax.y; ++y) { |
| 656 | for (int x = pmin.x; x <= pmax.x; ++x) { | 662 | for (int x = pmin.x; x <= pmax.x; ++x) { |
| @@ -670,17 +676,15 @@ static void DrawTriangle3PostClip(swgfx* gfx, const sgTri4* const tri) { | |||
| 670 | const R p_depth = dot3(bar, depths); | 676 | const R p_depth = dot3(bar, depths); |
| 671 | const R z = 1.f / p_one_over_z; | 677 | const R z = 1.f / p_one_over_z; |
| 672 | const sgVec2 uv = (sgVec2){p_u_over_z * z, p_v_over_z * z}; | 678 | const sgVec2 uv = (sgVec2){p_u_over_z * z, p_v_over_z * z}; |
| 673 | R* depth = Depth(gfx, x, y); | 679 | const R* depth = Depth(gfx, x, y); |
| 674 | if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { | 680 | if ((0.f <= p_depth) && (p_depth <= 1.f) && (p_depth <= *depth)) { |
| 675 | *depth = p_depth; | ||
| 676 | const sgPixel colour = Sample(texture->image, texture->filter, uv); | ||
| 677 | // TODO: When doing lighting, need to tone-map here and apply inverse | 681 | // TODO: When doing lighting, need to tone-map here and apply inverse |
| 678 | // gamma here. | 682 | // gamma here. |
| 679 | //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; | 683 | //const sgPixel colour = {(uint8_t)(bar.x*255.f), (uint8_t)(bar.y*255.f), (uint8_t)(bar.z*255.f), 255}; |
| 680 | //const sgPixel colour = {(int)(z*255.f), (int)(z*255.f), (int)(z*255.f), 255}; | 684 | //const sgPixel colour = {(int)(z*255.f), (int)(z*255.f), (int)(z*255.f), 255}; |
| 681 | //const sgPixel colour = {255, 0, 255, 255}; | 685 | //const sgPixel colour = {255, 0, 255, 255}; |
| 682 | //const sgPixel colour = {(int)(uv.x * 255.f), (int)(uv.y * 255.f), 255, 255}; | 686 | //const sgPixel colour = {(int)(uv.x * 255.f), (int)(uv.y * 255.f), 255, 255}; |
| 683 | SetPixel(gfx, (sgVec2i){x,y}, colour); | 687 | SetPixelDeferred(gfx, (sgVec2i){x,y}, p_depth, gfx->activeTexture, uv); |
| 684 | } | 688 | } |
| 685 | } | 689 | } |
| 686 | } | 690 | } |
| @@ -746,7 +750,7 @@ swgfx* sgNew(int width, int height, void* mem) { | |||
| 746 | gfx->dims = (sgVec2i){width, height}; | 750 | gfx->dims = (sgVec2i){width, height}; |
| 747 | gfx->colour = SG_ALLOC(&aligned, N, sgPixel); | 751 | gfx->colour = SG_ALLOC(&aligned, N, sgPixel); |
| 748 | gfx->depth = SG_ALLOC(&aligned, N, R); | 752 | gfx->depth = SG_ALLOC(&aligned, N, R); |
| 749 | gfx->textureId = SG_ALLOC(&aligned, N, sgTextureId); | 753 | gfx->texture = SG_ALLOC(&aligned, N, sgTextureId); |
| 750 | gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); | 754 | gfx->texcoords = SG_ALLOC(&aligned, N, sgVec2); |
| 751 | gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); | 755 | gfx->textureRegister = SG_ALLOC(&aligned, SWGFX_TEXTURE_REGISTER_SIZE, sgTexture); |
| 752 | gfx->activeTexture = DefaultTextureId; | 756 | gfx->activeTexture = DefaultTextureId; |
| @@ -850,6 +854,10 @@ void sgPerspective(swgfx* gfx, R fovy, R aspect, R near, R far) { | |||
| 850 | 854 | ||
| 851 | void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { | 855 | void sgViewport(swgfx* gfx, int x0, int y0, int width, int height) { |
| 852 | assert(gfx); | 856 | assert(gfx); |
| 857 | assert(x0 >= 0); | ||
| 858 | assert(y0 >= 0); | ||
| 859 | assert((x0 + width) <= gfx->dims.x); | ||
| 860 | assert((y0 + height) <= gfx->dims.y); | ||
| 853 | gfx->viewport = (sgViewport_t){x0, y0, width, height}; | 861 | gfx->viewport = (sgViewport_t){x0, y0, width, height}; |
| 854 | } | 862 | } |
| 855 | 863 | ||
| @@ -872,14 +880,14 @@ void sgClear(swgfx* gfx) { | |||
| 872 | const int N = gfx->dims.x * gfx->dims.y; | 880 | const int N = gfx->dims.x * gfx->dims.y; |
| 873 | memset(gfx->colour, 0, N * sizeof(*gfx->colour)); | 881 | memset(gfx->colour, 0, N * sizeof(*gfx->colour)); |
| 874 | for (int i = 0; i < N; ++i) { | 882 | for (int i = 0; i < N; ++i) { |
| 875 | gfx->depth[i] = 1.0f; | 883 | gfx->depth[i] = DepthClearValue; |
| 876 | } | 884 | } |
| 877 | } | 885 | } |
| 878 | 886 | ||
| 879 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { | 887 | void sgPixels(swgfx* gfx, size_t count, const sgVec2i* positions, sgPixel colour) { |
| 880 | assert(gfx); | 888 | assert(gfx); |
| 881 | for (size_t i = 0; i < count; ++i) { | 889 | for (size_t i = 0; i < count; ++i) { |
| 882 | SetPixel(gfx, positions[i], colour); | 890 | SetPixelColour(gfx, positions[i], colour); |
| 883 | } | 891 | } |
| 884 | } | 892 | } |
| 885 | 893 | ||
| @@ -956,6 +964,23 @@ static void ImageExp(sgPixel* pixels, int width, int height, R exp) { | |||
| 956 | } | 964 | } |
| 957 | } | 965 | } |
| 958 | 966 | ||
| 967 | void sgLighting(swgfx* gfx) { | ||
| 968 | assert(gfx); | ||
| 969 | const int N = gfx->dims.x * gfx->dims.y; | ||
| 970 | for (int i = 0; i < N; ++i) { | ||
| 971 | const R depth = gfx->depth[i]; | ||
| 972 | if (depth != DepthClearValue) { | ||
| 973 | const sgTextureId texid = gfx->texture[i]; | ||
| 974 | const sgTexture* texture = &gfx->textureRegister[texid]; | ||
| 975 | const sgVec2 uv = gfx->texcoords[i]; | ||
| 976 | sgPixel* colour = &gfx->colour[i]; | ||
| 977 | // TODO: Actual lighting. | ||
| 978 | const sgPixel albedo = Sample(texture->image, texture->filter, uv); | ||
| 979 | *colour = albedo; | ||
| 980 | } | ||
| 981 | } | ||
| 982 | } | ||
| 983 | |||
| 959 | void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) { | 984 | void sgGamma(swgfx* gfx, sgPixel* pixels, int width, int height) { |
| 960 | assert(gfx); | 985 | assert(gfx); |
| 961 | assert(pixels); | 986 | assert(pixels); |
| @@ -968,19 +993,7 @@ void sgGammaInv(swgfx* gfx, sgPixel* pixels, int width, int height) { | |||
| 968 | ImageExp(pixels, width, height, 1.0f/2.2f); | 993 | ImageExp(pixels, width, height, 1.0f/2.2f); |
| 969 | } | 994 | } |
| 970 | 995 | ||
| 971 | static bool ViewportWithinBuffer(swgfx* gfx) { | ||
| 972 | assert(gfx); | ||
| 973 | const sgViewport_t vp = gfx->viewport; | ||
| 974 | return ((vp.x0 + vp.width) <= gfx->dims.x) && | ||
| 975 | ((vp.y0 + vp.height) <= gfx->dims.y); | ||
| 976 | } | ||
| 977 | |||
| 978 | sgCounters sgGetCounters(const swgfx* gfx) { | 996 | sgCounters sgGetCounters(const swgfx* gfx) { |
| 979 | assert(gfx); | 997 | assert(gfx); |
| 980 | return gfx->counters; | 998 | return gfx->counters; |
| 981 | } | 999 | } |
| 982 | |||
| 983 | void sgCheck(swgfx* gfx) { | ||
| 984 | assert(gfx); | ||
| 985 | assert(ViewportWithinBuffer(gfx)); | ||
| 986 | } | ||
