daedalus/Source/SysPSP/HLEGraphics/DisplayListDebugger.cpp
2022-06-17 08:08:16 +10:00

1233 lines
34 KiB
C++

/*
Copyright (C) 2006 StrmnNrmn
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "BuildOptions.h"
#include "Base/Types.h"
#include "HLEGraphics/DisplayListDebugger.h"
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
#include "Combiner/RenderSettings.h"
#include "HLEGraphics/CachedTexture.h"
#include "HLEGraphics/DLDebug.h"
#include "HLEGraphics/DLParser.h"
#include "HLEGraphics/Microcode.h"
#include "SysPSP/HLEGraphics/RendererPSP.h"
#include "HLEGraphics/TextureCache.h"
#include "HLEGraphics/TextureInfo.h"
#include "Graphics/GraphicsContext.h"
#include "Graphics/NativeTexture.h"
#include "Core/ROM.h"
#include "Debug/Dump.h"
#include "System/IO.h"
#include "Interface/Preferences.h"
#include "Utility/Timer.h"
#include "System/Timing.h"
#include <set>
#include <vector>
#include <algorithm>
#include "Math/Math.h" // VFPU Math
#include "Base/MathUtil.h"
#include <pspctrl.h>
#include <pspgu.h>
using std::sort;
//*****************************************************************************
//
//*****************************************************************************
extern float TEST_VARX, TEST_VARY;
extern DebugBlendSettings gDBlend;
// FIXME: we shouldn't rely on this global state.
// We should call DLParser_Process(kUnlimitedInstructionCount) when we enter the debugger, and that will return a count. T
extern u32 gNumInstructionsExecuted;
//*****************************************************************************
//
//*****************************************************************************
static bool gDebugDisplayList = false;
static bool gSingleStepFrames = false;
class CDisplayListDebugger
{
public:
virtual ~CDisplayListDebugger();
static CDisplayListDebugger * Create();
virtual void Run() = 0;
};
bool DLDebugger_IsDebugging()
{
return gDebugDisplayList;
}
void DLDebugger_RequestDebug()
{
gDebugDisplayList = true;
}
bool DLDebugger_Process()
{
// DLParser_Process may set this flag, so check again after execution
if(gDebugDisplayList)
{
CDisplayListDebugger * debugger = CDisplayListDebugger::Create();
debugger->Run();
delete debugger;
gDebugDisplayList = gSingleStepFrames;
gSingleStepFrames = false;
return true;
}
return false;
}
//*****************************************************************************
//
//*****************************************************************************
namespace
{
// const char * const TERMINAL_TOP_LEFT = "\033[2A\033[2K";
const char * const TERMINAL_TOP_LEFT = "\033[H";
const char * const TERMINAL_CLEAR_SCREEN = "\033[2J";
const char * const TERMINAL_CLEAR_LINE = "\033[2K";
const char * const TERMINAL_SAVE_POS = "\033[2s";
const char * const TERMINAL_RESTORE_POS = "\033[2u";
const char * const TERMINAL_RED = "\033[1;31m";
const char * const TERMINAL_GREEN = "\033[1;32m";
const char * const TERMINAL_YELLOW = "\033[1;33m";
const char * const TERMINAL_BLUE = "\033[1;34m";
const char * const TERMINAL_MAGENTA = "\033[1;35m";
const char * const TERMINAL_CYAN = "\033[1;36m";
const char * const TERMINAL_WHITE = "\033[1;37m";
//const char * const gDDLOText[] =
//{
// "Combiner Explorer", // DDLO_COMBINER_EXPLORER
// "Display List Length", // DDLO_DLIST_LENGTH
// "Decal Offset", // DDLO_DECAL_OFFSET
// "Texture Viewer", // DDLO_TEXTURE_VIEWER
// "Dump Textures", // DDLO_DUMP_TEXTURES
// "Dump Dlist", // DDLO_DUMP_DLIST
//};
struct SPspPadState
{
v2 Stick;
u32 OldButtons;
u32 NewButtons;
};
//*****************************************************************************
//
//*****************************************************************************
class CDebugMenuOption
{
public:
CDebugMenuOption();
virtual ~CDebugMenuOption() {}
virtual void Enter() {}
virtual void Exit() {}
virtual void Update( const SPspPadState & pad_state, float elapsed_time ) = 0;
virtual bool OverrideDisplay() const { return false; }
bool NeedsUpdateDisplay() const { return mRefreshDisplay; }
void UpdateDisplay();
virtual const char * GetDescription() const = 0;
protected:
void InvalidateDisplay() { mRefreshDisplay = true; }
virtual void Display() const = 0;
private:
bool mRefreshDisplay;
};
CDebugMenuOption::CDebugMenuOption()
: mRefreshDisplay( true )
{
}
void CDebugMenuOption::UpdateDisplay()
{
Display();
mRefreshDisplay = false;
}
//*****************************************************************************
//
//*****************************************************************************
class CCombinerExplorerDebugMenuOption : public CDebugMenuOption
{
public:
CCombinerExplorerDebugMenuOption();
virtual void Display() const;
virtual void Update( const SPspPadState & pad_state, float elapsed_time );
virtual const char * GetDescription() const { return "Combiner Explorer"; }
private:
u32 mSelectedIdx;
};
CCombinerExplorerDebugMenuOption::CCombinerExplorerDebugMenuOption()
: mSelectedIdx( 0 )
{
}
void CCombinerExplorerDebugMenuOption::Display() const
{
const std::set< u64 > & combiner_states( gRendererPSP->GetRecordedCombinerStates() );
printf( " Use [] to return\n" );
printf( " Use O to select on/off\n" );
printf( " Use /\\ to highlight texture\n" );
printf( " Use up/down to move cursor\n\n" );
printf( " %sHandled Inexact Blend\n", TERMINAL_GREEN );
printf( " %sForced Blend\n", TERMINAL_MAGENTA );
printf( " %sAuto Combined or Default Inexact Blend\n", TERMINAL_CYAN );
printf( " %sSelected for Blend Explorer\n\n", TERMINAL_RED );
printf( "%sCombiner States in use:\n", TERMINAL_WHITE );
u32 idx( 0 );
u64 selected_mux( 0 );
for(std::set<u64>::const_iterator it = combiner_states.begin(); it != combiner_states.end(); ++it)
{
u64 state( *it );
bool selected( idx == mSelectedIdx );
bool disabled( gRendererPSP->IsCombinerStateDisabled( state ) );
//bool unhandled( gRendererPSP->IsCombinerStateUnhandled( state ) );
bool forced( gRendererPSP->IsCombinerStateForced( state ) );
bool idefault( gRendererPSP->IsCombinerStateDefault( state ) );
const char * text_col;
if(selected)
{
//text_col = TERMINAL_YELLOW;
selected_mux = state;
}
if(disabled)
{
text_col = TERMINAL_RED;
}
else if(forced)
{
text_col = TERMINAL_MAGENTA;
}
else if(idefault)
{
text_col = TERMINAL_CYAN;
}
else
{
text_col = TERMINAL_GREEN;
}
printf( " %s%c%08x%08x\n", text_col, selected ? '*' : ' ', u32(state >> 32), u32(state) );
idx++;
}
printf( "%s\n", TERMINAL_WHITE );
if( selected_mux != 0 )
{
DLDebug_PrintMux( stdout, selected_mux );
RendererPSP::SBlendStateEntry entry1( gRendererPSP->LookupBlendState( selected_mux, false ) );
if( entry1.OverrideFunction != nullptr )
{
printf( "1 Cycle: Overridden\n" );
}
else if( entry1.States != nullptr )
{
printf( "1 Cycle:\n" );
entry1.States->Print();
}
RendererPSP::SBlendStateEntry entry2( gRendererPSP->LookupBlendState( selected_mux, true ) );
if( entry2.OverrideFunction != nullptr )
{
printf( "2 Cycles: Overridden\n" );
}
else if( entry2.States != nullptr )
{
printf( "2 Cycles:\n" );
entry2.States->Print();
}
}
}
void CCombinerExplorerDebugMenuOption::Update( const SPspPadState & pad_state, float elapsed_time )
{
const std::set< u64 > & combiner_states( gRendererPSP->GetRecordedCombinerStates() );
u32 idx( 0 );
u64 selected_state( 0 );
for(std::set<u64>::const_iterator it = combiner_states.begin(); it != combiner_states.end(); ++it)
{
if(idx == mSelectedIdx)
{
selected_state = *it;
}
idx++;
}
u32 state_count( combiner_states.size() );
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_UP)
{
mSelectedIdx = (mSelectedIdx > 0) ? mSelectedIdx - 1 : mSelectedIdx;
InvalidateDisplay();
}
if(pad_state.NewButtons & PSP_CTRL_DOWN)
{
mSelectedIdx = (mSelectedIdx < state_count-1) ? mSelectedIdx + 1 : mSelectedIdx;
InvalidateDisplay();
}
if(pad_state.NewButtons & PSP_CTRL_TRIANGLE)
{
if(selected_state != 0)
{
gRendererPSP->ToggleDisableCombinerState( selected_state );
gRendererPSP->ToggleNastyTexture( true );
InvalidateDisplay();
}
}
if(pad_state.NewButtons & PSP_CTRL_CIRCLE)
{
if(selected_state != 0)
{
gRendererPSP->ToggleDisableCombinerState( selected_state );
InvalidateDisplay();
}
}
}
}
//*****************************************************************************
//
//*****************************************************************************
class CBlendDebugMenuOption : public CDebugMenuOption
{
public:
CBlendDebugMenuOption();
virtual void Display() const;
virtual void Update( const SPspPadState & pad_state, float elapsed_time );
virtual const char * GetDescription() const { return "Blend Explorer"; }
private:
u32 mIdx;
u32 mSel;
bool modify;
};
CBlendDebugMenuOption::CBlendDebugMenuOption()
: mIdx( 0 )
, mSel( 0 )
, modify( false )
{
}
void CBlendDebugMenuOption::Display() const
{
const char * const ForceColor[8] =
{
"( OFF )",
"( c32::White )",
"( c32::Black )",
"( c32::Red )",
"( c32::Green )",
"( c32::Blue )",
"( c32::Magenta )",
"( c32::Gold )"
};
const char * const PSPtxtFunc[10] =
{
"( GU_TFX_MODULATE, GU_TCC_RGB )",
"( GU_TFX_MODULATE, GU_TCC_RGBA )",
"( GU_TFX_BLEND, GU_TCC_RGB )",
"( GU_TFX_BLEND, GU_TCC_RGBA )",
"( GU_TFX_ADD, GU_TCC_RGB )",
"( GU_TFX_ADD, GU_TCC_RGBA )",
"( GU_TFX_REPLACE, GU_TCC_RGB )",
"( GU_TFX_REPLACE, GU_TCC_RGBA )",
"( GU_TFX_DECAL, GU_TCC_RGB )",
"( GU_TFX_DECAL, GU_TCC_RGBA )"
};
const char * const CAdj[5] =
{
"( OFF )",
"( details.PrimColour )",
"( details.PrimColour.ReplicateAlpha() )",
"( details.EnvColour )",
"( details.EnvColour.ReplicateAlpha() )"
};
const char * const ENVAdj[3] =
{
"( OFF )",
"( details.EnvColour.GetColour() )",
"( details.PrimColour.GetColour() )"
};
if( modify )
{
switch( mSel )
{
case 0: gDBlend.SetRGB = mIdx % 5; break;
case 1: gDBlend.SetA = mIdx % 5; break;
case 2: gDBlend.SetRGBA = mIdx % 5; break;
case 3: gDBlend.ModRGB = mIdx % 5; break;
case 4: gDBlend.ModA = mIdx % 5; break;
case 5: gDBlend.ModRGBA = mIdx % 5; break;
case 6: gDBlend.SubRGB = mIdx % 5; break;
case 7: gDBlend.SubA = mIdx % 5; break;
case 8: gDBlend.SubRGBA = mIdx % 5; break;
case 9: gDBlend.AOpaque = mIdx & 1; break;
case 10: gDBlend.sceENV = mIdx % 3; break;
case 11: gDBlend.TXTFUNC = mIdx % 10; break;
case 12: gDBlend.TexInstall = mIdx & 1; break;
case 13: gDBlend.ForceRGB = mIdx % 8; break;
}
}
#define BLEND_SELECTION(x) (mSel==x && modify) ? TERMINAL_GREEN : TERMINAL_WHITE, mSel==x ? '*' : ' '
printf( "Blend Explorer\n");
printf( " Use [] to return\n" );
printf( " Use X to modify\n" );
printf( " Use up/down to choose & left/right to adjust\n\n\n" );
printf( " Blending Options\n" );
printf( " %s%cdetails.ColourAdjuster.SetRGB%s\n", BLEND_SELECTION(0), CAdj[gDBlend.SetRGB]);
printf( " %s%cdetails.ColourAdjuster.SetA%s\n", BLEND_SELECTION(1), CAdj[gDBlend.SetA]);
printf( " %s%cdetails.ColourAdjuster.SetRGBA%s\n", BLEND_SELECTION(2), CAdj[gDBlend.SetRGBA]);
printf( " %s%cdetails.ColourAdjuster.ModulateRGB%s\n", BLEND_SELECTION(3), CAdj[gDBlend.ModRGB]);
printf( " %s%cdetails.ColourAdjuster.ModulateA%s\n", BLEND_SELECTION(4), CAdj[gDBlend.ModA]);
printf( " %s%cdetails.ColourAdjuster.ModulateRGBA%s\n", BLEND_SELECTION(5), CAdj[gDBlend.ModRGBA]);
printf( " %s%cdetails.ColourAdjuster.SubtractRGB%s\n", BLEND_SELECTION(6), CAdj[gDBlend.SubRGB]);
printf( " %s%cdetails.ColourAdjuster.SubtractA%s\n", BLEND_SELECTION(7), CAdj[gDBlend.SubA]);
printf( " %s%cdetails.ColourAdjuster.SubtractRGBA%s\n", BLEND_SELECTION(8), CAdj[gDBlend.SubRGBA]);
printf( " %s%cdetails.ColourAdjuster.SetAOpaque() %s\n", BLEND_SELECTION(9), gDBlend.AOpaque ? "ON" : "OFF");
printf( "%s\n", TERMINAL_WHITE );
printf( " Environment Color (only works with GU_TFX_BLEND option)\n" );
printf( " %s%csceGuTexEnvColor%s\n", BLEND_SELECTION(10), ENVAdj[gDBlend.sceENV]);
printf( "%s\n", TERMINAL_WHITE );
printf( " Texture Blending Function\n" );
printf( " %s%csceGuTexFunc%s\n", BLEND_SELECTION(11), PSPtxtFunc[gDBlend.TXTFUNC]);
printf( "%s\n", TERMINAL_WHITE );
printf( " Other Options\n" );
printf( " %s%cTexture Enabled: %s\n", BLEND_SELECTION(12), gDBlend.TexInstall ? "ON" : "OFF");
printf( " %s%cdetails.ColourAdjuster.SetRGB%s\n", BLEND_SELECTION(13), ForceColor[gDBlend.ForceRGB]);
printf( "%s\n", TERMINAL_WHITE );
#undef BLEND_SELECTION
}
void CBlendDebugMenuOption::Update( const SPspPadState & pad_state, float elapsed_time )
{
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_UP)
{
mSel = (mSel > 0) ? mSel - 1 : mSel;
modify = 0;
}
if(pad_state.NewButtons & PSP_CTRL_DOWN)
{
mSel = (mSel < 13) ? mSel + 1 : mSel; //Number of menu rows
modify = 0;
}
if(pad_state.NewButtons & PSP_CTRL_LEFT)
{
mIdx = (mIdx > 0) ? mIdx - 1 : mIdx; //min select
}
if(pad_state.NewButtons & PSP_CTRL_RIGHT)
{
mIdx = (mIdx < 9) ? mIdx + 1 : mIdx; //max select
}
if(pad_state.NewButtons & PSP_CTRL_CROSS)
{
modify ^= true;
}
InvalidateDisplay();
}
}
//*****************************************************************************
//
//*****************************************************************************
class CTextureExplorerDebugMenuOption : public CDebugMenuOption
{
public:
CTextureExplorerDebugMenuOption();
virtual void Display() const;
virtual void Update( const SPspPadState & pad_state, float elapsed_time );
virtual const char * GetDescription() const { return "Texture Explorer"; }
virtual bool OverrideDisplay() const;
private:
u32 mSelectedIdx;
bool mDisplayTexture;
u32 mScaleFactor;
v2 mTextureOffset;
CRefPtr<CNativeTexture> mCheckerTexture;
std::vector<CTextureCache::STextureInfoSnapshot> mSnapshot;
};
namespace
{
bool OrderTextures( const CTextureCache::STextureInfoSnapshot & lhs, const CTextureCache::STextureInfoSnapshot & rhs )
{
return lhs.Info.GetLoadAddress() < rhs.Info.GetLoadAddress();
}
#define GL_TRUE 1
#define GL_FALSE 0
const u32 gCheckTextureWidth( 16 );
const u32 gCheckTextureHeight( 16 );
u16 gCheckTexture[gCheckTextureWidth * gCheckTextureHeight ] =
{
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb, 0xfbbb,
};
}
CTextureExplorerDebugMenuOption::CTextureExplorerDebugMenuOption()
: mSelectedIdx( 0 )
, mDisplayTexture( false )
, mScaleFactor( 2 )
, mTextureOffset( 0,0 )
{
mCheckerTexture = CNativeTexture::Create( gCheckTextureWidth, gCheckTextureHeight, TexFmt_4444 );
if( mCheckerTexture != nullptr )
{
DAEDALUS_ASSERT( mCheckerTexture->GetBytesRequired() == sizeof( gCheckTexture ), "Incorrect size for checker texture" );
mCheckerTexture->SetData( gCheckTexture, nullptr );
}
{
// The lock isn't really needed, as on the PSP we run this single threaded.
MutexLock lock(CTextureCache::Get()->GetDebugMutex());
CTextureCache::Get()->Snapshot( lock, mSnapshot );
}
sort( mSnapshot.begin(), mSnapshot.end(), &OrderTextures );
}
struct TextureVtx
{
v2 t0;
v3 pos;
};
DAEDALUS_STATIC_ASSERT( sizeof(TextureVtx) == 20 );
#define TEXTURE_VERTEX_FLAGS (GU_TEXTURE_32BITF|GU_VERTEX_32BITF )
bool CTextureExplorerDebugMenuOption::OverrideDisplay() const
{
if( !mDisplayTexture )
return false;
CRefPtr<CNativeTexture> texture;
u32 texture_width = 32;
u32 texture_height = 32;
if( mSelectedIdx < mSnapshot.size() )
{
texture = mSnapshot[ mSelectedIdx ].Texture;
const TextureInfo & info = mSnapshot[ mSelectedIdx ].Info;
texture_width = info.GetWidth();
texture_height = info.GetHeight();
}
CGraphicsContext::Get()->BeginFrame();
sceGuDisable(GU_DEPTH_TEST);
sceGuDepthMask( GL_TRUE ); // GL_TRUE to disable z-writes
sceGuShadeModel( GU_FLAT );
sceGuTexFilter(GU_NEAREST,GU_NEAREST);
sceGuTexScale(1.0f,1.0f);
sceGuTexOffset(0.0f,0.0f);
sceGuDisable(GU_ALPHA_TEST);
sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGBA);
sceGuSetMatrix( GU_PROJECTION, reinterpret_cast< const ScePspFMatrix4 * >( &gMatrixIdentity ) );
const f32 screen_width( 480.0f );
const f32 screen_height( 272.0f );
if ( mCheckerTexture != nullptr )
{
u32 num_verts( 2 );
TextureVtx* p_verts = (TextureVtx*)sceGuGetMemory(num_verts*sizeof(TextureVtx));
mCheckerTexture->InstallTexture();
sceGuDisable(GU_BLEND);
sceGuTexWrap(GU_REPEAT,GU_REPEAT);
p_verts[0].pos = v3( 0.0f, 0.0f, 0.0f );
p_verts[0].t0 = v2( 0.0f, 0.0f );
p_verts[1].pos = v3( screen_width, screen_height, 0.0f );
p_verts[1].t0 = v2( screen_width, screen_height );
sceGuDrawArray(GU_SPRITES,TEXTURE_VERTEX_FLAGS|GU_TRANSFORM_2D,num_verts,nullptr,p_verts);
}
if( texture != nullptr )
{
u32 num_verts( 2 );
TextureVtx* p_verts = (TextureVtx*)sceGuGetMemory(num_verts*sizeof(TextureVtx));
texture->InstallTexture();
f32 display_width( f32( texture_width * mScaleFactor ) );
f32 display_height( f32( texture_height * mScaleFactor ) );
f32 left_offset( (screen_width - display_width) / 2.0f - mTextureOffset.x );
f32 top_offset( (screen_height - display_height) / 2.0f - mTextureOffset.y );
// Clamp to pixel boundary
left_offset = f32( s32( left_offset ) );
top_offset = f32( s32( top_offset ) );
sceGuBlendFunc( GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
sceGuEnable(GU_BLEND);
sceGuTexWrap(GU_CLAMP,GU_CLAMP);
p_verts[0].pos = v3( left_offset, top_offset, 0.0f );
p_verts[0].t0 = v2( 0.0f, 0.0f );
p_verts[1].pos = v3( left_offset + display_width, top_offset + display_height, 0.0f );
p_verts[1].t0 = v2( (float)texture_width, (float)texture_height );
sceGuDrawArray(GU_SPRITES,TEXTURE_VERTEX_FLAGS|GU_TRANSFORM_2D,num_verts,nullptr,p_verts);
}
CGraphicsContext::Get()->EndFrame();
return true;
}
void CTextureExplorerDebugMenuOption::Display() const
{
printf( "Textures in use:\n" );
printf( " Use [] to return\n" );
printf( " Use /\\ to force texture reload\n" );
printf( " Use X to toggle display on/off:\n" );
printf( "\nThere are %d textures\n\n", mSnapshot.size() );
s32 min_to_show( mSelectedIdx - 16 );
s32 max_to_show( mSelectedIdx + 16 );
if( min_to_show < 0 )
{
s32 num_spare( 0 - min_to_show );
max_to_show = std::clamp< s32 >( max_to_show + num_spare, 0, mSnapshot.size() - 1 );
min_to_show = 0;
}
if( max_to_show >= s32( mSnapshot.size() ) )
{
s32 num_spare( max_to_show - (mSnapshot.size() - 1) );
min_to_show = std::clamp< s32 >( min_to_show - num_spare, 0, mSnapshot.size() - 1 );
max_to_show = mSnapshot.size() - 1;
}
printf( " # LoadAddr (x,y -> w x h, p) fmt/size tmem pal\n" );
for( s32 i = min_to_show; i <= max_to_show; ++i )
{
DAEDALUS_ASSERT( i >= 0 && i < s32( mSnapshot.size() ), "Invalid snapshot index" );
const TextureInfo & ti = mSnapshot[i].Info;
bool selected = u32( i ) == mSelectedIdx;
const char * text_col = selected ? TERMINAL_YELLOW : TERMINAL_WHITE;
// XXXX
u32 left = 0;
u32 top = 0;
printf( " %s%03d %c%08x (%d,%d -> %dx%d, %d) %s/%dbpp, %04x, %04x\n",
text_col, i, selected ? '*' : ' ',
ti.GetLoadAddress(),
left, top, ti.GetWidth(), ti.GetHeight(), ti.GetPitch(),
ti.GetFormatName(), ti.GetSizeInBits(),
ti.GetTmemAddress(), ti.GetPalette() );
}
printf( "%s\n", TERMINAL_WHITE );
}
void CTextureExplorerDebugMenuOption::Update( const SPspPadState & pad_state, float elapsed_time )
{
u32 texture_count( mSnapshot.size() );
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_UP)
{
mSelectedIdx = (mSelectedIdx > 0) ? mSelectedIdx - 1 : mSelectedIdx;
InvalidateDisplay();
}
if(pad_state.NewButtons & PSP_CTRL_DOWN)
{
mSelectedIdx = (mSelectedIdx < texture_count-1) ? mSelectedIdx + 1 : mSelectedIdx;
InvalidateDisplay();
}
if(pad_state.NewButtons & PSP_CTRL_TRIANGLE)
{
CTextureCache::Get()->DropTextures();
}
if(pad_state.NewButtons & PSP_CTRL_CROSS)
{
mDisplayTexture = !mDisplayTexture;
}
if(pad_state.NewButtons & PSP_CTRL_LTRIGGER)
{
mScaleFactor = mScaleFactor / 2;
if(mScaleFactor < 1)
{
mScaleFactor = 1;
}
}
if(pad_state.NewButtons & PSP_CTRL_RTRIGGER)
{
mScaleFactor = mScaleFactor * 2;
if(mScaleFactor > 16)
{
mScaleFactor = 16;
}
}
}
if(mDisplayTexture)
{
const float STICK_ADJUST_PIXELS_PER_SECOND = 256.0f;
if(fabsf(pad_state.Stick.x) < 0.001f && fabsf(pad_state.Stick.y) < 0.001f)
{
mTextureOffset *= powf( 0.997f, elapsed_time * 1000.0f );
}
else
{
mTextureOffset += pad_state.Stick * STICK_ADJUST_PIXELS_PER_SECOND * elapsed_time;
}
}
}
//*****************************************************************************
//
//*****************************************************************************
class CDisplayListLengthDebugMenuOption : public CDebugMenuOption
{
public:
CDisplayListLengthDebugMenuOption(u32 total, u32 * limit);
virtual void Display() const;
virtual void Update( const SPspPadState & pad_state, float elapsed_time );
virtual const char * GetDescription() const { return "Display List Length"; }
private:
u32 mTotalInstructionCount;
u32 * mInstructionCountLimit;
float mFractionalAdjustment;
};
CDisplayListLengthDebugMenuOption::CDisplayListLengthDebugMenuOption(u32 total, u32 * limit)
: mTotalInstructionCount( total )
, mInstructionCountLimit( limit )
, mFractionalAdjustment( 0.0f )
{
}
void CDisplayListLengthDebugMenuOption::Display() const
{
printf( "Display list length %d / %d:\n", *mInstructionCountLimit, mTotalInstructionCount );
printf( " Use [] to return\n" );
printf( " Use up/down to adjust\n" );
}
void CDisplayListLengthDebugMenuOption::Update( const SPspPadState & pad_state, float elapsed_time )
{
float rate_adjustment( 1.0f );
if(pad_state.NewButtons & PSP_CTRL_RTRIGGER)
{
rate_adjustment = 5.0f;
}
float new_adjustment( 0.0f );
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_UP)
{
new_adjustment = -1;
}
if(pad_state.NewButtons & PSP_CTRL_DOWN)
{
new_adjustment = +1;
}
}
const float STICK_ADJUST_PER_SECOND = 100.0f;
new_adjustment += pad_state.Stick.y * STICK_ADJUST_PER_SECOND * rate_adjustment * elapsed_time;
mFractionalAdjustment += new_adjustment;
s32 adjustment = s32( mFractionalAdjustment );
if( adjustment != 0 )
{
s32 new_limit = *mInstructionCountLimit + adjustment;
*mInstructionCountLimit = u32( std::clamp< s32 >( new_limit, 0, mTotalInstructionCount ) );
mFractionalAdjustment -= float( adjustment );
InvalidateDisplay();
}
}
//*****************************************************************************
//
//*****************************************************************************
class CDecalOffsetDebugMenuOption : public CDebugMenuOption
{
public:
virtual void Display() const;
virtual void Update( const SPspPadState & pad_state, float elapsed_time );
virtual const char * GetDescription() const { return "Test variables"; }
};
void CDecalOffsetDebugMenuOption::Display() const
{
printf( "Test variable X:%0.2f Y:%0.2f\n", TEST_VARX, TEST_VARY );
printf( " Use [] to return\n" );
printf( " Use stick up/down & left/right to adjust\n" );
}
void CDecalOffsetDebugMenuOption::Update( const SPspPadState & pad_state, float elapsed_time )
{
const float CHANGE_PER_SECOND = 1000;
if( pad_state.Stick.x != 0 )
{
//TEST_VAR += pad_state.Stick.y * CHANGE_PER_SECOND * elapsed_time;
if(pad_state.Stick.x > 0) TEST_VARX += CHANGE_PER_SECOND * elapsed_time;
else TEST_VARX -= CHANGE_PER_SECOND * elapsed_time;
InvalidateDisplay();
}
if( pad_state.Stick.y != 0 )
{
if(pad_state.Stick.y < 0) TEST_VARY += CHANGE_PER_SECOND * elapsed_time;
else TEST_VARY -= CHANGE_PER_SECOND * elapsed_time;
InvalidateDisplay();
}
}
}
//*************************************************************************************
//
//*************************************************************************************
class IDisplayListDebugger : public CDisplayListDebugger
{
public:
virtual ~IDisplayListDebugger() {}
virtual void Run();
};
//*************************************************************************************
//
//*************************************************************************************
CDisplayListDebugger * CDisplayListDebugger::Create()
{
return new IDisplayListDebugger;
}
//*************************************************************************************
//
//*************************************************************************************
CDisplayListDebugger::~CDisplayListDebugger()
{
}
//*************************************************************************************
//
//*************************************************************************************
void IDisplayListDebugger::Run()
{
//
// Enter the debug menu as soon as select is newly pressed
//
SceCtrlData pad;
SPspPadState pad_state;
pad_state.OldButtons = 0;
sceCtrlPeekBufferPositive(&pad, 1);
pad_state.OldButtons = pad.Buttons;
bool menu_button_pressed( false );
u64 freq;
NTiming::GetPreciseFrequency( &freq );
float freq_inv = 1.0f / f32( freq );
CTimer timer;
using DebugMenuOptionVector = std::vector< CDebugMenuOption * >;
DebugMenuOptionVector menu_options;
u32 total_instruction_count = gNumInstructionsExecuted;
u32 instruction_limit = gNumInstructionsExecuted;
menu_options.push_back( new CCombinerExplorerDebugMenuOption );
menu_options.push_back( new CBlendDebugMenuOption );
menu_options.push_back( new CTextureExplorerDebugMenuOption );
menu_options.push_back( new CDisplayListLengthDebugMenuOption(total_instruction_count, &instruction_limit) );
menu_options.push_back( new CDecalOffsetDebugMenuOption );
u32 highlighted_option( 0 );
CDebugMenuOption * p_current_option( nullptr );
// Remain paused until the Select button is pressed again
bool need_update_display( true );
bool dump_next_screen( false );
bool dump_texture_dlist( false );
while( (pad_state.NewButtons & PSP_CTRL_SELECT) != 0 || !menu_button_pressed )
{
//guSwapBuffersBehaviour( PSP_DISPLAY_SETBUF_IMMEDIATE );
if( dump_next_screen )
{
dump_next_screen = false;
CGraphicsContext::Get()->DumpScreenShot();
}
DLDebugOutput * debug_output = nullptr;
if( dump_texture_dlist )
{
dump_texture_dlist = false;
printf( TERMINAL_TOP_LEFT );
printf( TERMINAL_CLEAR_LINE );
printf( "Dumping Display List and Textures...\n" );
// Dump the display list
debug_output = DLDebug_CreateFileOutput();
// Dump textures
MutexLock lock(CTextureCache::Get()->GetDebugMutex());
std::vector<CTextureCache::STextureInfoSnapshot> snapshot;
CTextureCache::Get()->Snapshot( lock, snapshot );
sort( snapshot.begin(), snapshot.end(), &OrderTextures );
// Dump each in turn
for( u32 i = 0; i < snapshot.size(); ++i )
{
CachedTexture::DumpTexture( snapshot[i].Info, snapshot[i].Texture );
}
}
CGraphicsContext::Get()->BeginFrame();
CGraphicsContext::Get()->ClearToBlack();
CGraphicsContext::Get()->EndFrame();
u64 time_before;
NTiming::GetPreciseTime( &time_before );
//
// Re-render the current frame
//
bool render_dlist( true );
if( p_current_option != nullptr )
{
if( p_current_option->OverrideDisplay() )
{
render_dlist = false;
}
}
if( render_dlist )
{
DLParser_Process(instruction_limit, debug_output);
// We can delete the sink as soon as we're done with it.
delete debug_output;
debug_output = nullptr;
}
u64 time_after;
NTiming::GetPreciseTime( &time_after );
//
// Figure out how long the last frame took
//
u64 elapsed_ticks( time_after - time_before );
float elapsed_ms( f32(elapsed_ticks) * 1000.0f * freq_inv );
float framerate( 0.0f );
if(elapsed_ms > 0)
{
framerate = 1000.0f / elapsed_ms;
}
CGraphicsContext::Get()->UpdateFrame( false );
//sceDisplayWaitVblankStart();
sceCtrlPeekBufferPositive(&pad, 1);
pad_state.NewButtons = pad.Buttons;
const s32 STICK_DEADZONE = 20;
s32 stick_x( pad.Lx - 128 );
s32 stick_y( pad.Ly - 128 );
if(stick_x >= -STICK_DEADZONE && stick_x <= STICK_DEADZONE)
{
stick_x = 0;
}
if(stick_y >= -STICK_DEADZONE && stick_y <= STICK_DEADZONE)
{
stick_y = 0;
}
pad_state.Stick.x = float(stick_x) / 128.0f;
pad_state.Stick.y = float(stick_y) / 128.0f;
float actual_elapsed_time( timer.GetElapsedSeconds() );
//
// Update input
//
if( p_current_option != nullptr )
{
p_current_option->Update( pad_state, actual_elapsed_time );
if(p_current_option->NeedsUpdateDisplay())
{
need_update_display = true;
}
}
//
// Refresh display
//
if(need_update_display)
{
printf( TERMINAL_CLEAR_SCREEN );
printf( TERMINAL_TOP_LEFT );
printf( "Dlist took %dms (%fHz) [%d/%d]\n", s32(elapsed_ms), framerate, instruction_limit, total_instruction_count );
printf( "\n" );
if( p_current_option != nullptr )
{
p_current_option->UpdateDisplay();
}
else
{
printf( "(HOME) -> Resume game\n" );
printf( "(START) -> Screen shot\n" );
printf( "(SELECT) -> Go to next frame\n" );
printf( "(R-TRIG) -> Dump DList & Textures\n" );
printf( "(L-TRIG) -> Scale Screen\n\n" );
u32 idx = 0;
for( DebugMenuOptionVector::const_iterator it = menu_options.begin(); it != menu_options.end(); ++it, idx++ )
{
bool selected( idx == highlighted_option );
CDebugMenuOption * p_option( *it );
printf( "%c%s\n", selected ? '*' : ' ', p_option->GetDescription() );
}
}
need_update_display = false;
printf( TERMINAL_SAVE_POS );
}
else
{
// Just update timing info
printf( TERMINAL_TOP_LEFT );
printf( TERMINAL_CLEAR_LINE );
printf( "Dlist took %dms (%fHz) [%d/%d]\n", s32(elapsed_ms), framerate, instruction_limit, total_instruction_count );
printf( TERMINAL_RESTORE_POS );
}
fflush( stdout );
//
// Input
//
if( p_current_option != nullptr )
{
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_SQUARE)
{
p_current_option = nullptr;
need_update_display = true;
}
}
}
else
{
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_UP)
{
if( highlighted_option > 0 )
{
highlighted_option--;
need_update_display = true;
}
}
if(pad_state.NewButtons & PSP_CTRL_DOWN)
{
if( highlighted_option < menu_options.size() - 1 )
{
highlighted_option++;
need_update_display = true;
}
}
if(pad_state.NewButtons & PSP_CTRL_CROSS)
{
p_current_option = menu_options[ highlighted_option ];
need_update_display = true;
}
}
}
if(pad_state.OldButtons != pad_state.NewButtons)
{
if(pad_state.NewButtons & PSP_CTRL_HOME)
{
menu_button_pressed = true;
}
if(pad_state.NewButtons & PSP_CTRL_START)
{
dump_next_screen = true;
}
if(pad_state.NewButtons & PSP_CTRL_LTRIGGER)
{
gGlobalPreferences.ViewportType = EViewportType( (gGlobalPreferences.ViewportType+1) % NUM_VIEWPORT_TYPES );
CGraphicsContext::Get()->ClearAllSurfaces();
}
if(pad_state.NewButtons & PSP_CTRL_RTRIGGER)
{
dump_texture_dlist = true;
}
if(pad_state.NewButtons & PSP_CTRL_SELECT)
{
gSingleStepFrames = true;
menu_button_pressed = true;
}
}
pad_state.OldButtons = pad_state.NewButtons;
}
//
// Clean up
//
for( DebugMenuOptionVector::const_iterator it = menu_options.begin(); it != menu_options.end(); ++it )
{
CDebugMenuOption * p_option( *it );
delete p_option;
}
}
#endif