d3d: apply aspect ratio correction to the backbuffer (#1956)
* d3d: apply aspect ratio correction to the backbuffer * d3d: optimise aspect ratio correction + allow run-time aspect change (eg: dashboard) * d3d: fix typo * d3d: fix borders on aspect ratio change + apply aspect ratio correction to FMV * d3d: fix indenting in SetAspectRatioScale * d3d: add comment to explain clear
This commit is contained in:
parent
79391fc55a
commit
ee6a61c364
|
@ -108,7 +108,10 @@ static bool g_bHack_UnlockFramerate = false; // ignore t
|
|||
static bool g_bHasDepth = false; // Does device have a Depth Buffer?
|
||||
static bool g_bHasStencil = false; // Does device have a Stencil Buffer?
|
||||
static DWORD g_dwPrimPerFrame = 0; // Number of primitives within one frame
|
||||
|
||||
static float g_AspectRatioScale = 1.0f;
|
||||
static UINT g_AspectRatioScaleWidth = 0;
|
||||
static UINT g_AspectRatioScaleHeight = 0;
|
||||
static D3DSURFACE_DESC g_HostBackBufferDesc;
|
||||
static Settings::s_video g_XBVideo;
|
||||
|
||||
// D3D based variables
|
||||
|
@ -2826,6 +2829,48 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer
|
|||
return CacheEntry;
|
||||
}
|
||||
|
||||
void UpdateHostBackBufferDesc()
|
||||
{
|
||||
IDirect3DSurface *pCurrentHostBackBuffer = nullptr;
|
||||
auto hRet = g_pD3DDevice->GetBackBuffer(
|
||||
0, // iSwapChain
|
||||
0, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer);
|
||||
|
||||
if (hRet != D3D_OK) {
|
||||
CxbxKrnlCleanup("Unable to get host backbuffer surface");
|
||||
}
|
||||
|
||||
hRet = pCurrentHostBackBuffer->GetDesc(&g_HostBackBufferDesc);
|
||||
if (hRet != D3D_OK) {
|
||||
pCurrentHostBackBuffer->Release();
|
||||
CxbxKrnlCleanup("Unable to determine host backbuffer dimensions");
|
||||
}
|
||||
|
||||
pCurrentHostBackBuffer->Release();
|
||||
}
|
||||
|
||||
void SetAspectRatioScale(xbox::X_D3DPRESENT_PARAMETERS* pPresentationParameters)
|
||||
{
|
||||
// NOTE: Some games use anamorphic widesceen (expecting a 4:3 surface to be displayed at 16:9)
|
||||
// For those, we *lie* about the default width, for the scaler
|
||||
// 720p / 1080i are *always* widescreen, and will have the correct backbuffer size, so we only
|
||||
// apply this 'hack' for non-hd resolutions
|
||||
g_AspectRatioScaleWidth = pPresentationParameters->BackBufferWidth;
|
||||
g_AspectRatioScaleHeight = pPresentationParameters->BackBufferHeight;
|
||||
|
||||
if (pPresentationParameters->Flags & X_D3DPRESENTFLAG_WIDESCREEN &&
|
||||
pPresentationParameters->BackBufferHeight < 720) {
|
||||
// Lie and pretend we are 1280x720, this works because this ratio is only used in calculations
|
||||
// and not used as actual raw input values
|
||||
g_AspectRatioScaleWidth = 1280;
|
||||
g_AspectRatioScaleHeight = 720;
|
||||
}
|
||||
|
||||
const auto imageAspect = (float)g_AspectRatioScaleWidth / (float)g_AspectRatioScaleHeight;
|
||||
const auto screenAspect = (float)g_HostBackBufferDesc.Width / (float)g_HostBackBufferDesc.Height;
|
||||
g_AspectRatioScale = screenAspect > imageAspect ? (float)g_HostBackBufferDesc.Height / (float)g_AspectRatioScaleHeight : (float)g_HostBackBufferDesc.Width / (float)g_AspectRatioScaleWidth;
|
||||
}
|
||||
|
||||
#define CXBX_D3DMULTISAMPLE_XSCALE(type) (((type) & xbox::X_D3DMULTISAMPLE_XSCALE_MASK) >> xbox::X_D3DMULTISAMPLE_XSCALE_SHIFT)
|
||||
#define CXBX_D3DMULTISAMPLE_YSCALE(type) (((type) & xbox::X_D3DMULTISAMPLE_YSCALE_MASK) >> xbox::X_D3DMULTISAMPLE_YSCALE_SHIFT)
|
||||
|
||||
|
@ -2953,6 +2998,10 @@ void Direct3D_CreateDevice_End()
|
|||
g_Xbox_D3DDevice = (DWORD*)it->second;
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateHostBackBufferDesc();
|
||||
SetAspectRatioScale(&g_EmuCDPD.XboxPresentationParameters);
|
||||
|
||||
// If the Xbox version of CreateDevice didn't call SetRenderTarget, we must derive the default backbuffer ourselves
|
||||
// This works because CreateDevice always sets the current render target to the Xbox Backbuffer
|
||||
// In later XDKs, it does this inline rather than by calling D3DDevice_SetRenderTarget
|
||||
|
@ -3166,6 +3215,9 @@ HRESULT WINAPI xbox::EMUPATCH(D3DDevice_Reset)
|
|||
// Store the new multisampling configuration
|
||||
SetXboxMultiSampleType(pPresentationParameters->MultiSampleType);
|
||||
|
||||
// Update scaling aspect ratio
|
||||
SetAspectRatioScale(pPresentationParameters);
|
||||
|
||||
// Since Reset will call create a new backbuffer surface, we can clear our current association
|
||||
// NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below.
|
||||
// We must not reset the values to nullptr, since the XDK will re-use the same addresses for the data headers
|
||||
|
@ -4990,14 +5042,27 @@ DWORD WINAPI xbox::EMUPATCH(D3DDevice_Swap)
|
|||
HRESULT hRet = g_pD3DDevice->GetBackBuffer(
|
||||
0, // iSwapChain
|
||||
0, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer);
|
||||
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer - Unable to get backbuffer surface!");
|
||||
if (hRet == D3D_OK) {
|
||||
assert(pCurrentHostBackBuffer != nullptr);
|
||||
|
||||
// Get backbuffer dimensions; TODO : remember this once, at creation/resize time
|
||||
D3DSURFACE_DESC BackBufferDesc;
|
||||
pCurrentHostBackBuffer->GetDesc(&BackBufferDesc);
|
||||
|
||||
// Clear the backbuffer surface, this prevents artifacts when switching aspect-ratio
|
||||
// Test-case: Dashboard
|
||||
IDirect3DSurface* pExistingRenderTarget = nullptr;
|
||||
hRet = g_pD3DDevice->GetRenderTarget(0, &pExistingRenderTarget);
|
||||
if (hRet == D3D_OK) {
|
||||
g_pD3DDevice->SetRenderTarget(0, pCurrentHostBackBuffer);
|
||||
g_pD3DDevice->Clear(
|
||||
/*Count=*/0,
|
||||
/*pRects=*/nullptr,
|
||||
D3DCLEAR_TARGET | (g_bHasDepth ? D3DCLEAR_ZBUFFER : 0) | (g_bHasStencil ? D3DCLEAR_STENCIL : 0),
|
||||
/*Color=*/0xFF000000, // TODO : Use constant for this
|
||||
/*Z=*/g_bHasDepth ? 1.0f : 0.0f,
|
||||
/*Stencil=*/0);
|
||||
g_pD3DDevice->SetRenderTarget(0, pExistingRenderTarget);
|
||||
}
|
||||
|
||||
// TODO: Implement a hot-key to change the filter?
|
||||
// Note: LoadSurfaceFilter Must be D3DTEXF_NONE, D3DTEXF_POINT or D3DTEXF_LINEAR
|
||||
// Before StretchRects we used D3DX_FILTER_POINT here, but that gave jagged edges in Dashboard.
|
||||
|
@ -5008,13 +5073,23 @@ DWORD WINAPI xbox::EMUPATCH(D3DDevice_Swap)
|
|||
|
||||
auto pXboxBackBufferHostSurface = GetHostSurface(g_pXbox_BackBufferSurface, D3DUSAGE_RENDERTARGET);
|
||||
if (pXboxBackBufferHostSurface) {
|
||||
// Blit Xbox BackBuffer to host BackBuffer
|
||||
// TODO: Respect aspect ratio
|
||||
// Calculate the target width/height
|
||||
const auto width = g_AspectRatioScaleWidth * g_AspectRatioScale;
|
||||
const auto height = g_AspectRatioScaleHeight * g_AspectRatioScale;
|
||||
|
||||
// Calculate the centered rectangle
|
||||
RECT dest{};
|
||||
dest.top = (LONG)((g_HostBackBufferDesc.Height - height) / 2);
|
||||
dest.left = (LONG)((g_HostBackBufferDesc.Width - width) / 2);
|
||||
dest.right = (LONG)(dest.left + width);
|
||||
dest.bottom = (LONG)(dest.top + height);
|
||||
|
||||
// Blit Xbox BackBuffer to host BackBuffer
|
||||
hRet = g_pD3DDevice->StretchRect(
|
||||
/* pSourceSurface = */ pXboxBackBufferHostSurface,
|
||||
/* pSourceRect = */ nullptr,
|
||||
/* pDestSurface = */ pCurrentHostBackBuffer,
|
||||
/* pDestRect = */ nullptr,
|
||||
/* pDestRect = */ &dest,
|
||||
/* Filter = */ LoadSurfaceFilter
|
||||
);
|
||||
|
||||
|
@ -5083,17 +5158,32 @@ DWORD WINAPI xbox::EMUPATCH(D3DDevice_Swap)
|
|||
float xScale, yScale;
|
||||
GetMultiSampleScale(xScale, yScale);
|
||||
|
||||
xScale = (float)BackBufferDesc.Width / ((float)XboxBackBufferWidth / xScale);
|
||||
yScale = (float)BackBufferDesc.Height / ((float)XboxBackBufferHeight / yScale);
|
||||
const auto width = g_AspectRatioScaleWidth * g_AspectRatioScale;
|
||||
const auto height = g_AspectRatioScaleHeight * g_AspectRatioScale;
|
||||
xScale = (float)width / ((float)XboxBackBufferWidth / xScale);
|
||||
yScale = (float)height / ((float)XboxBackBufferHeight / yScale);
|
||||
|
||||
// Scale the destination co-ordinates by the correct scale factor
|
||||
EmuDestRect.top = (LONG)(EmuDestRect.top * yScale);
|
||||
EmuDestRect.left = (LONG)(EmuDestRect.left * xScale);
|
||||
EmuDestRect.bottom = (LONG)(EmuDestRect.bottom * yScale);
|
||||
EmuDestRect.right = (LONG)(EmuDestRect.right * xScale);
|
||||
|
||||
// Finally, adjust to correct on-screen position (
|
||||
EmuDestRect.top += (LONG)((g_HostBackBufferDesc.Height - height) / 2);
|
||||
EmuDestRect.left += (LONG)((g_HostBackBufferDesc.Width - width) / 2);
|
||||
EmuDestRect.right += (LONG)((g_HostBackBufferDesc.Width - width) / 2);
|
||||
EmuDestRect.bottom += (LONG)((g_HostBackBufferDesc.Height - height) / 2);
|
||||
} else {
|
||||
// Use backbuffer width/height since that may differ from the Window size
|
||||
EmuDestRect.right = BackBufferDesc.Width;
|
||||
EmuDestRect.bottom = BackBufferDesc.Height;
|
||||
const auto width = g_AspectRatioScaleWidth * g_AspectRatioScale;
|
||||
const auto height = g_AspectRatioScaleHeight * g_AspectRatioScale;
|
||||
|
||||
// Calculate the centered rectangle
|
||||
EmuDestRect.top = (LONG)((g_HostBackBufferDesc.Height - height) / 2);
|
||||
EmuDestRect.left = (LONG)((g_HostBackBufferDesc.Width - width) / 2);
|
||||
EmuDestRect.right = (LONG)(EmuDestRect.left + width);
|
||||
EmuDestRect.bottom = (LONG)(EmuDestRect.top + height);
|
||||
}
|
||||
|
||||
// load the YUY2 into the backbuffer
|
||||
|
@ -5103,12 +5193,12 @@ DWORD WINAPI xbox::EMUPATCH(D3DDevice_Swap)
|
|||
// (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/285)
|
||||
{
|
||||
// Use our (bounded) copy when bounds exceed :
|
||||
if (EmuDestRect.right > (LONG)BackBufferDesc.Width) {
|
||||
EmuDestRect.right = (LONG)BackBufferDesc.Width;
|
||||
if (EmuDestRect.right > (LONG)g_HostBackBufferDesc.Width) {
|
||||
EmuDestRect.right = (LONG)g_HostBackBufferDesc.Width;
|
||||
}
|
||||
|
||||
if (EmuDestRect.bottom > (LONG)BackBufferDesc.Height) {
|
||||
EmuDestRect.bottom = (LONG)BackBufferDesc.Height;
|
||||
if (EmuDestRect.bottom > (LONG)g_HostBackBufferDesc.Height) {
|
||||
EmuDestRect.bottom = (LONG)g_HostBackBufferDesc.Height;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5149,7 +5239,6 @@ DWORD WINAPI xbox::EMUPATCH(D3DDevice_Swap)
|
|||
if (hRet != D3D_OK) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Couldn't load Xbox overlay to host surface : %X", hRet);
|
||||
} else {
|
||||
// TODO: Respect aspect ratio
|
||||
hRet = g_pD3DDevice->StretchRect(
|
||||
/* pSourceSurface = */ pTemporaryOverlaySurface,
|
||||
/* pSourceRect = */ &EmuSourRect,
|
||||
|
|
Loading…
Reference in New Issue