From dfbc356cdf36ca5e68ae60f3d2fc0431c1965524 Mon Sep 17 00:00:00 2001 From: Dragorn421 Date: Sun, 27 Feb 2022 22:21:26 +0100 Subject: [PATCH] Document vismono (#1107) * Document and explain vismono * Make it more clear "CFB" means Color Frame Buffer * Rename: Update DList/TLUT -> Desaturate DList/TLUT --- include/functions.h | 3 - include/ultra64/gbi.h | 1 + include/z64.h | 2 +- src/code/z_vismono.c | 149 +++++++++++++++++++++++++++++------------- 4 files changed, 107 insertions(+), 48 deletions(-) diff --git a/include/functions.h b/include/functions.h index fe0a927a9c..e6b721f5d2 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1381,10 +1381,7 @@ void func_800ACE90(struct_801664F0* this); void func_800ACE98(struct_801664F0* this, Gfx** gfxp); void VisMono_Init(VisMono* this); void VisMono_Destroy(VisMono* this); -void VisMono_UpdateTexture(VisMono* this, u16* tex); -Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx); void VisMono_Draw(VisMono* this, Gfx** gfxp); -void VisMono_DrawOld(VisMono* this); void func_800AD920(struct_80166500* this); void func_800AD950(struct_80166500* this); void func_800AD958(struct_80166500* this, Gfx** gfxp); diff --git a/include/ultra64/gbi.h b/include/ultra64/gbi.h index 82b3589ae2..4849c590cc 100644 --- a/include/ultra64/gbi.h +++ b/include/ultra64/gbi.h @@ -218,6 +218,7 @@ #define GPACK_RGBA5551(r, g, b, a) ((((r)<<8) & 0xf800) | \ (((g)<<3) & 0x7c0) | \ (((b)>>2) & 0x3e) | ((a) & 0x1)) +#define GPACK_IA16(i, a) (((i) << 8) | (a)) #define GPACK_ZDZ(z, dz) ((z) << 2 | (dz)) /* diff --git a/include/z64.h b/include/z64.h index c97508796c..063c188d7a 100644 --- a/include/z64.h +++ b/include/z64.h @@ -1829,7 +1829,7 @@ typedef struct { /* 0x08 */ Color_RGBA8_u32 primColor; /* 0x0C */ Color_RGBA8_u32 envColor; /* 0x10 */ u16* tlut; - /* 0x14 */ Gfx* monoDl; + /* 0x14 */ Gfx* dList; } VisMono; // size = 0x18 // Vis... diff --git a/src/code/z_vismono.c b/src/code/z_vismono.c index 9de42d9663..de065988e9 100644 --- a/src/code/z_vismono.c +++ b/src/code/z_vismono.c @@ -1,10 +1,27 @@ +/** + * Color frame buffer effect to desaturate the colors. + */ + #include "global.h" -// (Note: 80 = SCREEN_HEIGHT/3, see VisMono_DrawTexture) -// This may not have been kept up-to-date with the code, 1+1+1+80*(7+2+2+3)+1+1 makes more sense -#define DLSIZE (1 + 3 + 1 + 1 + 80 * (7 + 2 + 2 + 3) + 1) +// Height of the fragments the color frame buffer (CFB) is split into. +// It is the maximum amount of lines such that all rgba16 SCREEN_WIDTH-long lines fit into +// the half of tmem (0x800 bytes) dedicated to color-indexed data. +#define VISMONO_CFBFRAG_HEIGHT (0x800 / (SCREEN_WIDTH * G_IM_SIZ_16b_BYTES)) -// framebuffer +// Maximum size of the dlist written by `VisMono_DesaturateDList`. +// `VisMono_DesaturateDList` consistently uses `VISMONO_DLSIZE - 2` double words, so this can be 2 less. +#define VISMONO_DLSIZE (3 + SCREEN_HEIGHT / VISMONO_CFBFRAG_HEIGHT * (7 + 2 + 2 + 3) + 2 + 2) + +// How much each color component contributes to the desaturated result. +// These coefficients are close to what the YUV color space defines Y (luminance) as: +// https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB +#define VISMONO_FAC_RED 2 +#define VISMONO_FAC_GREEN 4 +#define VISMONO_FAC_BLUE 1 +#define VISMONO_FAC_NORM (0x1F * VISMONO_FAC_RED + 0x1F * VISMONO_FAC_GREEN + 0x1F * VISMONO_FAC_BLUE) + +// color framebuffer extern u16 D_0F000000[]; void VisMono_Init(VisMono* this) { @@ -22,47 +39,89 @@ void VisMono_Init(VisMono* this) { } void VisMono_Destroy(VisMono* this) { - SystemArena_FreeDebug(this->monoDl, "../z_vismono.c", 137); + SystemArena_FreeDebug(this->dList, "../z_vismono.c", 137); } -void VisMono_UpdateTexture(VisMono* this, u16* tex) { +void VisMono_DesaturateTLUT(VisMono* this, u16* tlut) { s32 i; for (i = 0; i < 256; i++) { - tex[i] = ((((i >> 3 & 0x1F) * 2 + (i << 2 & 0x1F) * 4) * 0xFF / 0xD9) << 8) | - (((i >> 6 & 0x1F) * 4 + (i >> 1 & 0x1F)) * 0xFF / 0xD9); + // `tlut[i]` is a IA16 color + // `i` corresponds to either byte of a RGBA16 color RRRR_RGGG GGBB_BBBA from the color frame buffer + + // The high byte I (intensity) corresponds to `i` being interpreted as the high byte RRRR_RGGG + // I = (RRRRR * FAC_RED + GGG00 * FAC_GREEN) * (255 / FAC_NORM) + + // The low byte A (alpha) corresponds to `i` being interpreted as the low byte GGBB_BBBA + // A = (000GG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM) + + // Note: I + A = (RRRRR * FAC_RED + GGGGG * FAC_GREEN + BBBBB * FAC_BLUE) * (255 / FAC_NORM) + + tlut[i] = GPACK_IA16( + ((i >> 3 & 0x1F) * VISMONO_FAC_RED + (i << 2 & 0x1F) * VISMONO_FAC_GREEN) * 255 / VISMONO_FAC_NORM, + ((i >> 6 & 0x1F) * VISMONO_FAC_GREEN + (i >> 1 & 0x1F) * VISMONO_FAC_BLUE) * 255 / VISMONO_FAC_NORM); } } -Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx) { +Gfx* VisMono_DesaturateDList(VisMono* this, Gfx* gfx) { s32 y; - s32 height = 3; - u16* tex = D_0F000000; + s32 height = VISMONO_CFBFRAG_HEIGHT; + u16* cfbFrag = D_0F000000; gDPPipeSync(gfx++); + // `G_TT_IA16`: use color-indexed images, and IA16 palettes gDPSetOtherMode(gfx++, G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_IA16 | G_TL_TILE | G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_1PRIMITIVE, G_AC_NONE | G_ZS_PRIM | GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) | G_RM_CLD_SURF2); + // First color cycle sums texel 1 alpha and texel 0 color + // By using IA16 palettes, this means summing A (from the IA16 color texel 1 maps to) + // with I (from the IA16 color texel 0 maps to) gDPSetCombineLERP(gfx++, 1, 0, TEXEL1_ALPHA, TEXEL0, 0, 0, 0, 1, PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, 0, 0, 0, PRIMITIVE); for (y = 0; y <= SCREEN_HEIGHT - height; y += height) { - gDPLoadTextureBlock(gfx++, tex, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0, + // Load a few lines of the color frame buffer + gDPLoadTextureBlock(gfx++, cfbFrag, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); - gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, - G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); - gDPSetTileSize(gfx++, G_TX_RENDERTILE, (2 << 2), 0, ((SCREEN_WIDTH * 2 + 1) << 2), (2 << 2)); + // Set texel 0 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT` + // Its position in texture image space is shifted along +S by 2 + gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, G_TX_RENDERTILE, + 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); + gDPSetTileSize(gfx++, G_TX_RENDERTILE, 2 << 2, 0, (SCREEN_WIDTH * 2 + 1) << 2, + (VISMONO_CFBFRAG_HEIGHT - 1) << 2); - gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, 1, 1, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, - G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); - gDPSetTileSize(gfx++, 1, (1 << 2), 0, ((SCREEN_WIDTH * 2) << 2), (2 << 2)); + // Set texel 1 to be a CI8 image with width `SCREEN_WIDTH * 2` and height `VISMONO_CFBFRAG_HEIGHT` + // Its position in texture image space is shifted along +S by 1 + gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2 * G_IM_SIZ_8b_LINE_BYTES / 8, 0x0, 1, 1, + G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); + gDPSetTileSize(gfx++, 1, 1 << 2, 0, (SCREEN_WIDTH * 2) << 2, (VISMONO_CFBFRAG_HEIGHT - 1) << 2); - gSPTextureRectangle(gfx++, 0, y << 2, (SCREEN_WIDTH << 2), (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0, - 2 << 10, 1 << 10); - tex += SCREEN_WIDTH * height; + // Draw a `SCREEN_WIDTH` wide, `height` high rectangle. + // Texture coordinate T (vertical) starts at 0 and changes by one each line (dtdy = 1) + // Texture coordinate S (horizontal) starts at 2 and changes by two each column (dsdx = 2) + + // Because texel 0 is shifted by 2 and texel 1 only by 1 along +S, + // a pixel at S coordinates s = 2+2*n will look at the 2*n-th byte of texel 0 and the 2*n+1-th byte of texel 1. + // (in "s = 2+2*n" the first "2" is the starting S coordinate and the second "2" is the dsdx value) + + // The 2*n-th byte of texel 0 is the high byte of the n-th RGBA16 color of the color frame buffer. + // The 2*n+1-th byte of texel 1 is the low byte of the n-th RGBA16 color of the color frame buffer. + + // With the TLUT computed by `VisMono_DesaturateTLUT`: + // The 2*n-th byte of texel 0 maps to a IA16 color where the high byte I (intensity) corresponds to + // the high byte of the n-th RGBA16 color of the color frame buffer. + // The 2*n+1-th byte of texel 1 maps to a IA16 color where the low byte A (alpha) corresponds to + // the low byte of the n-th RGBA16 color of the color frame buffer. + + // Since the combiner is in part set up to sum texel 0 color (I, intensity) with texel 1 alpha (A, alpha), + // the resulting color in the drawn rectangle is a desaturated color as defined by the `VISMONO_FAC_*` values. + + gSPTextureRectangle(gfx++, 0, y << 2, SCREEN_WIDTH << 2, (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0, 2 << 10, + 1 << 10); + cfbFrag += SCREEN_WIDTH * height; } gDPPipeSync(gfx++); @@ -73,32 +132,33 @@ Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx) { void VisMono_Draw(VisMono* this, Gfx** gfxp) { Gfx* gfx = *gfxp; u16* tlut; - Gfx* monoDL; - Gfx* glistpEnd; + Gfx* dList; + Gfx* dListEnd; if (this->tlut) { tlut = this->tlut; } else { - tlut = Graph_DlistAlloc(&gfx, 256 * sizeof(u16)); - VisMono_UpdateTexture(this, tlut); + tlut = Graph_DlistAlloc(&gfx, 256 * G_IM_SIZ_16b_BYTES); + VisMono_DesaturateTLUT(this, tlut); } - if (this->monoDl) { - monoDL = this->monoDl; + if (this->dList) { + dList = this->dList; } else { - monoDL = Graph_DlistAlloc(&gfx, DLSIZE * sizeof(Gfx)); - glistpEnd = VisMono_DrawTexture(this, monoDL); + dList = Graph_DlistAlloc(&gfx, VISMONO_DLSIZE * sizeof(Gfx)); + dListEnd = VisMono_DesaturateDList(this, dList); - if (!(glistpEnd <= monoDL + DLSIZE)) { - LOG_ADDRESS("glistp_end", glistpEnd, "../z_vismono.c", 257); - LOG_ADDRESS("mono_dl", monoDL, "../z_vismono.c", 258); - LOG_ADDRESS("mono_dl + (1+3+1+1+80*(7+2+2+3)+1)", monoDL + DLSIZE, "../z_vismono.c", 259); - LOG_ADDRESS("(1+3+1+1+80*(7+2+2+3)+1)", DLSIZE, "../z_vismono.c", 260); + if (!(dListEnd <= dList + VISMONO_DLSIZE)) { + LOG_ADDRESS("glistp_end", dListEnd, "../z_vismono.c", 257); + LOG_ADDRESS("mono_dl", dList, "../z_vismono.c", 258); + LOG_ADDRESS("mono_dl + (1+3+1+1+80*(7+2+2+3)+1)", dList + VISMONO_DLSIZE, "../z_vismono.c", 259); + LOG_ADDRESS("(1+3+1+1+80*(7+2+2+3)+1)", VISMONO_DLSIZE, "../z_vismono.c", 260); } - ASSERT(glistpEnd <= monoDL + DLSIZE, "glistp_end <= mono_dl + DLSIZE", "../z_vismono.c", 262); + ASSERT(dListEnd <= dList + VISMONO_DLSIZE, "glistp_end <= mono_dl + DLSIZE", "../z_vismono.c", 262); } gDPPipeSync(gfx++); + if (this->setScissor == true) { gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); } @@ -108,23 +168,24 @@ void VisMono_Draw(VisMono* this, Gfx** gfxp) { gDPLoadTLUT_pal256(gfx++, tlut); - gSPDisplayList(gfx++, monoDL); + gSPDisplayList(gfx++, dList); + gDPPipeSync(gfx++); *gfxp = gfx; } void VisMono_DrawOld(VisMono* this) { - Gfx* glistpEnd; + Gfx* dListEnd; - if (!this->tlut) { - this->tlut = SystemArena_MallocDebug(256 * sizeof(u16), "../z_vismono.c", 283); - VisMono_UpdateTexture(this, this->tlut); + if (this->tlut == NULL) { + this->tlut = SystemArena_MallocDebug(256 * G_IM_SIZ_16b_BYTES, "../z_vismono.c", 283); + VisMono_DesaturateTLUT(this, this->tlut); } - if (!this->monoDl) { - this->monoDl = SystemArena_MallocDebug(DLSIZE * sizeof(Gfx), "../z_vismono.c", 289); - glistpEnd = VisMono_DrawTexture(this, this->monoDl); - ASSERT(glistpEnd <= this->monoDl + DLSIZE, "glistp_end <= this->mono_dl + DLSIZE", "../z_vismono.c", 292); + if (this->dList == NULL) { + this->dList = SystemArena_MallocDebug(VISMONO_DLSIZE * sizeof(Gfx), "../z_vismono.c", 289); + dListEnd = VisMono_DesaturateDList(this, this->dList); + ASSERT(dListEnd <= this->dList + VISMONO_DLSIZE, "glistp_end <= this->mono_dl + DLSIZE", "../z_vismono.c", 292); } }