Sign in to follow this  
Haytil

[SOLVED] ID3DXFont flickers on release, fine on debug

Recommended Posts

Hello, I am having a problem when running my program in release build versus running the debug build. I am using MSVC++ 2005 Express Edition and DirectX 9.0. When I run the debug version, everything builds and works fine. When I run the release version, everything builds fine, but some of the functionality fails. Specifically, my ID3DXFont routine fails to draw the text I have specified in the release version - it always flickers in and out for a half second at the beginning of my run. Then, in the majority (~80%) of cases, the text simply fails to appear at all after that - however, the rest of the time (~20% of cases), the text will stop flickering and render solidly without issue. The debug version has no flickering or vanishing issues. I've tried commenting out ALL of my rendering code, except that needed to clear the screen, begin and end font drawing, etc. The problem persists. I don't believe any of my devices or DirectX objects, font-related or not, are failing - I throw an exception whenever I find FAILED(function call) occurs. Any advice? EDIT: Code is below, the problem's been found and solved (see below for solution). Thanks guys! [Edited by - Gauvir_Mucca on February 25, 2010 7:12:28 PM]

Share this post


Link to post
Share on other sites
This to me actually sounds like a rendering loop problem. If you make un-ordered or multiple calls to the device begin and end scene ( also including clear and present ) functions this tends to happen.

Which is impossible to tell without seeing any code.

Regards

Chad

Share this post


Link to post
Share on other sites
Ok, I've tried to learn more about this behavior as well as get some code to share with you. First let me share some more of the symptoms I've discovered:

-If all I attempt to do is clear the screen and then draw text, everything works fine, in both release and debug versions.

-Adding code to the rendering loop often makes the font flicker and then disappear entirely in the release version. In the debug version it always works.

Whether the additional code makes the font drawing flicker and fail or not seems entirely arbitrary. Rendering-related code (drawing sprites, locking and unlocking the back buffer, plotting pixels, drawing lines from a vertex buffer, etc.) sometimes causes the font-rendering code to fail. Sometimes it doesn't.

Frustratingly, rendering-related code that is present and not commented out, but NOT called, will sometimes make the font-rendering fail. For instance, if all I do is attempt to draw the font and then add any lines to the vertex buffer that are visible, the font-rendering will fail - even when no lines are visible (and thus the code to add lines to the vertex buffer is not called because it fails the visibility "if" check)!

This is entirely arbitrary and does not seem logical to me. If certain lines of code aren't called, their presence in the rendering loop should make no difference!

-When one of two programs (or both) are running in the background on the computer, the font rendering works to some degree in the release version. One will cause it to flicker (rather than not drawing at all), and the other will cause a section of the text to draw (rather than not drawing at all), until I press a key on the keyboard, at which point the font rendering works fine.

(What do I mean by "a section of the text?" If you imagine a rectangle encapsulating the text, then split the rectangle into columns, then the first column is drawn, but the rest is not. Essentially, the first 4.5 characters of each line is visible. When I press a key on the keyboard, the rest of characters begin to be drawn as well.)

The programs are "aim.exe" and "quickset.exe." These programs both run in the background and track keyboard hits - somehow, that interaction is prompting DirectX to flicker in and out of view in the first case, and draw entirely in the second case (once a key on the keyboard has been struck). I discovered this by noticing from my MSVC output log that my program was loading in .dlls from these two programs.



I think this last point is interesting, but not very helpful - let's try and solve the first problem. If we clear that up, I'm sure the last problem will be cleared up as well. I agree with the above poster that something probably isn't being initialized correctly.

Let me now share some code with you.

Here is the code for setting up DirectX and font rendering. Please tell me if something is missing:

CGraphicsSystem::Init()
{
pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);

if (pD3D9 == NULL)
{
throw Exception("Could not create Direct3D9 Object.", __FILE__, __LINE__);
}

// get the display mode
D3DDISPLAYMODE d3ddm;
pD3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

// set the presentation parameters
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferWidth = m_Screen_Width;
d3dpp.BackBufferHeight = m_Screen_Height;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = !m_Fullscreen;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; // for frame rate detection

DWORD behavior_flags;

if (m_Maintain_Double_Precision)
{
behavior_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE;
}
else
{
behavior_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}

if (FAILED(pD3D9->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, m_HWND,
behavior_flags,
&d3dpp, &pD3DDevice9)))
{
throw Exception("Could not create a Direct3D9 device.", __FILE__, __LINE__);
}

// For checking capabilities
D3DCAPS9 caps;
pD3DDevice9->GetDeviceCaps(&caps);

if(FAILED(D3DXCreateSprite(pD3DDevice9, &pD3DXSprite)))
{
throw Exception("Could not create a Direct3D9 Sprite Object.", __FILE__, __LINE__);
}

if(pD3DXSprite == NULL)
{
throw Exception("A valid Direct3D9 Sprite Object was not created.", __FILE__, __LINE__);
}

// Get the back buffer for pixel plotting
if(FAILED(pD3DDevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pD3DSurface9)))
{
throw Exception("Could not get the back buffer of the Direct3D9 device.", __FILE__,
__LINE__);
}

// Create the font
LOGFONT log_font = {16, //height
0, //width;
0, // lfEscapement;
0, //lfOrientation;
FW_BOLD, // lfWeight;
FALSE, // lfItalic;
FALSE, // lfUnderline;
FALSE, // lfStrikeOut;
DEFAULT_CHARSET, // lfCharSet;
OUT_DEFAULT_PRECIS, //lfOutPrecision;
CLIP_DEFAULT_PRECIS, // lfClipPrecision;
ANTIALIASED_QUALITY,// lfQuality;
DEFAULT_PITCH,// lfPitchAndFamily;
"Arial"// lfFaceName[LF_FACESIZE];
};

if(FAILED(D3DXCreateFontIndirect(pD3DDevice9, &log_font, &pD3DFont)))
{
throw Exception("Could not create a Direct3D9 Font.", __FILE__, __LINE__);
}

if(FAILED(D3DXCreateLine(pD3DDevice9, &pD3DXLine)))
{
throw Exception("Could not create a Direct3D9 Line object.", __FILE__, __LINE__);
}

pD3DXLine->SetWidth(1.0f);
pD3DXLine->SetAntialias(TRUE);

if(FAILED(pD3DDevice9->CreateVertexBuffer(2 * MAX_NUM_LINES * sizeof(LINEVERTEX),
0, D3DFVF_LINEVERTEX, D3DPOOL_DEFAULT,
&pLVB, NULL)))
{
throw Exception("Could not create a Line Vertex Buffer.", __FILE__, __LINE__);
}

m_Line_Vertex_Buffer = new LINEVERTEX[2 * MAX_NUM_LINES];

for (int i = 0; i < 2 * MAX_NUM_LINES; i++)
{
m_Line_Vertex_Buffer[i].m_rhw = 1.0f;
m_Line_Vertex_Buffer[i].m_z = 0;
}

m_Num_Lines_Buffer = 0;

for (int i = 0; i < MAX_NUM_VERTEX_BUFFERS; i++)
{
if (m_Vertex_Format[i] != 0)
{
if(FAILED(pD3DDevice9->CreateVertexBuffer(m_Buffer_Size[i] * m_Vertex_Size[i],
0, m_Vertex_Format[i], D3DPOOL_DEFAULT,
&pTVB[i], NULL)))
{
char buffer[160];
sprintf(buffer, "Could not recreate vertex buffer of index %d. Buffer size was %d, Vertex size was %d, Vertex Format was %d",
i, m_Buffer_Size[i], m_Vertex_Size[i], m_Vertex_Format[i]);
throw Exception(buffer, __FILE__, __LINE__);
}
}
}

pRect = new D3DLOCKED_RECT;
}



Here's a listing of the variables used by my GraphicsSystem class:


LPDIRECT3D9 pD3D9;
LPDIRECT3DDEVICE9 pD3DDevice9;
LPDIRECT3DSURFACE9 pD3DSurface9;
LPD3DXSPRITE pD3DXSprite;
LPD3DXLINE pD3DXLine;
LPD3DXFONT pD3DFont;

LPDIRECT3DVERTEXBUFFER9 pLVB; // Buffer to hold line vertices
int m_Num_Lines_Buffer;
LINEVERTEX* m_Line_Vertex_Buffer;

D3DLOCKED_RECT* pRect;

int m_Pitch;
DWORD* pBack_Buffer;
RECT Font_Rect;

int m_Screen_Height;
int m_Screen_Width;

bool m_Fullscreen;
bool m_Maintain_Double_Precision;

HWND m_HWND;

bool m_Rendering;
bool m_Sprite_Drawing;
bool m_Font_Drawing;
bool m_Pixel_Drawing;
bool m_Line_Drawing;




Here are functions related to my render loop. This example fails:


void CInGameGameState::Render_Active()
{
// THIS IS MY RENDER LOOP
Graphics_System->Clear_Screen(0, 0, 0);

Graphics_System->Begin_Rendering();

// IF THE FOLLOWING LINE IS COMMENTED OUT, FONT RENDERING WORKS
// IF NOT, FONT RENDERING FAILS
// m_World->Render_Background_Stars();

Graphics_System->Begin_Font_Drawing();

Graphics_System->Draw_Text("test",0,600,D3D_Red);

Graphics_System->End_Font_Drawing();

Graphics_System->End_Rendering();

Graphics_System->Render_Screen();
}



Here are the functions called in the above code.

void CGraphicsSystem::Clear_Screen(BYTE i_red, BYTE i_green, BYTE i_blue)
{
if(FAILED(pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(i_red, i_green, i_blue), 1.0f, 0)))
{
char buffer[256];
sprintf(buffer, "Could not clear the screen of the Direct3D9 device. Color was R = %d, G = %d, B = %d.",
i_red, i_green, i_blue);
throw Exception(buffer, __FILE__, __LINE__);
}
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Begin_Rendering()
{
if(FAILED(pD3DDevice9->BeginScene()))
{
throw Exception("Could not begin scene with the Direct3D9 device.", __FILE__,
__LINE__);
}

m_Rendering = true;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::End_Rendering()
{
if(FAILED(pD3DDevice9->EndScene()))
{
throw Exception("Could not end scene with the Direct3D9 device.", __FILE__,
__LINE__);
}

m_Rendering = false;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Begin_Font_Drawing()
{
if (m_Rendering == false)
{
Begin_Rendering();
}

if(FAILED(pD3DFont->Begin()))
{
throw Exception("Could not begin drawing with the Direct3D9 Font object.",
__FILE__, __LINE__);
}

m_Font_Drawing = true;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::End_Font_Drawing()
{
if(FAILED(pD3DFont->End()))
{
throw Exception("Could not end drawing with the Direct3D9 Font object.",
__FILE__, __LINE__);
}

m_Font_Drawing = false;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Draw_Text(char* i_buffer, int i_x, int i_y, DWORD i_color)
{
if (m_Font_Drawing == false)
{
Begin_Font_Drawing();
}

RECT temp_rect;

temp_rect.left = i_x;
temp_rect.top = i_y;

if(FAILED(pD3DFont->DrawText(i_buffer, -1, &temp_rect, DT_LEFT, i_color)))
{
char buffer[512];
sprintf(buffer, "Could not draw text using the Direct3D9 Font object, at x = %d, y = %d. Text was: %s.",
i_x, i_y, i_buffer);
throw Exception(buffer, __FILE__, __LINE__);
}
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Render_Screen()
{
if (m_Pixel_Drawing == true)
{
End_Pixel_Drawing();
}

if (m_Sprite_Drawing == true)
{
End_Sprite_Drawing();
}

if (m_Font_Drawing == true)
{
End_Font_Drawing();
}

if (m_Line_Drawing == true)
{
End_Single_Line_Drawing();
}

if (m_Rendering == true)
{
End_Rendering();
}


if(FAILED(pD3DDevice9->Present(NULL, NULL, NULL, NULL)))
{
m_Failed_To_Present++;

if (m_Failed_To_Present > PRESENT_FAILURE_TOLERANCE)
{
throw Exception("Could not present the scene with the Direct3D9 device.",
__FILE__, __LINE__);
}
}
else
{
m_Failed_To_Present = 0;
}
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Begin_Pixel_Drawing()
{
Lock_Back_Buffer();

m_Pixel_Drawing = true;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::End_Pixel_Drawing()
{
Unlock_Back_Buffer();

m_Pixel_Drawing = false;
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Lock_Back_Buffer()
{
if(FAILED(pD3DSurface9->LockRect(pRect, NULL, 0)))
{
throw Exception("Could not lock back buffer surface.", __FILE__, __LINE__);
}

m_Pitch = pRect->Pitch / 4;
pBack_Buffer = (DWORD*)(pRect->pBits);
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Unlock_Back_Buffer()
{
if(FAILED(pD3DSurface9->UnlockRect()))
{
throw Exception("Could not unlock back buffer surface.", __FILE__, __LINE__);
}
}

///////////////////////////////////////////////////////////

void CGraphicsSystem::Draw_Pixel(int i_x, int i_y, DWORD* i_color)
{
if (m_Pixel_Drawing == false)
{
Begin_Pixel_Drawing();
}

if ((i_x >= 0) && (i_x < m_Screen_Width) && (i_y >= 0)
&& (i_y < m_Screen_Height))
{
pBack_Buffer[i_x + m_Pitch * i_y] = *i_color;
}
else
if ((i_x < 0) || (i_x >= m_Screen_Width))
{
throw InvalidArgumentException(i_x, "Attempting to draw pixel at invalid X value",
__FILE__, __LINE__);
}
else
if ((i_y < 0) || (i_y >= m_Screen_Height))
{
throw InvalidArgumentException(i_y, "Attempting to draw pixel at invalid Y value",
__FILE__, __LINE__);
}
}

///////////////////////////////////////////////////////////

void CWorld::Render_Background_Stars()
{
DWORD color = D3D_Light_Gray;
if (m_Camera.m_SF >= 7.5)
{
color = D3D_Gray;
}

Graphics_System->Begin_Pixel_Drawing();

for (int i = 0; i < m_Num_Background_Star_Planes; i++)
{
for (int j = 0; j < m_Num_Stars_Per_Plane; j++)
{
Graphics_System->Draw_Pixel((int)m_Background_Stars[i][j].x, (int)m_Background_Stars[i][j].y, &color);
}
}

Graphics_System->End_Pixel_Drawing();
}

///////////////////////////////////////////////////////////



A brief summary of what happens in the render loop:

1.) I clear the screen to black (with ID3DDevice9.Clear())
2.) I begin rendering (with ID3DDevice9.BeginScene())
3.) I lock the back buffer (with ID3DSurface9.Lock())
4.) I plot pixels in a for loop, by setting values in a locked back buffer
5.) I unlock the back buffer (with ID3DSurface9.Unlock())
6.) I begin font rendering (with ID3DFont.Begin())
7.) I draw text (with ID3DFont.DrawText())
8.) I end font rendering (with ID3DFont.End())
9.) I end rendering (with ID3DDevice9.EndScene())
10.) I present the rendered scene (with ID3DDevice9.Present())

As noted, that above example fails in release mode, but works in debug mode. If I omit steps 3-5 (don't plot pixels), it works in release mode as well as debug mode.

No exceptions are thrown.

Please let me know what you think. Thanks for your help!

Share this post


Link to post
Share on other sites
I don't see any render states and texture stage states being set. Try these calls in front of the font drawing calls:


m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );

m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 8 );

Share this post


Link to post
Share on other sites
Quote:
Original post by Endurion
I don't see any render states and texture stage states being set. Try these calls in front of the font drawing calls:


m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );

m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 8 );


Ok, I tried placing this code in my Begin_Font_Drawing() function, but still no luck. The font still flickers and fails.

I've also discovered that, having since recompiled, the font drawing once again always fails in release version, whether or not I call the Render_Background_Stars() function. That is, even if I don't

-Lock the back buffer
-Plot pixels and
-Unlock the back buffer

I still can't draw the font successfully. Like I said, this behavior seems quite arbitrary - recompiles make it work and break in an inconsistent manner. Debug version works perfectly, as always.

Any other suggestions?

Share this post


Link to post
Share on other sites
So many boolean variables and if-checks. Make sure you are initializing all boolean variables to avoid passing those if-checks unintentionally.

[Edited by - Scarabus2 on January 5, 2010 6:05:21 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by streamer
temp_rect.left = i_x;
temp_rect.top = i_y;

you're missing right and bottom values [smile]


You're absolutely right...and adding that fix seems to solve the problem! What a simple, stupid mistake I've made.

Thanks!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this