diff --git a/resource/Cxbx.rc b/resource/Cxbx.rc index 68a6add6b..641f567fa 100644 Binary files a/resource/Cxbx.rc and b/resource/Cxbx.rc differ diff --git a/src/Cxbx/ResCxbx.h b/src/Cxbx/ResCxbx.h index bfc4c47cb..43f491b45 100644 --- a/src/Cxbx/ResCxbx.h +++ b/src/Cxbx/ResCxbx.h @@ -135,6 +135,7 @@ #define ID_HACKS_UNCAPFRAMERATE 40097 #define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098 #define ID_HACKS_PATCHCPUFREQUENCY 40099 +#define ID_HACKS_SCALEVIEWPORT 40100 #define IDC_STATIC -1 // Next default values for new objects diff --git a/src/Cxbx/Wnd.cpp b/src/Cxbx/Wnd.cpp index 9e65c24e3..d7118cb85 100644 --- a/src/Cxbx/Wnd.cpp +++ b/src/Cxbx/Wnd.cpp @@ -45,8 +45,8 @@ Wnd::Wnd(HINSTANCE x_hInstance) : m_hInstance(x_hInstance) m_wndname = "Cxbx-Reloaded Generic Window"; m_x = 150; m_y = 150; - m_w = 320; - m_h = 240; + m_w = 640; + m_h = 480; m_parent = NULL; m_clsstyle = CS_HREDRAW | CS_VREDRAW; m_wndstyle = WS_CLIPCHILDREN | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE; diff --git a/src/Cxbx/WndMain.cpp b/src/Cxbx/WndMain.cpp index ee589eb8f..ccf934331 100644 --- a/src/Cxbx/WndMain.cpp +++ b/src/Cxbx/WndMain.cpp @@ -113,6 +113,52 @@ void WndMain::InitializeSettings() { #define TIMERID_FPS 0 #define TIMERID_LED 1 +void WndMain::ResizeWindow(HWND hwnd, bool bForGUI) +{ + RECT desktopRect; + RECT windowRect; + + // Set the window size back to it's GUI dimensions + m_w = 640; + m_h = 480; + if (!bForGUI) { + // For emulation, get the configured window dimensions + XBVideo XBVideoConf; + g_EmuShared->GetXBVideo(&XBVideoConf); + + const char* resolution = XBVideoConf.GetVideoResolution(); + if (2 != sscanf(resolution, "%d x %d", &m_w, &m_h)) { + DbgPrintf("Couldn't parse resolution : %s.\n", resolution); + } + } + + // TODO : Acknowledge DPI scaling here + GetWindowRect(GetDesktopWindow(), &desktopRect); + + // Limit width/height to desktop resolution + int dWidth = desktopRect.right - desktopRect.left; + int dHeight = desktopRect.bottom - desktopRect.top; + if (m_w > dWidth) + m_w = dWidth; + if (m_h > dHeight) + m_h = dHeight; + + // Center to desktop + m_x = desktopRect.left + ((desktopRect.right - desktopRect.left - m_w) / 2); + m_y = desktopRect.top + ((desktopRect.bottom - desktopRect.top - m_h) / 2); + + // Resize the window so it's client area can contain the requested resolution + windowRect = { m_x, m_y, m_x + m_w, m_y + m_h }; + AdjustWindowRectEx(&windowRect, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLong(hwnd, GWL_EXSTYLE)); + // TODO : For DPI screens, replace AdjustWindowRectEx by DwmGetWindowAttribute using DWMWA_EXTENDED_FRAME_BOUNDS + SetWindowPos(hwnd, 0, + windowRect.left, + windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + SWP_NOOWNERZORDER | SWP_NOZORDER); +} + WndMain::WndMain(HINSTANCE x_hInstance) : Wnd(x_hInstance), m_bCreated(false), @@ -130,9 +176,6 @@ WndMain::WndMain(HINSTANCE x_hInstance) : m_classname = "WndMain"; m_wndname = "Cxbx-Reloaded " _CXBX_VERSION; - m_w = 640; - m_h = 480; - m_XbeFilename = (char*)calloc(1, MAX_PATH); m_CxbxDebugFilename = (char*)calloc(1, MAX_PATH); @@ -142,16 +185,6 @@ WndMain::WndMain(HINSTANCE x_hInstance) : m_szRecentXbe[v] = 0; } - // center to desktop - { - RECT rect; - - GetWindowRect(GetDesktopWindow(), &rect); - - m_x = rect.left + (rect.right - rect.left)/2 - m_w/2; - m_y = rect.top + (rect.bottom - rect.top)/2 - m_h/2; - } - // load configuration from registry { DWORD dwDisposition, dwType, dwSize; @@ -197,6 +230,12 @@ WndMain::WndMain(HINSTANCE x_hInstance) : m_PatchCpuFrequency = 0; } + dwType = REG_DWORD; dwSize = sizeof(DWORD); + result = RegQueryValueEx(hKey, "HackScaleViewport", NULL, &dwType, (PBYTE)&m_ScaleViewport, &dwSize); + if (result != ERROR_SUCCESS) { + m_ScaleViewport = 0; + } + dwType = REG_DWORD; dwSize = sizeof(DWORD); result = RegQueryValueEx(hKey, "CxbxDebug", NULL, &dwType, (PBYTE)&m_CxbxDebug, &dwSize); if (result != ERROR_SUCCESS) { @@ -359,6 +398,9 @@ WndMain::~WndMain() dwType = REG_DWORD; dwSize = sizeof(DWORD); RegSetValueEx(hKey, "HackPatchCpuFrequency", 0, dwType, (PBYTE)&m_PatchCpuFrequency, dwSize); + dwType = REG_DWORD; dwSize = sizeof(DWORD); + RegSetValueEx(hKey, "HackScaleViewport", 0, dwType, (PBYTE)&m_ScaleViewport, dwSize); + dwType = REG_DWORD; dwSize = sizeof(DWORD); RegSetValueEx(hKey, "CxbxDebug", 0, dwType, (PBYTE)&m_CxbxDebug, dwSize); @@ -401,21 +443,10 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP SetMenu(hwnd, hMenu); } - // resize window so that client area := 640x480 - { - RECT cRect; - RECT wRect; + // Set window size to GUI dimensions + ResizeWindow(hwnd, /*bForGUI=*/true); - GetClientRect(hwnd, &cRect); - GetWindowRect(hwnd, &wRect); - - uint32 difW = (wRect.right - wRect.left) - (cRect.right); - uint32 difH = (wRect.bottom - wRect.top) - (cRect.bottom); - - MoveWindow(hwnd, wRect.left, wRect.top, difW + m_w, difH + m_h, TRUE); - } - - // initialize back buffer + // initialize back buffer { HDC hDC = GetDC(hwnd); @@ -477,8 +508,8 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP { CreateThread(NULL, NULL, CrashMonitorWrapper, (void*)this, NULL, NULL); // create the crash monitoring thread if (m_hwndChild == NULL) { - float fps = 0; - float mspf = 0; + float fps = 0.0f; + float mspf = 0.0f; int LedSequence[4] = { XBOX_LED_COLOUR_GREEN, XBOX_LED_COLOUR_GREEN, XBOX_LED_COLOUR_GREEN, XBOX_LED_COLOUR_GREEN }; g_EmuShared->SetCurrentMSpF(&mspf); g_EmuShared->SetCurrentFPS(&fps); @@ -654,13 +685,15 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP case VK_F9: { // Try to open the most recent Xbe if none is opened yet : - if (m_Xbe == nullptr) - OpenMRU(0); + if (!m_bIsStarted) { + if (m_Xbe == nullptr) + OpenMRU(0); - if (m_Xbe != nullptr) - if (!m_bIsStarted) + if (m_Xbe != nullptr) StartEmulation(hwnd, debuggerOn); + } } + break; default: { @@ -1338,6 +1371,11 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP RefreshMenus(); break; + case ID_HACKS_SCALEVIEWPORT: + m_ScaleViewport = !m_ScaleViewport; + RefreshMenus(); + break; + case ID_HELP_ABOUT: { ShowAboutDialog(hwnd); @@ -1768,6 +1806,9 @@ void WndMain::RefreshMenus() chk_flag = (m_PatchCpuFrequency) ? MF_CHECKED : MF_UNCHECKED; CheckMenuItem(settings_menu, ID_HACKS_PATCHCPUFREQUENCY, chk_flag); + + chk_flag = (m_ScaleViewport) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_HACKS_SCALEVIEWPORT, chk_flag); } // emulation menu @@ -2142,6 +2183,18 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState / g_EmuShared->SetUncapFramerate(&m_UncapFramerate); g_EmuShared->SetUseAllCores(&m_UseAllCores); g_EmuShared->SetPatchCpuFrequency(&m_PatchCpuFrequency); + g_EmuShared->SetScaleViewport(&m_ScaleViewport); + + if (m_ScaleViewport) { + // Set the window size to emulation dimensions + // Note : Doing this here assures the emulation process will use + // the configured dimensions (because if done inside the emulation + // process, that doesn't resize correctly sometimes) + // TODO : Instead of doing the resize before launch, move it + // towards CreateDevice emulation, using Xbox-requested dimensions + // (and fix the issue of incorrect resizing) + ResizeWindow(m_hwnd, /*bForGUI*/false); + } // shell exe { @@ -2211,6 +2264,8 @@ void WndMain::StopEmulation() UpdateCaption(); RefreshMenus(); + // Set the window size back to it's GUI dimensions + ResizeWindow(m_hwnd, /*bForGUI=*/true); } diff --git a/src/Cxbx/WndMain.h b/src/Cxbx/WndMain.h index 7182cb307..c479c1058 100644 --- a/src/Cxbx/WndMain.h +++ b/src/Cxbx/WndMain.h @@ -87,6 +87,11 @@ class WndMain : public Wnd static void SuggestFilename(const char *x_orig_filename, char *x_filename, char x_extension[4]); private: + // ****************************************************************** + // * resize the main window for either GUI or emulation + // ****************************************************************** + void ResizeWindow(HWND hwnd, bool bForGUI); + // ****************************************************************** // * after an xbe is loaded, some things must be updated // ****************************************************************** @@ -217,6 +222,7 @@ class WndMain : public Wnd int m_UncapFramerate; int m_UseAllCores; int m_PatchCpuFrequency; + int m_ScaleViewport; // ****************************************************************** // * debug output filenames diff --git a/src/CxbxKrnl/CxbxKrnl.cpp b/src/CxbxKrnl/CxbxKrnl.cpp index bc14d8ab0..a1a986abd 100644 --- a/src/CxbxKrnl/CxbxKrnl.cpp +++ b/src/CxbxKrnl/CxbxKrnl.cpp @@ -583,6 +583,7 @@ void PrintCurrentConfigurationLog() printf("Uncap Framerate: %s\n", g_UncapFramerate == 1 ? "On" : "Off"); printf("Run Xbox threads on all cores: %s\n", g_UseAllCores == 1 ? "On" : "Off"); printf("Patch CPU Frequency (rdtsc): %s\n", g_PatchCpuFrequency == 1 ? "On" : "Off"); + printf("Scale Xbox to host viewport (and back): %s\n", g_ScaleViewport == 1 ? "On" : "Off"); } printf("------------------------- END OF CONFIG LOG ------------------------\n"); @@ -1102,6 +1103,8 @@ __declspec(noreturn) void CxbxKrnlInit g_UseAllCores = !!HackEnabled; g_EmuShared->GetPatchCpuFrequency(&HackEnabled); g_PatchCpuFrequency = !!HackEnabled; + g_EmuShared->GetScaleViewport(&HackEnabled); + g_ScaleViewport = !!HackEnabled; } #ifdef _DEBUG_PRINT_CURRENT_CONF diff --git a/src/CxbxKrnl/Emu.cpp b/src/CxbxKrnl/Emu.cpp index 1423133ca..f10fdcb53 100644 --- a/src/CxbxKrnl/Emu.cpp +++ b/src/CxbxKrnl/Emu.cpp @@ -68,6 +68,7 @@ bool g_DisablePixelShaders = false; bool g_UncapFramerate = false; bool g_UseAllCores = false; bool g_PatchCpuFrequency = false; +bool g_ScaleViewport = false; // Delta added to host SystemTime, used in xboxkrnl::KeQuerySystemTime and xboxkrnl::NtSetSystemTime LARGE_INTEGER HostSystemTimeDelta = {}; diff --git a/src/CxbxKrnl/Emu.h b/src/CxbxKrnl/Emu.h index 62ad675d0..b825da40a 100644 --- a/src/CxbxKrnl/Emu.h +++ b/src/CxbxKrnl/Emu.h @@ -111,4 +111,5 @@ extern bool g_DisablePixelShaders; extern bool g_UncapFramerate; extern bool g_UseAllCores; extern bool g_PatchCpuFrequency; +extern bool g_ScaleViewport; #endif diff --git a/src/CxbxKrnl/EmuD3D8.cpp b/src/CxbxKrnl/EmuD3D8.cpp index 352495a3a..87030fcd6 100755 --- a/src/CxbxKrnl/EmuD3D8.cpp +++ b/src/CxbxKrnl/EmuD3D8.cpp @@ -99,9 +99,6 @@ static bool g_bSupportsFormatVolumeTexture[XTL::X_D3DFMT static bool g_bSupportsFormatCubeTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support surface format? static XTL::LPDIRECTDRAW7 g_pDD7 = NULL; // DirectDraw7 static XTL::DDCAPS g_DriverCaps = { 0 }; -static DWORD g_dwOverlayW = 640; // Cached Overlay Width -static DWORD g_dwOverlayH = 480; // Cached Overlay Height -static DWORD g_dwOverlayP = 640; // Cached Overlay Pitch static HBRUSH g_hBgBrush = NULL; // Background Brush static volatile bool g_bRenderWindowActive = false; static XBVideo g_XBVideo; @@ -923,7 +920,6 @@ void SetHostResource(XTL::X_D3DResource* pXboxResource, XTL::IDirect3DResource* hostResourceInfo.forceRehash = false; g_HostResources[key] = hostResourceInfo; - } XTL::IDirect3DSurface *GetHostSurface(XTL::X_D3DResource *pXboxResource) @@ -1119,6 +1115,40 @@ uint CxbxGetPixelContainerMipMapLevels return 1; } +uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer *pPixelContainer) +{ + DWORD Size = pPixelContainer->Size; + uint32_t Result; + + if (Size != 0) { + Result = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1; + } + else { + DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT; + + Result = 1 << l2w; + } + + return Result; +} + +uint32_t GetPixelContainerHeigth(XTL::X_D3DPixelContainer *pPixelContainer) +{ + DWORD Size = pPixelContainer->Size; + uint32_t Result; + + if (Size != 0) { + Result = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; + } + else { + DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT; + + Result = 1 << l2h; + } + + return Result; +} + VOID CxbxGetPixelContainerMeasures ( XTL::X_D3DPixelContainer *pPixelContainer, @@ -1387,36 +1417,24 @@ static DWORD WINAPI EmuRenderWindow(LPVOID lpVoid) // create the window { - DWORD dwStyle = (g_XBVideo.GetFullscreen() || (CxbxKrnl_hEmuParent == 0))? WS_OVERLAPPEDWINDOW : WS_CHILD; - - int nTitleHeight = GetSystemMetrics(SM_CYCAPTION); - int nBorderWidth = GetSystemMetrics(SM_CXSIZEFRAME); - int nBorderHeight = GetSystemMetrics(SM_CYSIZEFRAME); - - int x = 100, y = 100, nWidth = 640, nHeight = 480; - - nWidth += nBorderWidth*2; - nHeight += nBorderHeight*2 + nTitleHeight; - - sscanf(g_XBVideo.GetVideoResolution(), "%d x %d", &nWidth, &nHeight); - - if(g_XBVideo.GetFullscreen()) - { - x = y = nWidth = nHeight = 0; - dwStyle = WS_POPUP; - } - HWND hwndParent = GetDesktopWindow(); + DWORD dwStyle = WS_POPUP; + RECT windowRect = { 0 }; - if(!g_XBVideo.GetFullscreen()) - { - hwndParent = CxbxKrnl_hEmuParent; + if (!g_XBVideo.GetFullscreen()) { + hwndParent = CxbxKrnl_hEmuParent; + GetWindowRect(hwndParent, &windowRect); + dwStyle = (CxbxKrnl_hEmuParent == 0) ? WS_OVERLAPPEDWINDOW : WS_CHILD; } g_hEmuWindow = CreateWindow ( "CxbxRender", "Cxbx-Reloaded", - dwStyle, x, y, nWidth, nHeight, + dwStyle, + windowRect.left, + windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, hwndParent, NULL, GetModuleHandle(NULL), NULL ); } @@ -1805,25 +1823,51 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) { DbgPrintf("EmuD3D8: CreateDevice proxy thread received request.\n"); - if(g_EmuCDPD.bCreate) - { - // only one device should be created at once - // TODO: ensure all surfaces are somehow cleaned up? - if(g_pD3DDevice != nullptr) - { - DbgPrintf("EmuD3D8: CreateDevice proxy thread releasing old Device.\n"); + // only one device should be created at once + if (g_pD3DDevice != nullptr) { + DbgPrintf("EmuD3D8: CreateDevice proxy thread releasing old Device.\n"); - g_pD3DDevice->EndScene(); + g_pD3DDevice->EndScene(); - // Address DirectX Debug Runtime reported error in _DEBUG builds - // Direct3D8: (ERROR) :Not all objects were freed: the following indicate the types of unfreed objects. - #ifndef _DEBUG - while(g_pD3DDevice->Release() != 0); - #endif + for (auto &hostResourceIterator : g_HostResources) { + if (hostResourceIterator.second.pHostResource) { + (hostResourceIterator.second.pHostResource)->Release(); + } + } + g_HostResources.clear(); - g_pD3DDevice = nullptr; - } + // TODO: ensure all other resources are cleaned up too + g_EmuCDPD.hRet = g_pD3DDevice->Release(); + + // Address DirectX Debug Runtime reported error in _DEBUG builds + // Direct3D8: (ERROR) :Not all objects were freed: the following indicate the types of unfreed objects. + #ifndef _DEBUG + while(g_pD3DDevice->Release() != 0); + #endif + + g_pD3DDevice = nullptr; + + // cleanup overlay clipper + if (g_pDDClipper != nullptr) { + g_pDDClipper->Release(); + g_pDDClipper = nullptr; + } + + // cleanup directdraw surface + if (g_pDDSPrimary != nullptr) { + g_pDDSPrimary->Release(); + g_pDDSPrimary = nullptr; + } + + // cleanup directdraw + if (g_pDD7 != nullptr) { + g_pDD7->Release(); + g_pDD7 = nullptr; + } + } + + if (g_EmuCDPD.bCreate) { if(g_EmuCDPD.XboxPresentationParameters.BufferSurfaces[0] != NULL) EmuWarning("BufferSurfaces[0] : 0x%.08X", g_EmuCDPD.XboxPresentationParameters.BufferSurfaces[0]); @@ -1884,7 +1928,16 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) // retrieve resolution from configuration if(g_EmuCDPD.HostPresentationParameters.Windowed) { - sscanf(g_XBVideo.GetVideoResolution(), "%d x %d", &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, &g_EmuCDPD.HostPresentationParameters.BackBufferHeight); + const char* resolution = g_XBVideo.GetVideoResolution(); + if (2 != sscanf(resolution, "%d x %d", &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, &g_EmuCDPD.HostPresentationParameters.BackBufferHeight)) { + DbgPrintf("EmuCreateDeviceProxy: Couldn't parse resolution : %s.\n", resolution); + } + else { + if (g_EmuCDPD.HostPresentationParameters.BackBufferWidth == 640) + DbgPrintf("EmuCreateDeviceProxy: Default width wasn't updated.\n"); + if (g_EmuCDPD.HostPresentationParameters.BackBufferWidth == 480) + DbgPrintf("EmuCreateDeviceProxy: Default height wasn't updated.\n"); + } XTL::D3DDISPLAYMODE D3DDisplayMode; @@ -1897,11 +1950,20 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) { char szBackBufferFormat[16]; - sscanf(g_XBVideo.GetVideoResolution(), "%d x %d %*dbit %s (%d hz)", - &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, - &g_EmuCDPD.HostPresentationParameters.BackBufferHeight, - szBackBufferFormat, - &g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz); + const char* resolution = g_XBVideo.GetVideoResolution(); + if (4 != sscanf(resolution, "%d x %d %*dbit %s (%d hz)", + &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, + &g_EmuCDPD.HostPresentationParameters.BackBufferHeight, + szBackBufferFormat, + &g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz)) { + DbgPrintf("EmuCreateDeviceProxy: Couldn't parse resolution : %s.\n", resolution); + } + else { + if (g_EmuCDPD.HostPresentationParameters.BackBufferWidth == 640) + DbgPrintf("EmuCreateDeviceProxy: Default width wasn't updated.\n"); + if (g_EmuCDPD.HostPresentationParameters.BackBufferWidth == 480) + DbgPrintf("EmuCreateDeviceProxy: Default height wasn't updated.\n"); + } if(strcmp(szBackBufferFormat, "x1r5g5b5") == 0) g_EmuCDPD.HostPresentationParameters.BackBufferFormat = XTL::D3DFMT_X1R5G5B5; @@ -2151,48 +2213,10 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) // begin scene hRet = g_pD3DDevice->BeginScene(); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->BeginScene(2nd)"); - - // signal completion - g_EmuCDPD.bReady = false; } - else - { - // release direct3d - if(g_pD3DDevice != nullptr) - { - DbgPrintf("EmuD3D8: CreateDevice proxy thread releasing old Device.\n"); - g_pD3DDevice->EndScene(); - - g_EmuCDPD.hRet = g_pD3DDevice->Release(); - if(g_EmuCDPD.hRet == 0) - g_pD3DDevice = nullptr; - } - - // cleanup overlay clipper - if (g_pDDClipper != nullptr) - { - g_pDDClipper->Release(); - g_pDDClipper = nullptr; - } - - // cleanup directdraw surface - if(g_pDDSPrimary != nullptr) - { - g_pDDSPrimary->Release(); - g_pDDSPrimary = nullptr; - } - - // cleanup directdraw - if(g_pDD7 != nullptr) - { - g_pDD7->Release(); - g_pDD7 = nullptr; - } - - // signal completion - g_EmuCDPD.bReady = false; - } + // signal completion + g_EmuCDPD.bReady = false; } Sleep(1); @@ -3134,6 +3158,45 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer) *ppBackBuffer = EMUPATCH(D3DDevice_GetBackBuffer2)(BackBuffer); } +bool GetHostRenderTargetDimensions(DWORD *pHostWidth, DWORD *pHostHeight) +{ + XTL::IDirect3DSurface* pHostRenderTarget = nullptr; + + g_pD3DDevice->GetRenderTarget( +#ifdef CXBX_USE_D3D9 + 0, // RenderTargetIndex +#endif + &pHostRenderTarget); + + // The following can only work if we could retrieve a host render target + if (!pHostRenderTarget) + return false; + + // Get current host render target dimensions + XTL::D3DSURFACE_DESC HostRenderTarget_Desc; + pHostRenderTarget->GetDesc(&HostRenderTarget_Desc); + pHostRenderTarget->Release(); + + // Emulate field-rendering not by halving the host backbuffer, but by faking + // the host backbuffer to half-height, which results in a correct viewport scale : + if (g_EmuCDPD.XboxPresentationParameters.Flags & X_D3DPRESENTFLAG_FIELD) { + HostRenderTarget_Desc.Height /= 2; + } + + *pHostWidth = HostRenderTarget_Desc.Width; + *pHostHeight = HostRenderTarget_Desc.Height; + + return true; +} + +DWORD ScaleDWORD(DWORD Value, DWORD FromMax, DWORD ToMax) +{ + uint64_t tmp = Value; + tmp *= ToMax; + tmp /= FromMax; + return (DWORD)tmp; +} + // ****************************************************************** // * patch: D3DDevice_SetViewport // ****************************************************************** @@ -3146,42 +3209,41 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetViewport) LOG_FUNC_ONE_ARG(pViewport); - DWORD dwWidth = pViewport->Width; - DWORD dwHeight = pViewport->Height; + D3DVIEWPORT HostViewPort = *pViewport; - // resize to fit screen (otherwise crashes occur) - /*{ - if(dwWidth > 640) - { - EmuWarning("Resizing Viewport->Width to 640"); - ((D3DVIEWPORT*)pViewport)->Width = 640; - } + if (g_ScaleViewport) { +#if 0 // Disabled for now, as the Xbox code triggers an error-code 6 in uc_emu_start() + // Use a trampoline here, so GetViewport can be unpatched + XB_trampoline(VOID, WINAPI, D3DDevice_SetViewport, (CONST X_D3DVIEWPORT8 *)); + XB_D3DDevice_SetViewport(pViewport); +#endif - if(dwHeight > 480) - { - EmuWarning("Resizing Viewport->Height to 480"); - ((D3DVIEWPORT*)pViewport)->Height = 480; - } - }*/ + // Get current Xbox render target dimensions + DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXboxRenderTarget); + DWORD XboxRenderTarget_Height = GetPixelContainerHeigth(g_pXboxRenderTarget); - HRESULT hRet = g_pD3DDevice->SetViewport(pViewport); + // Get current host render target dimensions + DWORD HostRenderTarget_Width; + DWORD HostRenderTarget_Height; - // restore originals - /*{ - if(dwWidth > 640) - ((D3DVIEWPORT*)pViewport)->Width = dwWidth; + if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { - if(dwHeight > 480) - ((D3DVIEWPORT*)pViewport)->Height = dwHeight; - } + // Scale Xbox to host dimensions (avoiding hard-coding 640 x 480) + HostViewPort.X = ScaleDWORD(pViewport->X, XboxRenderTarget_Width, HostRenderTarget_Width); + HostViewPort.Y = ScaleDWORD(pViewport->Y, XboxRenderTarget_Height, HostRenderTarget_Height); + HostViewPort.Width = ScaleDWORD(pViewport->Width, XboxRenderTarget_Width, HostRenderTarget_Width); + HostViewPort.Height = ScaleDWORD(pViewport->Height, XboxRenderTarget_Height, HostRenderTarget_Height); + // TODO : Fix test-case Shenmue 2 (which halves height, leaving the bottom half unused) + HostViewPort.MinZ = pViewport->MinZ; // No need scale Z for now + HostViewPort.MaxZ = pViewport->MaxZ; + } + else { + EmuWarning("GetHostRenderTargetDimensions failed - SetViewport sets Xbox viewport instead!"); + } + } + HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetViewport"); - - if(FAILED(hRet)) - { - EmuWarning("Unable to set viewport! We're lying"); - hRet = D3D_OK; - }*/ } // ****************************************************************** @@ -3196,15 +3258,44 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetViewport) LOG_FUNC_ONE_ARG(pViewport); - HRESULT hRet = g_pD3DDevice->GetViewport(pViewport); + D3DVIEWPORT HostViewPort; + + HRESULT hRet = g_pD3DDevice->GetViewport(&HostViewPort); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetViewport"); - if(FAILED(hRet)) - { - EmuWarning("Unable to get viewport! - We're lying"); + if (!g_ScaleViewport) { + *pViewport = HostViewPort; + } + else { + // Note : We cannot return the Xbox viewport as set in EMUPATCH(D3DDevice_SetViewport) + // because various Xbox D3D functions reset the Xbox viewport. Since we call comparable + // functions on host D3D, the host viewport is better suited as a return value; + // We just need to scale the host viewport back to Xbox dimensions - the exact opposite + // operation from the up-scaling that happens in EMUPATCH(D3DDevice_SetViewport). - hRet = D3D_OK; - } + // Get current host render target dimensions + DWORD HostRenderTarget_Width; + DWORD HostRenderTarget_Height; + + if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { + + // Get current Xbox render target dimensions + DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXboxRenderTarget); + DWORD XboxRenderTarget_Height = GetPixelContainerHeigth(g_pXboxRenderTarget); + + // Scale host back to Xbox dimensions (avoiding hard-coding 640 x 480) + pViewport->X = ScaleDWORD(HostViewPort.X, HostRenderTarget_Width, XboxRenderTarget_Width); + pViewport->Y = ScaleDWORD(HostViewPort.Y, HostRenderTarget_Height, XboxRenderTarget_Height); + pViewport->Width = ScaleDWORD(HostViewPort.Width, HostRenderTarget_Width, XboxRenderTarget_Width); + pViewport->Height = ScaleDWORD(HostViewPort.Height, HostRenderTarget_Height, XboxRenderTarget_Height); + pViewport->MinZ = HostViewPort.MinZ; // No need scale Z for now + pViewport->MaxZ = HostViewPort.MaxZ; + } + else { + *pViewport = HostViewPort; + EmuWarning("GetHostRenderTargetDimensions failed - GetViewport returns host viewport instead!"); + } + } } // LTCG specific D3DDevice_GetViewportOffsetAndScale function... @@ -3244,6 +3335,10 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetViewportOffsetAndScale) LOG_FUNC_ARG(pScale) LOG_FUNC_END; + LOG_TEST_CASE("D3DDevice_GetViewportOffsetAndScale"); // Get us some test-cases + // Test case : Spongebob - Battle for Bikini Bottom + +#if 0 float fScaleX = 1.0f; float fScaleY = 1.0f; float fScaleZ = 1.0f; @@ -3251,22 +3346,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetViewportOffsetAndScale) float fOffsetY = 0.5 + 1.0/32; X_D3DVIEWPORT8 Viewport; - EMUPATCH(D3DDevice_GetViewport)(&Viewport); - - - pScale->x = 1.0f; - pScale->y = 1.0f; - pScale->z = 1.0f; - pScale->w = 1.0f; - - pOffset->x = 0.0f; - pOffset->y = 0.0f; - pOffset->z = 0.0f; - pOffset->w = 0.0f; - -/* pScale->x = (float)Viewport.Width * 0.5f * fScaleX; pScale->y = (float)Viewport.Height * -0.5f * fScaleY; pScale->z = (Viewport.MaxZ - Viewport.MinZ) * fScaleZ; @@ -3276,9 +3357,17 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetViewportOffsetAndScale) pOffset->y = (float)Viewport.Height * fScaleY * 0.5f + (float)Viewport.Y * fScaleY + fOffsetY; pOffset->z = Viewport.MinZ * fScaleZ; pOffset->w = 0; -*/ +#else + pScale->x = 1.0f; + pScale->y = 1.0f; + pScale->z = 1.0f; + pScale->w = 1.0f; - + pOffset->x = 0.0f; + pOffset->y = 0.0f; + pOffset->z = 0.0f; + pOffset->w = 0.0f; +#endif } // LTCG specific D3DDevice_SetShaderConstantMode function... // This uses a custom calling convention where parameter is passed in EAX @@ -4521,12 +4610,11 @@ void CreateHostResource(XTL::X_D3DResource *pResource, int iTextureStage, DWORD XTL::X_D3DPixelContainer *pPixelContainer = (XTL::X_D3DPixelContainer*)pResource; XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer); DWORD D3DUsage = 0; + XTL::D3DPOOL D3DPool = XTL::D3DPOOL_MANAGED; // TODO : Nuance D3DPOOL where/when needed - if (pPixelContainer == g_pXboxDepthStencil) { - if (EmuXBFormatIsDepthBuffer(X_Format)) - D3DUsage = D3DUSAGE_DEPTHSTENCIL; - else - EmuWarning("Updating DepthStencil %s with an incompatible format!", ResourceTypeName); + if (EmuXBFormatIsDepthBuffer(X_Format)) { + D3DUsage = D3DUSAGE_DEPTHSTENCIL; + D3DPool = XTL::D3DPOOL_DEFAULT; } else if (pPixelContainer == g_pXboxRenderTarget) { if (EmuXBFormatIsRenderTarget(X_Format)) @@ -4670,8 +4758,6 @@ void CreateHostResource(XTL::X_D3DResource *pResource, int iTextureStage, DWORD XTL::IDirect3DTexture *pNewHostTexture = nullptr; // for X_D3DRTYPE_TEXTURE XTL::IDirect3DVolumeTexture *pNewHostVolumeTexture = nullptr; // for X_D3DRTYPE_VOLUMETEXTURE XTL::IDirect3DCubeTexture *pNewHostCubeTexture = nullptr; // for X_D3DRTYPE_CUBETEXTURE - - XTL::D3DPOOL D3DPool = XTL::D3DPOOL_MANAGED; // TODO : Nuance D3DPOOL where/when needed HRESULT hRet; // Create the surface/volume/(volume/cube/)texture @@ -5220,11 +5306,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_UpdateOverlay) if (pSurface == NULL) { EmuWarning("pSurface == NULL!"); } else { - uint08 *pYUY2SourceBuffer = (uint08*)GetDataFromXboxResource(pSurface); - - g_dwOverlayW = (pSurface->Size & X_D3DSIZE_WIDTH_MASK) + 1; - g_dwOverlayH = ((pSurface->Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; - g_dwOverlayP = (((pSurface->Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * 64; + uint08 *pOverlayData = (uint08*)GetDataFromXboxResource(pSurface); + UINT OverlayWidth, OverlayHeight, OverlayDepth, OverlayRowPitch, OverlaySlicePitch; + CxbxGetPixelContainerMeasures( + (XTL::X_D3DPixelContainer *)pSurface, + 0, // dwMipMapLevel + &OverlayWidth, &OverlayHeight, &OverlayDepth, &OverlayRowPitch, &OverlaySlicePitch); RECT EmuSourRect; RECT EmuDestRect; @@ -5232,7 +5319,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_UpdateOverlay) if (SrcRect != NULL) { EmuSourRect = *SrcRect; } else { - SetRect(&EmuSourRect, 0, 0, g_dwOverlayW, g_dwOverlayH); + SetRect(&EmuSourRect, 0, 0, OverlayWidth, OverlayHeight); } if (DstRect != NULL) { @@ -5279,9 +5366,9 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_UpdateOverlay) /* pDestSurface = */ pCurrentHostBackBuffer, /* pDestPalette = */ nullptr, // Palette not needed for YUY2 /* pDestRect = */DstRect, // Either the unmodified original (can be NULL) or a pointer to our local variable - /* pSrcMemory = */ pYUY2SourceBuffer, // Source buffer + /* pSrcMemory = */ pOverlayData, // Source buffer /* SrcFormat = */ D3DFMT_YUY2, - /* SrcPitch = */ g_dwOverlayP, + /* SrcPitch = */ OverlayRowPitch, /* pSrcPalette = */ nullptr, // Palette not needed for YUY2 /* SrcRect = */ &EmuSourRect, /* Filter = */ D3DX_FILTER_POINT, // Dxbx note : D3DX_FILTER_LINEAR gives a smoother image, but 'bleeds' across borders @@ -6570,8 +6657,10 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShader) // 59 (c-37) used for screen space transformation. if(g_VertexShaderConstantMode != X_D3DSCM_NORESERVEDCONSTANTS) { - // TODO: Proper solution. - static float vScale[] = { (2.0f / 640), (-2.0f / 480), 0.0f, 0.0f }; + D3DVIEWPORT ViewPort; + g_pD3DDevice->GetViewport(&ViewPort); + + float vScale[] = { (2.0f / ViewPort.Width), (-2.0f / ViewPort.Height), 0.0f, 0.0f }; static float vOffset[] = { -1.0f, 1.0f, 0.0f, 1.0f }; #ifdef CXBX_USE_D3D9 diff --git a/src/CxbxKrnl/EmuD3D8Types.h b/src/CxbxKrnl/EmuD3D8Types.h index e452d84ed..0ba6f76b4 100755 --- a/src/CxbxKrnl/EmuD3D8Types.h +++ b/src/CxbxKrnl/EmuD3D8Types.h @@ -358,7 +358,12 @@ typedef enum _X_D3DSET_DEPTH_CLIP_PLANES_FLAGS } X_D3DSET_DEPTH_CLIP_PLANES_FLAGS; +#define X_D3DPRESENTFLAG_LOCKABLE_BACKBUFFER 0x00000001 +#define X_D3DPRESENTFLAG_WIDESCREEN 0x00000010 #define X_D3DPRESENTFLAG_INTERLACED 0x00000020 +#define X_D3DPRESENTFLAG_PROGRESSIVE 0x00000040 +#define X_D3DPRESENTFLAG_FIELD 0x00000080 +#define X_D3DPRESENTFLAG_10X11PIXELASPECTRATIO 0x00000100 typedef struct _X_D3DDISPLAYMODE { diff --git a/src/CxbxKrnl/EmuShared.h b/src/CxbxKrnl/EmuShared.h index c99042fc0..9ffedb314 100644 --- a/src/CxbxKrnl/EmuShared.h +++ b/src/CxbxKrnl/EmuShared.h @@ -128,6 +128,8 @@ class EmuShared : public Mutex void SetUseAllCores(int* value) { Lock(); m_UseAllCores = *value; Unlock(); } void GetPatchCpuFrequency(int* value) { Lock(); *value = m_PatchCpuFrequeny; Unlock(); } void SetPatchCpuFrequency(int* value) { Lock(); m_PatchCpuFrequeny = *value; Unlock(); } + void GetScaleViewport(int* value) { Lock(); *value = m_ScaleViewport; Unlock(); } + void SetScaleViewport(int* value) { Lock(); m_ScaleViewport = *value; Unlock(); } // ****************************************************************** // * MSpF/Benchmark values Accessors @@ -202,6 +204,7 @@ class EmuShared : public Mutex bool m_bMultiXbeFlag; bool m_bDebugging; int m_LedSequence[4]; + int m_ScaleViewport; }; // ******************************************************************