Sign in to follow this  
  • entries
    557
  • comments
    1237
  • views
    422068

Untitled

Sign in to follow this  

152 views

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?
Sign in to follow this  


3 Comments


Recommended Comments

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.

Share this comment


Link to comment
Unless I'm missing something obvious, wouldn't D3DXLoadSurfaceFromMemory() do what you need?

Share this comment


Link to comment

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