diff --git a/include/m_Do/m_Do_graphic.h b/include/m_Do/m_Do_graphic.h index 5a97f4a326..c6b22654fa 100644 --- a/include/m_Do/m_Do_graphic.h +++ b/include/m_Do/m_Do_graphic.h @@ -31,6 +31,9 @@ public: void create(); void remove(); void draw(); +#if TARGET_PC + void draw2(); +#endif u8 getEnable() { return mEnable; } void setEnable(u8 i_enable) { mEnable = i_enable; } diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index dc6f80b63f..8c6b5e7c45 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1258,7 +1258,6 @@ void mDoGph_gInf_c::bloom_c::remove() { mMonoColor.a = 0; } - #if TARGET_PC static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, int dstWidth, int dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) { GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE); @@ -1267,11 +1266,11 @@ static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, int dstWidth, int dstH GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); } -void mDoGph_gInf_c::bloom_c::draw() { +void mDoGph_gInf_c::bloom_c::draw2() { ZoneScoped; - if (!dusk::getSettings().game.enableBloom) { - return; - } + // if (!dusk::getSettings().game.enableBloom) { + // return; + // } bool enabled = mEnable; if (mMonoColor.a == 0 && !enabled) @@ -1279,13 +1278,11 @@ void mDoGph_gInf_c::bloom_c::draw() { f32 width = mDoGph_gInf_c::getWidth(); f32 height = mDoGph_gInf_c::getHeight(); - GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f); - GXSetScissor(0, 0, width, height); GXLoadTexObj(getFrameBufferTexObj(), GX_TEXMAP0); GXSetNumChans(0); GXSetNumTexGens(1); - GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c); + GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_GREEN); GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA); GXSetZCompLoc(1); @@ -1296,7 +1293,7 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetCullMode(GX_CULL_NONE); GXSetDither(1); Mtx44 ortho; - C_MTXOrtho(ortho, 0.0f, height, 0.0f, width, 0.0f, 10.0f); + C_MTXOrtho(ortho, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 10.0f); GXLoadPosMtxImm(cMtx_getIdentity(), 0); GXSetProjection(ortho, GX_ORTHOGRAPHIC); GXSetCurrentMtx(0); @@ -1306,16 +1303,51 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S8, 0); - auto filterQuad = [&](int div) { - f32 x = width / div, y = height / div; + // Set up viewports for our pyramid. + enum { MaxDivNum = 10 }; + enum { + Pass0, + FinalPass, + BlurPass0, BlurPassN = BlurPass0 + MaxDivNum, + MaxTexNum, + }; + scissor_class divPorts[MaxDivNum]; + divPorts[0] = {0.0f, 0.0f, 1.0f, 1.0f}; // full-size texture + divPorts[1] = {0.0f, 0.0f, 0.5f, 0.5f}; // bloom texture (wide enough, half-tall) + divPorts[2] = {0.5f, 0.0f, 0.25f, 0.25f}; + for (int i = 3; i < ARRAY_SIZE(divPorts); i++) { + auto& port = divPorts[i]; + auto const& prev = divPorts[i - 1]; + port.x_orig = prev.x_orig; + port.y_orig = prev.y_orig + prev.height; + port.width = prev.width * 0.5f; + port.height = prev.height * 0.5f; + } + + auto divCopySrc = [&](int divNo) { + auto const& port = divPorts[divNo]; + GXSetTexCopySrc(port.x_orig * width, port.y_orig * height, port.width * width, port.height * height); + }; + + TGXTexObj tmpTex[MaxTexNum]; + auto divCopyTex = [&](uintptr_t texNo, int divNo) -> GXTexObj * { + auto const& port = divPorts[divNo]; + CopyToTexObj(&tmpTex[texNo], texNo, port.width * width, port.height * height); + return &tmpTex[texNo]; + }; + + auto divQuad = [&](int divNo) { + auto const& port = divPorts[divNo]; + f32 x0 = port.x_orig, y0 = port.y_orig; + f32 x1 = x0 + port.width, y1 = y0 + port.height; GXBegin(GX_QUADS, GX_VTXFMT0, 4); - GXPosition3f32(0, 0, -5); + GXPosition3f32(x0, y0, -5); GXTexCoord2s8(0, 0); - GXPosition3f32(x, 0, -5); + GXPosition3f32(x1, y0, -5); GXTexCoord2s8(1, 0); - GXPosition3f32(x, y, -5); + GXPosition3f32(x1, y1, -5); GXTexCoord2s8(1, 1); - GXPosition3f32(0, y, -5); + GXPosition3f32(x0, y1, -5); GXTexCoord2s8(0, 1); GXEnd(); }; @@ -1330,13 +1362,12 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP1, GX_TEV_SWAP1); GXSetTevColor(GX_TEVREG2, mMonoColor); GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_OR); - filterQuad(1); + divQuad(0); } if (enabled) { - enum PassID { Pass0, Pass1, Pass2 }; - - GXCreateFrameBuffer(width, height); + GXCreateFrameBuffer(width * 0.75f, height * 0.5f); + GXSetViewport(0.0f, 0.0f, width, height, 0.1f, 1.0f); // use oversized viewport to make the math easier GXSetNumTevStages(3); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); @@ -1359,75 +1390,96 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_OR); GXColorS10 tevColor0 = {(s16)-mPoint, (s16)-mPoint, (s16)-mPoint, 0x40}; GXSetTevColorS10(GX_TEVREG0, tevColor0); - GXColor tevColor1 = {mBlureRatio, mBlureRatio, mBlureRatio, mBlureRatio}; - GXSetTevColor(GX_TEVREG1, tevColor1); GXPixModeSync(); - filterQuad(2); + + // Create thresholded 1/2 image. + divQuad(1); GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); // Downsample and filter from 1/2 EFB into 1/4. - TGXTexObj tmpTex[3]; - GXSetTexCopySrc(0, 0, width / 2, height / 2); - CopyToTexObj(&tmpTex[0], Pass0, width / 4, height / 4); - GXLoadTexObj(&tmpTex[0], GX_TEXMAP0); + divCopySrc(1); + GXTexObj* texPass0 = divCopyTex(Pass0, 2); + GXLoadTexObj(texPass0, GX_TEXMAP0); + f32 blurScale = mBlureSize * ((448.0f / getHeight()) / 6400.0f); + + // Setup blur filter TEV. GXSetNumTexGens(8); - u32 texMtxID = GX_TEXMTX0; - int angle = 0; - GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); - for (int texCoord = (int)GX_TEXCOORD1; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) { - GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); - f32 dVar15 = mBlureSize * ((448.0f / getHeight()) / 6400.0f); - - mDoMtx_stack_c::transS((dVar15 * cM_scos(angle)) * getInvScale(), - dVar15 * cM_ssin(angle), 0.0f); - GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); - - texMtxID += 3; - angle += 0x2492; - } + auto SetupBlurMtx = [](float scale) { + u32 texMtxID = GX_TEXMTX0; + int angle = 0; + for (int texCoord = (int)GX_TEXCOORD0; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) { + GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); + mDoMtx_stack_c::transS((scale * cM_scos(angle)) * getInvScale(), + scale * cM_ssin(angle), 0.0f); + GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); + texMtxID += 3; + angle += 0x2000; + } + }; GXSetNumTevStages(8); - GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); - GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, GX_CC_ZERO); - GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); - GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); - GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); - for (int tevStage = (int)GX_TEVSTAGE1; tevStage < 8; tevStage++) { - GXSetTevOrder((GXTevStageID)tevStage, (GXTexCoordID)tevStage, GX_TEXMAP0, - GX_COLOR_NULL); - GXSetTevColorIn((GXTevStageID)tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, GX_CC_CPREV); - GXSetTevColorOp((GXTevStageID)tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, - GX_TEVPREV); - GXSetTevAlphaIn((GXTevStageID)tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0); - GXSetTevAlphaOp((GXTevStageID)tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, - GX_TEVPREV); + for (int stage = 0; stage < 8; stage++) { + GXTevStageID tevStage = (GXTevStageID)stage; + GXSetTevOrder(tevStage, (GXTexCoordID)stage, GX_TEXMAP0, GX_COLOR_NULL); + GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, stage == 0 ? GX_CC_ZERO : GX_CC_CPREV); + GXSetTevColorOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0); + GXSetTevAlphaOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); } - GXPixModeSync(); - // Blur filter from tmp_tex1 1/4 to EFB 1/4. - filterQuad(4); + // Successively downsample and apply blurs. + int divStart = 2; + int divNum = 6; - GXSetTexCopySrc(0, 0, width / 4, height / 4); - CopyToTexObj(&tmpTex[1], Pass1, width / 8, height / 8); - GXLoadTexObj(&tmpTex[1], GX_TEXMAP0); + // Distribute the brightness through each pass. + int totalNumPasses = (divNum - divStart) * 2; // down, up + float brightnessF32 = (mBlureRatio / 255.0f); + float brightnessPerPass = 255.0f * powf(brightnessF32, 1.0f / totalNumPasses); + GXSetTevColorS10(GX_TEVREG1, {0, 0, 0, s16(brightnessPerPass / 8)}); - // Upsample 1/8 buffer back up to 1/4 buffer. - GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_OR); - GXPixModeSync(); - GXInvalidateTexAll(); - filterQuad(4); + for (int i = divStart; i < divNum; i++) { + float blurStrength = 1.0f + (i - divStart) * 5.0f; + SetupBlurMtx(blurScale * blurStrength); + + // Apply blur filter. + divQuad(i); + + // Copy to next layer. + divCopySrc(i); + + // Set up for the next pass down. + GXTexObj * blurTex = divCopyTex(BlurPass0 + i, i + 1); + GXLoadTexObj(blurTex, GX_TEXMAP0); + } + + // All the way down at the bottom. Instead of blurring the bottom layer by itself, we blur when going up to the next layer. + // The remaining upscales are all just normal alpha blending. + divQuad(divNum); + + // Now successively alpha blend back up, don't blur anymore. + GXSetNumTevStages(1); + GXSetTevColorS10(GX_TEVREG1, {0, 0, 0, s16(brightnessPerPass)}); + GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + GXSetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_OR); + for (int i = divNum; i > divStart; i--) { + divCopySrc(i); + GXTexObj* upTex = divCopyTex(BlurPass0 + i, i); + GXLoadTexObj(upTex, GX_TEXMAP0); + divQuad(i - 1); + } // Now that we've upsampled and filtered our final bloom, copy 1/4 buffer. - GXSetTexCopySrc(0, 0, width / 4, height / 4); - CopyToTexObj(&tmpTex[2], Pass2, width / 4, height / 4); - GXLoadTexObj(&tmpTex[2], GX_TEXMAP0); + divCopySrc(2); + GXTexObj* texFinal = divCopyTex(FinalPass, 2); + GXLoadTexObj(texFinal, GX_TEXMAP0); GXRestoreFrameBuffer(); + GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f); // Now blend our bloom into the real FB. GXSetTevColor(GX_TEVREG0, mBlendColor); @@ -1437,14 +1489,27 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0); GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); - GXSetBlendMode(GX_BM_BLEND, mMode == 1 ? GX_BL_INVDSTCLR : GX_BL_ONE, GX_BL_SRCALPHA, GX_LO_OR); + GXSetBlendMode(GX_BM_BLEND, mMode == 1 ? GX_BL_INVDSTCLR : GX_BL_ONE, GX_BL_SRCALPHA, + GX_LO_OR); GXPixModeSync(); GXInvalidateTexAll(); - filterQuad(1); + divQuad(0); } } -#else +#endif + void mDoGph_gInf_c::bloom_c::draw() { + ZoneScoped; + if (!dusk::getSettings().game.enableBloom) { + return; + } + + static bool s_bloom2 = false; + if (s_bloom2) { + draw2(); + return; + } + bool enabled = mEnable && m_buffer != NULL; if (mMonoColor.a != 0 || enabled) { #if TARGET_PC @@ -1563,8 +1628,12 @@ void mDoGph_gInf_c::bloom_c::draw() { GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); for (int texCoord = (int)GX_TEXCOORD1; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) { GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); - + + #if TARGET_PC + f32 dVar15 = mBlureSize * ((448.0f / height) / 6400.0f); + #else f32 dVar15 = mBlureSize * (1.0f / 6400.0f); + #endif mDoMtx_stack_c::transS((dVar15 * cM_scos(angle)) * getInvScale(), dVar15 * cM_ssin(angle), 0.0f); @@ -1673,7 +1742,6 @@ void mDoGph_gInf_c::bloom_c::draw() { } } } -#endif static void retry_captue_frame(view_class* param_0, view_port_class* param_1, int param_2) { UNUSED(param_0);