Merge pull request #1703 from LukeUsher/fix-level-face-surface

Fix surface level/face mapping without patches!
This commit is contained in:
PatrickvL 2019-08-25 15:14:52 +02:00 committed by GitHub
commit a77403b7b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 120 additions and 99 deletions

View File

@ -1202,6 +1202,94 @@ VOID CxbxGetPixelContainerMeasures
} }
} }
void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pTexture, UINT& Level, XTL::D3DCUBEMAP_FACES& Face)
{
auto pSurfaceData = (uintptr_t)GetDataFromXboxResource(pSurface);
auto pTextureData = (uintptr_t)GetDataFromXboxResource(pTexture);
// Fast path: If the data pointers match, this must be the first surface within the texture
if ((pSurfaceData == pTextureData)) {
Level = 0;
Face = XTL::D3DCUBEMAP_FACE_POSITIVE_X;
return;
}
int numLevels = CxbxGetPixelContainerMipMapLevels(pTexture);
int numFaces = pTexture->Format & X_D3DFORMAT_CUBEMAP ? 6 : 1;
CxbxGetPixelContainerMipMapLevels(pTexture);
// First, we need to fetch the dimensions of both the surface and the texture, for use within our calculations
UINT textureWidth, textureHeight, textureDepth, textureRowPitch, textureSlicePitch;
CxbxGetPixelContainerMeasures(pTexture, 0, &textureWidth, &textureHeight, &textureDepth, &textureRowPitch, &textureSlicePitch);
UINT surfaceWidth, surfaceHeight, surfaceDepth, surfaceRowPitch, surfaceSlicePitch;
CxbxGetPixelContainerMeasures(pSurface, 0, &surfaceWidth, &surfaceHeight, &surfaceDepth, &surfaceRowPitch, &surfaceSlicePitch);
// Iterate through all faces and levels, until we find a matching pointer
bool isCompressed = XTL::EmuXBFormatIsCompressed(GetXboxPixelContainerFormat(pTexture));
int minSize = (isCompressed) ? 4 : 1;
int cubeFaceOffset = 0; int cubeFaceSize = 0;
auto pData = pTextureData;
for (int face = XTL::D3DCUBEMAP_FACE_POSITIVE_X; face <= numFaces; face++) {
int mipWidth = textureWidth;
int mipHeight = textureHeight;
int mipDepth = textureDepth;
int mipRowPitch = textureRowPitch;
int mipDataOffset = 0;
for (int level = 0; level < numLevels; level++) {
if (pData == pSurfaceData) {
Level = level;
Face = (XTL::D3DCUBEMAP_FACES)face;
return;
}
// Calculate size of this mipmap level
UINT dwMipSize = mipRowPitch * mipHeight;
if (isCompressed) {
dwMipSize /= 4;
}
// If this is the first face, set the cube face size
if (face == XTL::D3DCUBEMAP_FACE_POSITIVE_X) {
cubeFaceSize = ROUND_UP(textureDepth * dwMipSize, X_D3DTEXTURE_CUBEFACE_ALIGNMENT);
}
// Move to the next mip-map and calculate dimensions for the next iteration
mipDataOffset += dwMipSize;
if (mipWidth > minSize) {
mipWidth /= 2;
mipRowPitch /= 2;
}
if (mipHeight > minSize) {
mipHeight /= 2;
}
if (mipDepth > 1) {
mipDepth /= 2;
}
}
// Move to the next face
pData += cubeFaceSize;
}
LOG_TEST_CASE("Could not find Surface within Texture, falling back to Level = 0, Face = D3DCUBEMAP_FACE_POSITIVE_X");
Level = 0;
Face = XTL::D3DCUBEMAP_FACE_POSITIVE_X;
}
// Wrapper function to allow calling without passing a face
void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pBaseTexture, UINT& Level)
{
XTL::D3DCUBEMAP_FACES face;
GetSurfaceFaceAndLevelWithinTexture(pSurface, pBaseTexture, Level, face);
}
bool ConvertD3DTextureToARGBBuffer( bool ConvertD3DTextureToARGBBuffer(
XTL::X_D3DFORMAT X_Format, XTL::X_D3DFORMAT X_Format,
uint8_t *pSrc, uint8_t *pSrc,
@ -5052,27 +5140,46 @@ void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iText
// Don't init the Parent if the Surface and Surface Parent formats differ // Don't init the Parent if the Surface and Surface Parent formats differ
// Happens in some Outrun 2006 SetRenderTarget calls // Happens in some Outrun 2006 SetRenderTarget calls
if (pParentXboxTexture && (pXboxSurface->Format == pParentXboxTexture->Format)) { if (pParentXboxTexture && (pXboxSurface->Format == pParentXboxTexture->Format)) {
// For surfaces with a parent texture, map these to a host texture first
XTL::IDirect3DBaseTexture *pParentHostBaseTexture = GetHostBaseTexture(pParentXboxTexture, D3DUsage, iTextureStage); XTL::IDirect3DBaseTexture *pParentHostBaseTexture = GetHostBaseTexture(pParentXboxTexture, D3DUsage, iTextureStage);
XTL::IDirect3DSurface* pNewHostSurface;
switch (pParentHostBaseTexture->GetType()) { switch (pParentHostBaseTexture->GetType()) {
case XTL::D3DRTYPE_VOLUMETEXTURE: { case XTL::D3DRTYPE_VOLUMETEXTURE: {
LOG_TEST_CASE("Using child surface of VolumeTexture");
// TODO // TODO
break; break;
} }
case XTL::D3DRTYPE_CUBETEXTURE: { case XTL::D3DRTYPE_CUBETEXTURE: {
// TODO
// test-case : Burnout // test-case : Burnout
break; auto pParentHostTexture = (XTL::IDirect3DCubeTexture*)pParentHostBaseTexture;
}
case XTL::D3DRTYPE_TEXTURE: { XTL::D3DCUBEMAP_FACES CubeMapFace = XTL::D3DCUBEMAP_FACE_POSITIVE_X;
// For surfaces with a parent texture, map these to a host texture first UINT SurfaceLevel = 0;
XTL::IDirect3DTexture *pParentHostTexture = (XTL::IDirect3DTexture *)pParentHostBaseTexture; GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel, CubeMapFace);
UINT SurfaceLevel = 0; // TODO : Derive actual level based on pXboxSurface->Data delta to pParentXboxTexture->Data
XTL::IDirect3DSurface *pNewHostSurface; HRESULT hRet = pParentHostTexture->GetCubeMapSurface(CubeMapFace, SurfaceLevel, &pNewHostSurface);
HRESULT hRet = pParentHostTexture->GetSurfaceLevel(SurfaceLevel, &pNewHostSurface);
DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetSurfaceLevel"); DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetSurfaceLevel");
if (hRet == D3D_OK) { if (hRet == D3D_OK) {
SetHostSurface(pXboxSurface, pNewHostSurface); SetHostSurface(pXboxSurface, pNewHostSurface);
EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created surface level (%u, 0x%.08X, 0x%.08X)", EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created CubeTexture surface level (Face: %u, Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)",
CubeMapFace, SurfaceLevel, pResource, pNewHostSurface);
return;
}
break;
}
case XTL::D3DRTYPE_TEXTURE: {
XTL::IDirect3DTexture* pParentHostTexture = (XTL::IDirect3DTexture*)pParentHostBaseTexture;
UINT SurfaceLevel = 0;
GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel);
HRESULT hRet = pParentHostTexture->GetSurfaceLevel(SurfaceLevel, &pNewHostSurface);
DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetSurfaceLevel");
if (hRet == D3D_OK) {
SetHostSurface(pXboxSurface, pNewHostSurface);
EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created Texture surface level (Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)",
SurfaceLevel, pResource, pNewHostSurface); SurfaceLevel, pResource, pNewHostSurface);
return; return;
} }
@ -5309,7 +5416,7 @@ void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iText
case XTL::X_D3DRTYPE_VOLUME: { case XTL::X_D3DRTYPE_VOLUME: {
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
// Note : Host D3D can only(?) retrieve a volue like this : // Note : Host D3D can only(?) retrieve a volume like this :
// hRet = pNewHostVolumeTexture->GetVolumeLevel(level, &pNewHostVolume); // hRet = pNewHostVolumeTexture->GetVolumeLevel(level, &pNewHostVolume);
// So, we need to do this differently - we need to step up to the containing VolumeTexture, // So, we need to do this differently - we need to step up to the containing VolumeTexture,
// and retrieve and convert all of it's GetVolumeLevel() slices. // and retrieve and convert all of it's GetVolumeLevel() slices.
@ -5408,9 +5515,11 @@ void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iText
} }
SetHostCubeTexture(pResource, pNewHostCubeTexture); SetHostCubeTexture(pResource, pNewHostCubeTexture);
// TODO : Because cube face surfaces can be used as a render-target, // TODO : Cube face surfaces can be used as a render-target,
// we should call SetHostSurface() on all 6 faces, so that when Xbox // so we need to associate host surfaces to each surface of this cube texture
// code accesses a face, the host counterpart is already registered! // However, we can't do it here: On Xbox, a new Surface is created on every call to
// GetCubeMapSurface, so it needs to be done at surface conversion time by looking up
// the parent CubeTexture
EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)",
ResourceTypeName, pResource, pNewHostCubeTexture); ResourceTypeName, pResource, pNewHostCubeTexture);
break; break;
@ -7950,92 +8059,6 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_LightEnable)
return hRet; return hRet;
} }
// NOTE: NOT A PATCH
// This is the code common to both GetCubeMapSurface/GetCubeMapSurface2
HRESULT D3DCubeTexture_GetCubeMapSurfaceCommon
(
XTL::X_D3DCubeTexture* pThis,
XTL::D3DCUBEMAP_FACES FaceType,
XTL::UINT Level,
XTL::X_D3DSurface** ppCubeMapSurface
)
{
// Now ppCubeMapSurface correctly points to an Xbox surface, while pThis points to an Xbox Cube Texture
// We can use this to tie the host resources for both together, allowing Cube Mapping actually work!
auto pHostCubeTexture = (XTL::IDirect3DCubeTexture*)GetHostResource(pThis, XTL::EmuXBFormatIsRenderTarget(GetXboxPixelContainerFormat(pThis)) ? D3DUSAGE_RENDERTARGET : 0);
XTL::IDirect3DSurface* pHostCubeMapSurface;
XTL::HRESULT hRet = pHostCubeTexture->GetCubeMapSurface(FaceType, Level, &pHostCubeMapSurface);
if (FAILED(hRet)) {
return hRet;
}
// Tie the Xbox CubeMapSurface and the host CubeMapSurface together
SetHostSurface(*ppCubeMapSurface, pHostCubeMapSurface);
return hRet;
}
// ******************************************************************
// * patch: IDirect3DCubeTexture8_GetCubeMapSurface
// ******************************************************************
HRESULT WINAPI XTL::EMUPATCH(D3DCubeTexture_GetCubeMapSurface)
(
X_D3DCubeTexture* pThis,
D3DCUBEMAP_FACES FaceType,
UINT Level,
X_D3DSurface** ppCubeMapSurface
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pThis)
LOG_FUNC_ARG(FaceType)
LOG_FUNC_ARG(Level)
LOG_FUNC_ARG(ppCubeMapSurface)
LOG_FUNC_END;
// First, we need to fetch the Xbox cubemap surface via a trampoline
HRESULT hRet;
XB_trampoline(HRESULT, WINAPI, D3DCubeTexture_GetCubeMapSurface, (X_D3DCubeTexture*, D3DCUBEMAP_FACES, UINT, X_D3DSurface**));
hRet = XB_D3DCubeTexture_GetCubeMapSurface(pThis, FaceType, Level, ppCubeMapSurface);
// If the Xbox call failed, we must fail too
if (FAILED(hRet)) {
RETURN(hRet);
}
hRet = D3DCubeTexture_GetCubeMapSurfaceCommon(pThis, FaceType, Level, ppCubeMapSurface);
RETURN(hRet);
}
// ******************************************************************
// * patch: IDirect3DCubeTexture8_GetCubeMapSurface2
// ******************************************************************
XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DCubeTexture_GetCubeMapSurface2)
(
X_D3DCubeTexture* pThis,
D3DCUBEMAP_FACES FaceType,
UINT Level
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pThis)
LOG_FUNC_ARG(FaceType)
LOG_FUNC_ARG(Level)
LOG_FUNC_END;
// First, we need to fetch the Xbox cubemap surface via a trampoline
XB_trampoline(X_D3DSurface*, WINAPI, D3DCubeTexture_GetCubeMapSurface2, (X_D3DCubeTexture*, D3DCUBEMAP_FACES, UINT));
X_D3DSurface* pCubeMapSurface = XB_D3DCubeTexture_GetCubeMapSurface2(pThis, FaceType, Level);
// If the Xbox call failed, we must fail too
if (pCubeMapSurface == nullptr) {
RETURN(NULL);
}
D3DCubeTexture_GetCubeMapSurfaceCommon(pThis, FaceType, Level, &pCubeMapSurface);
return pCubeMapSurface;
}
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetRenderTarget // * patch: D3DDevice_SetRenderTarget
// ****************************************************************** // ******************************************************************

View File

@ -55,8 +55,6 @@ const uint32_t PATCH_IS_FIBER = 1 << 4;
// This allows for the eventual importing of Dxbx symbol files and even IDA signatures too! // This allows for the eventual importing of Dxbx symbol files and even IDA signatures too!
std::map<const std::string, const xbox_patch_t> g_PatchTable = { std::map<const std::string, const xbox_patch_t> g_PatchTable = {
// Direct3D // Direct3D
PATCH_ENTRY("D3DCubeTexture_GetCubeMapSurface", XTL::EMUPATCH(D3DCubeTexture_GetCubeMapSurface), PATCH_HLE_D3D),
PATCH_ENTRY("D3DCubeTexture_GetCubeMapSurface2", XTL::EMUPATCH(D3DCubeTexture_GetCubeMapSurface2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Begin", XTL::EMUPATCH(D3DDevice_Begin), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_Begin", XTL::EMUPATCH(D3DDevice_Begin), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush", XTL::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_BeginPush", XTL::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush2", XTL::EMUPATCH(D3DDevice_BeginPush2), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_BeginPush2", XTL::EMUPATCH(D3DDevice_BeginPush2), PATCH_HLE_D3D),