Skip to content

Commit 2d06e07

Browse files
feat(profiling): Add frame image capturing to Tracy profiling (TheSuperHackers#2202)
1 parent dcdeca1 commit 2d06e07

2 files changed

Lines changed: 223 additions & 1 deletion

File tree

GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ class Render2DClass;
4545
class RTS3DScene;
4646
class RTS2DScene;
4747
class RTS3DInterfaceScene;
48-
48+
class TextureClass;
49+
class SurfaceClass;
50+
struct IDirect3DTexture8;
51+
struct IDirect3DSurface8;
4952

5053
//=============================================================================
5154
/** W3D implementation of the game display which is responsible for creating
@@ -79,6 +82,7 @@ class W3DDisplay : public Display
7982
virtual void enableClipping( Bool onoff ) { m_isClippedEnabled = onoff; }
8083

8184
virtual void step(); ///< Do one fixed time step
85+
8286
virtual void draw( void ); ///< redraw the entire display
8387

8488
/// @todo Replace these light management routines with a LightManager singleton
@@ -172,6 +176,21 @@ class W3DDisplay : public Display
172176
Int64 m_timerAtCumuFPSStart;
173177
#endif
174178

179+
#ifdef TRACY_ENABLE
180+
bool InitTracyCaptureImage();
181+
void ResetTracyCaptureImage();
182+
void TracyCaptureImage();
183+
TextureClass *m_tracyRenderTarget = nullptr;
184+
SurfaceClass *m_tracySurfaceClass = nullptr;
185+
IDirect3DTexture8 *m_tracyIntermediateTexture = nullptr;
186+
DWORD m_tracySwizzleShader = 0;
187+
bool m_tracyFrameCapturing = true;
188+
#else
189+
inline void InitTracyCaptureImage() {};
190+
inline void TracyCaptureImage() {};
191+
inline void ResetTracyCaptureImage() {};
192+
#endif
193+
175194
enum
176195
{
177196
FPS, ///< debug display frames per second

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static void drawFramerateBar(void);
3939
#include <windows.h>
4040
#include <io.h>
4141
#include <time.h>
42+
#include <d3dx8core.h>
4243

4344
// USER INCLUDES //////////////////////////////////////////////////////////////
4445
#include "Common/FramePacer.h"
@@ -109,6 +110,8 @@ static void drawFramerateBar(void);
109110

110111
#include "WinMain.h"
111112

113+
#include <rts/profile.h>
114+
112115

113116
// DEFINE AND ENUMS ///////////////////////////////////////////////////////////
114117

@@ -411,6 +414,7 @@ W3DDisplay::W3DDisplay()
411414
//=============================================================================
412415
W3DDisplay::~W3DDisplay()
413416
{
417+
ResetTracyCaptureImage();
414418

415419
// get rid of the debug display
416420
delete m_debugDisplay;
@@ -541,6 +545,8 @@ void W3DDisplay::setGamma(Real gamma, Real bright, Real contrast, Bool calibrate
541545
//=============================================================================
542546
Bool W3DDisplay::setDisplayMode( UnsignedInt xres, UnsignedInt yres, UnsignedInt bitdepth, Bool windowed )
543547
{
548+
ResetTracyCaptureImage();
549+
544550
if (WW3D_ERROR_OK == WW3D::Set_Device_Resolution(xres,yres,bitdepth,windowed,true))
545551
{
546552
Render2DClass::Set_Screen_Resolution(RectClass(0, 0, xres, yres));
@@ -1650,6 +1656,202 @@ void W3DDisplay::step()
16501656

16511657
//DECLARE_PERF_TIMER(BigAssRenderLoop)
16521658

1659+
#ifdef TRACY_ENABLE
1660+
bool W3DDisplay::InitTracyCaptureImage()
1661+
{
1662+
// allocate render target
1663+
m_tracyRenderTarget = DX8Wrapper::Create_Render_Target(TRACY_FRAMEIMAGE_SIZE, TRACY_FRAMEIMAGE_SIZE, WW3D_FORMAT_A8R8G8B8);
1664+
if (!m_tracyRenderTarget)
1665+
return false;
1666+
1667+
// allocate surface class
1668+
const Real aspectRatio = static_cast<float>(getHeight()) / static_cast<float>(getWidth());
1669+
unsigned int tracyImageHeight = MIN(
1670+
static_cast<unsigned int>(WWMath::Round(static_cast<float>(TRACY_FRAMEIMAGE_SIZE) * aspectRatio)),
1671+
TRACY_FRAMEIMAGE_SIZE);
1672+
m_tracySurfaceClass = NEW_REF(SurfaceClass, (TRACY_FRAMEIMAGE_SIZE, tracyImageHeight, WW3D_FORMAT_A8R8G8B8));
1673+
if (!m_tracySurfaceClass)
1674+
return false;
1675+
1676+
// allocate intermediate texture
1677+
SurfaceClass *backBuffer = DX8Wrapper::_Get_DX8_Back_Buffer();
1678+
if (!backBuffer)
1679+
return false;
1680+
1681+
IDirect3DSurface8 *backBufferSurface = backBuffer->Peek_D3D_Surface();
1682+
D3DSURFACE_DESC backBufferSurfaceDesc;
1683+
HRESULT hr = backBufferSurface->GetDesc(&backBufferSurfaceDesc);
1684+
if (FAILED(hr))
1685+
return false;
1686+
1687+
IDirect3DDevice8 *device = DX8Wrapper::_Get_D3D_Device8();
1688+
hr = device->CreateTexture(
1689+
backBufferSurfaceDesc.Width,
1690+
backBufferSurfaceDesc.Height,
1691+
1,
1692+
D3DUSAGE_RENDERTARGET,
1693+
backBufferSurfaceDesc.Format,
1694+
D3DPOOL_DEFAULT,
1695+
&m_tracyIntermediateTexture);
1696+
if (FAILED(hr))
1697+
return false;
1698+
1699+
// swizzle shader
1700+
// TheSuperHackers @todo In DX9 with ps2.0 this shader will be much simpler
1701+
ID3DXBuffer *compiledShader = nullptr;
1702+
static const char *shader =
1703+
"ps.1.4\n"
1704+
"texld r0, t0\n"
1705+
"mov r1.a, r0.r\n"
1706+
"mov r2.a, r0.g\n"
1707+
"mov r3.a, r0.b\n"
1708+
"mul r0.rgb, r3.a, c0\n"
1709+
"mad r0.rgb, r2.a, c1, r0\n"
1710+
"mad r0.rgb, r1.a, c2, r0\n";
1711+
hr = D3DXAssembleShader(shader, strlen(shader), 0, nullptr, &compiledShader, nullptr);
1712+
if (FAILED(hr))
1713+
return false;
1714+
1715+
hr = DX8Wrapper::_Get_D3D_Device8()->CreatePixelShader((DWORD*)compiledShader->GetBufferPointer(), &m_tracySwizzleShader);
1716+
compiledShader->Release();
1717+
if (FAILED(hr))
1718+
return false;
1719+
1720+
return true;
1721+
}
1722+
1723+
void W3DDisplay::ResetTracyCaptureImage()
1724+
{
1725+
if (m_tracySurfaceClass)
1726+
{
1727+
REF_PTR_RELEASE(m_tracySurfaceClass);
1728+
}
1729+
if (m_tracyIntermediateTexture)
1730+
{
1731+
m_tracyIntermediateTexture->Release();
1732+
m_tracyIntermediateTexture = nullptr;
1733+
}
1734+
if (m_tracyRenderTarget)
1735+
{
1736+
REF_PTR_RELEASE(m_tracyRenderTarget);
1737+
}
1738+
if (m_tracySwizzleShader)
1739+
{
1740+
DX8Wrapper::_Get_D3D_Device8()->DeletePixelShader(m_tracySwizzleShader);
1741+
m_tracySwizzleShader = 0;
1742+
}
1743+
}
1744+
1745+
void W3DDisplay::TracyCaptureImage()
1746+
{
1747+
if (!TracyIsConnected || !m_tracyFrameCapturing)
1748+
return;
1749+
1750+
if (!m_tracyRenderTarget)
1751+
{
1752+
if (!InitTracyCaptureImage()) {
1753+
DEBUG_LOG("Tracy frame capturing failed to initialize and will be disabled.");
1754+
m_tracyFrameCapturing = false;
1755+
ResetTracyCaptureImage();
1756+
return;
1757+
}
1758+
}
1759+
1760+
// copy the backbuffer to an intermediate texture on the GPU
1761+
// TheSuperHackers @todo In DX9 it should be possible to sample the backbuffer directly and simplify this
1762+
SurfaceClass *backBuffer = DX8Wrapper::_Get_DX8_Back_Buffer();
1763+
IDirect3DSurface8 *backBufferSurface = backBuffer->Peek_D3D_Surface();
1764+
D3DSURFACE_DESC backBufferSurfaceDesc;
1765+
backBufferSurface->GetDesc(&backBufferSurfaceDesc);
1766+
1767+
IDirect3DSurface8 *tracyBackbufferTextureSurface;
1768+
m_tracyIntermediateTexture->GetSurfaceLevel(0, &tracyBackbufferTextureSurface);
1769+
DX8Wrapper::_Copy_DX8_Rects(backBufferSurface, nullptr, 0,
1770+
tracyBackbufferTextureSurface, nullptr);
1771+
tracyBackbufferTextureSurface->Release();
1772+
tracyBackbufferTextureSurface = nullptr;
1773+
1774+
backBufferSurface->Release();
1775+
backBufferSurface = nullptr;
1776+
REF_PTR_RELEASE(backBuffer);
1777+
1778+
// set render target to a small surface
1779+
IDirect3DSurface8 *smallRenderTargetSurface = m_tracyRenderTarget->Get_D3D_Surface_Level();
1780+
DX8Wrapper::Set_Render_Target(smallRenderTargetSurface, false);
1781+
1782+
// set viewport
1783+
IDirect3DDevice8 *device = DX8Wrapper::_Get_D3D_Device8();
1784+
D3DVIEWPORT8 restoreViewport;
1785+
device->GetViewport(&restoreViewport);
1786+
1787+
SurfaceClass::SurfaceDescription smallRenderDesc;
1788+
m_tracySurfaceClass->Get_Description(smallRenderDesc);
1789+
1790+
D3DVIEWPORT8 viewport;
1791+
viewport.X = 0;
1792+
viewport.Y = 0;
1793+
viewport.Width = TRACY_FRAMEIMAGE_SIZE;
1794+
viewport.Height = smallRenderDesc.Height;
1795+
viewport.MinZ = 0.0f;
1796+
viewport.MaxZ = 1.0f;
1797+
DX8Wrapper::Set_Viewport(&viewport);
1798+
1799+
// bind shader to convert BGRA to RGBA to match the format that Tracy expects
1800+
DX8Wrapper::Set_Pixel_Shader(m_tracySwizzleShader);
1801+
// TheSuperHackers @todo In DX9 with ps2.0 we wont need these pixel shader constants
1802+
static const float kMaskR[4] = {1.0f, 0.0f, 0.0f, 0.0f};
1803+
static const float kMaskG[4] = {0.0f, 1.0f, 0.0f, 0.0f};
1804+
static const float kMaskB[4] = {0.0f, 0.0f, 1.0f, 0.0f};
1805+
device->SetPixelShaderConstant(0, kMaskR, 1);
1806+
device->SetPixelShaderConstant(1, kMaskG, 1);
1807+
device->SetPixelShaderConstant(2, kMaskB, 1);
1808+
1809+
// draw texture scaled-down onto a small surface
1810+
struct QuadVertex
1811+
{
1812+
float x, y, z, rhw;
1813+
DWORD color;
1814+
float u;
1815+
float v;
1816+
} vtx[4];
1817+
const float left = -0.5f;
1818+
const float top = -0.5f;
1819+
const float right = static_cast<float>(TRACY_FRAMEIMAGE_SIZE) - 0.5f;
1820+
const float bottom = static_cast<float>(smallRenderDesc.Height) - 0.5f;
1821+
vtx[0] = {right, bottom, 0.0f, 1.0f, 0xffffffff, 1.0f, 1.0f};
1822+
vtx[1] = {right, top, 0.0f, 1.0f, 0xffffffff, 1.0f, 0.0f};
1823+
vtx[2] = {left, bottom, 0.0f, 1.0f, 0xffffffff, 0.0f, 1.0f};
1824+
vtx[3] = {left, top, 0.0f, 1.0f, 0xffffffff, 0.0f, 0.0f};
1825+
DX8Wrapper::Set_DX8_Texture(0, m_tracyIntermediateTexture);
1826+
DX8Wrapper::Set_Vertex_Shader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
1827+
device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vtx, sizeof(QuadVertex));
1828+
DX8Wrapper::Set_Pixel_Shader(0);
1829+
DX8Wrapper::Set_DX8_Texture(0, nullptr);
1830+
DX8Wrapper::Set_Viewport(&restoreViewport);
1831+
DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *) nullptr);
1832+
1833+
// copy the small surface pixels from GPU to CPU
1834+
RECT srcRect = { 0, 0, TRACY_FRAMEIMAGE_SIZE, smallRenderDesc.Height };
1835+
POINT dstPoint = { 0, 0 };
1836+
DX8Wrapper::_Copy_DX8_Rects(
1837+
smallRenderTargetSurface,
1838+
&srcRect,
1839+
1,
1840+
m_tracySurfaceClass->Peek_D3D_Surface(),
1841+
&dstPoint);
1842+
smallRenderTargetSurface->Release();
1843+
1844+
// send pixels to tracy
1845+
int pitch = 0;
1846+
void *bits = m_tracySurfaceClass->Lock(&pitch);
1847+
if (bits)
1848+
{
1849+
FrameImage(bits, TRACY_FRAMEIMAGE_SIZE, smallRenderDesc.Height, 0, false);
1850+
m_tracySurfaceClass->Unlock();
1851+
}
1852+
}
1853+
#endif
1854+
16531855
// W3DDisplay::draw ===========================================================
16541856
/** Draw the entire W3D Display */
16551857
//=============================================================================
@@ -1935,6 +2137,7 @@ void W3DDisplay::draw( void )
19352137
TheGraphDraw->render();
19362138
TheGraphDraw->clear();
19372139
#endif
2140+
TracyCaptureImage();
19382141
// render is all done!
19392142
WW3D::End_Render();
19402143
}

0 commit comments

Comments
 (0)