Skip to main content
gamedev.net
🔒 Locked

DX9 backbuffer trouble

Started by Nostalgia Feb 16, 2007 at 2:56 PM 7 replies 1.1k views
Original Post
Nostalgia
Nostalgia
Greetings, all! I'm migrating a DirectDraw application over to DX9, so I'm learning how to put bitmaps on screen (the app is an emulator whose engine generates a bitmap for me to render). I've put together a test application that works great windowed. When I went to full-screen I got odd behavior. All the test program does is print colored stripes across the screen. When the code as below is run, it works fine. If I add the two lines in D3DObj::Init that are currently commented out (backbuffer width and height), the stripes are messed up such that ist seems like the screen pitch is wrong - each line ends a little too early, so they seem to wrap around. It happens regardless of whether I set the Fullscreen flag. As soon as I specify a backbuffer size, this happens. Here's what I think is the relevant code (error checking removed for clarity). Let me know if there's anything else you need to see, or if you want pictures of the problem. Thanks for any help you can offer, -Joe

Poly		MyRect; // Set of vertices and texture
D3DObj		MyD3D;

void D3DObj::Init( HWND hWnd)
{
	m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
	
	D3DDISPLAYMODE d3ddm;
	m_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );
	
	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );

	d3dpp.Windowed   = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	//d3dpp.BackBufferWidth = 640;
	//d3dpp.BackBufferHeight = 480;
	m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp, &m_pd3dDevice );
}

int D3DObj::Render()
{
	// Begin the scene.
	m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0L );
	m_pd3dDevice->BeginScene();
	
	// Rendering of scene objects happens here.
	
	m_pd3dDevice->SetTexture( 0, MyRect.m_Tex.m_pTexture );
	
	m_pd3dDevice->SetStreamSource( 0, MyRect.m_VertexBuffer, 0, sizeof(CUSTOMVERTEX) );
	
	m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP , 0, 2 );
	
	// End the scene.
	m_pd3dDevice->EndScene();
	
	m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
	return 0;
}

void Poly::Init ()
{
	MyD3D.m_pd3dDevice->CreateVertexBuffer(
				4 * sizeof(CUSTOMVERTEX),
				0,
				D3DFVF_CUSTOMVERTEX,
				D3DPOOL_DEFAULT,
				&m_VertexBuffer,
				NULL ) );
	
	CUSTOMVERTEX *pVertices;
	m_VertexBuffer->Lock(
			0,
			4 * sizeof(CUSTOMVERTEX),
			(void **)&pVertices,
			0 ) );
	
	pVertices[0].position=D3DXVECTOR3( -1.0f,  1.0f, 0.5f );
	pVertices[0].color=0xffffffff;
	pVertices[0].tu=0.0;
	pVertices[0].tv=0.0;
	// same for 1, 2, 3
	
	m_VertexBuffer->Unlock();
	
	m_Tex.Init ();
}

void Texture::Init ()
{
	int alpha = 255; // fully opaque

	m_Width = 256;
	m_Height = 256 >> 2;
	
	MyD3D.m_pd3dDevice->CreateTexture(
				m_Width,
				m_Height,
				0,
				0,
				D3DFMT_A8R8G8B8,
				D3DPOOL_MANAGED,
				&m_pTexture,
				NULL );
	D3DSURFACE_DESC pDesc;		
	m_pTexture->GetLevelDesc(0, &pDesc );
	
	D3DLOCKED_RECT d3dlr;		
	m_pTexture->LockRect( 0, &d3dlr, 0, 0 );
	DWORD *pDst = (DWORD *)d3dlr.pBits;
	int DPitch = d3dlr.Pitch >> 2;

	int nRow = 0;
	for (int y = 0; y < m_Height; ++y)
	{
		for (int x = 0; x < m_Width; ++x)
		{
			const RGBQUAD *r = &(pal[g_bmp[y][x]]);
			pDst[nRow + x] = (alpha << 24) | (r->rgbRed << 16) | (r->rgbGreen << 8) | r->rgbBlue;
		}

		nRow += DPitch;
	}

	m_pTexture->UnlockRect (0);
}
Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/
blueshogun96
blueshogun96
Hi.

I've examined your Texture::Init code and it appears to me that your pitch calculation is wrong. I noticed that you are using a 32-bit texture RGBA8888 (D3DFMT_A8R8G8B8) and when calculating the pitch of a 32-bit buffer, you need to shift right by 3. So instead of doing this:

[source lang=cpp]int DPitch = d3dlr.Pitch >> 2;

Try this
[source lang=cpp]int DPitch = d3dlr.Pitch >> 3;


I hope this helps. BTW, what system are you trying to emulate?
Nostalgia
Nostalgia
Quote:
Original post by blueshogun96I've examined your Texture::Init code and it appears to me that your pitch calculation is wrong. I noticed that you are using a 32-bit texture RGBA8888 (D3DFMT_A8R8G8B8) and when calculating the pitch of a 32-bit buffer, you need to shift right by 3.

Thanks for the suggestion, but shifting by two is correct. I'm traversing an array of bytes, four bytes at a time, so I divide the pitch by 4. That means shift right by two.

I just tried shifting right by 3 and it breaks things completely. Understand that the display is perfect, except when I specify the backbuffer size in the initialization procedure. That's when it looks like the pitch is off, but only by a few pixels.

Quote:
Original post by blueshogun96BTW, what system are you trying to emulate?

The Intellivision. My emulator is already quite successful, but the versions I compiled lately using DirectDraw have been having video issues. So I'm moving to DirectX in the hopes of increasing compatibility and speed.

Thanks again,

-Joe

Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/
Nostalgia
Nostalgia
I just discovered something interesting - it doesn't happen on my computer at home. The display is perfect, full screen or windowed.

Is there some bit of information I should be pulling from the gfx card that I'm not?

Thanks!

-Joe
Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/
S1CA
S1CA
1. Consider what happens when Pitch isn't a multiple of sizeof(DWORD)..., it's a multiple in bytes not a multiple of the current format pixel storage size (remember pitch can also be negative in theory to support bottom-up storage). If the pitch on your test hardware was say 2562, due to the shifting, your code would treat that as a pitch of 2560.

2. m_Width = 256; m_Height = 256 >> 2; What happens if you use D3DSURFACE_DESC.Width and D3DSURFACE_DESC.Height instead?

3. You're passing 0 for the Levels parameter of CreateTexture() to ask for a mip-chain to be created for you. Does your card/driver support automatic mip creation? (D3DCAPS2_CANAUTOGENMIPMAP). Although D3D is meant to fall back to a single level texture if the hardware can't, I'd still be wary of not checking the caps, particularly if you use a PURE device.


Minor aside: every PC compiler I've used for the past 10 years has been clever enough to know how to turn "x / 4" into "x >> 2". Personally I'd always prefer clarity over premature optimisation.
Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site
Nostalgia
Nostalgia
S1CA, thanks for the input.

Quote:
Original post by S1CA
1. Consider what happens when Pitch isn't a multiple of sizeof(DWORD)..., it's a multiple in bytes not a multiple of the current format pixel storage size (remember pitch can also be negative in theory to support bottom-up storage). If the pitch on your test hardware was say 2562, due to the shifting, your code would treat that as a pitch of 2560.

This brings 2 questions to my mind:
1. If the texture was created as RGBA8888, how is it possible to have a pitch that's not a multiple of a DWORD (I'm not being argumentative, I just don't understand)?
2. If the pitch is NOT a multiple of sizeof(DWORD), how does one handle filling in the RGBA888 data?

Quote:
Original post by S1CA
2. m_Width = 256; m_Height = 256 >> 2; What happens if you use D3DSURFACE_DESC.Width and D3DSURFACE_DESC.Height instead?

I checked in the debugger - those are 256 and 64, respectively, so it's the same values.

Quote:
Original post by S1CA
3. You're passing 0 for the Levels parameter of CreateTexture() to ask for a mip-chain to be created for you. Does your card/driver support automatic mip creation? (D3DCAPS2_CANAUTOGENMIPMAP). Although D3D is meant to fall back to a single level texture if the hardware can't, I'd still be wary of not checking the caps, particularly if you use a PURE device.

I don't know the answer to this. This is sample code that I'm using as a learning base-point, and the text didn't say anything about it. I'll do some research.

Quote:
Original post by S1CA
Minor aside: every PC compiler I've used for the past 10 years has been clever enough to know how to turn "x / 4" into "x >> 2". Personally I'd always prefer clarity over premature optimisation.

Old habit :) You make a fine point.

Thanks again!

-Joe
Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/
S1CA
S1CA
Quote:
Original post by Nostalgia
S1CA, thanks for the input.

Quote:
Original post by S1CA
1. Consider what happens when Pitch isn't a multiple of sizeof(DWORD)..., it's a multiple in bytes not a multiple of the current format pixel storage size (remember pitch can also be negative in theory to support bottom-up storage). If the pitch on your test hardware was say 2562, due to the shifting, your code would treat that as a pitch of 2560.

This brings 2 questions to my mind:
1. If the texture was created as RGBA8888, how is it possible to have a pitch that's not a multiple of a DWORD (I'm not being argumentative, I just don't understand)?
2. If the pitch is NOT a multiple of sizeof(DWORD), how does one handle filling in the RGBA888 data?


1) video/graphics memory doesn't have a 'type', it's just plain memory, and from a practical standpoint you can think of it as addressed in bytes just like any other memory*. The format of a pixel just tells the GPU how to interpret that memory.

On some (maybe even most?) GPUs, video memory is actually a 2D array of memory rather than 1D (i.e. a big square/rectangular page of bytes). When resources are allocated, the device driver finds the best appropriately sized 2D gap within that big page. The 2D gap is measured in bytes.

A diagram might make this a bit clearer:
+---------------+---+------------------+--------+---------+|               | B |                  |        |         ||               +---+          C       |   D    |         ||      A        |   +------------------+        |         ||               |                      |        |         ||               |                      +--------+         ||               |                                         |+---------------+                                         ||                                                         ||                                                         ||                                                         ||                          FREE                           ||                                                         ||                                                         ||                                                         |+---------------------------------------------------------+


Resource A might be your D3DFMT_X8R8G8B8 back buffer.
Resource B might be a D3DFMT_L8 mouse cursor texture.
Resource C might be a D3DFMT_R5G6B5 texture.
Resource D might be a D3DFMT_A8R8G8B8 texture

For all of those surfaces, the pitch will be wider than the surface. For surface A, the pitch is likely to be a 'nice' number because the 2D page of video memory is probably aligned to a nice address boundary.

However, it's entirely possible that all the other surfaces after surface B could have pitches that aren't a multiple of their pixel storage size (consider 1x1 mip map levels).

GPUs/drivers do use pitch for other purposes too of course, some /do/ align surface widths to particular boundaries required by hardware.

The size of that 2D page isn't necessarily fixed either, for some it might be, for others it will be determined by the back buffer size, for others memory will be addressed differently altogether. Key point is that's implementation specific and the GPU/driver has that flexibility because of pitch.

[* it isn't necessarily addressed in bytes at a hardware implementation level either, but that's specific to each GPU - from the D3D API side, think of it as bytes]



2) keep the address of the start of the current line of the texture (in bytes, cast the pointer to DWORD when you need interpret the memory at that address as DWORD). At the end of each line, add on the byte pitch to the byte address.
Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site
Nostalgia
Nostalgia
Quote:
Original post by S1CA2) keep the address of the start of the current line of the texture (in bytes, cast the pointer to DWORD when you need interpret the memory at that address as DWORD). At the end of each line, add on the byte pitch to the byte address.

Thanks for the advice. I changed my bitmap code to look like this (scrBuff contains the bitmap I'm rendering):
D3DLOCKED_RECT d3dlr;		m_pTexture->LockRect(0, &d3dlr, 0, 0 );int nPitch = d3dlr.Pitch;BYTE *pScr = (BYTE *)d3dlr.pBits;for (y = 0; y < 192; y++){	for (x = 0, z = 0; x < 159; x++)	{		p0 = g.pal888[scrBuff[y][x]];		((DWORD *)pScr)[x] = p0;	}			pScr += nPitch;}


It doesn't change anything.

The problem happens in full screen with XYZ coors, AND in both full screen and windowed when I switch to XYZRHW coords. I'll post up some pictures in a few minutes to describe better.

-Joe
Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/
Nostalgia
Nostalgia
Ok, here we go. When using D3DFVF_XYZ in Windowed mode, I get a series of unbroken stripes, as I expect. When using D3DFVF_XYZ in Fullscreen mode OR using D3DFVF_XYZRHW in Fullscreen or windowed mode, I get the following:

Photobucket - Video and Image Hosting

Notice the diagonal tear. Thanks for any advice,

-Joe
Nostalgia, an Intellivision Emulatorhttp://www.gotmaille.com/nostalgia/

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.