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

Untitled

Sign in to follow this  
Evil Steve

147 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

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