Win32/D3D10 Window not painting properly

Started by
7 comments, last by ds2k5 12 years, 1 month ago

I'm having an annoying problem in a win32/d3d10 application. I'm using D3D to render a texture to the window, which worked fine as long as I kept constantly rendering, however I chose to render only when receiving WM_PAINT messages. Now when I start the application, the rendered texture is briefly shown but disappears the next instant, leaving a blank window. At first I thought there was something wrong with the rendering code, but user generated InvalidateRect calls (e.g. on mouse click/move etc.) get the texture rendered properly. Unfortunately, as soon as the window loses focus it becomes blank again.

I'm desperate, as after hours of research and debugging, I'm not really sure what is causing this. I suspect that it may have something to do with the validation of the window's update region, since
1.) ValidateRect won't actually validate the region (GetUpdateRect returns the same rectangle before and after ValidateRect)
2.) If I don't call either Begin-/EndPaint or DefWindowProc on WM_PAINT, the rendering works fine, however I keep getting WM_PAINT messages about once every second, because the update region doesn't get validated.

Although I wonder why ValidateRect doesn't work (as I'd like to avoid Begin-/EndPaint), when it seems to work for everyone else, I'm not sure it's actually the cause of my problem. It's probably some trivial issue, but I'm clueless and hoping that anyone else has encountered and solved it. Any hints are greatly appreciated.


[..]

case WM_ERASEBKGND:

return 1;

case WM_PAINT:

render();

break;

[..]

return DefWindowProc( hWnd, uMsg, wParam, lParam );

That's how the WM_PAINT and WM_ERASEBKGND messages get handled. The render() method would probably be more confusing than helpful and seems to work anyway.

Advertisement
From MSDN about WM_PAINT:

Return value

An application returns zero if it processes this message.


So don't pass WM_PAINT on to DefWindowProc, but return 0. In any case, you either need to call ValidateRect, or BeginPaint/EndPaint to avoid getting swamped with WM_PAINTs.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>


From MSDN about WM_PAINT:

Return value

An application returns zero if it processes this message.


So don't pass WM_PAINT on to DefWindowProc, but return 0. In any case, you either need to call ValidateRect, or BeginPaint/EndPaint to avoid getting swamped with WM_PAINTs.


I should have mentioned this, using BeginPaint/EndPaint and returning 0 results in the exact same behavior as passing the message to DefWindowProc. ValidateRect won't work at all, as I've mentioned, if I use it the window doesn't actually get validated and keeps receiving WM_PAINTs.

Edit: Main problem fixed. I didn't handle WM_NCPAINT, a simple "return 0;" fixed the blank window issue. Unfortunately ValidateRect still won't work and I'd prefer using it instead of BeginPaint/EndPaint.
How exactly are you drawing to your window and invalidating it?

You should never need to handle WM_NCPAINT, just pass it on to DefWindowProc.
It may work for now but there seems to be another problem lurking in there.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>


How exactly are you drawing to your window and invalidating it?

You should never need to handle WM_NCPAINT, just pass it on to DefWindowProc.
It may work for now but there seems to be another problem lurking in there.


The drawing part is mostly put together from several tutorials as it's my first time working with DX10. I use Direct2D to render to a texture which is then used to render a quad with. That's what the method looks like:

void Window::render()
{
// Direct2D rendering
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear( D2D1::ColorF( 0.0f, 0.0f, 1.0f, 0.5f ) );
// <unnecessary render code>
m_pRenderTarget->EndDraw();


// Direct3D rendering
m_pevWorld->SetMatrix( m_matWorld );
m_pevView->SetMatrix( m_matView );
m_pevProjection->SetMatrix( m_matProjection );

m_pDevice->ClearRenderTargetView( m_pRenderTargetView, D3DXCOLOR( 0.0f, 0.0f, 0.0f, 0.0f ) );

D3D10_TECHNIQUE_DESC techDesc;
m_pTechnique->GetDesc( &techDesc );
for( UINT i = 0; i < techDesc.Passes; ++i )
{
m_pTechnique->GetPassByIndex( i )->Apply( 0 );
m_pDevice->Draw( 4, 0 );
}

m_pSwapChain->Present( 1, 0 );
}


And this is everything being called on WM_PAINT. As for invalidating, so far I've only done it for testing.

case WM_RBUTTONDOWN:
InvalidateRect( hWnd, NULL, false );
break;


If the window needs repainting I call the render() method directly so it's only the system that actually invalidates the window.
I assume your call to ValidateRect looks exactly like InvalidateRect?

e.g.: ValidateRect( hWnd, NULL );

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Exactly.

case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint( hWnd, &ps );
render();
EndPaint( hWnd, &ps );
//ValidateRect( hWnd, NULL );
//RECT rect;
//if( GetUpdateRect( hWnd, &rect, false ) != 0 )
//{
// OutputDebugString( L"not validated\n" );
//}
//else
//{
// OutputDebugString( L"validated\n" );
//}
return 0;


If I use the commented part instead of BeginPaint/EndPaint I get only "not validated" output.
Hmm, can you provide a minimal sample where the problem occurs? The problem should also appear without any D3D code.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

While copying the essential code I actually found the problem myself. It's a trivial issue, just as I thought. One of the tutorials I used was on making transparent windows with Direct3D9Ex and DWM without using WS_EX_LAYERED, instead it said to use WS_EX_COMPOSITED, which I did. I thought I had checked if other styles were possible, but it seems I have not, since exchanging WS_EX_COMPOSITED for WS_EX_APPWINDOW fixed validation and transparency is still working.

Now I feel kind of stupid for not trying to reproduce the issue before, as copying the essential code wasn't exactly the most difficult thing to do. So thank you for asking me to do it, and the help in general. smile.png


[spoiler]
It doesn't matter anymore, but I've copied the code that produces the problem.

LRESULT WINAPI wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
ValidateRect( hWnd, NULL );
RECT rect;
if( GetUpdateRect( hWnd, &rect, false ) != 0 )
{
OutputDebugString( L"not validated\n" );
}
else
{
OutputDebugString( L"validated\n" );
}
return 0;
case WM_KEYDOWN:
if( LOWORD( wParam ) == VK_ESCAPE )
{
SendMessage( hWnd, WM_CLOSE, NULL, NULL );
}
break;
}

return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, INT )
{
WNDCLASSEX wc = {
sizeof( WNDCLASSEX ),
NULL,
wndProc,
NULL,
NULL,
hInstance,
LoadIcon( NULL, IDI_APPLICATION ),
LoadCursor( NULL, IDC_ARROW ),
NULL,
NULL,
L"FunnyWndClassName",
LoadIcon( NULL, IDI_APPLICATION ),
};

RegisterClassEx( &wc );

HWND hWnd = CreateWindowEx(
WS_EX_COMPOSITED, // exchange for WS_EX_APPWINDOW to fix validation
L"FunnyWndClassName",
L"FunnyWndName",
WS_POPUP | WS_SIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
400,
NULL,
NULL,
hInstance,
NULL );

ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );

MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) > 0 )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

return 0;
}
[/spoiler]

This topic is closed to new replies.

Advertisement