Merge pull request #2157 from NZJenkins/tidy-mip-code

Tidy mip code and work around DXT crash with small textures
This commit is contained in:
PatrickvL 2021-03-13 08:24:07 +01:00 committed by GitHub
commit 6db6256e45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 47 additions and 35 deletions

View File

@ -1542,6 +1542,14 @@ bool ConvertD3DTextureToARGBBuffer(
AdditionalArgument = DstRowPitch; AdditionalArgument = DstRowPitch;
if (EmuXBFormatIsCompressed(X_Format)) { if (EmuXBFormatIsCompressed(X_Format)) {
if (SrcWidth < 4 || SrcHeight < 4) {
// HACK: The compressed DXT conversion code currently writes more pixels than it should, which can cause a crash.
// This code will get hit when converting compressed texture mipmaps on hardware that somehow doesn't support DXT natively
// (or lied when Cxbx asked it if it does!)
EmuLog(LOG_LEVEL::WARNING, "Converting DXT textures smaller than a block is not currently implemented. Ignoring conversion!");
return true;
}
// All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines // All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines
SrcHeight = (SrcHeight + 3) / 4; SrcHeight = (SrcHeight + 3) / 4;
DstRowPitch *= 4; DstRowPitch *= 4;
@ -5697,7 +5705,6 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
bool bCubemap = pPixelContainer->Format & X_D3DFORMAT_CUBEMAP; bool bCubemap = pPixelContainer->Format & X_D3DFORMAT_CUBEMAP;
bool bSwizzled = EmuXBFormatIsSwizzled(X_Format); bool bSwizzled = EmuXBFormatIsSwizzled(X_Format);
bool bCompressed = EmuXBFormatIsCompressed(X_Format); bool bCompressed = EmuXBFormatIsCompressed(X_Format);
DWORD dwMinSize = (bCompressed) ? 4 : 1;
UINT dwBPP = EmuXBFormatBytesPerPixel(X_Format); UINT dwBPP = EmuXBFormatBytesPerPixel(X_Format);
UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pPixelContainer); UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pPixelContainer);
UINT dwWidth, dwHeight, dwDepth, dwRowPitch, dwSlicePitch; UINT dwWidth, dwHeight, dwDepth, dwRowPitch, dwSlicePitch;
@ -5731,22 +5738,6 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
LOG_TEST_CASE("CreateHostResource : Depth != 1"); LOG_TEST_CASE("CreateHostResource : Depth != 1");
} }
// The following is necessary for DXT* textures (4x4 blocks minimum)
// TODO: Figure out if this is necessary under other circumstances?
if (bCompressed) {
if (dwWidth < dwMinSize) {
LOG_TEST_CASE("CreateHostResource : dwWidth < dwMinSize");
EmuLog(LOG_LEVEL::WARNING, "Expanding %s width (%d->%d)", ResourceTypeName, dwWidth, dwMinSize);
dwWidth = dwMinSize;
}
if (dwHeight < dwMinSize) {
LOG_TEST_CASE("CreateHostResource : dwHeight < dwMinSize");
EmuLog(LOG_LEVEL::WARNING, "Expanding %s height (%d->%d)", ResourceTypeName, dwHeight, dwMinSize);
dwHeight = dwMinSize;
}
}
// One of these will be created : each also has an intermediate copy to allow UpdateTexture to work // One of these will be created : each also has an intermediate copy to allow UpdateTexture to work
// This means we don't need to lock the GPU resource anymore, so we can use D3DPOOL_DEFAULT to allow Stretch/CopyRects to work! // This means we don't need to lock the GPU resource anymore, so we can use D3DPOOL_DEFAULT to allow Stretch/CopyRects to work!
IDirect3DSurface *pNewHostSurface = nullptr; // for X_D3DRTYPE_SURFACE IDirect3DSurface *pNewHostSurface = nullptr; // for X_D3DRTYPE_SURFACE
@ -5962,23 +5953,37 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
DWORD dwCubeFaceOffset = 0; DWORD dwCubeFaceOffset = 0;
DWORD dwCubeFaceSize = 0; DWORD dwCubeFaceSize = 0;
D3DCUBEMAP_FACES last_face = (bCubemap) ? D3DCUBEMAP_FACE_NEGATIVE_Z : D3DCUBEMAP_FACE_POSITIVE_X; D3DCUBEMAP_FACES last_face = (bCubemap) ? D3DCUBEMAP_FACE_NEGATIVE_Z : D3DCUBEMAP_FACE_POSITIVE_X;
// Block size only applies to compressed DXT formats
// DXT1 block size is 8 bytes
// Other Xbox DXT formats are 16 bytes
DWORD blockSize = 0;
if (bCompressed) {
blockSize = X_Format == xbox::X_D3DFMT_DXT1 ? 8 : 16;
}
for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= last_face; face++) { for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= last_face; face++) {
// As we iterate through mipmap levels, we'll adjust the source resource offset // As we iterate through mipmap levels, we'll adjust the source resource offset
DWORD dwMipOffset = 0; DWORD dwMipOffset = 0;
DWORD dwMipWidth = dwWidth; DWORD pxMipWidth = dwWidth;
DWORD dwMipHeight = dwHeight; DWORD pxMipHeight = dwHeight;
DWORD dwMipDepth = dwDepth; DWORD pxMipDepth = dwDepth;
DWORD dwMipRowPitch = dwRowPitch; DWORD dwMipRowPitch = dwRowPitch;
DWORD dwSrcSlicePitch = dwMipRowPitch * dwMipHeight; // TODO DWORD dwSrcSlicePitch = dwMipRowPitch * pxMipHeight; // TODO
for (unsigned int mipmap_level = 0; mipmap_level < dwMipMapLevels; mipmap_level++) { for (unsigned int mipmap_level = 0; mipmap_level < dwMipMapLevels; mipmap_level++) {
// Calculate size of this mipmap level // Calculate size of this mipmap level
DWORD dwMipSize = dwMipRowPitch * dwMipHeight; DWORD numRows = pxMipHeight;
if (bCompressed) { if (bCompressed) {
dwMipSize /= 4; // Each row contains a 4x4 pixel blocks, instead of single pixels
// So divide by 4 to get the number of rows
numRows = (numRows + 3) / 4;
} }
DWORD dwMipSize = dwMipRowPitch * numRows;
// Lock the host resource // Lock the host resource
D3DLOCKED_RECT LockedRect = {}; D3DLOCKED_RECT LockedRect = {};
D3DLOCKED_BOX LockedBox = {}; D3DLOCKED_BOX LockedBox = {};
@ -6039,7 +6044,7 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
if (X_Format == xbox::X_D3DFMT_P8 && g_pXbox_Palette_Data[iTextureStage] == nullptr) { if (X_Format == xbox::X_D3DFMT_P8 && g_pXbox_Palette_Data[iTextureStage] == nullptr) {
LOG_TEST_CASE("Palettized texture bound without a palette"); LOG_TEST_CASE("Palettized texture bound without a palette");
memset(pDst, 0, dwDstRowPitch * dwMipHeight); memset(pDst, 0, dwDstRowPitch * pxMipHeight);
skipDueToNoPalette = true; skipDueToNoPalette = true;
} }
@ -6047,7 +6052,7 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
if (!skipDueToNoPalette) { if (!skipDueToNoPalette) {
if (!ConvertD3DTextureToARGBBuffer( if (!ConvertD3DTextureToARGBBuffer(
X_Format, X_Format,
pSrc, dwMipWidth, dwMipHeight, dwMipRowPitch, dwSrcSlicePitch, pSrc, pxMipWidth, pxMipHeight, dwMipRowPitch, dwSrcSlicePitch,
pDst, dwDstRowPitch, dwDstSlicePitch, pDst, dwDstRowPitch, dwDstSlicePitch,
dwDepth, dwDepth,
iTextureStage)) { iTextureStage)) {
@ -6058,7 +6063,7 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
else if (bSwizzled) { else if (bSwizzled) {
// First we need to unswizzle the texture data // First we need to unswizzle the texture data
EmuUnswizzleBox( EmuUnswizzleBox(
pSrc, dwMipWidth, dwMipHeight, dwMipDepth, pSrc, pxMipWidth, pxMipHeight, pxMipDepth,
dwBPP, dwBPP,
pDst, dwDstRowPitch, dwDstSlicePitch pDst, dwDstRowPitch, dwDstSlicePitch
); );
@ -6082,12 +6087,12 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
); );
} else { } else {
*/ */
if ((dwDstRowPitch == dwMipRowPitch) && (dwMipRowPitch == dwMipWidth * dwBPP)) { if ((dwDstRowPitch == dwMipRowPitch) && (dwMipRowPitch == pxMipWidth * dwBPP)) {
memcpy(pDst, pSrc, dwMipSize); memcpy(pDst, pSrc, dwMipSize);
} }
else { else {
for (DWORD v = 0; v < dwMipHeight; v++) { for (DWORD v = 0; v < pxMipHeight; v++) {
memcpy(pDst, pSrc, dwMipWidth * dwBPP); memcpy(pDst, pSrc, pxMipWidth * dwBPP);
pDst += dwDstRowPitch; pDst += dwDstRowPitch;
pSrc += dwMipRowPitch; pSrc += dwMipRowPitch;
} }
@ -6125,17 +6130,24 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
// Calculate the next mipmap level dimensions // Calculate the next mipmap level dimensions
dwMipOffset += dwMipSize; dwMipOffset += dwMipSize;
if (dwMipWidth > dwMinSize) { if (pxMipWidth > 1) {
dwMipWidth /= 2; pxMipWidth /= 2;
// Update the row pitch
dwMipRowPitch /= 2; dwMipRowPitch /= 2;
// The pitch can't be less than a block
if (dwMipRowPitch < blockSize) {
dwMipRowPitch = blockSize;
}
} }
if (dwMipHeight > dwMinSize) { if (pxMipHeight > 1) {
dwMipHeight /= 2; pxMipHeight /= 2;
} }
if (dwMipDepth > 1) { if (pxMipDepth > 1) {
dwMipDepth /= 2; pxMipDepth /= 2;
} }
} // for mipmap levels } // for mipmap levels