diff --git a/bin/segment2.c b/bin/segment2.c index 7f620876..20740545 100644 --- a/bin/segment2.c +++ b/bin/segment2.c @@ -5,40 +5,152 @@ #include "macros.h" #include "types.h" #include "game/ingame_menu.h" +#include "game/puppyprint.h" #include "make_const_nonconst.h" // SM64 (US/JP/EU/SH) Segment 02 #ifdef PUPPYPRINT -ALIGNED8 static const Texture small_font_1[] = { +ALIGNED8 static const Texture small_font_default[] = { #include "textures/segment2/custom_text.i4.inc.c" }; -ALIGNED8 static const Texture small_font_2[] = { -#include "textures/segment2/custom_text2.i4.inc.c" +ALIGNED8 static const Texture small_font_outline[] = { +#include "textures/segment2/custom_text2.ia4.inc.c" +}; +ALIGNED8 static const Texture small_font_plain[] = { +#include "textures/segment2/custom_text3.i4.inc.c" +}; +ALIGNED8 static const Texture small_font_vanilla[] = { +#include "textures/segment2/custom_text4.i4.inc.c" }; -const Texture *const puppyprint_font_lut[2] = { - small_font_1, small_font_2 +const u8 small_font_kerning_default[] = { + /*!*/ 4, /*"*/ 5, /*#*/ 0, /*$*/ 0, /*%*/ 8, /*&*/ 8, /*'*/ 2, /*(*/ 5, /*)*/ 5, /***/ 0, /*+*/ 8, /*,*/ 3, /*-*/ 8, /*.*/ 3, /*/*/ 8, /*0*/ 6, + /*1*/ 5, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 8, /*7*/ 7, /*8*/ 7, /*9*/ 6, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 6, /*F*/ 5, /*G*/ 8, /*H*/ 6, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 6, + /*Q*/ 8, /*R*/ 6, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 8, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 8, /*_*/ 0, /*`*/ 0, + /*a*/ 7, /*b*/ 7, /*c*/ 6, /*d*/ 7, /*e*/ 7, /*f*/ 7, /*g*/ 7, /*h*/ 7, /*i*/ 3, /*j*/ 5, /*k*/ 8, /*l*/ 4, /*m*/ 7, /*n*/ 7, /*o*/ 7, /*p*/ 7, + /*q*/ 7, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 7, /*w*/ 8, /*x*/ 6, /*y*/ 8, /*z*/ 7, /*~*/ 8, }; -static const u8 small_font_kerning_1[80] = { - /*0*/ 6, /*1*/ 5, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 8, /*7*/ 7, /*8*/ 7, /*9*/ 6, /*-*/ 8, /*+*/ 8, /*(*/ 5, /*)*/ 5, /*!*/ 4, /*?*/ 6, - /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 6, /*F*/ 5, /*G*/ 8, /*H*/ 6, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 6, - /*Q*/ 8, /*R*/ 6, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 8, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*"*/ 5, /*'*/ 2, /*:*/ 3, /*;*/ 3, /*.*/ 3, /*,*/ 3, - /*a*/ 7, /*b*/ 7, /*c*/ 6, /*d*/ 7, /*e*/ 7, /*f*/ 7, /*g*/ 7, /*h*/ 7, /*i*/ 3, /*j*/ 5, /*k*/ 8, /*l*/ 4, /*m*/ 7, /*n*/ 7, /*o*/ 7, /*p*/ 7, - /*q*/ 7, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 7, /*w*/ 8, /*x*/ 6, /*y*/ 8, /*z*/ 7, /*~*/ 8, /*¨*/ 7, /*^*/ 8, /*/*/ 8, /*%*/ 8, /*&*/ 8, +const u16 small_font_offsets_default[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 }; -static const u8 small_font_kerning_2[80] = { - /*0*/ 6, /*1*/ 6, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*-*/ 6, /*+*/ 6, /*(*/ 3, /*)*/ 3, /*!*/ 4, /*?*/ 5, - /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 6, /*J*/ 6, /*K*/ 6, /*L*/ 6, /*M*/ 6, /*N*/ 6, /*O*/ 6, /*P*/ 6, - /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 6, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*"*/ 4, /*'*/ 1, /*:*/ 2, /*;*/ 2, /*.*/ 2, /*,*/ 2, - /*a*/ 5, /*b*/ 5, /*c*/ 5, /*d*/ 5, /*e*/ 5, /*f*/ 5, /*g*/ 5, /*h*/ 5, /*i*/ 1, /*j*/ 5, /*k*/ 5, /*l*/ 3, /*m*/ 5, /*n*/ 5, /*o*/ 5, /*p*/ 5, - /*q*/ 5, /*r*/ 5, /*s*/ 5, /*t*/ 5, /*u*/ 5, /*v*/ 5, /*w*/ 5, /*x*/ 5, /*y*/ 5, /*z*/ 5, /*~*/ 6, /*¨*/ 5, /*^*/ 6, /*/*/ 5, /*%*/ 5, /*&*/ 6, +static const u8 small_font_kerning_outline[] = { + /*!*/ 3, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 6, /*'*/ 2, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 6, /*.*/ 2, /*/*/ 6, /*0*/ 6, + /*1*/ 6, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*:*/ 2, /*;*/ 2, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 5, /*J*/ 7, /*K*/ 6, /*L*/ 6, /*M*/ 6, /*N*/ 6, /*O*/ 6, /*P*/ 6, + /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 6, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 5, /*b*/ 5, /*c*/ 5, /*d*/ 5, /*e*/ 5, /*f*/ 5, /*g*/ 5, /*h*/ 5, /*i*/ 2, /*j*/ 6, /*k*/ 4, /*l*/ 2, /*m*/ 5, /*n*/ 5, /*o*/ 5, /*p*/ 5, + /*q*/ 5, /*r*/ 5, /*s*/ 5, /*t*/ 5, /*u*/ 5, /*v*/ 5, /*w*/ 5, /*x*/ 5, /*y*/ 5, /*z*/ 5, /*~*/ 6, }; -const u8 *const puppyprint_kerning_lut[2][80] = { - small_font_kerning_1, small_font_kerning_2 +const u16 small_font_offsets_outline[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +static const u8 small_font_kerning_plain[] = { + /*!*/ 5, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 7, /*'*/ 2, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 6, /*.*/ 2, /*/*/ 6, /*0*/ 6, + /*1*/ 5, /*2*/ 5, /*3*/ 5, /*4*/ 5, /*5*/ 5, /*6*/ 5, /*7*/ 5, /*8*/ 5, /*9*/ 5, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 6, /*@*/ 0, + /*A*/ 6, /*B*/ 6, /*C*/ 6, /*D*/ 6, /*E*/ 6, /*F*/ 6, /*G*/ 6, /*H*/ 6, /*I*/ 4, /*J*/ 6, /*K*/ 6, /*L*/ 6, /*M*/ 7, /*N*/ 7, /*O*/ 6, /*P*/ 6, + /*Q*/ 6, /*R*/ 6, /*S*/ 6, /*T*/ 6, /*U*/ 6, /*V*/ 6, /*W*/ 7, /*X*/ 6, /*Y*/ 6, /*Z*/ 6, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 6, /*b*/ 6, /*c*/ 6, /*d*/ 6, /*e*/ 6, /*f*/ 6, /*g*/ 6, /*h*/ 6, /*i*/ 3, /*j*/ 4, /*k*/ 6, /*l*/ 5, /*m*/ 7, /*n*/ 6, /*o*/ 6, /*p*/ 6, + /*q*/ 6, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 6, /*w*/ 7, /*x*/ 6, /*y*/ 6, /*z*/ 6, /*~*/ 7, +}; + +const u16 small_font_offsets_plain[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +static const u8 small_font_kerning_vanilla[] = { + /*!*/ 4, /*"*/ 4, /*#*/ 0, /*$*/ 0, /*%*/ 6, /*&*/ 7, /*'*/ 3, /*(*/ 4, /*)*/ 4, /***/ 0, /*+*/ 6, /*,*/ 2, /*-*/ 4, /*.*/ 3, /*/*/ 4, /*0*/ 5, + /*1*/ 5, /*2*/ 6, /*3*/ 6, /*4*/ 6, /*5*/ 6, /*6*/ 6, /*7*/ 6, /*8*/ 6, /*9*/ 6, /*:*/ 4, /*;*/ 4, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 5, /*@*/ 0, + /*A*/ 5, /*B*/ 5, /*C*/ 5, /*D*/ 5, /*E*/ 5, /*F*/ 5, /*G*/ 5, /*H*/ 5, /*I*/ 3, /*J*/ 5, /*K*/ 5, /*L*/ 5, /*M*/ 7, /*N*/ 7, /*O*/ 6, /*P*/ 5, + /*Q*/ 6, /*R*/ 5, /*S*/ 5, /*T*/ 5, /*U*/ 5, /*V*/ 5, /*W*/ 7, /*X*/ 6, /*Y*/ 5, /*Z*/ 5, /*[*/ 0, /*\\*/ 0, /*]*/ 0, /*^*/ 7, /*_*/ 0, /*`*/ 0, + /*a*/ 5, /*b*/ 4, /*c*/ 4, /*d*/ 4, /*e*/ 4, /*f*/ 5, /*g*/ 5, /*h*/ 4, /*i*/ 3, /*j*/ 4, /*k*/ 3, /*l*/ 2, /*m*/ 6, /*n*/ 4, /*o*/ 4, /*p*/ 4, + /*q*/ 5, /*r*/ 4, /*s*/ 4, /*t*/ 4, /*u*/ 4, /*v*/ 4, /*w*/ 7, /*x*/ 5, /*y*/ 4, /*z*/ 5, /*~*/ 6, +}; + +const u16 small_font_offsets_vanilla[] = { + /*!*/ 0, /*"*/ 8, /*#*/ 16, /*$*/ 24, /*%*/ 32, /*&*/ 40, /*'*/ 48, /*(*/ 56, /*)*/ 64, /***/ 72, /*+*/ 80, /*,*/ 88, /*-*/ 96, /*.*/ 104, /*/*/ 112, + /*0*/ 120, /*1*/ 128, /*2*/ 136, /*3*/ 144, /*4*/ 152, /*5*/ 160, /*6*/ 168, /*7*/ 176, /*8*/ 184, /*9*/ 192, /*:*/ 200, /*;*/ 208, /*<*/ 216, /*=*/ 216, + /*>*/ 0, /*?*/ 216, /*@*/ 0, /*A*/ 224, /*B*/ 232, /*C*/ 240, /*D*/ 248, /*E*/ 256, /*F*/ 264, /*G*/ 272, /*H*/ 280, /*I*/ 288, /*J*/ 296, /*K*/ 304, + /*L*/ 312, /*M*/ 320, /*N*/ 328, /*O*/ 336, /*P*/ 344, /*Q*/ 352, /*R*/ 360, /*S*/ 368, /*T*/ 376, /*U*/ 384, /*V*/ 392, /*W*/ 400, /*X*/ 408, /*Y*/ 416, + /*Z*/ 424, /*[*/ 432, /*\*/ 432, /*]*/ 432, /*^*/ 432, /*_*/ 440, /*`*/ 440, /*a*/ 440, /*b*/ 448, /*c*/ 456, /*d*/ 464, /*e*/ 472, /*f*/ 480, /*g*/ 488, + /*h*/ 496, /*i*/ 504, /*j*/ 512, /*k*/ 520, /*l*/ 528, /*m*/ 536, /*n*/ 544, /*o*/ 552, /*p*/ 560, /*q*/ 568, /*r*/ 576, /*s*/ 584, /*t*/ 592, /*u*/ 600, + /*v*/ 608, /*w*/ 616, /*x*/ 624, /*y*/ 632, /*z*/ 640, /*{*/ 648, /*|*/ 648, /*}*/ 648, /*~*/ 648, /*:)*/ 656 +}; + +const struct PPTextFont sPPFont_default = { + small_font_default, + small_font_kerning_default, + small_font_offsets_default, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_outline = { + small_font_outline, + small_font_kerning_outline, + small_font_offsets_outline, + NULL, + G_IM_FMT_IA, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_plain = { + small_font_plain, + small_font_kerning_plain, + small_font_offsets_plain, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont sPPFont_vanilla = { + small_font_vanilla, + small_font_kerning_vanilla, + small_font_offsets_vanilla, + NULL, + G_IM_FMT_I, G_IM_SIZ_4b, + 672, 12, + 8, 12 +}; + +const struct PPTextFont *const gPuppyPrintFontTable[] = { + &sPPFont_default, &sPPFont_outline, &sPPFont_plain, &sPPFont_vanilla +}; + + +const Texture *const puppyprint_font_lut[] = { + small_font_default, small_font_outline, small_font_plain, small_font_vanilla +}; + +const u8 *const puppyprint_kerning_lut[][95] = { + small_font_kerning_default, small_font_kerning_outline, small_font_kerning_plain, small_font_kerning_vanilla }; #endif diff --git a/include/config/config_debug.h b/include/config/config_debug.h index eee44caf..c0f87f31 100644 --- a/include/config/config_debug.h +++ b/include/config/config_debug.h @@ -41,7 +41,7 @@ /** * Enables a custom, enhanced performance profiler. (Enables PUPPYPRINT by default in config_safeguards). */ -// #define PUPPYPRINT_DEBUG 1 +// #define PUPPYPRINT_DEBUG /** * Uses cycles instead of microseconds in Puppyprint debug output. diff --git a/include/config/config_safeguards.h b/include/config/config_safeguards.h index 6cf2e986..3a7b9659 100644 --- a/include/config/config_safeguards.h +++ b/include/config/config_safeguards.h @@ -100,7 +100,7 @@ #define PUPPYPRINT #undef PUPPYPRINT_DEBUG - #define PUPPYPRINT_DEBUG 1 + #define PUPPYPRINT_DEBUG #undef VISUAL_DEBUG #define VISUAL_DEBUG diff --git a/include/segment_symbols.h b/include/segment_symbols.h index eba62dc8..e664c475 100644 --- a/include/segment_symbols.h +++ b/include/segment_symbols.h @@ -51,11 +51,18 @@ DECLARE_SEGMENT(goddard) DECLARE_SEGMENT(framebuffers) DECLARE_SEGMENT(assets) extern u8 _goddardSegmentStart[]; +extern u8 _goddardSegmentEnd[]; extern u8 _engineSegmentStart[]; extern u8 _engineSegmentBssEnd[]; +extern u8 _mainSegmentStart[]; extern u8 _mainSegmentEnd[]; extern u8 _engineSegmentEnd[]; +extern u8 _framebuffersSegmentBssStart[]; extern u8 _framebuffersSegmentBssEnd[]; +extern u8 _zbufferSegmentBssStart[]; +extern u8 _zbufferSegmentBssEnd[]; +extern u8 _buffersSegmentBssStart[]; +extern u8 _buffersSegmentBssEnd[]; DECLARE_LEVEL_SEGMENT(menu) DECLARE_LEVEL_SEGMENT(intro) diff --git a/src/audio/external.c b/src/audio/external.c index 4b8d9760..3502da1e 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -688,6 +688,7 @@ struct SPTask *create_next_audio_frame_task(void) { task->yield_data_size = 0; decrease_sample_dma_ttls(); + return gAudioTask; } #endif @@ -2186,7 +2187,7 @@ void play_music(u8 player, u16 seqArgs, u16 fadeTimer) { // Abort if the queue is already full. if (sBackgroundMusicQueueSize >= MAX_BACKGROUND_MUSIC_QUEUE_SIZE) { -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Sequence queue full, aborting."); #endif return; diff --git a/src/audio/heap.c b/src/audio/heap.c index 0270021d..f02d0a64 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -301,7 +301,7 @@ void sound_init_main_pools(s32 sizeForAudioInitPool) { sound_alloc_pool_init(&gAudioSessionPool, (gAudioHeap + sizeForAudioInitPool), (gAudioHeapSize - sizeForAudioInitPool)); } -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void puppyprint_get_allocated_pools(s32 *audioPoolList) { u32 i, j; const struct SoundAllocPool *pools[NUM_AUDIO_POOLS] = { @@ -1204,7 +1204,7 @@ void audio_reset_session(void) { #if defined(VERSION_JP) || defined(VERSION_US) s8 updatesPerFrame; #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG OSTime first = osGetTime(); #endif s32 j; @@ -1436,7 +1436,7 @@ void audio_reset_session(void) { gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; } #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG #ifdef PUPPYPRINT_DEBUG_CYCLES append_puppyprint_log("Audio Initialised in %dc.", (s32)(osGetTime() - first)); #else diff --git a/src/audio/heap.h b/src/audio/heap.h index c7b1494e..26a3e2dd 100644 --- a/src/audio/heap.h +++ b/src/audio/heap.h @@ -129,7 +129,7 @@ void *soundAlloc(struct SoundAllocPool *pool, u32 size); void *sound_alloc_uninitialized(struct SoundAllocPool *pool, u32 size); void sound_init_main_pools(s32 sizeForAudioInitPool); void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size); -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void puppyprint_get_allocated_pools(s32 *audioPoolList); #endif #ifdef VERSION_SH diff --git a/src/audio/internal.h b/src/audio/internal.h index bae4ff6d..1401e0d7 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -87,7 +87,7 @@ enum Codecs { #include "game/puppyprint.h" #ifdef VERSION_EU -/*#if PUPPYPRINT_DEBUG +/*#ifdef PUPPYPRINT_DEBUG #define eu_stubbed_printf_0(msg) append_puppyprint_log(msg) #define eu_stubbed_printf_1(msg, a) append_puppyprint_log(msg, a) #define eu_stubbed_printf_2(msg, a, b) append_puppyprint_log(msg, a, b) diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index 00144342..423062dd 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -2682,8 +2682,10 @@ void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { // This runs 240 times per second. void process_sequences(UNUSED s32 iterationsRemaining) { s32 i; + for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled == TRUE) { + #if defined(VERSION_EU) || defined(VERSION_SH) sequence_player_process_sequence(&gSequencePlayers[i]); sequence_player_process_sound(&gSequencePlayers[i]); @@ -2693,8 +2695,13 @@ void process_sequences(UNUSED s32 iterationsRemaining) { #endif } } + #if defined(VERSION_JP) || defined(VERSION_US) + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM); reclaim_notes(); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM, PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING); +#else + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING); #endif process_notes(); } diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 48a23d5d..98f8e3e5 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -73,13 +73,11 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI #ifdef VERSION_EU u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd); u64 *load_wave_samples(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamplesToLoad); -u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags); u64 *process_envelope(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight); #else u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd); u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad); -u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags); u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); u64 *process_envelope_inner(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, @@ -409,29 +407,6 @@ void synthesis_load_note_subs_eu(s32 updateIndex) { } #endif -#ifndef VERSION_EU -s32 get_volume_ramping(u16 sourceVol, u16 targetVol, s32 arg2) { - // This roughly computes 2^16 * (targetVol / sourceVol) ^ (8 / arg2), - // but with discretizations of targetVol, sourceVol and arg2. - f32 ret; - switch (arg2) { - default: - ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; - break; - case 128: - ret = gVolRampingLhs128[targetVol >> 8] * gVolRampingRhs128[sourceVol >> 8]; - break; - case 136: - ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; - break; - case 144: - ret = gVolRampingLhs144[targetVol >> 8] * gVolRampingRhs144[sourceVol >> 8]; - break; - } - return ret; -} -#endif - #ifdef VERSION_EU // TODO: (Scrub C) pointless mask and whitespace u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { @@ -536,11 +511,22 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { chunkLen += 8; } } + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_UPDATE, PROFILER_TIME_SUB_AUDIO_SEQUENCES); + AUDIO_PROFILER_START_SHARED(PROFILER_TIME_SUB_AUDIO_SEQUENCES, PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT); + process_sequences(i - 1); + + AUDIO_PROFILER_COMPLETE_AND_SWITCH(PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING, PROFILER_TIME_SUB_AUDIO_SEQUENCES, PROFILER_TIME_SUB_AUDIO_SYNTHESIS); + AUDIO_PROFILER_START_SHARED(PROFILER_TIME_SUB_AUDIO_SYNTHESIS, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); + if (gSynthesisReverb.useReverb) { prepare_reverb_ring_buffer(chunkLen, gAudioUpdatesPerFrame - i); } cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioUpdatesPerFrame - i); + + AUDIO_PROFILER_COMPLETE_AND_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS, PROFILER_TIME_SUB_AUDIO_UPDATE); + bufLen -= chunkLen; aiBufPtr += chunkLen; } @@ -705,7 +691,10 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI if (!gSynthesisReverb.useReverb) { aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); } else { if (gReverbDownsampleRate == 1) { // Put the oldest samples in the ring buffer into the wet channels @@ -741,7 +730,11 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI aMix(cmd++, 0, /*gain*/ 0x8000 + gSynthesisReverb.reverbGain, /*in*/ DMEM_ADDR_LEFT_CH, /*out*/ DMEM_ADDR_LEFT_CH); aDMEMMove(cmd++, DMEM_ADDR_LEFT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); } + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); + if (gReverbDownsampleRate == 1) { aSetSaveBufferPair(cmd++, 0, v1->lengthA, v1->startPos); if (v1->lengthB != 0) { @@ -1020,15 +1013,25 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { if (audioBookSample->loaded == 0x81) { v0_2 = sampleAddr + temp * 9; } else { + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA); + v0_2 = dma_sample_data( (uintptr_t) (sampleAddr + temp * 9), t0 * 9, flags, &synthesisState->sampleDmaIndex); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); } #else + // HACKERSM64_TODO: Is the EU thing above applicable to US? Could potentially save some resources. temp = (note->samplePosInt - s2 + 0x10) / 16; + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA); + v0_2 = dma_sample_data( (uintptr_t) (sampleAddr + temp * 9), t0 * 9, flags, ¬e->sampleDmaIndex); + + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); #endif a3 = (u32)((uintptr_t) v0_2 & 0xf); aSetBuffer(cmd++, 0, DMEM_ADDR_COMPRESSED_ADPCM_DATA, 0, t0 * 9 + a3); @@ -1197,16 +1200,18 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { noteSubEu->needsInit = FALSE; } - cmd = final_resample(cmd, synthesisState, bufLen * 2, resamplingRateFixedPoint, - noteSamplesDmemAddrBeforeResampling, flags); + // final resample + aSetBuffer(cmd++, /*flags*/ 0, noteSamplesDmemAddrBeforeResampling, /*dmemout*/ DMEM_ADDR_TEMP, bufLen * 2); + aResample(cmd++, flags, resamplingRateFixedPoint, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->finalResampleState)); #else if (note->needsInit == TRUE) { flags = A_INIT; note->needsInit = FALSE; } - cmd = final_resample(cmd, note, bufLen * 2, resamplingRateFixedPoint, - noteSamplesDmemAddrBeforeResampling, flags); + // final resample + aSetBuffer(cmd++, /*flags*/ 0, noteSamplesDmemAddrBeforeResampling, /*dmemout*/ DMEM_ADDR_TEMP, bufLen * 2); + aResample(cmd++, flags, resamplingRateFixedPoint, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->finalResampleState)); #endif #ifdef VERSION_EU @@ -1224,11 +1229,13 @@ u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { leftRight = 0; } + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB); #ifdef VERSION_EU cmd = process_envelope(cmd, noteSubEu, synthesisState, bufLen, 0, leftRight, flags); #else cmd = process_envelope(cmd, note, bufLen, 0, leftRight, flags); #endif + AUDIO_PROFILER_SWITCH(PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING); #ifdef VERSION_EU if (noteSubEu->usesHeadsetPanEffects) { @@ -1293,20 +1300,6 @@ u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad) { } #endif -#ifdef VERSION_EU -u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags) { - aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); - aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->finalResampleState)); - return cmd; -} -#else -u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags) { - aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); - aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->finalResampleState)); - return cmd; -} -#endif - #ifndef VERSION_EU u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, UNUSED u32 flags) { @@ -1406,8 +1399,24 @@ u64 *process_envelope(u64 *cmd, struct NoteSubEu *note, struct NoteSynthesisStat rampLeft = gCurrentLeftVolRamping[targetLeft >> 5] * gCurrentRightVolRamping[sourceLeft >> 5]; rampRight = gCurrentLeftVolRamping[targetRight >> 5] * gCurrentRightVolRamping[sourceRight >> 5]; #else - rampLeft = get_volume_ramping(vol->sourceLeft, vol->targetLeft, nSamples); - rampRight = get_volume_ramping(vol->sourceRight, vol->targetRight, nSamples); + // volume ramping + // This roughly computes 2^16 * (targetVol / sourceVol) ^ (8 / arg2), + // but with discretizations of targetVol, sourceVol and arg2. + switch (nSamples) { + case 128: + rampLeft = gVolRampingLhs128[vol->targetLeft >> 8] * gVolRampingRhs128[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs128[vol->targetRight >> 8] * gVolRampingRhs128[vol->sourceRight >> 8]; + break; + case 144: + rampLeft = gVolRampingLhs144[vol->targetLeft >> 8] * gVolRampingRhs144[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs144[vol->targetRight >> 8] * gVolRampingRhs144[vol->sourceRight >> 8]; + break; + case 136: + default: + rampLeft = gVolRampingLhs136[vol->targetLeft >> 8] * gVolRampingRhs136[vol->sourceLeft >> 8]; + rampRight = gVolRampingLhs136[vol->targetRight >> 8] * gVolRampingRhs136[vol->sourceRight >> 8]; + break; + } #endif // The operation's parameters change meanings depending on flags diff --git a/src/boot/memory.c b/src/boot/memory.c index 3703589b..5d06b47c 100644 --- a/src/boot/memory.c +++ b/src/boot/memory.c @@ -129,7 +129,7 @@ void main_pool_init(void *start, void *end) { sPoolListHeadL->next = NULL; sPoolListHeadR->prev = NULL; sPoolListHeadR->next = NULL; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG mempool = sPoolFreeSpace; #endif } @@ -334,8 +334,9 @@ void *load_segment(s32 segment, u8 *srcStart, u8 *srcEnd, u32 side, u8 *bssStart set_segment_base_addr(segment, addr); } } -#if PUPPYPRINT_DEBUG - ramsizeSegment[(segment + nameTable) - 2] = ((s32)srcEnd - (s32)srcStart); +#ifdef PUPPYPRINT_DEBUG + u32 ppSize = ALIGN16(srcEnd - srcStart) + 16; + set_segment_memory_printout(segment, ppSize); #endif return addr; } @@ -411,8 +412,9 @@ void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd) { main_pool_free(compressed); } } -#if PUPPYPRINT_DEBUG - ramsizeSegment[(segment + nameTable) - 2] = (s32)srcEnd - (s32)srcStart; +#ifdef PUPPYPRINT_DEBUG + u32 ppSize = ALIGN16((u32)*size) + 16; + set_segment_memory_printout(segment, ppSize); #endif return dest; } @@ -543,6 +545,9 @@ struct MemoryPool *mem_pool_init(u32 size, u32 side) { block->next = NULL; block->size = pool->totalSpace; } +#ifdef PUPPYPRINT_DEBUG + gPoolMem += ALIGN16(size) + 16; +#endif return pool; } diff --git a/src/buffers/buffers.h b/src/buffers/buffers.h index 064dac6b..941df2e0 100644 --- a/src/buffers/buffers.h +++ b/src/buffers/buffers.h @@ -8,10 +8,11 @@ #include "game/game_init.h" #include "game/main.h" #include "config.h" +#include "audio/data.h" extern u8 gDecompressionHeap[]; -extern u8 gAudioHeap[]; +extern u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE)]; extern u8 gIdleThreadStack[THREAD1_STACK]; extern u8 gThread3Stack[THREAD3_STACK]; diff --git a/src/engine/level_script.c b/src/engine/level_script.c index 1de2a462..8b41a2d9 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -347,6 +347,9 @@ void unmap_tlbs(void) { osUnmapTLB(gTlbEntries); gTlbSegments[i]--; gTlbEntries--; +#ifdef PUPPYPRINT_DEBUG + set_segment_memory_printout(i, 0); +#endif } } else { gTlbEntries -= gTlbSegments[i]; @@ -823,7 +826,7 @@ static void level_cmd_puppyvolume(void) { if ((sPuppyVolumeStack[gPuppyVolumeCount] = mem_pool_alloc(gPuppyMemoryPool, sizeof(struct sPuppyVolume))) == NULL) { sCurrentCmd = CMD_NEXT; gPuppyError |= PUPPY_ERROR_POOL_FULL; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Puppycamera volume allocation failed."); #endif return; @@ -873,7 +876,7 @@ static void level_cmd_puppylight_node(void) { #ifdef PUPPYLIGHTS gPuppyLights[gNumLights] = mem_pool_alloc(gLightsPool, sizeof(struct PuppyLight)); if (gPuppyLights[gNumLights] == NULL) { -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG append_puppyprint_log("Puppylight allocation failed."); #endif sCurrentCmd = CMD_NEXT; diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 9597f004..d82fd110 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -322,6 +322,7 @@ void mtxf_copy(register Mat4 dest, register Mat4 src) { /// Set mtx to the identity matrix. void mtxf_identity(register Mat4 mtx) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; f32 *dest; for (dest = ((f32 *) mtx + 1), i = 0; i < 14; dest++, i++) { @@ -334,6 +335,7 @@ void mtxf_identity(register Mat4 mtx) { /// Set dest to a translation matrix of vector b. void mtxf_translate(Mat4 dest, Vec3f b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i; register f32 *pen; for (pen = ((f32 *) dest + 1), i = 0; i < 12; pen++, i++) { @@ -354,6 +356,7 @@ void mtxf_translate(Mat4 dest, Vec3f b) { * i.e. a matrix representing a linear transformation over 3 space. */ void linear_mtxf_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = ((m[0][i] * v[0]) @@ -363,6 +366,7 @@ void linear_mtxf_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { } void linear_mtxf_mul_vec3f_and_translate(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = ((m[0][i] * v[0]) @@ -381,6 +385,7 @@ void linear_mtxf_mul_vec3f_and_translate(Mat4 m, Vec3f dst, Vec3f v) { * i.e. a matrix representing a linear transformation over 3 space. */ void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); s32 i; for (i = 0; i < 3; i++) { dst[i] = vec3_dot(m[i], v); @@ -389,6 +394,7 @@ void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) { /// Build a matrix that rotates around the z axis, then the x axis, then the y axis, and then translates. void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -414,6 +420,7 @@ void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { /// Build a matrix that rotates around the x axis, then the y axis, then the z axis, and then translates. UNUSED void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -439,6 +446,7 @@ UNUSED void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f trans, Vec3s rot) { /// Build a matrix that rotates around the z axis, then the x axis, then the y axis, and then translates and multiplies. void mtxf_rotate_zxy_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -469,6 +477,7 @@ void mtxf_rotate_zxy_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Ma /// Build a matrix that rotates around the x axis, then the y axis, then the z axis, and then translates and multiplies. void mtxf_rotate_xyz_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 sx = sins(rot[0]); register f32 cx = coss(rot[0]); register f32 sy = sins(rot[1]); @@ -504,6 +513,7 @@ void mtxf_rotate_xyz_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Ma * angle allows a bank rotation of the camera. */ void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f colX, colY, colZ; register f32 dx = (to[0] - from[0]); register f32 dz = (to[2] - from[2]); @@ -544,6 +554,7 @@ void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) { * 'angle' rotates the object while still facing the camera. */ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i; register f32 sx = scale[0]; register f32 sy = scale[1]; @@ -591,6 +602,7 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle) * 'yaw' is the angle which it should face */ void mtxf_shadow(Mat4 dest, Vec3f upDir, Vec3f pos, Vec3f scale, s16 yaw) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f lateralDir; Vec3f leftDir; Vec3f forwardDir; @@ -616,6 +628,7 @@ void mtxf_shadow(Mat4 dest, Vec3f upDir, Vec3f pos, Vec3f scale, s16 yaw) { * 'pos' is the object's position in the world */ void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f lateralDir; Vec3f leftDir; Vec3f forwardDir; @@ -641,6 +654,7 @@ void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) { * 'radius' is the distance from each triangle vertex to the center */ void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); struct Surface *floor; Vec3f point0, point1, point2; Vec3f forward; @@ -691,6 +705,7 @@ void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) { * then a. */ void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); Vec3f entry; register f32 *temp = (f32 *)a; register f32 *temp2 = (f32 *)dest; @@ -717,6 +732,7 @@ void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) { * Set matrix 'dest' to 'mtx' scaled by vector s */ void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, register Vec3f s) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 *temp = (f32 *)dest; register f32 *temp2 = (f32 *)mtx; register s32 i; @@ -737,6 +753,7 @@ void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, register Vec3f s) { * true for transformation matrices if the translation has a w component of 1. */ UNUSED void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register f32 x = b[0]; register f32 y = b[1]; register f32 z = b[2]; @@ -760,6 +777,7 @@ UNUSED void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { ((s16 *) mtx)[a ] = (((s32) b) >> 16); \ ((s16 *) mtx)[a + 16] = (((s32) b) & 0xFFFF); void mtxf_rotate_xy(Mtx *mtx, s16 angle) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); register s32 i = (coss(angle) * 0x10000); register s32 j = (sins(angle) * 0x10000); register f32 *temp = (f32 *)mtx; @@ -1264,7 +1282,7 @@ s32 anim_spline_poll(Vec3f result) { /** * @brief Checks if a ray intersects a surface using Möller–Trumbore intersection algorithm. - * + * * @param orig is the starting point of the ray. * @param dir is the normalized ray direction. * @param dir_length is the length of the ray. @@ -1312,7 +1330,7 @@ s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface f32 u = f * vec3f_dot(s, h); // Check if 'u' is within bounds. if ((u < 0.0f) || (u > 1.0f)) return FALSE; - // Make 'q' the cross product of 's' and edge 1. + // Make 'q' the cross product of 's' and edge 1. Vec3f q; vec3f_cross(q, s, e1); // Make 'v' the cos(angle) between the ray and 'q', divided by 'det'. @@ -1338,6 +1356,7 @@ void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f f32 length; Vec3f chk_hit_pos; f32 top, bottom; + PUPPYPRINT_GET_SNAPSHOT(); // Get upper and lower bounds of ray if (dir[1] >= 0.0f) { // Ray is upwards. @@ -1361,6 +1380,7 @@ void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f *max_length = length; } } + profiler_collision_update(first); } void find_surface_on_ray_cell(s32 cellX, s32 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length, s32 flags) { @@ -1391,6 +1411,7 @@ void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Ve f32 step; s32 i; const f32 invcell = 1.0f / CELL_SIZE; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_raycast); // Set that no surface has been hit *hit_surface = NULL; @@ -1488,6 +1509,7 @@ static ALWAYS_INLINE float construct_float(const float f) // Converts a floating point matrix to a fixed point matrix // Makes some assumptions about certain fields in the matrix, which will always be true for valid matrices. OPTIMIZE_OS void mtxf_to_mtx_fast(s16* dst, float* src) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix); float scale = construct_float(65536.0f / WORLD_SCALE); // Iterate over pairs of values in the input matrix for (int i = 0; i < 8; i++) diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index a597b8e7..8a40c8b4 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -205,10 +205,13 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { s32 numCollisions = 0; s32 x = colData->x; s32 z = colData->z; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_wall); + PUPPYPRINT_GET_SNAPSHOT(); colData->numWalls = 0; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return numCollisions; } @@ -232,6 +235,7 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { gNumCalls.wall++; #endif + profiler_collision_update(first); return numCollisions; } @@ -347,12 +351,15 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) { f32 height = CELL_HEIGHT_LIMIT; f32 dynamicHeight = CELL_HEIGHT_LIMIT; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_ceil); + PUPPYPRINT_GET_SNAPSHOT(); s32 x = posX; s32 y = posY; s32 z = posZ; *pceil = NULL; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return height; } @@ -395,6 +402,7 @@ f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) { gNumCalls.ceil++; #endif + profiler_collision_update(first); return height; } @@ -575,6 +583,9 @@ f32 unused_find_dynamic_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfl * Find the highest floor under a given position and return the height. */ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_floor); + PUPPYPRINT_GET_SNAPSHOT(); + f32 height = FLOOR_LOWER_LIMIT; f32 dynamicHeight = FLOOR_LOWER_LIMIT; @@ -588,6 +599,7 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { *pfloor = NULL; if (is_outside_level_bounds(x, z)) { + profiler_collision_update(first); return height; } // Each level is split into cells to limit load, find the appropriate cell. @@ -632,6 +644,8 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { // Increment the debug tracker. gNumCalls.floor++; #endif + + profiler_collision_update(first); return height; } @@ -698,6 +712,8 @@ s32 find_water_level_and_floor(s32 x, s32 y, s32 z, struct Surface **pfloor) { s32 loX, hiX, loZ, hiZ; TerrainData *p = gEnvironmentRegions; struct Surface *floor = NULL; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); s32 waterLevel = find_water_floor(x, y, z, &floor); if (p != NULL && waterLevel == FLOOR_LOWER_LIMIT) { @@ -723,6 +739,7 @@ s32 find_water_level_and_floor(s32 x, s32 y, s32 z, struct Surface **pfloor) { *pfloor = floor; } + profiler_collision_update(first); return waterLevel; } @@ -734,6 +751,8 @@ s32 find_water_level(s32 x, s32 z) { // TODO: Allow y pos s32 loX, hiX, loZ, hiZ; TerrainData *p = gEnvironmentRegions; struct Surface *floor = NULL; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); s32 waterLevel = find_water_floor(x, ((gCollisionFlags & COLLISION_FLAG_CAMERA) ? gLakituState.pos[1] : gMarioState->pos[1]), z, &floor); if ((p != NULL) && (waterLevel == FLOOR_LOWER_LIMIT)) { @@ -757,6 +776,8 @@ s32 find_water_level(s32 x, s32 z) { // TODO: Allow y pos } } + profiler_collision_update(first); + return waterLevel; } @@ -768,6 +789,8 @@ s32 find_poison_gas_level(s32 x, s32 z) { s32 loX, hiX, loZ, hiZ; s32 gasLevel = FLOOR_LOWER_LIMIT; TerrainData *p = gEnvironmentRegions; + PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.collision_water); + PUPPYPRINT_GET_SNAPSHOT(); if (p != NULL) { s32 numRegions = *p++; @@ -793,7 +816,8 @@ s32 find_poison_gas_level(s32 x, s32 z) { p += 6; } } - + + profiler_collision_update(first); return gasLevel; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index e4a505b9..1200d20f 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -498,6 +498,7 @@ u32 get_area_terrain_size(TerrainData *data) { * boxes (water, gas, JRB fog). */ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 *macroObjects) { + PUPPYPRINT_GET_SNAPSHOT(); s32 terrainLoadType; TerrainData *vertexData = NULL; u32 surfacePoolData; @@ -558,12 +559,14 @@ void load_area_terrain(s32 index, TerrainData *data, RoomData *surfaceRooms, s16 gNumStaticSurfaceNodes = gSurfaceNodesAllocated; gNumStaticSurfaces = gSurfacesAllocated; + profiler_collision_update(first); } /** * If not in time stop, clear the surface partitions. */ void clear_dynamic_surfaces(void) { + PUPPYPRINT_GET_SNAPSHOT(); if (!(gTimeStopState & TIME_STOP_ACTIVE)) { gSurfacesAllocated = gNumStaticSurfaces; gSurfaceNodesAllocated = gNumStaticSurfaceNodes; @@ -578,6 +581,7 @@ void clear_dynamic_surfaces(void) { sNumCellsUsed = 0; sClearAllCells = FALSE; } + profiler_collision_update(first); } /** @@ -691,6 +695,7 @@ static TerrainData sVertexData[600]; * Transform an object's vertices, reload them, and render the object. */ void load_object_collision_model(void) { + PUPPYPRINT_GET_SNAPSHOT(); TerrainData *collisionData = o->collisionData; f32 marioDist = o->oDistanceToMario; @@ -727,12 +732,14 @@ void load_object_collision_model(void) { } } COND_BIT((marioDist < o->oDrawingDistance), o->header.gfx.node.flags, GRAPH_RENDER_ACTIVE); + profiler_collision_update(first); } /** * Transform an object's vertices and add them to the static surface pool. */ void load_object_static_model(void) { + PUPPYPRINT_GET_SNAPSHOT(); TerrainData *collisionData = o->collisionData; u32 surfacePoolData; @@ -756,4 +763,5 @@ void load_object_static_model(void) { gNumStaticSurfaceNodes = gSurfaceNodesAllocated; gNumStaticSurfaces = gSurfacesAllocated; + profiler_collision_update(first); } diff --git a/src/game/area.c b/src/game/area.c index 3b5b851f..0072563a 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -376,10 +376,14 @@ void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 b } void render_game(void) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) { if (gCurrentArea->graphNode) { geo_process_root(gCurrentArea->graphNode, gViewportOverride, gViewportClip, gFBSetColor); } +#ifdef PUPPYPRINT + bzero(gCurrEnvCol, sizeof(ColorRGBA)); +#endif gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gViewport)); @@ -389,6 +393,9 @@ void render_game(void) { gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); render_text_labels(); +#ifdef PUPPYPRINT + puppyprint_print_deferred(); +#endif do_cutscene_handler(); print_displaying_credits_entry(); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, gBorderHeight, SCREEN_WIDTH, @@ -422,6 +429,9 @@ void render_game(void) { } } else { render_text_labels(); +#ifdef PUPPYPRINT + puppyprint_print_deferred(); +#endif if (gViewportClip != NULL) { clear_viewport(gViewportClip, gWarpTransFBSetColor); } else { @@ -431,11 +441,10 @@ void render_game(void) { gViewportOverride = NULL; gViewportClip = NULL; - - profiler_update(PROFILER_TIME_GFX); - profiler_print_times(); -#if PUPPYPRINT_DEBUG + profiler_update(PROFILER_TIME_GFX, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); + profiler_print_times(); +#ifdef PUPPYPRINT_DEBUG puppyprint_render_profiler(); #endif } diff --git a/src/game/camera.c b/src/game/camera.c index f9bf782c..59cee542 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -27,6 +27,7 @@ #include "level_table.h" #include "config.h" #include "puppyprint.h" +#include "profiling.h" #define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS) @@ -2865,12 +2866,12 @@ void update_lakitu(struct Camera *c) { gLakituState.defMode = c->defMode; } - /** * The main camera update function. * Gets controller input, checks for cutscenes, handles mode changes, and moves the camera */ void update_camera(struct Camera *c) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); gCamera = c; update_camera_hud_status(c); if (c->cutscene == CUTSCENE_NONE @@ -3113,6 +3114,7 @@ void update_camera(struct Camera *c) { } #endif gLakituState.lastFrameAction = sMarioCamState->action; + profiler_update(PROFILER_TIME_CAMERA, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } /** diff --git a/src/game/crash_screen.c b/src/game/crash_screen.c index b7fccab4..c716856d 100644 --- a/src/game/crash_screen.c +++ b/src/game/crash_screen.c @@ -18,7 +18,7 @@ enum crashPages { PAGE_CONTEXT, -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG PAGE_LOG, #endif PAGE_STACKTRACE, @@ -233,7 +233,7 @@ void draw_crash_context(OSThread *thread, s32 cause) { } -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG void draw_crash_log(void) { s32 i; crash_screen_draw_rect(25, 20, 270, 210); @@ -361,7 +361,7 @@ void draw_crash_screen(OSThread *thread) { crash_screen_print(30, 10, "Page:%02d L/Z: Left R: Right", crashPage); switch (crashPage) { case PAGE_CONTEXT: draw_crash_context(thread, cause); break; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG case PAGE_LOG: draw_crash_log(); break; #endif case PAGE_STACKTRACE: draw_stacktrace(thread, cause); break; diff --git a/src/game/fasttext.c b/src/game/fasttext.c index 3cb5bf9c..1332c2ac 100644 --- a/src/game/fasttext.c +++ b/src/game/fasttext.c @@ -38,7 +38,7 @@ int computeS(unsigned char letter) { static const u8 fast_text_font_kerning[] = { /* */ 2, /*!*/ 4, /*"*/ 5, /*#*/ 0, /*$*/ 0, /*%*/ 7, /*&*/ 7, /*'*/ 2, /*(*/ 5, /*)*/ 5, /***/ 0, /*+*/ 7, /*,*/ 3, /*-*/ 7, /*.*/ 3, /*/*/ 7, /*0*/ 7, /*1*/ 7, /*2*/ 7, /*3*/ 7, /*4*/ 7, /*5*/ 7, /*6*/ 7, /*7*/ 7, /*8*/ 7, /*9*/ 7, /*:*/ 3, /*;*/ 3, /*<*/ 0, /*=*/ 0, /*>*/ 0, /*?*/ 7, - /*@*/ 0, /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 7, /*F*/ 7, /*G*/ 7, /*H*/ 7, /*I*/ 6, /*J*/ 5, /*K*/ 7, /*L*/ 7, /*M*/ 7, /*N*/ 7, /*O*/ 7, + /*@*/ 0, /*A*/ 7, /*B*/ 7, /*C*/ 7, /*D*/ 7, /*E*/ 7, /*F*/ 7, /*G*/ 7, /*H*/ 7, /*I*/ 6, /*J*/ 7, /*K*/ 7, /*L*/ 7, /*M*/ 7, /*N*/ 7, /*O*/ 7, /*P*/ 7, /*Q*/ 7, /*R*/ 7, /*S*/ 7, /*T*/ 7, /*U*/ 7, /*V*/ 7, /*W*/ 7, /*X*/ 7, /*Y*/ 7, /*Z*/ 7, /*[*/ 0, /*\*/ 0, /*]*/ 0, /*^*/ 6, /*_*/ 0, /*`*/ 0, /*a*/ 6, /*b*/ 6, /*c*/ 6, /*d*/ 6, /*e*/ 6, /*f*/ 6, /*g*/ 6, /*h*/ 6, /*i*/ 2, /*j*/ 6, /*k*/ 5, /*l*/ 3, /*m*/ 6, /*n*/ 6, /*o*/ 6, /*p*/ 6, /*q*/ 6, /*r*/ 6, /*s*/ 6, /*t*/ 6, /*u*/ 6, /*v*/ 6, /*w*/ 6, /*x*/ 6, /*y*/ 6, /*z*/ 6, /*{*/ 0, /*|*/ 0, /*}*/ 0, /*~*/ 7, @@ -58,6 +58,7 @@ void drawSmallString_impl(Gfx **dl, int x, int y, const char* string, int r, int while (string[i] != '\0') { unsigned int cur_char = string[i]; + s32 goddamnJMeasure = string[i] == 'j' ? -1 : 0; if (cur_char == '\n') { xPos = x; @@ -73,7 +74,7 @@ void drawSmallString_impl(Gfx **dl, int x, int y, const char* string, int r, int } else { if (cur_char != ' ') { s = computeS(cur_char); - gSPTextureRectangle(dlHead++, (xPos + 0) << 2, (yPos + 0) << 2, (xPos + 8) << 2, (yPos + 12) << 2, 0, s << 5, 0, 1 << 10, 1 << 10); + gSPTextureRectangle(dlHead++, (xPos + 0) << 2, (yPos + 0) << 2, (xPos + 8) << 2, (yPos + 12) << 2, 0, (s << 5) - goddamnJMeasure, 0, 1 << 10, 1 << 10); } xPos += fast_text_font_kerning[cur_char - ' ']; } diff --git a/src/game/game_init.c b/src/game/game_init.c index cb451539..697fc4e5 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -727,6 +727,10 @@ void setup_game_memory(void) { gMarioAnimsMemAlloc = main_pool_alloc(MARIO_ANIMS_POOL_SIZE, MEMORY_POOL_LEFT); set_segment_base_addr(SEGMENT_MARIO_ANIMS, (void *) gMarioAnimsMemAlloc); setup_dma_table_list(&gMarioAnimsBuf, gMarioAnims, gMarioAnimsMemAlloc); +#ifdef PUPPYPRINT_DEBUG + set_segment_memory_printout(SEGMENT_MARIO_ANIMS, MARIO_ANIMS_POOL_SIZE); + set_segment_memory_printout(SEGMENT_DEMO_INPUTS, DEMO_INPUTS_POOL_SIZE); +#endif // Setup Demo Inputs List gDemoInputsMemAlloc = main_pool_alloc(DEMO_INPUTS_POOL_SIZE, MEMORY_POOL_LEFT); set_segment_base_addr(SEGMENT_DEMO_INPUTS, (void *) gDemoInputsMemAlloc); @@ -776,7 +780,9 @@ void thread5_game_loop(UNUSED void *arg) { draw_reset_bars(); continue; } - +#ifdef PUPPYPRINT_DEBUG + bzero(&gPuppyCallCounter, sizeof(gPuppyCallCounter)); +#endif // If any controllers are plugged in, start read the data for when // read_controller_inputs is called later. if (gControllerBits) { @@ -789,12 +795,14 @@ void thread5_game_loop(UNUSED void *arg) { audio_game_loop_tick(); select_gfx_pool(); read_controller_inputs(THREAD_5_GAME_LOOP); - profiler_update(PROFILER_TIME_CONTROLLERS); + profiler_update(PROFILER_TIME_CONTROLLERS, 0); + profiler_collision_reset(); addr = level_script_execute(addr); -#if !PUPPYPRINT_DEBUG && defined(VISUAL_DEBUG) + profiler_collision_completed(); +#if !defined(PUPPYPRINT_DEBUG) && defined(VISUAL_DEBUG) debug_box_input(); #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG puppyprint_profiler_process(); #endif diff --git a/src/game/hud.c b/src/game/hud.c index 66c9ccfc..75118b56 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -597,9 +597,6 @@ void render_hud(void) { if (gCustomDebugMode) { render_debug_mode(); } -#endif -#ifdef PUPPYPRINT - print_set_envcolour(255, 255, 255, 255); #endif } } diff --git a/src/game/level_update.c b/src/game/level_update.c index b22407d6..c3556d68 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -435,6 +435,11 @@ void init_mario_after_warp(void) { } #endif } +#ifdef PUPPYPRINT_DEBUG + gPuppyWarp = 0; + gLastWarpID = sWarpDest.nodeId; + gPuppyWarpArea = 0; +#endif } // used for warps inside one level @@ -845,6 +850,12 @@ void initiate_delayed_warp(void) { struct ObjectWarpNode *warpNode; s32 destWarpNode; +#ifdef PUPPYPRINT_DEBUG + if (gPuppyWarp) { + initiate_warp(gPuppyWarp, gPuppyWarpArea, 0x0A, 0); + } +#endif + if (sDelayedWarpOp != WARP_OP_NONE && --sDelayedWarpTimer == 0) { reset_dialog_render_state(); @@ -996,18 +1007,31 @@ s32 play_mode_normal(void) { warp_area(); check_instant_warp(); +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage != PUPPYPRINT_PAGE_RAM && sPPDebugPage != PUPPYPRINT_PAGE_LEVEL_SELECT) { + if (sTimerRunning && gHudDisplay.timer < 17999) { + gHudDisplay.timer++; + } + area_update_objects(); + } +#else if (sTimerRunning && gHudDisplay.timer < 17999) { gHudDisplay.timer++; } - area_update_objects(); +#endif update_hud_values(); #ifdef PUPPYLIGHTS delete_lights(); #endif - if (gCurrentArea != NULL) { +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage != PUPPYPRINT_PAGE_RAM && sPPDebugPage != PUPPYPRINT_PAGE_LEVEL_SELECT) { + update_camera(gCurrentArea->camera); + } +#else update_camera(gCurrentArea->camera); +#endif } initiate_painting_warp(); @@ -1029,7 +1053,7 @@ s32 play_mode_normal(void) { set_play_mode(PLAY_MODE_PAUSED); } } - + return FALSE; } @@ -1177,7 +1201,7 @@ s32 update_level(void) { s32 init_level(void) { s32 fadeFromColor = FALSE; -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG OSTime first = osGetTime(); #endif @@ -1263,7 +1287,7 @@ s32 init_level(void) { puppylights_allocate(); #endif -#if PUPPYPRINT_DEBUG +#ifdef PUPPYPRINT_DEBUG #ifdef PUPPYPRINT_DEBUG_CYCLES append_puppyprint_log("Level loaded in %dc", (s32)(osGetTime() - first)); #else diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 4ee6d7d4..47f7967b 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -547,11 +547,15 @@ void clear_objects(void) { * Update spawner and surface objects. */ void update_terrain_objects(void) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); gObjectCounter = update_objects_in_list(&gObjectLists[OBJ_LIST_SPAWNER]); - profiler_update(PROFILER_TIME_SPAWNER); + profiler_update(PROFILER_TIME_SPAWNER, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); +#ifdef PUPPYPRINT_DEBUG + first = profiler_get_delta(PROFILER_DELTA_COLLISION); +#endif gObjectCounter += update_objects_in_list(&gObjectLists[OBJ_LIST_SURFACE]); - profiler_update(PROFILER_TIME_DYNAMIC); + profiler_update(PROFILER_TIME_DYNAMIC, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); // If the dynamic surface pool has overflowed, throw an error. assert((uintptr_t)gDynamicSurfacePoolEnd <= (uintptr_t)gDynamicSurfacePool + DYNAMIC_SURFACE_POOL_SIZE, "Dynamic surface pool size exceeded"); @@ -566,12 +570,13 @@ void update_non_terrain_objects(void) { s32 i = 2; while ((listIndex = sObjectListUpdateOrder[i]) != -1) { + PROFILER_GET_SNAPSHOT_TYPE(PROFILER_DELTA_COLLISION); if (listIndex == OBJ_LIST_PLAYER) { - profiler_update(PROFILER_TIME_BEHAVIOR_BEFORE_MARIO); + profiler_update(PROFILER_TIME_BEHAVIOR_BEFORE_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } gObjectCounter += update_objects_in_list(&gObjectLists[listIndex]); if (listIndex == OBJ_LIST_PLAYER) { - profiler_update(PROFILER_TIME_MARIO); + profiler_update(PROFILER_TIME_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - first); } i++; } @@ -648,6 +653,9 @@ void update_objects(UNUSED s32 unused) { // Update all other objects that haven't been updated yet update_non_terrain_objects(); + + // Take a snapshot of the current collision processing time. + UNUSED u32 firstPoint = profiler_get_delta(PROFILER_DELTA_COLLISION); // Unload any objects that have been deactivated unload_deactivated_objects(); @@ -666,6 +674,6 @@ void update_objects(UNUSED s32 unused) { } gPrevFrameObjectCount = gObjectCounter; - - profiler_update(PROFILER_TIME_BEHAVIOR_AFTER_MARIO); + // Set the recorded behaviour time, minus the difference between the snapshotted collision time and the actual collision time. + profiler_update(PROFILER_TIME_BEHAVIOR_AFTER_MARIO, profiler_get_delta(PROFILER_DELTA_COLLISION) - firstPoint); } diff --git a/src/game/profiling.c b/src/game/profiling.c index 31dfcdef..644dff47 100644 --- a/src/game/profiling.c +++ b/src/game/profiling.c @@ -4,16 +4,12 @@ #include "profiling.h" #include "fasttext.h" +#include "puppyprint.h" #ifdef USE_PROFILER #define RDP_CYCLE_CONV(x) ((10 * (x)) / 625) // 62.5 million cycles per frame -typedef struct { - u32 counts[PROFILING_BUFFER_SIZE]; - u32 total; -} ProfileTimeData; - ProfileTimeData all_profiling_data[PROFILER_TIME_COUNT]; int profile_buffer_index = -1; @@ -21,11 +17,17 @@ int rsp_buffer_indices[PROFILER_RSP_COUNT]; // Holds either the start time if the task is running, or the amount of time the task has run for so far if yielded u32 rsp_pending_times[PROFILER_RSP_COUNT]; u32 prev_start; -u32 start; +u32 cur_start; u32 prev_time; u32 audio_start; u32 audio_buffer_index; u32 preempted_time; +u32 collision_time = 0; + +#ifdef AUDIO_PROFILING +u32 audio_subset_starts[AUDIO_SUBSET_SIZE]; +u32 audio_subset_tallies[AUDIO_SUBSET_SIZE]; +#endif static void buffer_update(ProfileTimeData* data, u32 new, int buffer_index) { u32 old = data->counts[buffer_index]; @@ -34,12 +36,12 @@ static void buffer_update(ProfileTimeData* data, u32 new, int buffer_index) { data->counts[buffer_index] = new; } -void profiler_update(enum ProfilerTime which) { +void profiler_update(enum ProfilerTime which, u32 delta) { u32 cur_time = osGetCount(); u32 diff; ProfileTimeData* cur_data = &all_profiling_data[which]; - diff = cur_time - prev_time; + diff = cur_time - prev_time - delta; u32 saved = __osDisableInt(); u32 cur_preempted_time = preempted_time; @@ -47,7 +49,7 @@ void profiler_update(enum ProfilerTime which) { __osRestoreInt(saved); if (cur_preempted_time > 0) { diff -= cur_preempted_time; - start += cur_preempted_time; + cur_start += cur_preempted_time; } buffer_update(cur_data, diff, profile_buffer_index); @@ -83,15 +85,58 @@ void profiler_rsp_resumed() { void profiler_audio_started() { audio_start = osGetCount(); + +#ifdef AUDIO_PROFILING + for (s32 i = 0; i < AUDIO_SUBSET_SIZE; i++) { + audio_subset_tallies[i] = 0; + } + + audio_subset_starts[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START] = audio_start; +#endif +} + +#ifdef PUPPYPRINT_DEBUG + +void profiler_collision_reset() { + collision_time = 0; +} + +void profiler_collision_update(u32 time) { + collision_time += osGetCount() - time; +} + +void profiler_collision_completed() { + ProfileTimeData* cur_data = &all_profiling_data[PROFILER_TIME_COLLISION]; + buffer_update(cur_data, collision_time, profile_buffer_index); +} + +#endif + +u32 profiler_get_delta(enum ProfilerDeltaTime which) { + if (which == PROFILER_DELTA_COLLISION) { + return collision_time; + } else { + return 0; + } } void profiler_audio_completed() { ProfileTimeData* cur_data = &all_profiling_data[PROFILER_TIME_AUDIO]; - u32 time = osGetCount() - audio_start; + u32 time = osGetCount(); u32 cur_index = audio_buffer_index; - preempted_time = time; - buffer_update(cur_data, time, cur_index); + preempted_time = time - audio_start; + buffer_update(cur_data, time - audio_start, cur_index); + +#ifdef AUDIO_PROFILING + audio_subset_tallies[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START] += time - audio_subset_starts[PROFILER_TIME_SUB_AUDIO_UPDATE - PROFILER_TIME_SUB_AUDIO_START]; + + for (s32 i = 0; i < AUDIO_SUBSET_SIZE; i++) { + cur_data = &all_profiling_data[i + PROFILER_TIME_SUB_AUDIO_START]; + buffer_update(cur_data, audio_subset_tallies[i], cur_index); + } +#endif + cur_index++; if (cur_index >= PROFILING_BUFFER_SIZE) { cur_index = 0; @@ -101,10 +146,10 @@ void profiler_audio_completed() { } static void update_fps_timer() { - u32 diff = start - prev_start; + u32 diff = cur_start - prev_start; buffer_update(&all_profiling_data[PROFILER_TIME_FPS], diff, profile_buffer_index); - prev_start = start; + prev_start = cur_start; } static void update_total_timer() { @@ -113,10 +158,15 @@ static void update_total_timer() { preempted_time = 0; __osRestoreInt(saved); - prev_time = start + cur_preempted_time; - profiler_update(PROFILER_TIME_TOTAL); + prev_time = cur_start + cur_preempted_time; + profiler_update(PROFILER_TIME_TOTAL, PROFILER_TIME_PUPPYPRINT1 + PROFILER_DELTA_PUPPYPRINT2); } +#ifdef PUPPYPRINT_DEBUG +extern u8 sPPDebugPage; +extern u8 fDebug; +#endif + static void update_rdp_timers() { u32 tmem = IO_READ(DPC_TMEM_REG); u32 cmd = IO_READ(DPC_BUFBUSY_REG); @@ -131,11 +181,6 @@ static void update_rdp_timers() { buffer_update(&all_profiling_data[PROFILER_TIME_PIPE], pipe, profile_buffer_index); } -#ifdef PUPPYPRINT_DEBUG -extern u8 sPPDebugPage; -extern u8 fDebug; -#endif - float profiler_get_fps() { return (1000000.0f * PROFILING_BUFFER_SIZE) / (OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_FPS].total)); } @@ -194,13 +239,13 @@ void profiler_print_times() { #ifndef PUPPYPRINT_DEBUG static u8 show_profiler = 0; - if (gPlayer1Controller->buttonPressed & L_TRIG) { + if ((gPlayer1Controller->buttonPressed & (L_TRIG | U_JPAD)) && (gPlayer1Controller->buttonDown & L_TRIG) && (gPlayer1Controller->buttonDown & U_JPAD)) { show_profiler ^= 1; } #endif #ifdef PUPPYPRINT_DEBUG - if (fDebug && sPPDebugPage == 0) { + if (fDebug && sPPDebugPage == PUPPYPRINT_PAGE_PROFILER) { #else if (show_profiler) { #endif @@ -222,11 +267,18 @@ void profiler_print_times() { "\n" "CPU\t\t%d (%d%%)\n" " Input\t\t%d\n" +#ifdef PUPPYPRINT_DEBUG + " Collision\t\t%d\n" +#else " Dynamic\t\t%d\n" +#endif " Mario\t\t\t%d\n" " Behavior\t\t%d\n" " Graph\t\t%d\n" " Audio\t\t\t%d\n" +#ifdef PUPPYPRINT_DEBUG + " Camera\t\t%d\n" +#endif "\n" "RDP\t\t%d (%d%%)\n" " Tmem\t\t\t%d\n" @@ -239,11 +291,18 @@ void profiler_print_times() { 1000000.0f / microseconds[PROFILER_TIME_FPS], total_cpu, total_cpu / 333, microseconds[PROFILER_TIME_CONTROLLERS], +#ifdef PUPPYPRINT_DEBUG + microseconds[PROFILER_TIME_COLLISION], +#else microseconds[PROFILER_TIME_DYNAMIC], +#endif microseconds[PROFILER_TIME_MARIO], microseconds[PROFILER_TIME_BEHAVIOR_BEFORE_MARIO] + microseconds[PROFILER_TIME_BEHAVIOR_AFTER_MARIO], microseconds[PROFILER_TIME_GFX], microseconds[PROFILER_TIME_AUDIO] * 2, // audio is 60Hz, so double the average +#ifdef PUPPYPRINT_DEBUG + microseconds[PROFILER_TIME_CAMERA], +#endif max_rdp, max_rdp / 333, microseconds[PROFILER_TIME_TMEM], microseconds[PROFILER_TIME_CMD], @@ -260,7 +319,7 @@ void profiler_print_times() { gDPSetTexturePersp(dlHead++, G_TP_NONE); gDPSetTextureFilter(dlHead++, G_TF_POINT); gDPSetTextureLUT(dlHead++, G_TT_NONE); - drawSmallStringCol(&dlHead, 10, 10, text_buffer, 255, 255, 255); + drawSmallStringCol(&dlHead, 10, 8, text_buffer, 255, 255, 255); gDisplayListHead = dlHead; } } @@ -273,7 +332,7 @@ void profiler_frame_setup() { profile_buffer_index = 0; } - prev_time = start = osGetCount(); + prev_time = cur_start = osGetCount(); } #endif diff --git a/src/game/profiling.h b/src/game/profiling.h index 48bbe34c..a52b801d 100644 --- a/src/game/profiling.h +++ b/src/game/profiling.h @@ -6,8 +6,30 @@ #include "config/config_debug.h" #include "config/config_safeguards.h" +#if defined(USE_PROFILER) && defined(PUPPYPRINT_DEBUG) +/** + * Toggle this define to enable verbose audio profiling with Pupprprint Debug. +*/ +#define AUDIO_PROFILING +#endif + +#define OS_GET_COUNT_INLINE(x) asm volatile("mfc0 %0, $9" : "=r"(x): ) + #define PROFILING_BUFFER_SIZE 64 +#define AUDIO_SUBSET_ENTRIES \ + PROFILER_TIME_SUB_AUDIO_START, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES = PROFILER_TIME_SUB_AUDIO_START, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_SCRIPT, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_RECLAIM, \ + PROFILER_TIME_SUB_AUDIO_SEQUENCES_PROCESSING, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_PROCESSING, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_ENVELOPE_REVERB, \ + PROFILER_TIME_SUB_AUDIO_SYNTHESIS_DMA, \ + PROFILER_TIME_SUB_AUDIO_UPDATE, \ + PROFILER_TIME_SUB_AUDIO_END + enum ProfilerTime { PROFILER_TIME_FPS, PROFILER_TIME_CONTROLLERS, @@ -17,7 +39,19 @@ enum ProfilerTime { PROFILER_TIME_MARIO, PROFILER_TIME_BEHAVIOR_AFTER_MARIO, PROFILER_TIME_GFX, + PROFILER_TIME_COLLISION, + PROFILER_TIME_CAMERA, +#ifdef PUPPYPRINT_DEBUG + PROFILER_TIME_PUPPYPRINT1, + PROFILER_TIME_PUPPYPRINT2, +#endif +#ifdef AUDIO_PROFILING + AUDIO_SUBSET_ENTRIES, + + PROFILER_TIME_AUDIO = PROFILER_TIME_SUB_AUDIO_END, +#else PROFILER_TIME_AUDIO, +#endif PROFILER_TIME_TOTAL, PROFILER_TIME_RSP_GFX, PROFILER_TIME_RSP_AUDIO, @@ -33,8 +67,29 @@ enum ProfilerRSPTime { PROFILER_RSP_COUNT }; +enum ProfilerDeltaTime { + PROFILER_DELTA_COLLISION, +#ifdef PUPPYPRINT_DEBUG + PROFILER_DELTA_PUPPYPRINT1, + PROFILER_DELTA_PUPPYPRINT2 +#endif +}; + +#ifndef PUPPYPRINT_DEBUG +#define PROFILER_TIME_PUPPYPRINT1 0 +#define PROFILER_TIME_PUPPYPRINT2 0 +#define PROFILER_DELTA_PUPPYPRINT1 0 +#define PROFILER_DELTA_PUPPYPRINT2 0 +#endif + #ifdef USE_PROFILER -void profiler_update(enum ProfilerTime which); +typedef struct { + u32 counts[PROFILING_BUFFER_SIZE]; + u32 total; +} ProfileTimeData; +extern ProfileTimeData all_profiling_data[PROFILER_TIME_COUNT]; + +void profiler_update(enum ProfilerTime which, u32 delta); void profiler_print_times(); void profiler_frame_setup(); void profiler_rsp_started(enum ProfilerRSPTime which); @@ -42,12 +97,29 @@ void profiler_rsp_completed(enum ProfilerRSPTime which); void profiler_rsp_resumed(); void profiler_audio_started(); void profiler_audio_completed(); +#ifdef PUPPYPRINT_DEBUG +void profiler_collision_reset(); +void profiler_collision_completed(); +void profiler_collision_update(u32 time); +#else +#define profiler_collision_reset() +#define profiler_collision_completed() +#define profiler_collision_update(time) +#endif +u32 profiler_get_delta(enum ProfilerDeltaTime which); +u32 profiler_get_cpu_microseconds(); +u32 profiler_get_rsp_microseconds(); +u32 profiler_get_rdp_microseconds(); // See profiling.c to see why profiler_rsp_yielded isn't its own function static ALWAYS_INLINE void profiler_rsp_yielded() { profiler_rsp_resumed(); +#define PROFILER_GET_SNAPSHOT() u32 first = osGetCount() +#define PROFILER_GET_SNAPSHOT_TYPE(type) u32 first = profiler_get_delta(type) } #else -#define profiler_update(which) +#define PROFILER_GET_SNAPSHOT() +#define PROFILER_GET_SNAPSHOT_TYPE(type) +#define profiler_update(which, delta) #define profiler_print_times() #define profiler_frame_setup() #define profiler_rsp_started(which) @@ -56,6 +128,71 @@ static ALWAYS_INLINE void profiler_rsp_yielded() { #define profiler_audio_started() #define profiler_audio_completed() #define profiler_rsp_yielded() +#define profiler_collision_reset() +#define profiler_collision_completed() +#define profiler_collision_update(time) +#define profiler_get_delta(which) 0 +#define profiler_get_cpu_microseconds() 0 +#define profiler_get_rsp_microseconds() 0 +#define profiler_get_rdp_microseconds() 0 #endif +#ifdef AUDIO_PROFILING +#define AUDIO_SUBSET_SIZE PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START +extern u32 audio_subset_starts[AUDIO_SUBSET_SIZE]; +extern u32 audio_subset_tallies[AUDIO_SUBSET_SIZE]; + +static ALWAYS_INLINE void profiler_audio_subset_switch_func(enum ProfilerTime complete, enum ProfilerTime start) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[complete] += time - audio_subset_starts[complete]; + audio_subset_starts[start] = time; +} + +static ALWAYS_INLINE void profiler_audio_subset_complete_and_switch_func(enum ProfilerTime complete1, enum ProfilerTime complete2, enum ProfilerTime start) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[complete1] += time - audio_subset_starts[complete1]; + audio_subset_tallies[complete2] += time - audio_subset_starts[complete2]; + audio_subset_starts[start] = time; +} + +static ALWAYS_INLINE void profiler_audio_subset_start_shared_func(enum ProfilerTime first, enum ProfilerTime new) { + audio_subset_starts[new] = audio_subset_starts[first]; +} + +static ALWAYS_INLINE void profiler_audio_subset_start_func(enum ProfilerTime index) { + OS_GET_COUNT_INLINE(audio_subset_starts[index]); +} + +static ALWAYS_INLINE void profiler_audio_subset_complete_func(enum ProfilerTime index) { + u32 time; + OS_GET_COUNT_INLINE(time); + + audio_subset_tallies[index] += time - audio_subset_starts[index]; +} + +#define AUDIO_PROFILER_SWITCH(complete, begin) profiler_audio_subset_switch_func(complete - PROFILER_TIME_SUB_AUDIO_START, begin - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_COMPLETE_AND_SWITCH(complete1, complete2, begin) profiler_audio_subset_complete_and_switch_func(complete1 - PROFILER_TIME_SUB_AUDIO_START, \ + complete2 - PROFILER_TIME_SUB_AUDIO_START, begin - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_START_SHARED(first, new) profiler_audio_subset_start_shared_func(first - PROFILER_TIME_SUB_AUDIO_START, new - PROFILER_TIME_SUB_AUDIO_START) + +// These two are unused by the default audio profiler; left in for cases of manual profiling of smaller functions as needed +#define AUDIO_PROFILER_START(which) profiler_audio_subset_start_func(which - PROFILER_TIME_SUB_AUDIO_START) +#define AUDIO_PROFILER_COMPLETE(which) profiler_audio_subset_complete_func(which - PROFILER_TIME_SUB_AUDIO_START) +#else // AUDIO_PROFILING +enum ProfilerTimeAudioUnused { + AUDIO_SUBSET_ENTRIES +}; +#define AUDIO_PROFILER_SWITCH(complete, begin) +#define AUDIO_PROFILER_COMPLETE_AND_SWITCH(complete1, complete2, begin) +#define AUDIO_PROFILER_START_SHARED(first, new) + +// These two are unused by the default audio profiler; left in for cases of manual profiling of smaller functions as needed +#define AUDIO_PROFILER_START(which) +#define AUDIO_PROFILER_COMPLETE(which) +#endif // AUDIO_PROFILING + #endif diff --git a/src/game/puppycam2.c b/src/game/puppycam2.c index 089155ad..20c6e205 100644 --- a/src/game/puppycam2.c +++ b/src/game/puppycam2.c @@ -1346,6 +1346,10 @@ void puppycam_loop(void) { if (!gPuppyCam.cutscene && sDelayedWarpOp == 0) { // Sets this before going through any possible modifications. gPuppyCam.flags = gPuppyCam.intendedFlags; +#ifdef PUPPYPRINT_DEBUG + if (sPPDebugPage == PUPPYPRINT_PAGE_CAMERA) + gPuppyCam.flags |= PUPPYCAM_BEHAVIOUR_FREE | PUPPYCAM_BEHAVIOUR_PITCH_ROTATION | PUPPYCAM_BEHAVIOUR_YAW_ROTATION; +#endif puppycam_input_core(); puppycam_projection(); puppycam_script(); diff --git a/src/game/puppyprint.c b/src/game/puppyprint.c index 141d2ed8..5d7074f2 100644 --- a/src/game/puppyprint.c +++ b/src/game/puppyprint.c @@ -44,90 +44,105 @@ a modern game engine's developer's console. #include "hud.h" #include "debug_box.h" #include "color_presets.h" +#include "buffers/buffers.h" +#include "profiling.h" +#include "segment_symbols.h" #ifdef PUPPYPRINT -ColorRGBA currEnv; +#define TAB_WIDTH 16 + #ifdef ENABLE_CREDITS_BENCHMARK u8 fDebug = TRUE; #else u8 fDebug = FALSE; #endif +u8 sPuppyprintTextBuffer[PUPPYPRINT_DEFERRED_BUFFER_SIZE]; +u32 sPuppyprintTextBufferPos; // Location in the buffer of puppyprint deferred text. +ColorRGBA gCurrEnvCol; + +#ifdef PUPPYPRINT_DEBUG -#if PUPPYPRINT_DEBUG -s8 benchViewer = FALSE; -u8 benchOption = 0; s8 logViewer = FALSE; u8 sPPDebugPage = 0; u8 sDebugMenu = FALSE; u8 sDebugOption = 0; -// Profiler values -s8 perfIteration = 0; -s16 benchmarkLoop = 0; -s32 benchmarkTimer = 0; -s32 benchmarkProgramTimer = 0; -s8 benchmarkType = 0; -// General -u32 rspTime = 0; -u32 rdpTime = 0; -s32 benchMark[NUM_BENCH_ITERATIONS + 2]; -// RAM -s8 ramViewer = FALSE; -s32 ramsizeSegment[NUM_TLB_SEGMENTS + 1] = { - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, - 0, 0, 0 -}; -s8 audioRamViewer = FALSE; +s32 ramsizeSegment[NUM_TLB_SEGMENTS + 1] = { 0 }; s32 mempool; +u32 gPoolMem; +u32 gPPSegScroll = 0; +u32 gMiscMem = 0; +struct CallCounter gPuppyCallCounter; -extern u8 _mainSegmentStart[]; -extern u8 _mainSegmentEnd[]; -extern u8 _engineSegmentStart[]; -extern u8 _engineSegmentEnd[]; -extern u8 _framebuffersSegmentBssStart[]; -extern u8 _framebuffersSegmentBssEnd[]; -extern u8 _buffersSegmentBssStart[]; -extern u8 _buffersSegmentBssEnd[]; -extern u8 _goddardSegmentStart[]; -extern u8 _goddardSegmentEnd[]; +#define NUM_RAM_CHARS 32 -// Here is stored the rom addresses of the global code segments. If you get rid of any, it's best to just write them as NULL. -u32 ramP[5][2] = { - {(u32)_buffersSegmentBssStart, (u32)_buffersSegmentBssEnd}, - {(u32)_mainSegmentStart, (u32)_mainSegmentEnd}, - {(u32)_engineSegmentStart, (u32)_engineSegmentEnd}, - {(u32)_framebuffersSegmentBssStart, (u32)_framebuffersSegmentBssEnd}, - {(u32)_goddardSegmentStart, (u32)_goddardSegmentEnd}, +// Another epic lookup table, for text this time. +const char ramNames[][NUM_RAM_CHARS] = { + "Buffers", + "Main", + "Engine", + "Framebuffers", + "ZBuffer", + "Goddard", + "Pools", + "Collision", + "Misc", + "Audio Heap" }; +enum RamNames { + RAM_BUFFERS, + RAM_MAIN, + RAM_ENGINE, + RAM_FRAMEBUFFERS, + RAM_ZBUFFER, + RAM_GODDARD, + RAM_POOLS, + RAM_COLLISION, + RAM_MISC, + RAM_AUDIO +}; + +const char segNames[][NUM_RAM_CHARS] = { + "HUD", + "Common1 GFX", + "Group0 GFX", + "GroupA GFX", + "GroupB GFX", + "Level GFX", + "Common0 GFX", + "Textures", + "Skybox", + "Effects", + "GroupA Geo", + "GroupB Geo", + "Level Geo", + "Common0 Geo", + "Entry", + "Mario Anims", + "Demos", + "Bhv Scripts", + "Menu", + "Level Scripts", + "Common1 Geo", + "Group0 Geo", + "", + "Languages" +}; + +const s8 nameTable = sizeof(ramNames) / NUM_RAM_CHARS; + void puppyprint_calculate_ram_usage(void) { - u32 temp[2]; - s32 i = 0; - - for (i = 0; i < 5; i++) { - if (!ramP[i][0] || !ramP[i][1]) { - continue; - } - temp[0] = ramP[i][0]; - temp[1] = ramP[i][1]; - ramsizeSegment[i] = temp[1] - temp[0]; - } - - // These are a bit hacky, but what can ye do eh? - // gEffectsMemoryPool is 0x4000, gObjectMemoryPool is 0x800. Epic C limitations mean I can't just sizeof their values :) - ramsizeSegment[5] = (EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL - + EFFECTS_MEMORY_POOL + OBJECT_MEMORY_POOL); - ramsizeSegment[6] = gTotalStaticSurfaceData + DYNAMIC_SURFACE_POOL_SIZE; - ramsizeSegment[7] = gAudioHeapSize; + ramsizeSegment[RAM_BUFFERS] = (u32)&_buffersSegmentBssEnd - (u32)&_buffersSegmentBssStart - gAudioHeapSize; + ramsizeSegment[RAM_MAIN] = (u32)&_mainSegmentEnd - (u32)&_mainSegmentStart; + ramsizeSegment[RAM_ENGINE] = (u32)&_engineSegmentEnd - (u32)&_engineSegmentStart; + ramsizeSegment[RAM_FRAMEBUFFERS] = (u32)&_framebuffersSegmentBssEnd - (u32)&_framebuffersSegmentBssStart; + ramsizeSegment[RAM_ZBUFFER] = (u32)&_zbufferSegmentBssEnd - (u32)&_zbufferSegmentBssStart; + ramsizeSegment[RAM_GODDARD] = (u32)&_goddardSegmentEnd - (u32)&_goddardSegmentStart; + ramsizeSegment[RAM_POOLS] = gPoolMem; + ramsizeSegment[RAM_COLLISION] = ((u32) gCurrStaticSurfacePoolEnd - (u32) gCurrStaticSurfacePool) + ((u32) gDynamicSurfacePoolEnd - (u32) gDynamicSurfacePool); + ramsizeSegment[RAM_MISC] = gMiscMem; + ramsizeSegment[RAM_AUDIO] = gAudioHeapSize; } #ifdef PUPPYPRINT_DEBUG_CYCLES @@ -138,24 +153,6 @@ void puppyprint_calculate_ram_usage(void) { #define RDP_CYCLE_CONV(x) ((10 * (x)) / 625) // 62.5 million cycles per frame #endif -void puppyprint_profiler_finished(void) { - s32 i = 0; - benchMark[NUM_BENCH_ITERATIONS ] = 0; - benchMark[NUM_BENCH_ITERATIONS + 1] = 0; - benchmarkTimer = 300; - benchViewer = FALSE; - - for (i = 0; i < (NUM_BENCH_ITERATIONS - 2); i++) { - benchMark[NUM_BENCH_ITERATIONS] += benchMark[i]; - if (benchMark[i] > benchMark[NUM_BENCH_ITERATIONS + 1]) { - benchMark[NUM_BENCH_ITERATIONS + 1] = benchMark[i]; - } - } - - benchMark[NUM_BENCH_ITERATIONS] /= NUM_BENCH_ITERATIONS; - benchmarkProgramTimer = CYCLE_CONV(osGetTime() - benchmarkProgramTimer); -} - // RGB colour lookup table for colouring all the funny ram prints. ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 255, 0, 0 }, @@ -170,8 +167,8 @@ ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 204, 0, 102 }, { 0, 153, 153 }, { 153, 255, 153 }, - { 0, 0, 128 }, - { 128, 0, 128 }, + { 127, 127, 255 }, + { 255, 127, 255 }, { 218, 165, 32 }, { 107, 142, 35 }, { 188, 143, 143 }, @@ -191,135 +188,145 @@ ColorRGB colourChart[NUM_TLB_SEGMENTS + 1] = { { 255, 255, 255 } }; -// Change this to alter the width of the bar at the bottom. -#define RAM_BAR_LENGTH 200 -#define RAM_BAR_MIN (SCREEN_CENTER_X - (RAM_BAR_LENGTH / 2)) -#define RAM_BAR_MAX (SCREEN_CENTER_X + (RAM_BAR_LENGTH / 2)) -#define RAM_BAR_TOP (SCREEN_HEIGHT - 30) -#define RAM_BAR_BOTTOM (SCREEN_HEIGHT - 22) - -void print_ram_bar(void) { - s32 i = 0; - f32 perfPercentage; - s32 graphPos = 0; - s32 prevGraph = RAM_BAR_MIN; - s32 ramsize = osGetMemSize(); - - prepare_blank_box(); - - for (i = 0; i < NUM_TLB_SEGMENTS; i++) { - if (ramsizeSegment[i] == 0) { - continue; - } - - perfPercentage = (RAM_BAR_LENGTH * ((f32)ramsizeSegment[i] / ramsize)); - graphPos = (prevGraph + CLAMP(perfPercentage, 1, RAM_BAR_MAX)); - render_blank_box(prevGraph, RAM_BAR_TOP, graphPos, RAM_BAR_BOTTOM, - colourChart[i][0], - colourChart[i][1], - colourChart[i][2], 255); - prevGraph = graphPos; - } - - perfPercentage = (RAM_BAR_LENGTH * ((f32)ramsizeSegment[NUM_TLB_SEGMENTS] / ramsize)); - graphPos = (prevGraph + CLAMP(perfPercentage, 1, RAM_BAR_MAX)); - render_blank_box(prevGraph, RAM_BAR_TOP, graphPos, RAM_BAR_BOTTOM, 255, 255, 255, 255); - prevGraph = graphPos; - - render_blank_box(prevGraph, RAM_BAR_TOP, RAM_BAR_MAX, RAM_BAR_BOTTOM, 0, 0, 0, 255); - - finish_blank_box(); +void swap(int* xp, int* yp) +{ + int temp = *xp; + *xp = *yp; + *yp = temp; } -// Another epic lookup table, for text this time. -const char ramNames[8][32] = { - "Buffers", - "Main", - "Engine", - "Framebuffers", - "Goddard", - "Pools", - "Collision", - "Audio Heap", -}; +void swapu(u8* xp, u8* yp) +{ + u8 temp = *xp; + *xp = *yp; + *yp = temp; +} -s8 nameTable = sizeof(ramNames) / NUM_TLB_SEGMENTS; +void sort_numbers(s32 *values, u8 *values2) +{ + int i, j, min_idx; + + // One by one move boundary of unsorted subarray + for (i = 0; i < NUM_TLB_SEGMENTS; i++) { + + if (values[i] == 0) + continue; + // Find the minimum element in unsorted array + min_idx = i; + for (j = i + 1; j < NUM_TLB_SEGMENTS; j++) + if (values[j] > values[min_idx]) + min_idx = j; + + // Swap the found minimum element + // with the first element + swap(&values[min_idx], &values[i]); + swapu(&values2[min_idx], &values2[i]); + } +} + +void set_segment_memory_printout(u32 segment, u32 amount) { + ramsizeSegment[segment + nameTable - 2] = amount; +} void print_ram_overview(void) { - s32 i = 0; - char textBytes[32]; - s32 x = 80; - s32 y = 16; - s32 drawn = 0; + char textBytes[64]; + s32 y = 56; + f32 ramSize = RAM_END - 0x80000000; + s32 tempNums[32]; + u8 tempPos[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}; prepare_blank_box(); - render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 192); + render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 168); finish_blank_box(); - for (i = 0; i <= NUM_TLB_SEGMENTS; i++) { - if (drawn == 16) { - x = 240; - y = 16; - } + memcpy(tempNums, &ramsizeSegment, 32 * 4); - if (ramsizeSegment[i] == 0) { + sort_numbers(tempNums, tempPos); + + print_set_envcolour(255, 255, 255, 255); + sprintf(textBytes, "Total:"); + print_small_text_light(24, 16- gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%06X",RAM_END - 0x80000000); + print_small_text_light(SCREEN_WIDTH/2, 16 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", mempool); + print_small_text_light(SCREEN_WIDTH - 24, 16 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "Used:"); + print_small_text_light(24, 28- gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%06X", (RAM_END - 0x80000000) - (main_pool_available() - 0x400)); + print_small_text_light(SCREEN_WIDTH/2, 28 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", 100.0f - (((f32)(main_pool_available() - 0x400) / (f32)(RAM_END - 0x80000000)) * 100)); + print_small_text_light(SCREEN_WIDTH - 24, 28 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "Free:"); + print_small_text_light(24, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", (main_pool_available() - 0x400)); + print_small_text_light(SCREEN_WIDTH/2, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", (((f32)(main_pool_available() - 0x400) / (f32)(RAM_END - 0x80000000)) * 100)); + print_small_text_light(SCREEN_WIDTH - 24, 40 - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); + for (u8 i = 0; i < NUM_TLB_SEGMENTS; i++) { + if (tempNums[i] == 0) { continue; } - - if (i < 8) { - sprintf(textBytes, "%s: %X", ramNames[i], ramsizeSegment[i]); - } else { - sprintf(textBytes, "Segment %02X: %X", ((i - nameTable) + 2), ramsizeSegment[i]); + if (y - gPPSegScroll > 0 && y - gPPSegScroll < SCREEN_HEIGHT) { + if (tempPos[i] < nameTable) { + sprintf(textBytes, "%s:", ramNames[tempPos[i]]); + } else { + sprintf(textBytes, "%s:", segNames[tempPos[i] - nameTable]); + } + print_small_text_light(24, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "0x%X", tempNums[i]); + print_small_text_light(SCREEN_WIDTH/2, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + sprintf(textBytes, "(%2.3f%%)", ((f32)tempNums[i] / ramSize) * 100.0f); + print_small_text_light(SCREEN_WIDTH - 24, y - gPPSegScroll, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_DEFAULT); } - - print_set_envcolour(colourChart[i][0], colourChart[i][1], colourChart[i][2], 255); - print_small_text(x, y, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); y += 12; - drawn++; } - - sprintf(textBytes, "RAM: %06X/%06X (%d_)", main_pool_available(), mempool, (s32)(((f32)main_pool_available() / (f32)mempool) * 100)); - print_small_text(SCREEN_CENTER_X, (SCREEN_HEIGHT - 16), textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); - - puppyprint_calculate_ram_usage(); - print_ram_bar(); } -const char *audioPoolNames[NUM_AUDIO_POOLS] = { - "Audio Init Pool", - "Notes And Buffers Pool", - "Persistent Sequence Pool", - "Persistent Bank Pool", - "Temporary Sequence Pool", - "Temporary Bank Pool", +static const char *audioPoolNames[NUM_AUDIO_POOLS] = { + "Audio Init Pool:\t\t\t\t ", + "Notes And Buffers Pool:\t ", + "Persistent Sequence Pool: ", + "Persistent Bank Pool:\t\t ", + "Temporary Sequence Pool:\t ", + "Temporary Bank Pool:\t\t ", #ifdef BETTER_REVERB "Better Reverb Pool", #endif }; -void print_audio_ram_overview(void) { - char textBytes[128]; - const s32 x = 16; - s32 y = 16; - s32 i = 0; +#ifdef AUDIO_PROFILING +static const char *audioBenchmarkNames[PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START] = { + "Sequence Processing:\t ", + " Script Parsing:\t\t\t ", + " Reclaim Notes:\t\t\t ", + " Note Processing:\t\t ", + "Synthesis:\t\t\t\t\t ", + " Note Processing:\t\t ", + " Envelopes / Reverb:\t ", + " Audio DMAs:\t\t\t\t ", + "Audio Update:\t\t\t\t " +}; + +STATIC_ASSERT(ARRAY_COUNT(audioBenchmarkNames) == PROFILER_TIME_SUB_AUDIO_END - PROFILER_TIME_SUB_AUDIO_START, "audioBenchmarkNames has incorrect number of entries!"); +#endif + +static void print_audio_ram_overview(s32 x, char *textBytes) { s32 percentage = 0; - s32 tmpY = y; + s32 y = SCREEN_HEIGHT - 6; s32 totalMemory[2] = { 0, 0 }; s32 audioPoolSizes[NUM_AUDIO_POOLS][2]; - prepare_blank_box(); - render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 192); - finish_blank_box(); puppyprint_get_allocated_pools(audioPoolSizes[0]); - y += 24; - for (i = 0; i < NUM_AUDIO_POOLS; i++) { + for (s8 i = NUM_AUDIO_POOLS - 1; i >= 0; i--) { + y -= 12; + if (audioPoolSizes[i][0] == 0) { percentage = 1000; } else { percentage = (((s64) audioPoolSizes[i][1] * 1000) / audioPoolSizes[i][0]); } - sprintf(textBytes, "%s: %X / %X (%d.%d_)", audioPoolNames[i], + sprintf(textBytes, " %s%X / %X (%d.%d%%)", audioPoolNames[i], audioPoolSizes[i][1], audioPoolSizes[i][0], percentage / 10, @@ -328,13 +335,12 @@ void print_audio_ram_overview(void) { print_set_envcolour(colourChart[i][0], colourChart[i][1], colourChart[i][2], 255); - print_small_text(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); - - y += 12; + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); totalMemory[0] += audioPoolSizes[i][0]; totalMemory[1] += audioPoolSizes[i][1]; } + y -= 12; if (totalMemory[0] == 0) { percentage = 0; @@ -342,63 +348,70 @@ void print_audio_ram_overview(void) { percentage = (((s64) totalMemory[1] * 1000) / totalMemory[0]); } if (totalMemory[0] == gAudioHeapSize) { - sprintf(textBytes, "TOTAL AUDIO MEMORY: %X / %X (%d.%d_)", + sprintf(textBytes, "TOTAL AUDIO MEMORY:\t\t%X / %X (%d.%d%%)", totalMemory[1], totalMemory[0], percentage / 10, percentage % 10); } else { - sprintf(textBytes, "TOTAL AUDIO MEMORY: %X / %X (Incorrect!)", + sprintf(textBytes, "TOTAL AUDIO MEMORY:\t\t%X / %X (Incorrect!)", totalMemory[1], totalMemory[0]); } - print_set_envcolour(colourChart[30][0], - colourChart[30][1], - colourChart[30][2], 255); - print_small_text(x, tmpY, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + print_set_envcolour(255, 255, 255, 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); } -void benchmark_custom(void) { - if ((benchmarkLoop == 0) - || (benchOption != 2)) { - return; - } - - OSTime lastTime; - while (TRUE) { - lastTime = osGetTime(); - // Insert your function here! - - if ((benchmarkLoop > 0) - && (benchOption == 2)) { - benchmarkLoop--; - - benchMark[benchmarkLoop] = (osGetTime() - lastTime); - if (benchmarkLoop == 0) { - puppyprint_profiler_finished(); - break; - } - } else { - break; - } - } -} - -const char benchNames[][32] = { - "Game Thread", - "Audio Thread", - "Custom", -}; - -void print_which_benchmark(void) { - char textBytes[40]; +static void print_audio_overview(void) { + char textBytes[128]; + const s32 x = 12; + s32 y = 6; + u32 us; + u32 percentInt; + u32 percentDec; prepare_blank_box(); - render_blank_box((SCREEN_CENTER_X - 50), 115, (SCREEN_CENTER_X + 50), 160, 0, 0, 0, 255); + render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 64, 64, 64, 95); finish_blank_box(); - sprintf(textBytes, "Select Option#%s#L: Confirm", benchNames[benchOption]); - print_small_text(SCREEN_CENTER_X, 120, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + + us = OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_AUDIO].total / PROFILING_BUFFER_SIZE) * 2; + percentInt = us / 33; + percentDec = percentInt % 10; + percentInt /= 10; + + sprintf(textBytes, "TOTAL AUDIO CPU:\t\t%d (%d.%d%%)", us, percentInt, percentDec); + + print_set_envcolour(255, 255, 255, 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + +#ifdef AUDIO_PROFILING + for (s32 i = 0; i < ARRAY_COUNT(audioBenchmarkNames); i++) { + y += 12; + + us = OS_CYCLES_TO_USEC(all_profiling_data[PROFILER_TIME_SUB_AUDIO_START + i].total / PROFILING_BUFFER_SIZE) * 2; + + if (audioBenchmarkNames[i][0] != ' ') { + percentInt = us / 33; + percentDec = percentInt % 10; + percentInt /= 10; + sprintf(textBytes, " %s%d (%d.%d%%)", audioBenchmarkNames[i], us, percentInt, percentDec); + } else { + sprintf(textBytes, " %s%d", audioBenchmarkNames[i], us); + } + + print_set_envcolour(colourChart[NUM_AUDIO_POOLS + i][0], + colourChart[NUM_AUDIO_POOLS + i][1], + colourChart[NUM_AUDIO_POOLS + i][2], 255); + print_small_text_light(x, y, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + } +#else + print_set_envcolour(255, 95, 95, 255); + print_small_text(x + 8, y + 12, "Verbose audio profiling is disabled!\nPlease toggle the AUDIO PROFILING define\n" + "In profiling.h.", PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); +#endif + + print_audio_ram_overview(x, textBytes); } char consoleLogTable[LOG_BUFFER_SIZE][255]; @@ -408,7 +421,6 @@ static char *write_to_buf(char *buffer, const char *data, size_t size) { } void append_puppyprint_log(const char *str, ...) { - s32 i; char textBytes[255]; memset(textBytes, 0, sizeof(textBytes)); @@ -421,7 +433,7 @@ void append_puppyprint_log(const char *str, ...) { #ifdef UNF osSyncPrintf(textBytes); #endif - for (i = 0; i < (LOG_BUFFER_SIZE - 1); i++) { + for (u8 i = 0; i < (LOG_BUFFER_SIZE - 1); i++) { memcpy(consoleLogTable[i], consoleLogTable[i + 1], 255); } memcpy(consoleLogTable[LOG_BUFFER_SIZE - 1], textBytes, 255); @@ -430,16 +442,15 @@ void append_puppyprint_log(const char *str, ...) { #define LINE_HEIGHT (8 + ((LOG_BUFFER_SIZE - 1) * 12)) void print_console_log(void) { - s32 i; prepare_blank_box(); render_blank_box(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 96); finish_blank_box(); - for (i = 0; i < LOG_BUFFER_SIZE; i++) { + for (u8 i = 0; i < LOG_BUFFER_SIZE; i++) { if (consoleLogTable[i] == NULL) { continue; } - print_small_text(16, (LINE_HEIGHT - (i * 12)), consoleLogTable[i], PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); + print_small_text_light(16, (LINE_HEIGHT - (i * 12)), consoleLogTable[i], PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_DEFAULT); } } #undef LINE_HEIGHT @@ -451,29 +462,21 @@ extern s16 gVisualSurfaceCount; #endif void puppyprint_render_collision(void) { - char textBytes[200]; - - sprintf(textBytes, "Static Pool Size: 0x%X#Dynamic Pool Size: 0x%X#Dynamic Pool Used: 0x%X#Surfaces Allocated: %d#Nodes Allocated: %d", gTotalStaticSurfaceData, DYNAMIC_SURFACE_POOL_SIZE,(uintptr_t)gDynamicSurfacePoolEnd - (uintptr_t)gDynamicSurfacePool, - gSurfacesAllocated, gSurfaceNodesAllocated); - print_small_text(304, 60, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, 1); - + char textBytes[128]; + sprintf(textBytes, "Static Pool Size: 0x%X\nDynamic Pool Size: 0x%X\nDynamic Pool Used: 0x%X\nSurfaces Allocated: %d\nNodes Allocated: %d", + gTotalStaticSurfaceData, + DYNAMIC_SURFACE_POOL_SIZE, + (uintptr_t)gDynamicSurfacePoolEnd - (uintptr_t)gDynamicSurfacePool, + gSurfacesAllocated, gSurfaceNodesAllocated); + print_small_text_light(SCREEN_WIDTH-16, 60, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, 1); #ifdef VISUAL_DEBUG - print_small_text(160, (SCREEN_HEIGHT - 42), "Use the dpad to toggle visual collision modes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); + print_small_text_light(160, (SCREEN_HEIGHT - 42), "Use the dpad to toggle visual collision modes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); switch (viewCycle) { - case 0: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: None", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 1: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 2: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - case 3: print_small_text(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes and Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 1); break; - } - if (gPlayer1Controller->buttonPressed & R_JPAD) viewCycle++; - if (gPlayer1Controller->buttonPressed & L_JPAD) viewCycle--; - - if (viewCycle == 4) { - viewCycle = 0; - } - if (viewCycle == 255) { - viewCycle = 3; + case 0: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: None", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 1: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 2: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; + case 3: print_small_text_light(160, (SCREEN_HEIGHT - 32), "Current view: Hitboxes and Surfaces", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); break; } hitboxView = ((viewCycle == 1) || (viewCycle == 3)); @@ -483,122 +486,183 @@ void puppyprint_render_collision(void) { extern void print_fps(s32 x, s32 y); -u32 profiler_get_cpu_cycles(); -u32 profiler_get_rsp_cycles(); -u32 profiler_get_rdp_cycles(); - -u32 profiler_get_cpu_microseconds(); -u32 profiler_get_rsp_microseconds(); -u32 profiler_get_rdp_microseconds(); - void print_basic_profiling(void) { - u32 cpuTime; - u32 rspTime; - u32 rdpTime; char textBytes[90]; - print_fps(16, 16); -#ifdef PUPPYPRINT_DEBUG_CYCLES - cpuTime = profiler_get_cpu_cycles(); - rspTime = profiler_get_rsp_cycles(); - rdpTime = profiler_get_rdp_cycles(); - sprintf(textBytes, "CPU: %dc (%d_)#RSP: %dc (%d_)#RDP: %dc (%d_)", - cpuTime, (cpuTime / 15625), - rspTime, (rspTime / 15625), - rdpTime, (rdpTime / 15625)); -#else - cpuTime = profiler_get_cpu_microseconds(); - rspTime = profiler_get_rsp_microseconds(); - rdpTime = profiler_get_rdp_microseconds(); - sprintf(textBytes, "CPU: %dus (%d_)#RSP: %dus (%d_)#RDP: %dus (%d_)", + u32 cpuTime = profiler_get_cpu_microseconds(); + u32 rspTime = profiler_get_rsp_microseconds(); + u32 rdpTime = profiler_get_rdp_microseconds(); + print_fps(16, 40); + sprintf(textBytes, "CPU: %dus (%d%%)\nRSP: %dus (%d%%)\nRDP: %dus (%d%%)", cpuTime, (cpuTime / 333), rspTime, (rspTime / 333), rdpTime, (rdpTime / 333)); -#endif - print_small_text(16, 28, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + print_small_text_light(16, 52, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); } void puppyprint_render_standard(void) { - char textBytes[80]; + char textBytes[128]; - sprintf(textBytes, "OBJ: %d/%d", gObjectCounter, OBJECT_POOL_CAPACITY); - print_small_text((SCREEN_WIDTH - 16), 16, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - -#ifndef ENABLE_CREDITS_BENCHMARK - // Very little point printing useless info if Mario doesn't even exist. - if (gMarioState->marioObj) { - sprintf(textBytes, "Mario Pos#X: %d#Y: %d#Z: %d#D: %X#A: %x", - (s32)(gMarioState->pos[0]), - (s32)(gMarioState->pos[1]), - (s32)(gMarioState->pos[2]), - (u16)(gMarioState->faceAngle[1]), - (u32)(gMarioState->action & ACT_ID_MASK)); - print_small_text((SCREEN_WIDTH - 16), 32, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - } - // Same for the camera, especially so because this will crash otherwise. - if (gCamera) { - sprintf(textBytes, "Camera Pos#X: %d#Y: %d#Z: %d#D: %X", - (s32)(gCamera->pos[0]), - (s32)(gCamera->pos[1]), - (s32)(gCamera->pos[2]), - (u16)(gCamera->yaw)); - print_small_text((SCREEN_WIDTH - 16), 140, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); - } -#endif - - // Old profiler bar code, can be restored one day if desired to work with new profiler - // s32 graphPos; - // s32 prevGraph; - // u32 i; - // s32 viewedNums; - - // prepare_blank_box(); - // viewedNums = 0; - - // // Render CPU breakdown bar. - // for (i = 0; i < CPU_TABLE_MAX; i++) { - // if (perfPercentage[i] == 0 - // && (i != CPU_TABLE_MAX - 1)) { - // continue; - // } - - // if (viewedNums == 0) { - // graphPos = ((SCREEN_WIDTH - 96) + perfPercentage[i]); - // render_blank_box((SCREEN_WIDTH - 96), barY, graphPos, (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } else if (i == (CPU_TABLE_MAX - 1)) { - // graphPos = ((SCREEN_WIDTH - 96) + perfPercentage[i]); - // render_blank_box(prevGraph, barY, (SCREEN_WIDTH - 16), (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } else { - // graphPos += perfPercentage[i]; - // render_blank_box(prevGraph, barY, graphPos, (barY + 8), - // cpu_ordering_table[i].colour[0], - // cpu_ordering_table[i].colour[1], - // cpu_ordering_table[i].colour[2], 255); - // } - - // viewedNums++; - // prevGraph = graphPos; - // } - - // finish_blank_box(); + sprintf(textBytes, "Matrix Muls: %d\n\nCollision Checks\nFloors: %d\nWalls: %d\nCeilings: %d\n Water: %d\nRaycasts: %d", + gPuppyCallCounter.matrix, + gPuppyCallCounter.collision_floor, + gPuppyCallCounter.collision_wall, + gPuppyCallCounter.collision_ceil, + gPuppyCallCounter.collision_water, + gPuppyCallCounter.collision_raycast + ); + print_small_text_light(SCREEN_WIDTH-16, 32, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); } void puppyprint_render_minimal(void) { print_basic_profiling(); } +void render_coverage_map(void) { + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); + gDPSetBlendColor(gDisplayListHead++, 0xFF, 0xFF, 0xFF, 0xFF); + gDPSetPrimDepth(gDisplayListHead++, 0xFFFF, 0xFFFF); + gDPSetDepthSource(gDisplayListHead++, G_ZS_PRIM); + gDPSetRenderMode(gDisplayListHead++, G_RM_VISCVG, G_RM_VISCVG2); + gDPFillRectangle(gDisplayListHead++, 0,0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1); +} + +void puppycamera_debug_view(void) { + char textBytes[80]; + // Very little point printing useless info if Mayro doesn't even exist. + if (gMarioState->marioObj) { + sprintf(textBytes, "Mario Pos\nX: %d\nY: %d\nZ: %d\nD: %X\nA: %x", + (s32)(gMarioState->pos[0]), + (s32)(gMarioState->pos[1]), + (s32)(gMarioState->pos[2]), + (u16)(gMarioState->faceAngle[1]), + (u32)(gMarioState->action & ACT_ID_MASK)); + print_small_text_light(16, 140, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + } + // Same for the camera, especially so because this will crash otherwise. + if (gCamera) { + sprintf(textBytes, "Camera Pos\nX: %d\nY: %d\nZ: %d\nD: %X", + (s32)(gCamera->pos[0]), + (s32)(gCamera->pos[1]), + (s32)(gCamera->pos[2]), + (u16)(gCamera->yaw)); + print_small_text_light((SCREEN_WIDTH - 16), 140, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); + } +} + +#define STUB_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8) textname, +#define DEFINE_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) textname, + +static char sLevelNames[][32] = { + #include "levels/level_defines.h" +}; +#undef STUB_LEVEL +#undef DEFINE_LEVEL + +static s16 sLevelSelectOption = 0; +static s8 sLevelSelectOptionArea = 0; +u8 gPuppyWarp = 0; +u8 gPuppyWarpArea = 0; + +void puppyprint_level_select_menu(void) { + s32 posY; + s32 renderedText = 0; + char textBytes[32]; + prepare_blank_box(); + render_blank_box_rounded((SCREEN_WIDTH/2) - 80, (SCREEN_HEIGHT/2) - 60, (SCREEN_WIDTH/2) + 80, (SCREEN_HEIGHT/2) + 60, 0, 0, 0, 160); + finish_blank_box(); + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) - 58, "Pick a level", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) + 64, "(Area must have warp node of 0x0A)\nDpad Left/Right: Area / A: Warp\nYellow is current level.", PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); + for (u32 i = 0; i < sizeof(sLevelNames) / 32; i++) { + s32 yOffset = sLevelSelectOption > 8 ? sLevelSelectOption-8 : 0; + posY = ((renderedText-yOffset) * 10); + if (sLevelNames[i][0] == 0) { + continue; + } + renderedText++; + if (posY < 0 || posY > 84) { + continue; + } + if ((u32) sLevelSelectOption == i) { + sprintf(textBytes, "%s - %d", sLevelNames[i], sLevelSelectOptionArea + 1); + print_set_envcolour(0xFF, 0x40, 0x40, 0xFF); + } + else + if ((u32) gCurrLevelNum-1 == i) { + sprintf(textBytes, "%s", sLevelNames[i]); + print_set_envcolour(0xFF, 0xFF, 0x40, 0xFF); + } + else{ + sprintf(textBytes, "%s", sLevelNames[i]); + print_set_envcolour(0xFF, 0xFF, 0xFF, 0xFF); + } + print_small_text_light(SCREEN_WIDTH/2, (SCREEN_HEIGHT/2) - 40 + posY, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); + } +} + +u8 gLastWarpID = 0; + +void puppyprint_render_general_vars(void) { + + char textBytes[200]; + u32 floorType = 0; + u32 objParams = 0; + if (gMarioState->floor) { + floorType = gMarioState->floor->type; + } + if (gMarioState->interactObj) { + objParams = gMarioState->interactObj->oBehParams; + } + + + sprintf(textBytes, "World\n\nObjects: %d/%d\n\nLevel ID: %d\nCourse ID: %d\nArea ID: %d\nRoom ID: %d\n\nInteract: \n0x%08X\nWarp: 0x%02X", + gObjectCounter, + OBJECT_POOL_CAPACITY, + gCurrLevelNum, + gCurrCourseNum, + gCurrAreaIndex, + gMarioCurrentRoom, + objParams, + gLastWarpID + ); + print_small_text_light(SCREEN_WIDTH - 16, 36, textBytes, PRINT_TEXT_ALIGN_RIGHT, PRINT_ALL, FONT_OUTLINE); + +#ifndef ENABLE_CREDITS_BENCHMARK + // Very little point printing useless info if Mario doesn't even exist. + if (gMarioState->marioObj) { + sprintf(textBytes, "Mario\n\nX: %d\nY: %d\nZ: %d\nYaw: 0x%04X\n\nfVel: %1.1f\nyVel: %1.1f\n\nHealth: %03X\nAction: 0x%02X\nFloor Type: 0x%02X\nWater Height: %d", + (s32)(gMarioState->pos[0]), + (s32)(gMarioState->pos[1]), + (s32)(gMarioState->pos[2]), + (u16)(gMarioState->faceAngle[1]), + (f32)(gMarioState->forwardVel), + (f32)(gMarioState->vel[1]), + (s32)(gMarioState->health), + (u32)(gMarioState->action & ACT_ID_MASK), + (u32)(floorType), + (s32)(gMarioState->waterLevel) + ); + print_small_text_light(16, 36, textBytes, PRINT_TEXT_ALIGN_LEFT, PRINT_ALL, FONT_OUTLINE); + sprintf(textBytes, "Gfx Pool: %d / %d", ((u32)gDisplayListHead - ((u32)gGfxPool->buffer)) / 4, GFX_POOL_SIZE); + print_small_text_light(SCREEN_WIDTH/2, SCREEN_HEIGHT-16, textBytes, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_OUTLINE); + } +#endif +} + struct PuppyPrintPage ppPages[] = { - {&puppyprint_render_standard, "Standard" }, - {&puppyprint_render_minimal, "Minimal" }, - {&print_audio_ram_overview, "Audio" }, - {&print_ram_overview, "Segments" }, - {&puppyprint_render_collision, "Collision"}, - {&print_console_log, "Log" }, +#ifdef USE_PROFILER + {&puppyprint_render_standard, "Profiler"}, + {&puppyprint_render_minimal, "Minimal"}, +#endif + {&puppyprint_render_general_vars, "General"}, + {&print_audio_overview, "Audio"}, + {&print_ram_overview, "Segments"}, + {&puppyprint_render_collision, "Collision"}, + {&print_console_log, "Log"}, + {&puppyprint_level_select_menu, "Level Select"}, + {&render_coverage_map, "Coverage"}, +#ifdef PUPPYCAM + {&puppycamera_debug_view, "Unlock Camera"}, +#endif }; #define MENU_BOX_WIDTH 128 @@ -610,8 +674,8 @@ void render_page_menu(void) { s32 scrollY = (36 / (MAX_DEBUG_OPTIONS - 1)); prepare_blank_box(); - render_blank_box(32, 32, (32 + MENU_BOX_WIDTH), (32 + 72), 0x00, 0x00, 0x00, 0xC0); - render_blank_box(((32 + MENU_BOX_WIDTH) - 8), (32 + (scrollY * sDebugOption)), (32 + MENU_BOX_WIDTH), (32 + (scrollY * sDebugOption) + 36), 0xFF, 0xFF, 0xFF, 0xFF); + render_blank_box_rounded(32, 32, (32 + MENU_BOX_WIDTH), (32 + 72), 0x00, 0x00, 0x00, 0xC0); + render_blank_box_rounded(((32 + MENU_BOX_WIDTH) - 8), (32 + (scrollY * sDebugOption)), (32 + MENU_BOX_WIDTH), (32 + (scrollY * sDebugOption) + 36), 0xFF, 0xFF, 0xFF, 0xFF); finish_blank_box(); for (i = 0; i < (s32)MAX_DEBUG_OPTIONS; i++) { @@ -624,27 +688,34 @@ void render_page_menu(void) { print_set_envcolour(0xFF, 0xFF, 0xFF, 0xFF); } - print_small_text((28 + (MENU_BOX_WIDTH / 2)), posY, ppPages[i].name, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, 0); + print_small_text_light((28 + (MENU_BOX_WIDTH / 2)), posY, ppPages[i].name, PRINT_TEXT_ALIGN_CENTRE, PRINT_ALL, FONT_DEFAULT); } } } void puppyprint_render_profiler(void) { + PUPPYPRINT_GET_SNAPSHOT(); + bzero(&gCurrEnvCol, sizeof(ColorRGBA)); print_set_envcolour(255, 255, 255, 255); if (!fDebug) { + profiler_update(PROFILER_TIME_PUPPYPRINT1, osGetCount() - first); return; } - - (ppPages[sPPDebugPage].func)(); + if (ppPages[sPPDebugPage].func != NULL) { + (ppPages[sPPDebugPage].func)(); + } if (sDebugMenu) { render_page_menu(); } + profiler_update(PROFILER_TIME_PUPPYPRINT1, osGetCount() - first); } void puppyprint_profiler_process(void) { + PUPPYPRINT_GET_SNAPSHOT(); + if (fDebug && (gPlayer1Controller->buttonPressed & L_TRIG)) { sDebugMenu ^= TRUE; if (sDebugMenu == FALSE) { @@ -671,18 +742,91 @@ void puppyprint_profiler_process(void) { if (sDebugOption >= (sizeof(ppPages) / sizeof(struct PuppyPrintPage))) { sDebugOption = 0; } + } else { + if (sPPDebugPage == PUPPYPRINT_PAGE_LEVEL_SELECT) + { + if (gPlayer1Controller->buttonPressed & U_JPAD) { + sLevelSelectOption--; + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption--; + } + if (sLevelSelectOption <= 0) { + sLevelSelectOption = LEVEL_COUNT - 2; + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption--; + } + } + } + if (gPlayer1Controller->buttonPressed & D_JPAD) { + sLevelSelectOption = (sLevelSelectOption + 1) % (LEVEL_COUNT - 2); + // If there is no level entry to this ID, skip over. + while (sLevelNames[sLevelSelectOption][0] == 0 && sLevelSelectOption < LEVEL_COUNT) { + sLevelSelectOption = (sLevelSelectOption + 1) % (LEVEL_COUNT - 2); + } + } + if (gPlayer1Controller->buttonPressed & R_JPAD) { + sLevelSelectOptionArea++; + if (sLevelSelectOptionArea > AREA_COUNT - 1) { + sLevelSelectOptionArea = 0; + } + } else if (gPlayer1Controller->buttonPressed & L_JPAD) { + sLevelSelectOptionArea--; + if (sLevelSelectOptionArea < 0) { + sLevelSelectOptionArea = AREA_COUNT - 1; + } + } + if (gPlayer1Controller->buttonPressed & A_BUTTON) { + sPPDebugPage = 0; + gPuppyWarp = sLevelSelectOption + 1; + gPuppyWarpArea = sLevelSelectOptionArea + 1; + } + } else { + if (gCurrLevelNum > 3) { + sLevelSelectOption = gCurrLevelNum; + } else { + sLevelSelectOption = LEVEL_CASTLE_GROUNDS; + } + } + // Collision toggles. +#ifdef VISUAL_DEBUG + if (sPPDebugPage == PUPPYPRINT_PAGE_COLLISION) + { + if (gPlayer1Controller->buttonPressed & R_JPAD) + viewCycle++; + if (gPlayer1Controller->buttonPressed & L_JPAD) + viewCycle--; + if (viewCycle == 4) + viewCycle = 0; + if (viewCycle == 255) + viewCycle = 3; + } +#endif + if (sPPDebugPage == PUPPYPRINT_PAGE_RAM) { + if (gPlayer1Controller->buttonDown & U_JPAD && gPPSegScroll > 0) { + gPPSegScroll -= 4; + } else if (gPlayer1Controller->buttonDown & D_JPAD && gPPSegScroll < (12 * 32)){ + gPPSegScroll += 4; + } + } } + profiler_update(PROFILER_TIME_PUPPYPRINT2, osGetCount() - first); } #endif -void print_set_envcolour(s32 r, s32 g, s32 b, s32 a) { - if ((r != currEnv[0]) - || (g != currEnv[1]) - || (b != currEnv[2]) - || (a != currEnv[3])) { +s32 print_set_envcolour(u8 r, u8 g, u8 b, u8 a) { + if ((r != gCurrEnvCol[0]) + || (g != gCurrEnvCol[1]) + || (b != gCurrEnvCol[2]) + || (a != gCurrEnvCol[3])) { gDPSetEnvColor(gDisplayListHead++, (Color)r, (Color)g, (Color)b, (Color)a); - vec4_set(currEnv, r, g, b, a); + vec4_set(gCurrEnvCol, r, g, b, a); + + return TRUE; } + + return FALSE; } #define BLANK 0, 0, 0, ENVIRONMENT, 0, 0, 0, ENVIRONMENT @@ -700,11 +844,27 @@ void finish_blank_box(void) { // This does some epic shenanigans to figure out the optimal way to draw this. // If the width is a multiple of 4, then use fillmode (fastest) // Otherwise, if there's transparency, it uses that rendermode, which is slower than using opaque rendermodes. -void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a) { +void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a) { + if (x2 < x1) + { + u32 temp = x2; + x2 = x1; + x1 = temp; + } + if (y2 < y1) + { + u32 temp = y2; + y2 = y1; + y1 = temp; + } + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + if (x2 > SCREEN_WIDTH) x2 = SCREEN_WIDTH; + if (y2 > SCREEN_HEIGHT) y2 = SCREEN_HEIGHT; s32 cycleadd = 0; gDPPipeSync(gDisplayListHead++); if (((absi(x1 - x2) % 4) == 0) && (a == 255)) { - gDPSetCycleType( gDisplayListHead++, G_CYC_FILL); + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetRenderMode(gDisplayListHead++, G_RM_NOOP, G_RM_NOOP); cycleadd = 1; } else { @@ -720,28 +880,95 @@ void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a gDPSetFillColor(gDisplayListHead++, (GPACK_RGBA5551(r, g, b, 1) << 16) | GPACK_RGBA5551(r, g, b, 1)); print_set_envcolour(r, g, b, a); gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - cycleadd, y2 - cycleadd); + gDPPipeSync(gDisplayListHead++); } -extern s32 text_iterate_command(const char *str, s32 i, s32 runCMD); -extern void get_char_from_byte(u8 letter, s32 *textX, s32 *textY, s32 *spaceX, s32 *offsetY, s32 font); +// Same as above, but with rounded edges. +// Follows all the same rules of usage. +void render_blank_box_rounded(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a) { + if (x2 < x1) + { + u32 temp = x2; + x2 = x1; + x1 = temp; + } + if (y2 < y1) + { + u32 temp = y2; + y2 = y1; + y1 = temp; + } + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + if (x2 > SCREEN_WIDTH) x2 = SCREEN_WIDTH; + if (y2 > SCREEN_HEIGHT) y2 = SCREEN_HEIGHT; + s32 cycleadd = 0; + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); + if (a == 255) { + gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + } + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 1) << 16 | GPACK_RGBA5551(r, g, b, 1)); + print_set_envcolour(r, g, b, a); + gDPFillRectangle(gDisplayListHead++, x1+4, y1, x2-4, y1+1); + gDPFillRectangle(gDisplayListHead++, x1+2, y1+1, x2-2, y1+2); + gDPFillRectangle(gDisplayListHead++, x1+1, y1+2, x2-1, y1+4); + gDPFillRectangle(gDisplayListHead++, x1+1, y2-4, x2-1, y2-2); + gDPFillRectangle(gDisplayListHead++, x1+2, y2-2, x2-2, y2-1); + gDPFillRectangle(gDisplayListHead++, x1+4, y2-1, x2-4, y2); + if (ABS(x1 - x2) % 4 == 0 && a == 255) { + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); + gDPSetRenderMode(gDisplayListHead++, G_RM_NOOP, G_RM_NOOP); + cycleadd = 1; + } + gDPFillRectangle(gDisplayListHead++, x1, y1+4, x2 - cycleadd, y2-4 - cycleadd); + gDPPipeSync(gDisplayListHead++); +} + +s8 shakeToggle = 0; +s8 waveToggle = 0; +s8 rainbowToggle = 0; +f32 textSize = 1.0f; // The value that's used as a baseline multiplier before applying text size modifiers. Make sure to set it back when you're done. +f32 textSizeTotal = 1.0f; // The value that's read to set the text size. Do not mess with this. +f32 textSizeTemp = 1.0f; // The value that's set when modifying text size mid draw. Also do not mess with this. +u16 textTempScale = 1024; // A fixed point means of referring to scale. +u8 textOffsets[2]; // Represents the dimensions of the text (12 x 8), and written to when size is modified. +u8 topLineHeight; // Represents the peak line height of the current line. Prevents vertical overlapping. +u8 gMonoSpace = FALSE; // Ignore kerning. s32 get_text_width(const char *str, s32 font) { s32 i = 0; s32 textPos = 0; s32 wideX = 0; - s32 textX, textY, offsetY, spaceX; + s32 textX; + s8 offsetY; + u8 spaceX; + s32 strLen = (signed)strlen(str); + s32 commandOffset; + u8 pad; - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos = 0; - } - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); + textSizeTemp = 1.0f; + textSizeTotal = textSizeTemp * textSize; + + for (i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + if (str[i] == '\n') { + textPos = 0; + break; + } } - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - textPos += spaceX + 1; + if (i >= strLen) + break; + + get_char_from_byte(&textX, &textPos, str[i], &pad, &spaceX, &offsetY, font); + textPos += (spaceX + 1) * textSizeTotal; wideX = MAX(textPos, wideX); } return wideX; @@ -749,18 +976,39 @@ s32 get_text_width(const char *str, s32 font) { s32 get_text_height(const char *str) { s32 i= 0; - s32 textPos = 0; + s32 textPos; + s32 strLen = (signed)strlen(str); + s32 commandOffset; - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos += 12; + textSizeTemp = 1.0f; + textSizeTotal = textSizeTemp * textSize; + topLineHeight = 12 * textSizeTotal; + textPos = topLineHeight; + + for (i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + } + + if (i >= strLen) + break; + + if (str[i] == '\n') { + textPos += topLineHeight; + topLineHeight = 12 * textSizeTotal; + continue; } } return textPos; } + + const Gfx dl_small_text_begin[] = { gsDPPipeSync(), gsDPSetCycleType( G_CYC_1CYCLE), @@ -770,256 +1018,510 @@ const Gfx dl_small_text_begin[] = { gsSPEndDisplayList(), }; -s8 shakeToggle = 0; -s8 waveToggle = 0; +void set_text_size_params(void) { + textSizeTotal = textSizeTemp * textSize; + textTempScale = 1024/textSizeTotal; + textOffsets[0] = 8 * textSizeTotal; + textOffsets[1] = 12 * textSizeTotal; + topLineHeight = MAX(12.0f * textSizeTotal, topLineHeight); +} -void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, s32 font) { +static s8 sTextShakeTable[] = { + 1, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 1, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 1, 0, 1, 0, 0 +}; + +void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font) { s32 textX = 0; - s32 textY = 0; - s32 offsetY = 0; - s32 i = 0; s32 textPos[2] = { 0, 0 }; - s32 spaceX = 0; - s32 wideX[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - s32 tx = amount; - s32 shakePos[2]; - s32 wavePos; - s32 lines = 0; - s32 xlu = currEnv[3]; + u16 wideX[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + s32 textLength = amount; s32 prevxlu = 256; // Set out of bounds, so it will *always* be different at first. - Texture *(*fontTex)[] = segmented_to_virtual(&puppyprint_font_lut); + s32 strLen = strlen(str); + s32 commandOffset; + f32 wavePos; + f32 shakePos[2]; + s8 offsetY = 0; + u8 spaceX = 0; + u8 widthX = 0; + u8 lines = 0; + u8 xlu = gCurrEnvCol[3]; + u8 shakeTablePos = 0; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); shakeToggle = 0; - waveToggle = 0; + waveToggle = 0; + rainbowToggle = 0; + textSizeTemp = 1.0f; + set_text_size_params(); - if (amount == PRINT_ALL) { - tx = (signed)strlen(str); + if (amount <= PRINT_ALL || amount > strLen) { + textLength = strLen; } + // Calculate the text width for centre and right aligned text. gSPDisplayList(gDisplayListHead++, dl_small_text_begin); - if (align == PRINT_TEXT_ALIGN_CENTRE) { - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; + if (align == PRINT_TEXT_ALIGN_CENTRE || align == PRINT_TEXT_ALIGN_RIGHT) { + for (s32 i = 0; i < strLen; i++) { + while (i < strLen && str[i] == '<') { + commandOffset = text_iterate_command(str, i, FALSE); + if (commandOffset == 0) + break; + + i += commandOffset; + } + + if (i >= strLen || str[i] == '\0') + break; + + if (str[i] == '\n') { textPos[0] = 0; lines++; + wideX[lines] = 0; + continue; } - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); - } - - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - textPos[0] += (spaceX + 1); + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + textPos[0] += (spaceX + 1) * textSizeTotal; wideX[lines] = MAX(textPos[0], wideX[lines]); } - textPos[0] = -(wideX[0] / 2); - } else if (align == PRINT_TEXT_ALIGN_RIGHT) { - for (i = 0; i < (signed)strlen(str); i++) { - if (str[i] == '#') { - i++; - textPos[0] = 0; - lines++; - } else { - textPos[0] += (spaceX + 1); - } - - if (str[i] == '<') { - i += text_iterate_command(str, i, FALSE); - } - - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - - wideX[lines] = MAX(textPos[0], wideX[lines]); + if (align == PRINT_TEXT_ALIGN_CENTRE) { + textPos[0] = -(wideX[0] / 2); + } else { + textPos[0] = -(wideX[0]); } - textPos[0] = -wideX[0]; } + //Reset text size properties. + textSizeTemp = 1.0f; + set_text_size_params(); + topLineHeight = 12.0f * textSizeTotal; lines = 0; - gDPLoadTextureBlock_4b(gDisplayListHead++, (*fontTex)[font], G_IM_FMT_I, 128, 60, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); - for (i = 0; i < tx; i++) { - if (str[i] == '#') { - i++; + + shakeTablePos = gGlobalTimer % sizeof(sTextShakeTable); + gDPLoadTextureBlock_4b(gDisplayListHead++, fnt->tex, fnt->fmt, fnt->imW, fnt->imH, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); + + for (s32 i = 0, j = 0; i < textLength; i++, j++) { + if (str[i] == '\n') { lines++; if (align == PRINT_TEXT_ALIGN_RIGHT) { - textPos[0] = -(wideX[lines] ); + textPos[0] = -(wideX[lines]); } else { textPos[0] = -(wideX[lines] / 2); } - textPos[1] += 12; + textPos[1] += topLineHeight; + topLineHeight = (f32) fnt->txH * textSizeTotal; + continue; } - if (str[i] == '<') { - i += text_iterate_command(str, i, TRUE); + while (i < textLength && str[i] == '<') { + commandOffset = text_iterate_command(str, i, TRUE); + if (commandOffset == 0) + break; + + i += commandOffset; + textLength += commandOffset; + } + + if (i >= textLength || str[i] == '\0') { + break; } if (shakeToggle) { - shakePos[0] = (-1 + (random_u16() & 0x1)); - shakePos[1] = (-1 + (random_u16() & 0x1)); + shakePos[0] = (sTextShakeTable[shakeTablePos++] * textSizeTotal); + if (shakeTablePos == sizeof(sTextShakeTable)) + shakeTablePos = 0; + shakePos[1] = sTextShakeTable[shakeTablePos++] * textSizeTotal; + if (shakeTablePos == sizeof(sTextShakeTable)) + shakeTablePos = 0; } else { shakePos[0] = 0; shakePos[1] = 0; } + if (waveToggle) { - wavePos = ((sins((gGlobalTimer * 3000) + (i * 10000))) * 2); + wavePos = ((sins((gGlobalTimer * 3000) + (j * 10000))) * 2) * textSizeTotal; } else { wavePos = 0; } - get_char_from_byte(str[i], &textX, &textY, &spaceX, &offsetY, font); - if (xlu != prevxlu) { - prevxlu = xlu; - if (xlu > 250) { - gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); - } else { - gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + s32 goddamnJMeasure = textX == 256 ? -1 : 0; // Hack to fix a rendering bug. + if (str[i] != ' ' && str[i] != '\t') { + if (xlu != prevxlu) { + prevxlu = xlu; + if (xlu > 250) { + gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + } } + + gSPScisTextureRectangle(gDisplayListHead++, (x + textPos[0] + (s16)(shakePos[0])) << 2, + (y + textPos[1] + (s16)((shakePos[1] + offsetY + wavePos))) << 2, + (x + textPos[0] + (s16)((shakePos[0] + (widthX * textSizeTotal)))) << 2, + (y + textPos[1] + (s16)((wavePos + offsetY + shakePos[1] + textOffsets[1]))) << 2, + G_TX_RENDERTILE, (textX << 6) + goddamnJMeasure, 0, textTempScale, textTempScale); + } + textPos[0] += (spaceX + 1) * textSizeTotal; + } + + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + // Color reverted to pure white in dl_rgba16_text_end, so carry it over to gCurrEnvCol! + // NOTE: if this behavior is ever removed, make sure gCurrEnvCol gets enforced here if the text color is ever altered in the text_iterate_command function. + gCurrEnvCol[0] = 255; gCurrEnvCol[1] = 255; gCurrEnvCol[2] = 255; gCurrEnvCol[3] = 255; +} + +// A more lightweight version of print_small_text. +// Strips all text modifiers so that only standard text remains. +// Can still support external colouring. +// Around 30% faster than regular printing. +void print_small_text_light(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font) { + s32 textX = 0; + s32 textPos[2] = { 0, 0 }; + u16 wideX[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + s32 textLength = amount; + s32 prevxlu = 256; // Set out of bounds, so it will *always* be different at first. + s32 strLen = strlen(str); + s8 offsetY = 0; + u8 spaceX = 0; + u8 lines = 0; + u8 widthX = 0; + u8 xlu = gCurrEnvCol[3]; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); + + if (amount <= PRINT_ALL || amount > strLen) { + textLength = strLen; + } + + // Calculate the text width for centre and right aligned text. + gSPDisplayList(gDisplayListHead++, dl_small_text_begin); + if (align == PRINT_TEXT_ALIGN_CENTRE || align == PRINT_TEXT_ALIGN_RIGHT) { + for (s32 i = 0; i < strLen; i++) { + if (str[i] == '\n') { + textPos[0] = 0; + lines++; + wideX[lines] = 0; + continue; + } + + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + textPos[0] += (spaceX + 1) * textSizeTotal; + wideX[lines] = MAX(textPos[0], wideX[lines]); } - gSPScisTextureRectangle(gDisplayListHead++, ((x + shakePos[0] + textPos[0] ) << 2), - ((y + shakePos[1] + offsetY + textPos[1] + wavePos) << 2), - ((x + textPos[0] + shakePos[0] + 8) << 2), - ((y + wavePos + offsetY + shakePos[1] + 12 + textPos[1]) << 2), - G_TX_RENDERTILE, (textX << 6), (textY << 6), (1 << 10), (1 << 10)); + if (align == PRINT_TEXT_ALIGN_CENTRE) { + textPos[0] = -(wideX[0] / 2); + } else { + textPos[0] = -(wideX[0]); + } + } + + lines = 0; + gDPLoadTextureBlock_4b(gDisplayListHead++, fnt->tex, fnt->fmt, fnt->imW, fnt->imH, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), 0, 0, 0, G_TX_NOLOD, G_TX_NOLOD); + + for (s32 i = 0, j = 0; i < textLength; i++, j++) { + if (str[i] == '\n') { + lines++; + if (align == PRINT_TEXT_ALIGN_RIGHT) { + textPos[0] = -(wideX[lines]); + } else { + textPos[0] = -(wideX[lines] / 2); + } + textPos[1] += 12; + continue; + } + + if (i >= textLength || str[i] == '\0') { + break; + } + + get_char_from_byte(&textX, &textPos[0], str[i], &widthX, &spaceX, &offsetY, font); + s32 goddamnJMeasure = textX == 256 ? -1 : 0; // Hack to fix a rendering bug. + if (str[i] != ' ' && str[i] != '\t') { + if (xlu != prevxlu) { + prevxlu = xlu; + if (xlu > 250) { + gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); + } else { + gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF); + } + } + + gSPScisTextureRectangle(gDisplayListHead++, (x + textPos[0]) << 2, + (y + textPos[1] + offsetY) << 2, + (x + textPos[0] + widthX) << 2, + (y + textPos[1] + offsetY + fnt->txH) << 2, + G_TX_RENDERTILE, (textX << 6) + goddamnJMeasure, 0, 1024, 1024); + } textPos[0] += (spaceX + 1); } gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + // Color reverted to pure white in dl_rgba16_text_end, so carry it over to gCurrEnvCol! + // NOTE: if this behavior is ever removed, make sure gCurrEnvCol gets enforced here if the text color is ever altered in the text_iterate_command function. + gCurrEnvCol[0] = 255; gCurrEnvCol[1] = 255; gCurrEnvCol[2] = 255; gCurrEnvCol[3] = 255; +} + +// Return color hex nibble +s32 get_hex_value_at_offset(const char *str, s32 primaryOffset, u32 nibbleOffset, u32 garbageReturnsEnv) { + s32 val = str[primaryOffset + nibbleOffset]; + s32 shiftVal = 4 * ((nibbleOffset + 1) % 2); + + if (nibbleOffset > 7) + garbageReturnsEnv = FALSE; + + if (val >= 'A' && val <= 'F') + return (val - 'A' + 0xA) << shiftVal; + if (val >= 'a' && val <= 'f') + return (val - 'a' + 0xA) << shiftVal; + if (val >= '0' && val <= '9') + return (val - '0') << shiftVal; + + if (garbageReturnsEnv) // Return gCurrEnvCol color value + return gCurrEnvCol[nibbleOffset / 2] & (0x0F << shiftVal); + + // Just return 0 otherwise + return 0; } s32 text_iterate_command(const char *str, s32 i, s32 runCMD) { s32 len = 0; - while ((str[i + len] != '>') && ((i + len) < (signed)strlen(str))) len++; + const char *newStr = &str[i]; + s32 lastCharIndex = (signed)strlen(newStr) - 1; + + while ((newStr[len] != '>') && (len < lastCharIndex)) len++; + if (newStr[len] != '>') + return 0; + len++; - if (runCMD) { - if (strncmp((str + i), "", 5) == 0) { // Simple text colour effect. goes up to 99 for each, so 99000000 is red. - // Each value is taken from the strong. The first is multiplied by 10, because it's a larger significant value, then it adds the next digit onto it. - s32 r = (((str[i + 5] - '0') * 10) - + (str[i + 6] - '0')); - s32 g = (((str[i + 7] - '0') * 10) - + (str[i + 8] - '0')); - s32 b = (((str[i + 9] - '0') * 10) - + (str[i + 10] - '0')); - s32 a = (((str[i + 11] - '0') * 10) - + (str[i + 12] - '0')); - // Multiply each value afterwards by 2.575f to make 255. - print_set_envcolour((r * 2.575f), - (g * 2.575f), - (b * 2.575f), - (a * 2.575f)); - } else if (strncmp((str + i), "", 6) == 0) { // Same as above, except it fades between two colours. The third set of numbers is the speed it fades. - s32 r = (((str[i + 6] - '0') * 10) - + (str[i + 7] - '0')); - s32 g = (((str[i + 8] - '0') * 10) - + (str[i + 9] - '0')); - s32 b = (((str[i + 10] - '0') * 10) - + (str[i + 11] - '0')); - s32 a = (((str[i + 12] - '0') * 10) - + (str[i + 13] - '0')); - s32 r2 = (((str[i + 15] - '0') * 10) - + (str[i + 16] - '0')); - s32 g2 = (((str[i + 17] - '0') * 10) - + (str[i + 18] - '0')); - s32 b2 = (((str[i + 19] - '0') * 10) - + (str[i + 20] - '0')); - s32 a2 = (((str[i + 21] - '0') * 10) - + (str[i + 22] - '0')); - s32 spd = (((str[i + 24] - '0') * 10) - + (str[i + 25] - '0')); - // Find the median. - s32 r3 = (r + r2) * 1.2875f; - s32 g3 = (g + g2) * 1.2875f; - s32 b3 = (b + b2) * 1.2875f; - s32 a3 = (a + a2) * 1.2875f; - // Find the difference. - s32 r4 = (r - r2) * 1.2875f; - s32 g4 = (g - g2) * 1.2875f; - s32 b4 = (b - b2) * 1.2875f; - s32 a4 = (a - a2) * 1.2875f; - // Now start from the median, and wave from end to end with the difference, to create the fading effect. - f32 sTimer = sins(gGlobalTimer * spd * 50); - print_set_envcolour((r3 + (sTimer * r4)), - (g3 + (sTimer * g4)), - (b3 + (sTimer * b4)), - (a3 + (sTimer * a4))); - } else if (strncmp((str + i), "", 8) == 0) { // Toggles the happy colours :o) Do it again to disable it. - s32 r = (coss( gGlobalTimer * 600 ) + 1) * 127; - s32 g = (coss((gGlobalTimer * 600) + 21845) + 1) * 127; - s32 b = (coss((gGlobalTimer * 600) - 21845) + 1) * 127; - print_set_envcolour(r, g, b, 255); - } else if (strncmp((str + i), "", 7) == 0) { // Toggles text that shakes on the spot. Do it again to disable it. - shakeToggle ^= 1; - } else if (strncmp((str + i), "", 6) == 0) { // Toggles text that waves around. Do it again to disable it. - waveToggle ^= 1; + // Ignores runCMD, because it's important this is ALWAYS ran. + if (len == 10 && strncmp((newStr), "", 6) == 0) { // Set the text size here. 100 is scale 1.0, with 001 being scale 0.01. this caps at 999. Going lower than 001 + // Will make the text unreadable on console, so only do it, + textSizeTemp = (newStr[6] - '0'); + textSizeTemp += (newStr[7] - '0')/10.0f; + textSizeTemp += (newStr[8] - '0')/100.0f; + textSizeTemp = CLAMP(textSizeTemp, 0.01f, 10.0f); + set_text_size_params(); + } else if (len == 14 && strncmp((newStr), "", 5) == 0) { // Simple text colour effect. goes up to FF for each, so FF0000FF is red. + // Each value is taken from the string. The first is shifted left 4 bits, because it's a larger significant value, then it adds the next digit onto it. + // Reverting to envcoluor can be achieved by passing something like , or it could be combined with real colors for just partial reversion like for instance. + if (!runCMD) + return len; + + s32 rgba[4]; + + for (s32 j = 0; j < 4; j++) { + rgba[j] = get_hex_value_at_offset(newStr, 5, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 5, (2 * j) + 1, TRUE); } + + rainbowToggle = 0; + gDPSetEnvColor(gDisplayListHead++, (Color) rgba[0], (Color) rgba[1], (Color) rgba[2], (Color) rgba[3]); // Don't use print_set_envcolour here + } else if (len == 27 && strncmp((newStr), "", 6) == 0) { // Same as above, except it fades between two colours. The third set of numbers is the speed it fades. + if (!runCMD) + return len; + + s32 rgba[4]; + + // Find transition speed and set timer value + s32 spd = get_hex_value_at_offset(newStr, 24, 0, FALSE) | get_hex_value_at_offset(newStr, 24, 1, FALSE); + f32 sTimer = sins(gGlobalTimer * spd * 50); + + for (s32 j = 0; j < 4; j++) { + s32 col1 = get_hex_value_at_offset(newStr, 6, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 6, (2 * j) + 1, TRUE); + s32 col2 = get_hex_value_at_offset(newStr, 15, 2 * j, TRUE) | get_hex_value_at_offset(newStr, 15, (2 * j) + 1, TRUE); + + // Final color value determined by median of two colors + a point in the end-to-end width of the difference between the two colors. + // Said point changes based on the sTimer value in the form of a sine wave, which helps to create the fading effect. + rgba[j] = ((col1 + col2) / 2) + (s32) (sTimer * ((col1 - col2) / 2)); + } + + rainbowToggle = 0; + gDPSetEnvColor(gDisplayListHead++, (Color) rgba[0], (Color) rgba[1], (Color) rgba[2], (Color) rgba[3]); // Don't use print_set_envcolour here + } else if (len == 9 && strncmp((newStr), "", 9) == 0) { // Toggles the happy colours :o) Do it again to disable it. + if (!runCMD) + return len; + + rainbowToggle ^= 1; + if (rainbowToggle) { + s32 r = (coss(gGlobalTimer * 600) + 1) * 127; + s32 g = (coss((gGlobalTimer * 600) + (0x10000 / 3)) + 1) * 127; + s32 b = (coss((gGlobalTimer * 600) - (0x10000 / 3)) + 1) * 127; + gDPSetEnvColor(gDisplayListHead++, (Color) r, (Color) g, (Color) b, (Color) gCurrEnvCol[3]); // Don't use print_set_envcolour here, also opt to use alpha value from gCurrEnvCol + } else { + gDPSetEnvColor(gDisplayListHead++, (Color) gCurrEnvCol[0], (Color) gCurrEnvCol[1], (Color) gCurrEnvCol[2], (Color) gCurrEnvCol[3]); // Reset text to envcolor + } + } else if (len == 7 && strncmp((newStr), "", 7) == 0) { // Toggles text that shakes on the spot. Do it again to disable it. + if (!runCMD) + return len; + + shakeToggle ^= 1; + } else if (len == 6 && strncmp((newStr), "", 6) == 0) { // Toggles text that waves around. Do it again to disable it. + if (!runCMD) + return len; + + waveToggle ^= 1; + } else { + return 0; // Invalid command string; display everything inside to make this clear to the user. } + return len; } -void get_char_from_byte(u8 letter, s32 *textX, s32 *textY, s32 *spaceX, s32 *offsetY, s32 font) { +void get_char_from_byte(s32 *textX, s32 *textPos, u8 letter, u8 *wideX, u8 *spaceX, s8 *offsetY, u8 font) { *offsetY = 0; - u8 **textKern = segmented_to_virtual(puppyprint_kerning_lut); - u8 *textLen = segmented_to_virtual(textKern[font]); + u32 let = letter - '!'; + struct PPTextFont **fntPtr = segmented_to_virtual(gPuppyPrintFontTable); + struct PPTextFont *fnt = segmented_to_virtual(fntPtr[font]); - if (letter >= '0' && letter <= '9') { // Line 1 - *textX = ((letter - '0') * 4); - *textY = 0; - *spaceX = textLen[(letter - '0') + 0]; - } else if (letter >= 'A' && letter <= 'P') { // Line 2 - *textX = ((letter - 'A') * 4); - *textY = 6; - *spaceX = textLen[(letter - 'A') + 16]; - } else if (letter >= 'Q' && letter <= 'Z') { // Line 3 - *textX = ((letter - 'Q') * 4); - *textY = 12; - *spaceX = textLen[(letter - 'Q') + 32]; - } else if (letter >= 'a' && letter <= 'p') { // Line 4 - *textX = ((letter - 'a') * 4); - *textY = 18; - *spaceX = textLen[(letter - 'a') + 48]; - } else if (letter >= 'q' && letter <= 'z') { // Line 5 - *textX = ((letter - 'q') * 4); - *textY = 24; - *spaceX = textLen[(letter - 'q') + 64]; - } else { // Space, the final frontier. - *textX = 128; - *textY = 12; - *spaceX = 2; + if (letter == ' ') { + *spaceX = 2; + return; + } + + if (letter == '\t') { + s32 offset = (*textPos % TAB_WIDTH); + if (offset == TAB_WIDTH - 1) { + offset -= TAB_WIDTH; + } + *textPos += TAB_WIDTH - offset - 1; + *spaceX = 0; + return; + } + + if (fnt->kern == NULL || gMonoSpace) { + *spaceX = fnt->txW; + } else { + u8 *kern = segmented_to_virtual(fnt->kern); + *spaceX = kern[let]; + + } + if (fnt->offset == NULL) { + *textX = let >> 2; + *wideX = fnt->txW; + } else { + u16 *off = segmented_to_virtual(fnt->offset); + *textX = off[let] >> 1; + *wideX = (off[let + 1] - off[let]); } switch (letter) { - case '-': *textX = 40; *textY = 0; *spaceX = textLen[10]; break; // Hyphen - case '+': *textX = 44; *textY = 0; *spaceX = textLen[11]; break; // Plus - case '(': *textX = 48; *textY = 0; *spaceX = textLen[12]; break; // Open Bracket - case ')': *textX = 52; *textY = 0; *spaceX = textLen[13]; break; // Close Bracket - case '!': *textX = 56; *textY = 0; *spaceX = textLen[14]; break; // Exclamation mark - case '?': *textX = 60; *textY = 0; *spaceX = textLen[15]; break; // Question mark - - case '"': *textX = 40; *textY = 12; *spaceX = textLen[42]; break; // Speech mark - case'\'': *textX = 44; *textY = 12; *spaceX = textLen[43]; break; // Apostrophe - case ':': *textX = 48; *textY = 12; *spaceX = textLen[44]; break; // Colon - case ';': *textX = 52; *textY = 12; *spaceX = textLen[45]; break; // Semicolon - case '.': *textX = 56; *textY = 12; *spaceX = textLen[46]; break; // Full stop - case ',': *textX = 60; *textY = 12; *spaceX = textLen[47]; break; // Comma - - case '~': *textX = 40; *textY = 24; *spaceX = textLen[74]; break; // Tilde - case '@': *textX = 44; *textY = 24; *spaceX = textLen[75]; break; // Umlaut - case '^': *textX = 48; *textY = 24; *spaceX = textLen[76]; break; // Caret - case '/': *textX = 52; *textY = 24; *spaceX = textLen[77]; break; // Slash - case '_': *textX = 56; *textY = 24; *spaceX = textLen[78]; break; // Percent - case '&': *textX = 60; *textY = 24; *spaceX = textLen[79]; break; // Ampersand - // This is for the letters that sit differently on the line. It just moves them down a bit. - case 'g': *offsetY = 1; break; - case 'q': *offsetY = 1; break; - case 'p': if (font == FONT_DEFAULT) *offsetY = 3; break; - case 'y': if (font == FONT_DEFAULT) *offsetY = 1; break; + case 'g': *offsetY = 2 * textSizeTotal; break; + case 'q': *offsetY = 2 * textSizeTotal; break; + case 'j': *offsetY = 2 * textSizeTotal; break; + case 'p': *offsetY = 2 * textSizeTotal; break; + case 'y': *offsetY = 2 * textSizeTotal; break; } } +// Because we're using bcopy when both reading and writing to the text buffer, this doesn't care about alignment with the multi-bit types. +struct PuppyprintDeferredBufferHeader { + u16 x; + u16 y; + u8 red; + u8 green; + u8 blue; + u8 alpha; + u8 alignment; + u8 textBufferLength; + u8 font; + u8 isLightText; +}; + +static u8 gIsLightText = FALSE; + +// This is where the deferred printing will be stored. When text is made, it will store text with a 12 byte header, then the rest will be the text data itself. +// The first 4 bytes of the header will be the X and Y pos +// The next 4 bytes will be the current envcolour set by print_set_envcolour +// Then the string length, text alignment, amount and font each get a byte. +// The data afterwards is the text data itself, using the string length byte to know when to stop. +#define HEADERSIZE sizeof(struct PuppyprintDeferredBufferHeader) +#define MAX_U8_STRING_SIZE (U8_MAX - 1) // Needs 255th character reserved for null terminator +void print_small_text_buffered(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font) { + s32 strLen = strlen(str); + + if (amount <= PRINT_ALL || amount > MAX_U8_STRING_SIZE) + amount = MAX_U8_STRING_SIZE; + + amount = MIN(strLen, amount); + if (amount <= 0) + return; // No point in printing an empty string + + // Compare the cursor position and the string length with null terminator, plus 12 (header size) and return if it overflows. + if (sPuppyprintTextBufferPos + HEADERSIZE + (u8) amount + 1 > sizeof(sPuppyprintTextBuffer)) + return; + + x += 0x8000; + y += 0x8000; + + struct PuppyprintDeferredBufferHeader header; + header.x = (x & 0xFFFF); + header.y = (y & 0xFFFF); + header.red = gCurrEnvCol[0]; + header.green = gCurrEnvCol[1]; + header.blue = gCurrEnvCol[2]; + header.alpha = gCurrEnvCol[3]; + header.alignment = align; + header.textBufferLength = (u8) amount; + header.font = font; + header.isLightText = gIsLightText; + + bcopy(&header, &sPuppyprintTextBuffer[sPuppyprintTextBufferPos], HEADERSIZE); + sPuppyprintTextBufferPos += HEADERSIZE; + bcopy(str, &sPuppyprintTextBuffer[sPuppyprintTextBufferPos], header.textBufferLength); + sPuppyprintTextBufferPos += header.textBufferLength; + sPuppyprintTextBuffer[sPuppyprintTextBufferPos++] = '\0'; // Apply null terminator onto end of string +} + +void print_small_text_buffered_light(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font) { + gIsLightText = TRUE; + print_small_text_buffered(x, y, str, align, amount, font); + gIsLightText = FALSE; +} + +void puppyprint_print_deferred(void) { + if (sPuppyprintTextBufferPos == 0) + return; + bzero(&gCurrEnvCol, sizeof(ColorRGBA)); + print_set_envcolour(255, 255, 255, 255); + + for (u32 i = 0; i < sPuppyprintTextBufferPos;) { + struct PuppyprintDeferredBufferHeader header; + bcopy(&sPuppyprintTextBuffer[i], &header, HEADERSIZE); + i += HEADERSIZE; + + s32 x = header.x; + s32 y = header.y; + x -= 0x8000; + y -= 0x8000; + ColorRGBA originalEnvCol = {gCurrEnvCol[0], gCurrEnvCol[1], gCurrEnvCol[2], gCurrEnvCol[3]}; + print_set_envcolour(header.red, header.green, header.blue, header.alpha); + + char *text = (char *) &sPuppyprintTextBuffer[i]; + if (header.isLightText) { + print_small_text_light(x, y, text, header.alignment, header.textBufferLength, header.font); + } else { + print_small_text(x, y, text, header.alignment, header.textBufferLength, header.font); + } + + print_set_envcolour(originalEnvCol[0], originalEnvCol[1], originalEnvCol[2], originalEnvCol[3]); + i += header.textBufferLength + 1; // Null terminator not accounted for by text buffer length + } + + //Reset the position back to zero, effectively clearing the buffer. + sPuppyprintTextBufferPos = 0; +} + void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNUSED s32 scaleX, UNUSED s32 scaleY, s32 mode) { s32 posW, posH, imW, imH, modeSC, mOne; s32 i = 0; @@ -1098,7 +1600,7 @@ void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNU gDPLoadSync(gDisplayListHead++); gDPLoadTextureTile(gDisplayListHead++, - image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), ((posH + imH) - 1), 0, (G_TX_NOMIRROR | G_TX_WRAP), (G_TX_NOMIRROR | G_TX_WRAP), maskW, maskH, 0, 0); + image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), ((posH + imH) - 1), 0, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), maskW, maskH, 0, 0); gSPScisTextureRectangle(gDisplayListHead++, ((x + posW) << 2), ((y + posH) << 2), @@ -1114,7 +1616,7 @@ void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, UNU posW = i * imW; gDPLoadSync(gDisplayListHead++); gDPLoadTextureTile(gDisplayListHead++, - image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), (height - 1), 0, (G_TX_NOMIRROR | G_TX_WRAP), (G_TX_NOMIRROR | G_TX_WRAP), maskW, maskH, 0, 0); + image, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, height, posW, posH, ((posW + imW) - 1), (height - 1), 0, (G_TX_NOMIRROR | G_TX_CLAMP), (G_TX_NOMIRROR | G_TX_CLAMP), maskW, maskH, 0, 0); gSPScisTextureRectangle(gDisplayListHead++, (x + posW) << 2, (y + posH) << 2, diff --git a/src/game/puppyprint.h b/src/game/puppyprint.h index c5d90f88..39708df6 100644 --- a/src/game/puppyprint.h +++ b/src/game/puppyprint.h @@ -1,9 +1,45 @@ #pragma once -#include "segment2.h" +#include "profiling.h" -#define NUM_BENCH_ITERATIONS 150 +// This is how many indexes of timers are saved at once. higher creates a smoother average, but naturally uses more RAM. 32's fine. +#define NUM_PERF_ITERATIONS 32 +#define PERF_AGGREGATE NUM_PERF_ITERATIONS +#define PERF_TOTAL NUM_PERF_ITERATIONS + 1 #define LOG_BUFFER_SIZE 16 +#define PUPPYPRINT_DEFERRED_BUFFER_SIZE 0x1000 + +#ifdef PUPPYPRINT_DEBUG +#define PUPPYPRINT_ADD_COUNTER(x) x++ +#define PUPPYPRINT_GET_SNAPSHOT() u32 first = osGetCount() +#define PUPPYPRINT_GET_SNAPSHOT_TYPE(type) u32 first = profiler_get_delta(type) +#else +#define PUPPYPRINT_ADD_COUNTER(x) +#define PUPPYPRINT_GET_SNAPSHOT() +#define PUPPYPRINT_GET_SNAPSHOT_TYPE(type) +#endif + +struct PPTextFont { + Texture *tex; // Pointer to the texture for the font + u8 *kern; // Pointer to the kerning table + u16 *offset; // Pointer to the character offset table + u8 *pal; // Pointer to the texture pallete. Coming + u8 fmt; // Texture format, same as F3D macros + u8 siz; // Texture size, same as F3D macros + u16 imW; // Texture width + u8 imH; // Texture height + u8 txW; // Individual character width + u8 txH; // Individual character height +}; + +struct CallCounter { + u16 collision_floor; + u16 collision_wall; + u16 collision_ceil; + u16 collision_water; + u16 collision_raycast; + u16 matrix; +}; struct PuppyPrintPage{ void (*func)(); @@ -20,11 +56,39 @@ enum Benchmark { enum PuppyprintTextAlign { PRINT_TEXT_ALIGN_LEFT = 0, PRINT_TEXT_ALIGN_CENTRE = 1, + PRINT_TEXT_ALIGN_CENTER = 1, PRINT_TEXT_ALIGN_RIGHT = 2, PRINT_ALL = -1, }; -#if PUPPYPRINT_DEBUG +enum rspFlags +{ + RSP_NONE, + RSP_AUDIO_START, + RSP_GFX_START, + RSP_AUDIO_FINISHED, + RSP_GFX_FINISHED, + RSP_GFX_PAUSED, + RSP_GFX_RESUME, +}; + +enum PPPages { +#ifdef USE_PROFILER + PUPPYPRINT_PAGE_PROFILER, + PUPPYPRINT_PAGE_MINIMAL, +#endif + PUPPYPRINT_PAGE_GENERAL, + PUPPYPRINT_PAGE_AUDIO, + PUPPYPRINT_PAGE_RAM, + PUPPYPRINT_PAGE_COLLISION, + PUPPYPRINT_PAGE_LOG, + PUPPYPRINT_PAGE_LEVEL_SELECT, +#ifdef PUPPYCAM + PUPPYPRINT_PAGE_CAMERA +#endif +}; + +#ifdef PUPPYPRINT_DEBUG #ifdef BETTER_REVERB #define NUM_AUDIO_POOLS 7 #else @@ -35,35 +99,45 @@ enum PuppyprintTextAlign { enum PuppyFont { FONT_DEFAULT, FONT_OUTLINE, - FONT_NUM, + FONT_PLAIN, + FONT_VANILLA, + FONT_NUM }; extern u8 sPPDebugPage; extern u8 gPuppyFont; -extern s8 perfIteration; -extern s16 benchmarkLoop; -extern s32 benchmarkTimer; -extern ColorRGBA currEnv; +extern ColorRGBA gCurrEnvCol; extern s32 ramsizeSegment[33]; -extern s8 nameTable; +extern const s8 nameTable; extern s32 mempool; -extern u8 benchOption; +extern f32 textSize; +extern u32 gPoolMem; +extern u32 gMiscMem; +extern u8 gPuppyWarp; +extern u8 gPuppyWarpArea; +extern u8 gLastWarpID; +extern struct CallCounter gPuppyCallCounter; -// General -extern s32 benchMark[NUM_BENCH_ITERATIONS + 2]; - -extern void puppyprint_profiler_process(void); extern void puppyprint_render_profiler(void); -extern void puppyprint_profiler_finished(void); -extern void print_set_envcolour(s32 r, s32 g, s32 b, s32 a); +extern s32 print_set_envcolour(u8 r, u8 g, u8 b, u8 a); extern void prepare_blank_box(void); extern void finish_blank_box(void); -extern void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, s32 font); +extern void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font); extern void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, s32 scaleX, s32 scaleY, s32 mode); extern s32 get_text_height(const char *str); extern s32 get_text_width(const char *str, s32 font); extern void prepare_blank_box(void); extern void finish_blank_box(void); -extern void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, s32 r, s32 g, s32 b, s32 a); +extern void render_blank_box(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a); +extern void render_blank_box_rounded(s32 x1, s32 y1, s32 x2, s32 y2, u8 r, u8 g, u8 b, u8 a); extern void append_puppyprint_log(const char *str, ...); extern char consoleLogTable[LOG_BUFFER_SIZE][255]; +extern void print_small_text_buffered(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font); +extern void puppyprint_print_deferred(void); +extern s32 puppyprint_strlen(const char *str); +extern void set_segment_memory_printout(u32 segment, u32 amount); +extern void print_small_text_light(s32 x, s32 y, const char *str, s32 align, s32 amount, u8 font); +extern void print_small_text_buffered_light(s32 x, s32 y, const char *str, u8 align, s32 amount, u8 font); +void puppyprint_profiler_process(void); +s32 text_iterate_command(const char *str, s32 i, s32 runCMD); +void get_char_from_byte(s32 *textX, s32 *textPos, u8 letter, u8 *wideX, u8 *spaceX, s8 *offsetY, u8 font); diff --git a/src/game/segment2.h b/src/game/segment2.h index c83f8ed7..20fd6d23 100644 --- a/src/game/segment2.h +++ b/src/game/segment2.h @@ -4,8 +4,9 @@ #include #include -extern void *puppyprint_font_lut[2]; -extern void *puppyprint_kerning_lut[2][80]; +extern void *puppyprint_font_lut[]; +extern void *puppyprint_kerning_lut[][82]; +extern const struct PPTextFont *const gPuppyPrintFontTable[]; extern u8 seg2_course_name_table[]; extern u8 seg2_act_name_table[]; diff --git a/src/game/sound_init.c b/src/game/sound_init.c index 3e5c41eb..d0988f59 100644 --- a/src/game/sound_init.c +++ b/src/game/sound_init.c @@ -368,14 +368,13 @@ void thread4_sound(UNUSED void *arg) { OSMesg msg; osRecvMesg(&sSoundMesgQueue, &msg, OS_MESG_BLOCK); - profiler_audio_started(); + profiler_audio_started(); // also starts PROFILER_TIME_SUB_AUDIO_UPDATE inside if (gResetTimer < 25) { - struct SPTask *spTask; - spTask = create_next_audio_frame_task(); + struct SPTask *spTask = create_next_audio_frame_task(); if (spTask != NULL) { dispatch_audio_sptask(spTask); } } - profiler_audio_completed(); + profiler_audio_completed(); // also completes PROFILER_TIME_SUB_AUDIO_UPDATE inside } } diff --git a/textures/segment2/custom_text.i4.png b/textures/segment2/custom_text.i4.png index c6ed0af8..4bf8a055 100644 Binary files a/textures/segment2/custom_text.i4.png and b/textures/segment2/custom_text.i4.png differ diff --git a/textures/segment2/custom_text2.i4.png b/textures/segment2/custom_text2.i4.png deleted file mode 100644 index 00c4f282..00000000 Binary files a/textures/segment2/custom_text2.i4.png and /dev/null differ diff --git a/textures/segment2/custom_text2.ia4.png b/textures/segment2/custom_text2.ia4.png new file mode 100644 index 00000000..389a6cf0 Binary files /dev/null and b/textures/segment2/custom_text2.ia4.png differ diff --git a/textures/segment2/custom_text3.i4.png b/textures/segment2/custom_text3.i4.png new file mode 100644 index 00000000..0e106e66 Binary files /dev/null and b/textures/segment2/custom_text3.i4.png differ diff --git a/textures/segment2/custom_text4.i4.png b/textures/segment2/custom_text4.i4.png new file mode 100644 index 00000000..7316c4bd Binary files /dev/null and b/textures/segment2/custom_text4.i4.png differ diff --git a/tools/n64graphics.c b/tools/n64graphics.c index f1783137..0e4513e1 100644 --- a/tools/n64graphics.c +++ b/tools/n64graphics.c @@ -466,7 +466,7 @@ ia *png2ia(const char *png_filename, int *width, int *height) switch (channels) { case 3: // red, green, blue case 4: // red, green, blue, alpha - ERROR("Warning: averaging RGB PNG to create IA\n"); + // ERROR("Warning: averaging RGB PNG to create IA\n"); // Comment out to silence Puppyprint texture warnings for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { int idx = j*w + i;