Untitled

posted in DruinkJournal
Published January 14, 2008
Advertisement
Well, all I can say is "Ukh". Adding support for non-np2 cards (Like my server's) was ugly. Because BSP files need texture repeat, I have to stretch the texture from the BSP file - Which comes in as, say 24x64 - to a pow2 size, 32x64 in my example. Now that wouldn't be too bad, if there was a simple way to do that. D3D's StretchRect isn't suitable here, because I need to create two surfaces in the default pool, they can't both be offscreen surfaces, so one needs to be a texture - and that requires that the card supports StretchRect from textures.

So, I went for another method - create two offscreen surfaces, use GetDC() on both, and then use the GDI StretchBlt() function to blit. So, here's my function:
PTexture::SP PTextureMgr::CreateScaledTexture(const EID& id, u32 nWidth, u32 nHeight, u32 nFlags,											  const u32* pData, u32 nDataWidth, u32 nDataHeight){	Assert(nWidth != nDataWidth || nHeight != nDataHeight, "Data size is the same as the texture size. Use CreateTexture()");	// Create an offscreen surface that is the same size as the data buffer	LPDIRECT3DSURFACE9 pSourceSurf;	HRESULT hResult = m_pDevice->CreateOffscreenPlainSurface(nDataWidth, nDataHeight, D3DFMT_X8R8G8B8,		D3DPOOL_SYSTEMMEM, &pSourceSurf, NULL);	if(FAILED(hResult))	{		std::wstringstream str;		str << L"Failed to create surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Lock surface	D3DLOCKED_RECT rect;	hResult = pSourceSurf->LockRect(&rect, NULL, 0);	if(FAILED(hResult))	{		pSourceSurf->Release();		std::wstringstream str;		str << L"Failed to lock surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Make sure there's no alpha	#ifdef BUILD_DEBUG		for(u32 y=0; y		{			for(u32 x=0; x			{				Assert((pData[y*nDataWidth + x]&0xff000000)==0xff000000,					"Can't use CreateScaledTexture() when source data has an alpha channel");			}		}	#endif	// Fill - do a fast copy if we can	if((u32)rect.Pitch == nDataWidth * 4)	{		memcpy(rect.pBits, pData, nDataWidth * nDataHeight * 4);	}	else	{		for(UINT y=0; y		{			memcpy((BYTE*)rect.pBits + y*rect.Pitch, pData, nDataWidth * 4);			pData += nDataWidth;		}	}	// Unlock	pSourceSurf->UnlockRect();	// Create target texture - Remove mipmap flag, we'll generate them once texture is filled	PTexture::SP pTexture = CreateTexture(id, nWidth, nHeight, nFlags& (~ETexture::FLAG_GenerateMipMaps));	if(!pTexture)	{		pSourceSurf->Release();		return 0;	}	// We can't StretchRect() to the texture because it's not in the default	// pool. We can't StretchRect() to another surface because you can't StretchRect()	// between two offscreen surfaces. The only choice is to use GetDC() and GDI	// StretchBlt() between two surfaces, then to update the texture.	// Create offscreen surface the same size as the target texture	LPDIRECT3DSURFACE9 pDestSurf;	hResult = m_pDevice->CreateOffscreenPlainSurface(nWidth, nHeight, D3DFMT_X8R8G8B8,		D3DPOOL_SYSTEMMEM, &pDestSurf, NULL);	if(FAILED(hResult))	{		pSourceSurf->Release();		std::wstringstream str;		str << L"Failed to create surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Get the DC for both surfaces	HDC hDCSource, hDCDest;	hResult = pSourceSurf->GetDC(&hDCSource);	if(FAILED(hResult))	{		pDestSurf->Release();		pSourceSurf->Release();		std::wstringstream str;		str << L"Failed to create surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	hResult = pDestSurf->GetDC(&hDCDest);	if(FAILED(hResult))	{		pSourceSurf->ReleaseDC(hDCSource);		pDestSurf->Release();		pSourceSurf->Release();		std::wstringstream str;		str << L"Failed to create surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Check we support StretchBlt	int nCaps = GetDeviceCaps(hDCDest, RASTERCAPS);	if(!(nCaps & RC_STRETCHBLT))	{		pDestSurf->ReleaseDC(hDCDest);		pSourceSurf->ReleaseDC(hDCSource);		pDestSurf->Release();		pSourceSurf->Release();		std::wstringstream str;		str << L"StretchBlt() is not supported on this hardware";		str << DXGetErrorString(GetLastError());		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Blit	if(!StretchBlt(hDCDest, 0, 0, nWidth, nHeight, hDCSource, 0, 0, nDataWidth, nDataHeight, SRCCOPY))	{		pDestSurf->ReleaseDC(hDCDest);		pSourceSurf->ReleaseDC(hDCSource);		pDestSurf->Release();		pSourceSurf->Release();		std::wstringstream str;		str << L"StretchBlt() failed. Error: ";		str << DXGetErrorString(GetLastError());		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Done with DCs and source surface now	pDestSurf->ReleaseDC(hDCDest);	pSourceSurf->ReleaseDC(hDCSource);	pSourceSurf->Release();	// Get top level surface of texture	LPDIRECT3DSURFACE9 pTextureSurface;	hResult = pTexture->GetTexture()->GetSurfaceLevel(0, &pTextureSurface);	if(FAILED(hResult))	{		pDestSurf->Release();		std::wstringstream str;		str << L"GetSurfaceLevel failed. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Lock dest surface (Which is now the source surface for the copy)	D3DLOCKED_RECT rectSrc;	hResult = pDestSurf->LockRect(&rectSrc, NULL, D3DLOCK_READONLY);	if(FAILED(hResult))	{		pDestSurf->Release();		pTextureSurface->Release();		std::wstringstream str;		str << L"Failed to lock surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Lock texture surface (Which is the dest surface for the copy)	D3DLOCKED_RECT rectDest;	hResult = pTextureSurface->LockRect(&rectDest, NULL, 0);	if(FAILED(hResult))	{		pDestSurf->UnlockRect();		pTextureSurface->Release();		pDestSurf->Release();		std::wstringstream str;		str << L"Failed to lock surface. Error: ";		str << DXGetErrorString(hResult);		PApp::Get().SetError(str.str());		ELog::Get().ErrorFormat(L"TEXMGR  : %s\n", PApp::Get().GetError().c_str());		return 0;	}	// Get format for texture	PPixelFormat fmt;	if(!GetPixelFormat(m_fmtTextures, fmt))	{		Assert(false, "GetPixelFormat failed!");	}	Assert(fmt.nBpp == 32, "Need to account for non-32bit textures");	// Copy scanlines - do a fast copy if we can	if((u32)rectSrc.Pitch == nWidth * fmt.nBpp/8 &&		(u32)rectDest.Pitch == nWidth * fmt.nBpp/8)	{		memcpy(rectDest.pBits, rectSrc.pBits, rectSrc.Pitch * nHeight);	}	else	{		u8* pDest = (u8*)rectDest.pBits;		u8* pSource = (u8*)rectSrc.pBits;		for(UINT y=0; y		{			memcpy(pDest, pSource, nWidth * fmt.nBpp/8);			pDest += rectDest.Pitch;			pSource += rectSrc.Pitch;		}	}	// Unlock and release surfaces	pTextureSurface->UnlockRect();	pDestSurf->UnlockRect();	pTextureSurface->Release();	pDestSurf->Release();	// Generate mipmaps if needed, and return texture	if(nFlags & ETexture::FLAG_GenerateMipMaps)		pTexture->GetTexture()->GenerateMipSubLevels();	return pTexture;}

I loose alpha from that, but it's not too bad. Still, there has to be a neater / faster way of doing it, anyone got any ideas?
Previous Entry Untitled
Next Entry Untitled
0 likes 3 comments

Comments

benryves
I had some interesting experiences with my video card handling the non-power-of-two textures badly. [rolleyes]

I just wrote my own texture scaling code (rounding up to the next largest valid texture size).
January 14, 2008 07:45 AM
Evil Steve
Quote:Original post by benryves
I had some interesting experiences with my video card handling the non-power-of-two textures badly. [rolleyes]

I just wrote my own texture scaling code (rounding up to the next largest valid texture size).
Yeah, I read that part of your journal after I'd written that code. I might just change it to do manual filtering though, since my method involves creating two temp surfaces, which can't be good. Ideally the filtering I use to upscale the texture should be the same as the filtering used to render the texture, since otherwise I might get artifacts, but there's nothing noticible at the moment.
January 14, 2008 01:38 PM
Adam_42
Unless I'm missing something obvious, wouldn't D3DXLoadSurfaceFromMemory() do what you need?
February 08, 2008 08:55 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement