Skip to content

Commit bd376c4

Browse files
committed
perf(radar): Reduce cost of radar pixel draw in PartitionManager::refreshShroudForLocalPlayer by 96% (#2072)
1 parent 3d12b39 commit bd376c4

File tree

7 files changed

+137
-41
lines changed

7 files changed

+137
-41
lines changed

Core/GameEngine/Include/Common/Radar.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,11 @@ class Radar : public Snapshot,
218218
/// empty the entire shroud
219219
virtual void clearShroud() = 0;
220220

221-
/// set the shroud level at shroud cell x,y
222-
virtual void setShroudLevel( Int x, Int y, CellShroudStatus setting ) = 0;
221+
/// TheSuperHackers @performance xezon 20/12/2025 Provides beginSetShroudLevel and endSetShroudLevel to improve performance.
222+
/// Calling setShroudLevel many times is very expensive because it will lock a render resource on every call.
223+
virtual void setShroudLevel( Int x, Int y, CellShroudStatus setting ) = 0; ///< set the shroud level at shroud cell x,y
224+
virtual void beginSetShroudLevel() {} ///< call this once before multiple calls to setShroudLevel for better performance
225+
virtual void endSetShroudLevel() {} ///< call this once after beginSetShroudLevel and setShroudLevel
223226

224227
protected:
225228

Core/GameEngineDevice/Include/W3DDevice/Common/W3DRadar.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
3939
class TextureClass;
40+
class SurfaceClass;
4041
class TerrainLogic;
4142

4243
// PROTOTYPES /////////////////////////////////////////////////////////////////////////////////////
@@ -63,7 +64,9 @@ class W3DRadar : public Radar
6364
virtual void draw( Int pixelX, Int pixelY, Int width, Int height ); ///< draw the radar
6465

6566
virtual void clearShroud();
66-
virtual void setShroudLevel(Int x, Int y, CellShroudStatus setting);
67+
virtual void setShroudLevel(Int x, Int y, CellShroudStatus setting); ///< set the shroud level at shroud cell x,y
68+
virtual void beginSetShroudLevel(); ///< call this once before multiple calls to setShroudLevel for better performance
69+
virtual void endSetShroudLevel(); ///< call this once after beginSetShroudLevel and setShroudLevel
6770

6871
virtual void refreshTerrain( TerrainLogic *terrain );
6972
virtual void refreshObjects();
@@ -104,6 +107,10 @@ class W3DRadar : public Radar
104107
WW3DFormat m_shroudTextureFormat; ///< format to use for shroud texture
105108
Image *m_shroudImage; ///< shroud image abstraction for drawing
106109
TextureClass *m_shroudTexture; ///< shroud texture
110+
SurfaceClass *m_shroudSurface; ///< surface to shroud texture
111+
void *m_shroudSurfaceBits; ///< shroud surface bits
112+
int m_shroudSurfacePitch; ///< shroud surface pitch
113+
UnsignedInt m_shroudSurfacePixelSize; ///< shroud surface pixel size
107114

108115
Int m_textureWidth; ///< width for all radar textures
109116
Int m_textureHeight; ///< height for all radar textures

Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "W3DDevice/GameClient/W3DShroud.h"
5555
#include "WW3D2/texture.h"
5656
#include "WW3D2/dx8caps.h"
57+
#include "WWMath/vector2i.h"
5758

5859

5960

@@ -132,7 +133,7 @@ void W3DRadar::initializeTextureFormats( void )
132133
}
133134

134135
//-------------------------------------------------------------------------------------------------
135-
/** Delete resources used specifically in this W3D radar implemetation */
136+
/** Delete resources used specifically in this W3D radar implementation */
136137
//-------------------------------------------------------------------------------------------------
137138
void W3DRadar::deleteResources( void )
138139
{
@@ -167,6 +168,9 @@ void W3DRadar::deleteResources( void )
167168
deleteInstance(m_shroudImage);
168169
m_shroudImage = nullptr;
169170

171+
DEBUG_ASSERTCRASH(m_shroudSurface == NULL, ("W3DRadar::deleteResources: m_shroudSurface is expected NULL"));
172+
DEBUG_ASSERTCRASH(m_shroudSurfaceBits == NULL, ("W3DRadar::deleteResources: m_shroudSurfaceBits is expected NULL"));
173+
170174
}
171175

172176
//-------------------------------------------------------------------------------------------------
@@ -844,6 +848,10 @@ W3DRadar::W3DRadar( void )
844848
m_shroudTextureFormat = WW3D_FORMAT_UNKNOWN;
845849
m_shroudImage = nullptr;
846850
m_shroudTexture = nullptr;
851+
m_shroudSurface = nullptr;
852+
m_shroudSurfaceBits = nullptr;
853+
m_shroudSurfacePitch = 0;
854+
m_shroudSurfacePixelSize = 0;
847855

848856
m_textureWidth = RADAR_CELL_WIDTH;
849857
m_textureHeight = RADAR_CELL_HEIGHT;
@@ -1301,9 +1309,6 @@ void W3DRadar::setShroudLevel(Int shroudX, Int shroudY, CellShroudStatus setting
13011309
if (!shroud)
13021310
return;
13031311

1304-
SurfaceClass* surface = m_shroudTexture->Get_Surface_Level();
1305-
DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for Shroud texture") );
1306-
13071312
Int mapMinX = shroudX * shroud->getCellWidth();
13081313
Int mapMinY = shroudY * shroud->getCellHeight();
13091314
Int mapMaxX = (shroudX+1) * shroud->getCellWidth();
@@ -1315,21 +1320,19 @@ void W3DRadar::setShroudLevel(Int shroudX, Int shroudY, CellShroudStatus setting
13151320
worldPoint.x = mapMinX;
13161321
worldPoint.y = mapMinY;
13171322
worldToRadar( &worldPoint, &radarPoint );
1318-
Int radarMinX = radarPoint.x;
1319-
Int radarMinY = radarPoint.y;
1323+
const Int radarMinX = radarPoint.x;
1324+
const Int radarMinY = radarPoint.y;
13201325

13211326
worldPoint.x = mapMaxX;
13221327
worldPoint.y = mapMaxY;
13231328
worldToRadar( &worldPoint, &radarPoint );
1324-
Int radarMaxX = radarPoint.x;
1325-
Int radarMaxY = radarPoint.y;
1329+
const Int radarMaxX = radarPoint.x;
1330+
const Int radarMaxY = radarPoint.y;
13261331

1327-
/*
1328-
Int radarMinX = REAL_TO_INT_FLOOR(mapMinX / getXSample());
1329-
Int radarMinY = REAL_TO_INT_FLOOR(mapMinY / getYSample());
1330-
Int radarMaxX = REAL_TO_INT_CEIL(mapMaxX / getXSample());
1331-
Int radarMaxY = REAL_TO_INT_CEIL(mapMaxY / getYSample());
1332-
*/
1332+
// Int radarMinX = REAL_TO_INT_FLOOR(mapMinX / getXSample());
1333+
// Int radarMinY = REAL_TO_INT_FLOOR(mapMinY / getYSample());
1334+
// Int radarMaxX = REAL_TO_INT_CEIL(mapMaxX / getXSample());
1335+
// Int radarMaxY = REAL_TO_INT_CEIL(mapMaxY / getYSample());
13331336

13341337
/// @todo srj -- this really needs to smooth the display!
13351338

@@ -1343,15 +1346,69 @@ void W3DRadar::setShroudLevel(Int shroudX, Int shroudY, CellShroudStatus setting
13431346
else
13441347
alpha = 0;
13451348

1346-
for( Int y = radarMinY; y <= radarMaxY; y++ )
1349+
if (m_shroudSurface == NULL)
13471350
{
1348-
for( Int x = radarMinX; x <= radarMaxX; x++ )
1351+
// This is expensive.
1352+
SurfaceClass* surface = m_shroudTexture->Get_Surface_Level();
1353+
DEBUG_ASSERTCRASH( surface, ("W3DRadar: Can't get surface for Shroud texture") );
1354+
1355+
for( Int y = radarMinY; y <= radarMaxY; y++ )
13491356
{
1350-
if( legalRadarPoint( x, y ) )
1357+
for( Int x = radarMinX; x <= radarMaxX; x++ )
1358+
{
13511359
surface->DrawPixel( x, y, GameMakeColor( 0, 0, 0, alpha ) );
1360+
}
13521361
}
1362+
REF_PTR_RELEASE(surface);
13531363
}
1354-
REF_PTR_RELEASE(surface);
1364+
else
1365+
{
1366+
// This is cheap.
1367+
DEBUG_ASSERTCRASH(m_shroudSurfaceBits != NULL, ("W3DRadar::setShroudLevel: m_shroudSurfaceBits is not expected NULL"));
1368+
DEBUG_ASSERTCRASH(m_shroudSurfacePixelSize != 0, ("W3DRadar::setShroudLevel: m_shroudSurfacePixelSize is not expected 0"));
1369+
const Color color = GameMakeColor( 0, 0, 0, alpha );
1370+
1371+
for( Int y = radarMinY; y <= radarMaxY; ++y )
1372+
{
1373+
UnsignedByte* row = static_cast<UnsignedByte*>(m_shroudSurfaceBits) + y * m_shroudSurfacePitch;
1374+
for( Int x = radarMinX; x <= radarMaxX; ++x )
1375+
{
1376+
UnsignedByte* column = row + x * m_shroudSurfacePixelSize;
1377+
1378+
switch (m_shroudSurfacePixelSize)
1379+
{
1380+
case 1: *column = (UnsignedByte)(color & 0xFF); break;
1381+
case 2: *reinterpret_cast<UnsignedShort*>(column) = (UnsignedShort)(color & 0xFFFF); break;
1382+
case 4: *reinterpret_cast<UnsignedInt*>(column) = (UnsignedInt)color; break;
1383+
}
1384+
}
1385+
}
1386+
}
1387+
}
1388+
1389+
void W3DRadar::beginSetShroudLevel()
1390+
{
1391+
DEBUG_ASSERTCRASH( m_shroudSurface == NULL, ("W3DRadar::beginSetShroudLevel: m_shroudSurface is expected NULL") );
1392+
m_shroudSurface = m_shroudTexture->Get_Surface_Level();
1393+
DEBUG_ASSERTCRASH( m_shroudSurface != NULL, ("W3DRadar::beginSetShroudLevel: Can't get surface for Shroud texture") );
1394+
1395+
SurfaceClass::SurfaceDescription sd;
1396+
m_shroudSurface->Get_Description(sd);
1397+
m_shroudSurfaceBits = m_shroudSurface->Lock(&m_shroudSurfacePitch);
1398+
m_shroudSurfacePixelSize = Get_Bytes_Per_Pixel(sd.Format);
1399+
}
1400+
1401+
void W3DRadar::endSetShroudLevel()
1402+
{
1403+
DEBUG_ASSERTCRASH( m_shroudSurface != NULL, ("W3DRadar::endSetShroudLevel: m_shroudSurface is not expected NULL") );
1404+
if (m_shroudSurfaceBits != NULL)
1405+
{
1406+
m_shroudSurface->Unlock();
1407+
m_shroudSurfaceBits = NULL;
1408+
m_shroudSurfacePitch = 0;
1409+
m_shroudSurfacePixelSize = 0;
1410+
}
1411+
REF_PTR_RELEASE(m_shroudSurface);
13551412
}
13561413

13571414
//-------------------------------------------------------------------------------------------------

Core/Libraries/Source/WWVegas/WW3D2/surfaceclass.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ void SurfaceClass::Get_Description(SurfaceDescription &surface_desc)
205205
surface_desc.Width = d3d_desc.Width;
206206
}
207207

208-
void * SurfaceClass::Lock(int * pitch)
208+
void *SurfaceClass::Lock(int *pitch)
209209
{
210210
D3DLOCKED_RECT lock_rect;
211211
::ZeroMemory(&lock_rect, sizeof(D3DLOCKED_RECT));
@@ -214,6 +214,22 @@ void * SurfaceClass::Lock(int * pitch)
214214
return (void *)lock_rect.pBits;
215215
}
216216

217+
void *SurfaceClass::Lock(int *pitch, const Vector2i &min, const Vector2i &max)
218+
{
219+
D3DLOCKED_RECT lock_rect;
220+
::ZeroMemory(&lock_rect, sizeof(D3DLOCKED_RECT));
221+
222+
RECT rect;
223+
rect.left = min.I;
224+
rect.top = min.J;
225+
rect.right = max.I;
226+
rect.bottom = max.J;
227+
DX8_ErrorCode(D3DSurface->LockRect(&lock_rect, &rect, 0));
228+
229+
*pitch = lock_rect.Pitch;
230+
return (void *)lock_rect.pBits;
231+
}
232+
217233
void SurfaceClass::Unlock(void)
218234
{
219235
DX8_ErrorCode(D3DSurface->UnlockRect());
@@ -312,7 +328,7 @@ void SurfaceClass::Copy(const unsigned char *other)
312328
* HISTORY: *
313329
* 5/2/2001 hy : Created. *
314330
*=============================================================================================*/
315-
void SurfaceClass::Copy(Vector2i &min,Vector2i &max, const unsigned char *other)
331+
void SurfaceClass::Copy(const Vector2i &min, const Vector2i &max, const unsigned char *other)
316332
{
317333
SurfaceDescription sd;
318334
Get_Description(sd);

Core/Libraries/Source/WWVegas/WW3D2/surfaceclass.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ class SurfaceClass : public W3DMPO, public RefCountClass
7979
void Get_Description(SurfaceDescription &surface_desc);
8080

8181
// Lock / unlock the surface
82-
void * Lock(int * pitch);
82+
void *Lock(int *pitch);
83+
void *Lock(int *pitch, const Vector2i &min, const Vector2i &max);
8384
void Unlock(void);
8485

8586
// HY -- The following functions are support functions for font3d
@@ -97,7 +98,7 @@ class SurfaceClass : public W3DMPO, public RefCountClass
9798
void Copy(const unsigned char *other);
9899

99100
// support for copying from a byte array
100-
void Copy(Vector2i &min,Vector2i &max, const unsigned char *other);
101+
void Copy(const Vector2i &min, const Vector2i &max, const unsigned char *other);
101102

102103
// copies the contents of one surface to another, stretches
103104
void Stretch_Copy(

Generals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,15 +3047,21 @@ void PartitionManager::refreshShroudForLocalPlayer()
30473047
TheDisplay->clearShroud();
30483048
TheRadar->clearShroud();
30493049

3050-
const Int playerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex();
3051-
for (int i = 0; i < m_totalCellCount; ++i)
3050+
if (m_totalCellCount != 0)
30523051
{
3053-
Int x = m_cells[i].getCellX();
3054-
Int y = m_cells[i].getCellY();
3055-
CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
3056-
TheDisplay->setShroudLevel(x, y, status);
3057-
TheRadar->setShroudLevel(x, y, status);
3058-
m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
3052+
const Int playerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex();
3053+
TheRadar->beginSetShroudLevel();
3054+
3055+
for (int i = 0; i < m_totalCellCount; ++i)
3056+
{
3057+
const Int x = m_cells[i].getCellX();
3058+
const Int y = m_cells[i].getCellY();
3059+
const CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
3060+
TheDisplay->setShroudLevel(x, y, status);
3061+
TheRadar->setShroudLevel(x, y, status);
3062+
m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
3063+
}
3064+
TheRadar->endSetShroudLevel();
30593065
}
30603066
}
30613067

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,15 +3054,21 @@ void PartitionManager::refreshShroudForLocalPlayer()
30543054
TheDisplay->clearShroud();
30553055
TheRadar->clearShroud();
30563056

3057-
const Int playerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex();
3058-
for (int i = 0; i < m_totalCellCount; ++i)
3057+
if (m_totalCellCount != 0)
30593058
{
3060-
Int x = m_cells[i].getCellX();
3061-
Int y = m_cells[i].getCellY();
3062-
CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
3063-
TheDisplay->setShroudLevel(x, y, status);
3064-
TheRadar->setShroudLevel(x, y, status);
3065-
m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
3059+
const Int playerIndex = rts::getObservedOrLocalPlayer()->getPlayerIndex();
3060+
TheRadar->beginSetShroudLevel();
3061+
3062+
for (int i = 0; i < m_totalCellCount; ++i)
3063+
{
3064+
const Int x = m_cells[i].getCellX();
3065+
const Int y = m_cells[i].getCellY();
3066+
const CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
3067+
TheDisplay->setShroudLevel(x, y, status);
3068+
TheRadar->setShroudLevel(x, y, status);
3069+
m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
3070+
}
3071+
TheRadar->endSetShroudLevel();
30663072
}
30673073
}
30683074

0 commit comments

Comments
 (0)