Ok I think I've come up with a solution.
Instead of thinking old school tile maps I need to break it down into large primitives. Direct3D is very good at doing large primitives with one texture.
Solution:
No tiles at all
Only use tiles in the editor. When I save the tile maps, don't save them as data, but save them as 1024x1024 or 512x512 textures. In other words, save each portion of the map as one texture and stick that in the data file. At run-time I simply render a quad 1024x1024 (or whatever resolution we decide on) and use a quad-tree system to see which quads are on-screen. For those that are within screen limits, draw the whole quad. Hardware is very good at clipping large quads.
So this breaks the DrawPrimitive and SetTexture down from (Tilesize/ScreenWidth)^2 to 4 max for any one screen since it's possible that you could be at the intersection of 4 of these quads.
Tile-specific effects can still be done by using decaling, render to texture, etc, etc. I will still know where in the world the tiles are by just doing a little math based on the world scroll values.
I think this will be a major speed up and will take less memory than storing one instance of each tile. This will free up a lot of resources. It's an odd-way to do pixel perfect scrolling but other methods just don't work anymore.
Scrolling using texture transform flags
Using one large texture won't work either since you cannot fit a 4096x4096 texture in video memory and yet you could have a world size that large. The alternative to this would be to cache in portions of the texture at a time, but again, this requires locking surfaces which is what I want to avoid. I would love to just transform texture u,v's to scroll the picture because it would be uber fast......but I cannot do this. I would have to cache in a portion of the data, use D3DXCreateTexture to make it a texture, and then render it. This would happen at 1024x1024 boundaries and I believe it would cause slow downs. I may even try to store a 4096x4096 texture in a file and then create several smaller textures from it using my tile extraction code. They would just be larger tiles. Then perhaps I may try this image cache scheme and just scroll the u,v's.
For those of you interested in the tile extraction code:
CTextureManager
Code:
bool CTextureManager::AddFromFileEx(std::string File,DWORD dwWidth,DWORD dwHeight,
DWORD &dwOutWidth,DWORD &dwOutHeight)
{
//Create base texture
IDirect3DTexture9 *pSourceTexture=NULL;
D3DXCreateTextureFromFile(m_pDevice,File.c_str(),&pSourceTexture);
//Now lock the surface
D3DLOCKED_RECT sourceRect;
pSourceTexture->LockRect(0,&sourceRect,NULL,0);
//Get surface desc
D3DSURFACE_DESC sourceDesc;
pSourceTexture->GetLevelDesc(0,&sourceDesc);
dwOutWidth=sourceDesc.Width;
dwOutHeight=sourceDesc.Height;
//Pointer to surface (buffer)
DWORD *pSourceBits=(DWORD *)sourceRect.pBits;
DWORD dwSourceOffset=0;
DWORD dwStartSourceOffset=0;
//X and Y counters
DWORD dwX=0,dwY=0;
do
{
do
{
//Create new texture object
CTexture *pTexture=new CTexture;
//Create texture from buffer
pTexture->CreateFromImageEx(m_pDevice,
dwWidth,
dwHeight,
pSourceBits,
dwSourceOffset,
sourceRect.Pitch);
//Add texture to vector
m_vTextures.push_back(pTexture);
//Update loop variables
dwX+=dwWidth;
dwSourceOffset+=dwWidth;
} while (dwX<(sourceRect.Pitch/4));
//Move down one cell size in source texture
dwX=0;
dwStartSourceOffset+=((sourceRect.Pitch/4)*dwHeight);
dwSourceOffset=dwStartSourceOffset;
dwY+=dwHeight;
} while (dwY<sourceDesc.Height);
return false;
}
CTexture.h
Code:
//Grabs a texture of width,height from pSourceBuffer
void CTexture::CreateFromImageEx(IDirect3DDevice9 *pDevice,
DWORD dwTargetWidth,
DWORD dwTargetHeight,
DWORD *pSourceBuffer,
DWORD dwSourceOffset,
DWORD dwSourcePitch)
{
//Create a blank texture
D3DXCreateTexture(pDevice,
dwTargetWidth,
dwTargetHeight,
D3DX_DEFAULT,
0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
&m_pTexture);
//Lock it's surface and get a pointer to it
D3DLOCKED_RECT rect;
m_pTexture->LockRect(0,&rect,NULL,0);
DWORD *ptrBits=(DWORD *)rect.pBits;
//Surface description
D3DSURFACE_DESC desc;
m_pTexture->GetLevelDesc(0,&desc);
//Offsets
DWORD dwOffset=0;
DWORD dwStartOffset=0;
DWORD dwStartSourceOffset=dwSourceOffset;
//Start the copying
for (DWORD i=0;i<desc.Height;i++)
{
for (DWORD j=0;j<desc.Width;j++)
{
//Copy from source to this texture's surface
ptrBits[dwOffset]=pSourceBuffer[dwSourceOffset];
dwSourceOffset++;
dwOffset++;
}
dwStartSourceOffset+=(dwSourcePitch/4);
dwSourceOffset=dwStartSourceOffset;
dwStartOffset+=(rect.Pitch/4);
dwOffset=dwStartOffset;
}
//Unlock surface
m_pTexture->UnlockRect(0);
}