Eliminate int->float conversions
If
the data being converted is semi-static, like a frame time quantum or the width
of a screen, the data can be duplicated and functions that read the integer
version or the floating-point version can fetch it without penalty.
Example:
typedef struct ScreenSize_t {
int m_iWidth;
int m_iHeight;
float m_fWidth;
float m_fHeight;
inline Void SetHeight(int iWidth) {
m_iWidth = iWidth;
m_fWidth = static_cast(iWidth);
};
} ScreenSize_t;
So
functions that need an integer value for processing load from the "i" form of
the members, while functions that require a floating-point input fetch from the
"f"
form. These values could be updated by an inline function that updates both the
integer and floating-point versions at the same time.
Another
form of integer to floating point conversion is where an iterator is used and
converted. Since floating point compares have their own set of issues, they too
need to be minimized. In this example, the angle is generated with each loop by
converting it from an integer to a floating point number, causing a
Load-Hit-Store
VOIDDebugDraw::DrawRing( const XMFLOAT3 &Origin,
const XMFLOAT3 &MajorAxis, const XMFLOAT3 &MinorAxis, D3DCOLOR Color )
{
static const DWORD dwRingSegments = 32;
MeshVertexP verts[ dwRingSegments + 1 ];
XMVECTOR vOrigin = XMLoadFloat3( &Origin );
XMVECTOR vMajor = XMLoadFloat3( &MajorAxis );
XMVECTOR vMinor = XMLoadFloat3( &MinorAxis );
FLOAT fAngleDelta = XM_2PI / (float)dwRingSegments;
for( DWORD i = 0; i<dwRingSegments; i++)
{
FLOAT fAngle = (FLOAT)i * fAngleDelta;
XMVECTOR Pos;
Pos = XMVectorAdd( vOrigin, XMVectorScale( vMajor, cosf( fAngle ) ) );
Pos = XMVectorAdd( Pos, XMVectorScale( vMinor, sinf( fAngle ) ) );
XMStoreFloat3( (XMFLOAT3*)&verts[i], Pos );
}
verts[ dwRingSegments ] = verts[0];
SimpleShaders::SetDeclPos();
SimpleShaders::BeginShader_Transformed_ConstantColor
( g_matViewProjection, Color );
g_pd3dDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, dwRingSegments, (const
VOID*)verts, sizeof( MeshVertexP ) );
SimpleShaders::EndShader();
}
With
three lines changed, the Load-Hit-Store is removed and the functionality is
intact.
VOID DebugDraw::DrawRing( const XMFLOAT3 &Origin,
const XMFLOAT3 &MajorAxis, const XMFLOAT3 &MinorAxis, D3DCOLOR Color )
{
static const DWORD dwRingSegments = 32;
MeshVertexP verts[ dwRingSegments + 1 ];
XMVECTOR vOrigin = XMLoadFloat3( &Origin );
XMVECTOR vMajor = XMLoadFloat3( &MajorAxis );
XMVECTOR vMinor = XMLoadFloat3( &MinorAxis );
FLOAT fAngleDelta = XM_2PI / (float)dwRingSegments;
FLOAT fi = 0.0f; // Added a copy of i as a float
for( DWORD i = 0; i<dwRingSegments; i++, fi+=1.0f ) // Inc fi
{
FLOAT fAngle = fi * fAngleDelta; // NO int to float conversion
XMVECTOR Pos;
Pos = XMVectorAdd( vOrigin, XMVectorScale( vMajor, cosf( fAngle ) ) );
Pos = XMVectorAdd( Pos, XMVectorScale( vMinor, sinf( fAngle ) ) );
XMStoreFloat3( (XMFLOAT3*)&verts[i], Pos );
}
verts[ dwRingSegments ] = verts[0];
SimpleShaders::SetDeclPos();
SimpleShaders::BeginShader_Transformed_ConstantColor
( g_matViewProjection, Color );
g_pd3dDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, dwRingSegments, (const
VOID*)verts, sizeof( MeshVertexP ) );
SimpleShaders::EndShader();
}
Faster, 360, Code! Code!
It
takes only a little discipline to write clean code, but it's also easy to
create code that can inadvertently introduce performance bottlenecks. Using
Microsoft tools like PIX will help you track down some of these, but the best
way to avoid bottlenecks, is to be aware of how they can exist so that they aren't
written into the code in the first place.
A
good understanding of the underlying hardware is not crucial to modern game
programming from a high level. However, with a solid foundation of how CPUs
work as well as how they interact with the memory subsystems, programmers can
write software that maximizes performance.
|