From ec0ada5614012f9d71c3b1054a3b5504c2ebfa0f Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sat, 16 May 2026 19:16:30 +0800
Subject: [PATCH 01/21] Refactor ddraw render backend into software surface +
directx12 swapchain
---
.gitignore | 1 +
Phobos.props | 2 +-
Phobos.vcxproj | 14 +
YRpp | 2 +-
src/Render/Functions.cpp | 556 +++++++++++++++
src/Render/Functions.h | 25 +
src/Render/Hooks.Mouse.cpp | 60 ++
src/Render/Hooks.Options.cpp | 15 +
src/Render/Hooks.Surface.cpp | 14 +
src/Render/Hooks.cpp | 217 ++++++
src/Render/Mouse.cpp | 332 +++++++++
src/Render/Mouse.h | 106 +++
src/Render/Options.cpp | 0
src/Render/Options.h | 15 +
src/Render/Renderer.cpp | 1231 ++++++++++++++++++++++++++++++++++
src/Render/Renderer.h | 142 ++++
src/Render/Surface.cpp | 286 ++++++++
src/Render/Surface.h | 99 +++
18 files changed, 3115 insertions(+), 2 deletions(-)
create mode 100644 src/Render/Functions.cpp
create mode 100644 src/Render/Functions.h
create mode 100644 src/Render/Hooks.Mouse.cpp
create mode 100644 src/Render/Hooks.Options.cpp
create mode 100644 src/Render/Hooks.Surface.cpp
create mode 100644 src/Render/Hooks.cpp
create mode 100644 src/Render/Mouse.cpp
create mode 100644 src/Render/Mouse.h
create mode 100644 src/Render/Options.cpp
create mode 100644 src/Render/Options.h
create mode 100644 src/Render/Renderer.cpp
create mode 100644 src/Render/Renderer.h
create mode 100644 src/Render/Surface.cpp
create mode 100644 src/Render/Surface.h
diff --git a/.gitignore b/.gitignore
index ea89168d2d..913d5a2244 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@ out
.venv/
debug.log
+Phobos.log
\ No newline at end of file
diff --git a/Phobos.props b/Phobos.props
index bee83b6da5..e24e1ac0b2 100644
--- a/Phobos.props
+++ b/Phobos.props
@@ -30,7 +30,7 @@
$(Configuration)\
$(Configuration)\IntDir\
- dbghelp.lib;onecore.lib
+ dbghelp.lib;onecore.lib;imm32.lib
diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index 5d2d15eca0..3ea12b25cf 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -265,6 +265,15 @@
+
+
+
+
+
+
+
+
+
@@ -388,6 +397,11 @@
+
+
+
+
+
diff --git a/YRpp b/YRpp
index 613922aa10..171250122a 160000
--- a/YRpp
+++ b/YRpp
@@ -1 +1 @@
-Subproject commit 613922aa10f47d547baa00ab4b67e04b6b5de4b7
+Subproject commit 171250122ad6bc5b461b2b896f96eb6ec2d0ba16
diff --git a/src/Render/Functions.cpp b/src/Render/Functions.cpp
new file mode 100644
index 0000000000..da3c6cb5ad
--- /dev/null
+++ b/src/Render/Functions.cpp
@@ -0,0 +1,556 @@
+#include "Functions.h"
+
+#include
+
+#include "Surface.h"
+#include "Renderer.h"
+#include "Mouse.h"
+#include "Options.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+bool __fastcall RenderDX::AllocateSurfaces(const RectangleStruct& hidden_rect, const RectangleStruct& composite_rect, const RectangleStruct& tile_rect, const RectangleStruct& sidebar_rect, bool hidden_first) {
+ Debug::Log("[RenderDX] Allocating new surfaces\n");
+
+ if (DSurface::Alternate) {
+ Debug::Log("[RenderDX] Deleting AlternateSurface\n");
+ GameDelete(DSurface::Alternate);
+ DSurface::Alternate = nullptr;
+ }
+
+ if (DSurface::Hidden) {
+ Debug::Log("[RenderDX] Deleting HiddenSurface\n");
+ GameDelete(DSurface::Hidden);
+ DSurface::Hidden = nullptr;
+ }
+
+ if (DSurface::Composite) {
+ Debug::Log("[RenderDX] Deleting CompositeSurface\n");
+ GameDelete(DSurface::Composite);
+ DSurface::Composite = nullptr;
+ }
+
+ if (DSurface::Tile) {
+ Debug::Log("[RenderDX] Deleting TileSurface\n");
+ GameDelete(DSurface::Tile);
+ DSurface::Tile = nullptr;
+ }
+
+ if (DSurface::Sidebar) {
+ Debug::Log("[RenderDX] Deleting SidebarSurface\n");
+ GameDelete(DSurface::Sidebar);
+ DSurface::Sidebar = nullptr;
+ }
+
+ if (hidden_first && hidden_rect.Width > 0 && hidden_rect.Height > 0) {
+ DSurface::Hidden = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ DSurface::Hidden->Fill(0);
+ Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ }
+
+ if (composite_rect.Width > 0 && composite_rect.Height > 0) {
+ DSurface::Composite = GameCreate(composite_rect.Width, composite_rect.Height);
+ DSurface::Composite->Fill(0);
+ Debug::Log("[RenderDX] CompositeSurface (%dx%d)\n", composite_rect.Width, composite_rect.Height);
+ }
+
+ if (tile_rect.Width > 0 && tile_rect.Height > 0) {
+ DSurface::Tile = GameCreate(tile_rect.Width, tile_rect.Height);
+ DSurface::Tile->Fill(0);
+ Debug::Log("[RenderDX] TileSurface (%dx%d)\n", tile_rect.Width, tile_rect.Height);
+ }
+
+ if (sidebar_rect.Width > 0 && sidebar_rect.Height > 0) {
+ DSurface::Sidebar = GameCreate(sidebar_rect.Width, sidebar_rect.Height);
+ DSurface::Sidebar->Fill(0);
+ Debug::Log("[RenderDX] SidebarSurface (%dx%d)\n", sidebar_rect.Width, sidebar_rect.Height);
+ }
+
+ if (!hidden_first && hidden_rect.Width > 0 && hidden_rect.Height > 0) {
+ DSurface::Hidden = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ DSurface::Hidden->Fill(0);
+ Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ }
+
+ if (hidden_rect.Width > 0 && hidden_rect.Height > 0) {
+ DSurface::Alternate = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ DSurface::Alternate->Fill(0);
+ Debug::Log("[RenderDX] AlternateSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ }
+
+ return true;
+}
+
+bool __fastcall RenderDX::SetVideoMode(HWND, int width, int height, int bits_per_pixel) {
+ Debug::Log("[RenderDX] Setting video mode to %dx%d@%d\n", width, height, bits_per_pixel);
+
+ if (!DXRenderer::Instance().IsRendererReady()) {
+ Debug::Log("[RenderDX] Renderer is not ready\n");
+ return false;
+ }
+
+ ResetVideoMode();
+ if (!DXRenderer::Instance().CreateRenderer(width, height, bits_per_pixel)) {
+ Debug::Log("[RenderDX] Failed to create renderer\n");
+ return false;
+ }
+
+ Drawing::RenderWidth = width;
+ Drawing::RenderHeight = height;
+ Drawing::RenderBitsPerPixel = bits_per_pixel;
+
+ RenderDX::UpdateScale();
+
+ return true;
+}
+
+void __fastcall RenderDX::ResetVideoMode() {
+ Debug::Log("[RenderDX] Resetting video mode\n");
+
+ DXRenderer::Instance().DestroyRenderer();
+
+ Drawing::RenderWidth = 0;
+ Drawing::RenderHeight = 0;
+ Drawing::RenderBitsPerPixel = 0;
+
+ RenderDX::ResetScale();
+}
+
+static bool WindowResizeInProgress = false;
+static bool DeferredWindowResize = false;
+static int DeferredWindowWidth = 0;
+static int DeferredWindowHeight = 0;
+
+static void RecalcMouseWindowRegion(bool rebuildCursor) {
+ if (!DXMouse::Instance)
+ return;
+
+ DXMouse::Instance->Recalc_Capture_Region();
+ if (rebuildCursor)
+ DXMouse::Instance->Rebuild_Cursor_Image();
+}
+
+static void ApplyWindowResize(int width, int height) {
+ DXRenderer::Instance().ResizeWindow(width, height);
+ RecalcMouseWindowRegion(true);
+}
+
+static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch (msg) {
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_MBUTTONDBLCLK:
+ case WM_MOUSEWHEEL:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP:
+ {
+ // Scale mouse inputs before they are processed by SDL or the game.
+ if (RenderDX::ShouldScale()) {
+ int x = GET_X_LPARAM(lparam);
+ int y = GET_Y_LPARAM(lparam);
+
+ x = RenderDX::ClientToRenderX(x);
+ y = RenderDX::ClientToRenderY(y);
+
+ lparam = MAKELPARAM(x, y);
+ }
+ break;
+ }
+
+ case WM_MOVE:
+ {
+ if (DXMouse::Instance) {
+ DXMouse::Instance->Recalc_Capture_Region();
+ }
+ return 0; // handled
+ }
+
+ case WM_ENTERSIZEMOVE:
+ {
+ WindowResizeInProgress = true;
+ DeferredWindowResize = false;
+ DeferredWindowWidth = 0;
+ DeferredWindowHeight = 0;
+ return 0; // handled
+ }
+
+ case WM_EXITSIZEMOVE:
+ {
+ WindowResizeInProgress = false;
+
+ if (DeferredWindowResize) {
+ int width = DeferredWindowWidth;
+ int height = DeferredWindowHeight;
+
+ RECT clientRect {};
+ if (::GetClientRect(hwnd, &clientRect)) {
+ width = clientRect.right - clientRect.left;
+ height = clientRect.bottom - clientRect.top;
+ }
+
+ if (width > 0 && height > 0)
+ ApplyWindowResize(width, height);
+
+ DeferredWindowResize = false;
+ DeferredWindowWidth = 0;
+ DeferredWindowHeight = 0;
+ }
+
+ return 0; // handled
+ }
+
+ case WM_SIZE:
+ {
+ const int width = LOWORD(lparam);
+ const int height = HIWORD(lparam);
+ if (wparam == SIZE_MINIMIZED || width == 0 || height == 0) {
+ DeferredWindowResize = false;
+ RecalcMouseWindowRegion(false);
+ }
+ else if (WindowResizeInProgress) {
+ DeferredWindowResize = true;
+ DeferredWindowWidth = width;
+ DeferredWindowHeight = height;
+ RecalcMouseWindowRegion(false);
+ }
+ else {
+ ApplyWindowResize(width, height);
+ }
+
+ return 0; // handled
+ }
+
+ case WM_SYSKEYDOWN:
+ {
+ // Handle Alt+Enter for fullscreen toggle
+ if (wparam == VK_RETURN && (lparam & (1 << 29))) {
+ DXRenderer::Instance().ToggleFullscreen();
+ if (DXMouse::Instance) {
+ DXMouse::Instance->Recalc_Capture_Region();
+ DXMouse::Instance->Rebuild_Cursor_Image();
+ }
+ return 0; // handled
+ }
+ break;
+ }
+
+ case WM_SETCURSOR:
+ {
+ // Prevent the system from setting the cursor when it's over our window, since we handle it ourselves.
+ if (LOWORD(lparam) == HTCLIENT) {
+ if (DXMouse::Instance)
+ DXMouse::Instance->Set_Cached_Cursor();
+ return TRUE; // handled
+ }
+ break;
+ }
+
+ case WM_ACTIVATEAPP:
+ {
+ if (DXRenderOptions::Config().PauseGameWhenLoseFocus)
+ break; // goto the original window procedure to allow the game to pause when losing focus
+ if (hwnd == Game::hWnd) {
+ Unsorted::ScenarioInit = true; // game is always active
+ if (wparam) {
+ Debug::Log("[RenderDX] Game window activated\n");
+ if (DXMouse::Instance)
+ DXMouse::Instance->Capture_Mouse();
+ }
+ else {
+ Debug::Log("[RenderDX] Game window deactivated\n");
+ if (DXMouse::Instance)
+ DXMouse::Instance->Release_Mouse();
+ }
+ }
+ return 0; // handled - prevent the game from pausing when the window is deactivated
+ }
+ }
+
+ // Call original window procedure for default processing
+ return reinterpret_cast(0x7775C0)(hwnd, msg, wparam, lparam);
+}
+
+void __fastcall RenderDX::CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height) {
+ Debug::Log("[RenderDX] Creating main window\n");
+ if (!DXRenderer::Instance().CreateMainWindow(instance, cmd_show, width, height, MainWindowProc)) {
+ Debug::Log("[RenderDX] Failed to create main window\n");
+ ::MessageBoxA(nullptr, "Failed to create main window", "Error", MB_ICONERROR);
+ ::ExitProcess(0xC0DEBEEF);
+ }
+}
+
+void __fastcall RenderDX::DestroyMainWindow() {
+ Debug::Log("[RenderDX] Destroying main window\n");
+ DXRenderer::Instance().DestroyMainWindow();
+}
+
+bool __fastcall RenderDX::UpdateScreen(Surface* surface) {
+ if (!surface) {
+ Debug::Log("[RenderDX] UpdateScreen called with null surface\n");
+ return false;
+ }
+
+ const bool shouldScale = ShouldScale();
+ DXRenderer::Instance().SetRenderScale(shouldScale);
+
+ // Retrieve the game surface data
+ if (void* pixels = surface->Lock(0, 0)) {
+ if (!DXRenderer::Instance().UploadSurfaceToTexture(pixels, surface->GetPitch())) {
+ Debug::Log("[RenderDX] Failed to upload surface to texture\n");
+ surface->Unlock();
+ return false;
+ }
+ surface->Unlock();
+ }
+
+ static bool scaled = ShouldScale();
+
+ // Extra process on scaling change
+ if (scaled != shouldScale) {
+ scaled = shouldScale;
+ if (DXMouse::Instance)
+ DXMouse::Instance->Rebuild_Cursor_Image();
+ }
+
+ DXRenderer::Instance().Present();
+
+ return true;
+}
+
+bool __fastcall RenderDX::ShouldScale() {
+ return Unsorted::SpecialDialog == 0 && Unsorted::WSDialogCount == 0;
+}
+
+static void RebuildDisplayState(const RectangleStruct& view_rect) {
+ auto temp = view_rect;
+ temp.X = GameOptionsClass::Instance.SidebarMode ? 0 : 168;
+ temp.Y = 16;
+ temp.Width -= 168;
+ temp.Height -= 16;
+
+ DSurface::ViewBounds = view_rect;
+ Drawing::RenderWidth = view_rect.Width;
+ Drawing::RenderHeight = view_rect.Height;
+
+ DSurface::Primary = DXSurface::CreatePrimary();
+
+ RenderDX::AllocateSurfaces(
+ view_rect,
+ RectangleStruct { 0,0,temp.Width,view_rect.Height },
+ RectangleStruct { 0,0,temp.Width,view_rect.Height },
+ RectangleStruct { 0,0,168,view_rect.Height },
+ false
+ );
+ DSurface::Temp = DSurface::Hidden;
+
+ if (DXMouse::Instance) {
+ DXMouse::Instance->Rebuild_Cursor_Image();
+ }
+
+ SidebarClass::Instance.Set_View_Dimensions(temp);
+ SidebarClass::Instance.Init_IO();
+ SidebarClass::Instance.Activate(1);
+ SidebarClass::Instance.InitGUI();
+ SidebarClass::Instance.MarkNeedsRedraw(2); // REDRAW_ALL
+ DXMouse::Instance->Show_Mouse();
+}
+
+bool __fastcall RenderDX::ChangeDisplayMode(int width, int height) {
+ Debug::Log("[RenderDX] Changing display mode to %dx%d\n", width, height);
+
+ // Save current window position
+ RectangleStruct old_rect = DSurface::ViewBounds;
+ if (old_rect.Width <= 0 || old_rect.Height <= 0) {
+ if (Drawing::RenderWidth > 0 && Drawing::RenderHeight > 0) {
+ Debug::Log("[RenderDX] Current view bounds are invalid, using RenderWidth/RenderHeight\n");
+ old_rect = RectangleStruct { 0, 0, Drawing::RenderWidth, Drawing::RenderHeight };
+ }
+ }
+
+ const int old_render_width = Drawing::RenderWidth;
+ const int old_render_height = Drawing::RenderHeight;
+ const int old_render_bpp = Drawing::RenderBitsPerPixel;
+
+ int old_window_x = 0;
+ int old_window_y = 0;
+ int old_window_width = DXRenderer::Instance().GetWindowWidth();
+ int old_window_height = DXRenderer::Instance().GetWindowHeight();
+
+ DXMouse::Instance->Hide_Mouse();
+
+ // Delete the old primary surface
+ if (DSurface::Primary) {
+ Debug::Log("[RenderDX] Deleting old primary surface\n");
+ GameDelete(DSurface::Primary);
+ DSurface::Primary = nullptr;
+ }
+
+ if (DXRenderer::Instance().IsWindowed()) {
+ int window_width = width;
+ int window_height = height;
+
+ RECT temp;
+ ::GetWindowRect(Game::hWnd, &temp);
+ old_window_x = temp.left;
+ old_window_y = temp.top;
+ old_window_width = temp.right - temp.left;
+ old_window_height = temp.bottom - temp.top;
+
+ int center_x = old_window_x + old_window_width / 2;
+ int center_y = old_window_y + old_window_height / 2;
+
+ int new_x = center_x - window_width / 2;
+ int new_y = center_y - window_height / 2;
+
+ DXRenderer::Instance().MoveWindow(new_x, new_y, window_width, window_height);
+
+ Debug::Log("[RenderDX] Moved window to (%d, %d) with size %dx%d\n", new_x, new_y, window_width, window_height);
+ }
+
+ // Recreate all intermediates
+ if (!SetVideoMode(Game::hWnd, width, height, 16)) {
+ if (DXRenderer::Instance().IsWindowed()) {
+ DXRenderer::Instance().MoveWindow(old_window_x, old_window_y, old_window_width, old_window_height);
+ Debug::Log("[RenderDX] Restore window to (%d, %d) with size %dx%d\n", old_window_x, old_window_y, old_window_width, old_window_height);
+ }
+
+ if (old_rect.X > 0 && old_rect.Y > 0 && old_render_width > 0 && old_render_height > 0) {
+ Debug::Log("[RenderDX] Restoring old display mode.\n");
+ if (!SetVideoMode(Game::hWnd, old_render_width, old_render_height, old_render_bpp)) {
+ Debug::Log("[RenderDX] Failed to restore old display mode.\n");
+ DXMouse::Instance->Show_Mouse();
+ return false;
+ }
+ RebuildDisplayState(old_rect);
+ }
+ else {
+ Debug::Log("[RenderDX] Old view bounds are invalid, cannot restore\n");
+ }
+
+ DXMouse::Instance->Show_Mouse();
+ return false;
+ }
+
+ RectangleStruct new_view_rect = { 0, 0, width, height };
+ RebuildDisplayState(new_view_rect);
+ Debug::Log("[RenderDX]: ViewBounds: %dx%d\n", width, height);
+ Debug::Log("[RenderDX] Mode change complete.\n");
+
+ return true;
+}
+
+static float ScaleX = 1.0f;
+static float ScaleY = 1.0f;
+static float ViewportX = 0.0f;
+static float ViewportY = 0.0f;
+
+float __fastcall RenderDX::GetXScale() {
+ return ScaleX;
+}
+
+float __fastcall RenderDX::GetYScale() {
+ return ScaleY;
+}
+
+int __fastcall RenderDX::ClientToRenderX(int x) {
+ if (Drawing::RenderWidth <= 0)
+ return x;
+
+ return std::clamp(static_cast((x - ViewportX) * ScaleX), 0, Drawing::RenderWidth - 1);
+}
+
+int __fastcall RenderDX::ClientToRenderY(int y) {
+ if (Drawing::RenderHeight <= 0)
+ return y;
+
+ return std::clamp(static_cast((y - ViewportY) * ScaleY), 0, Drawing::RenderHeight - 1);
+}
+
+void __fastcall RenderDX::UpdateScale() {
+ const float viewportWidth = DXRenderer::Instance().GetViewportWidth();
+ const float viewportHeight = DXRenderer::Instance().GetViewportHeight();
+ ViewportX = DXRenderer::Instance().GetViewportX();
+ ViewportY = DXRenderer::Instance().GetViewportY();
+
+ if (Drawing::RenderWidth <= 0 || Drawing::RenderHeight <= 0 || viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
+ ResetScale();
+ return;
+ }
+
+ ScaleX = static_cast(Drawing::RenderWidth) / viewportWidth;
+ ScaleY = static_cast(Drawing::RenderHeight) / viewportHeight;
+}
+
+void __fastcall RenderDX::ResetScale() {
+ ScaleX = 1.0f;
+ ScaleY = 1.0f;
+ ViewportX = 0.0f;
+ ViewportY = 0.0f;
+}
+
+int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD bitdepth) {
+ std::vector> modes;
+ DEVMODE devmode{};
+ DWORD mode_index = 0;
+
+ while (::EnumDisplaySettingsA(nullptr, mode_index++, &devmode)) {
+ const DWORD w = devmode.dmPelsWidth;
+ const DWORD h = devmode.dmPelsHeight;
+ const DWORD bpp = devmode.dmBitsPerPel;
+
+ if (w >= minw && h >= minh && w <= maxw && h <= maxh && bpp == bitdepth) {
+ modes.emplace_back(static_cast(w), static_cast(h));
+ }
+ }
+
+ if (modes.empty()) {
+ return nullptr;
+ }
+
+ std::sort(modes.begin(), modes.end());
+ modes.erase(std::unique(modes.begin(), modes.end()), modes.end());
+
+ const size_t count = modes.size();
+ const size_t bytes = sizeof(int) * (count * 2 + 1);
+
+ int* list = static_cast(YRMemory::Allocate(bytes));
+ std::memset(list, 0, bytes);
+
+ int* ptr = list;
+ for (const auto& mode : modes) {
+ *ptr++ = mode.first;
+ *ptr++ = mode.second;
+ }
+
+ return list;
+}
+
+void __fastcall RenderDX::MainProcHandlePaint() {
+ if (DXMouse::Instance && DSurface::Primary && DSurface::Hidden && DSurface::Composite) {
+ if (Unsorted::ScenarioStarted) {
+ GScreenClass::UpdatePrimarySurface(DXMouse::Instance->Is_Captured(), DSurface::Composite, nullptr);
+ SidebarClass::Instance.BlitSidebar(true);
+ }
+ else if (Game::IsMoviePlaying()) {
+ Game::BlitMovie();
+ }
+ else {
+ GScreenClass::UpdatePrimarySurface(DXMouse::Instance->Is_Captured(), DSurface::Hidden, nullptr);
+ }
+ }
+}
diff --git a/src/Render/Functions.h b/src/Render/Functions.h
new file mode 100644
index 0000000000..8f97794811
--- /dev/null
+++ b/src/Render/Functions.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include
+
+class Surface;
+
+class RenderDX {
+public:
+ static bool __fastcall AllocateSurfaces(const RectangleStruct& hidden_rect, const RectangleStruct& composite_rect, const RectangleStruct& tile_rect, const RectangleStruct& sidebar_rect, bool hidden_first);
+ static bool __fastcall SetVideoMode(HWND, int width, int height, int bits_per_pixel);
+ static void __fastcall ResetVideoMode();
+ static void __fastcall CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height);
+ static void __fastcall DestroyMainWindow();
+ static bool __fastcall UpdateScreen(Surface* surface);
+ static bool __fastcall ShouldScale();
+ static bool __fastcall ChangeDisplayMode(int width, int height);
+ static float __fastcall GetXScale();
+ static float __fastcall GetYScale();
+ static int __fastcall ClientToRenderX(int x);
+ static int __fastcall ClientToRenderY(int y);
+ static void __fastcall UpdateScale();
+ static void __fastcall ResetScale();
+ static int* __fastcall EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD bitdepth);
+ static void __fastcall MainProcHandlePaint();
+};
diff --git a/src/Render/Hooks.Mouse.cpp b/src/Render/Hooks.Mouse.cpp
new file mode 100644
index 0000000000..c60153197c
--- /dev/null
+++ b/src/Render/Hooks.Mouse.cpp
@@ -0,0 +1,60 @@
+#include "Mouse.h"
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+DEFINE_HOOK(0x6BDEF9, WinMain_CreateWWMouse, 0x5) {
+ DXMouse::Instance = GameCreate(DSurface::Primary, Game::hWnd);
+ R->EAX(DXMouse::Instance);
+ return 0x6BDF25;
+}
+
+static HANDLE MouseThread;
+static std::binary_semaphore MouseThreadSemaphore { 0 };
+
+static DWORD WINAPI _MouseThread(LPVOID) {
+ while (!MouseThreadSemaphore.try_acquire_for(std::chrono::milliseconds(10))) {
+ if (DXMouse::Instance)
+ DXMouse::Instance->Process_Mouse();
+ }
+ return 0;
+}
+
+static void __fastcall _DXMouse_StartMouseThread() {
+ MouseThread = ::CreateThread(nullptr, 0, _MouseThread, nullptr, 0, nullptr);
+ if (!MouseThread) {
+ MouseThreadSemaphore.release();
+ return;
+ }
+ ::SetThreadPriority(MouseThread, THREAD_PRIORITY_TIME_CRITICAL);
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x7B84F0, _DXMouse_StartMouseThread);
+
+static void __fastcall _DXMouse_EndMouseThread() {
+ MouseThreadSemaphore.release();
+ if (MouseThread) {
+ ::WaitForSingleObject(MouseThread, INFINITE);
+ ::CloseHandle(MouseThread);
+ MouseThread = nullptr;
+ }
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x7B86B0, _DXMouse_EndMouseThread);
+
+static void __fastcall _DXMouse_ProcessMouse(DXMouse* This) {
+ This->Process_Mouse();
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x7BA090, _DXMouse_ProcessMouse);
+
+DEFINE_HOOK_AGAIN(0x72429E, DXMouse_TooltipManager_GetMousePosition, 0xA);
+DEFINE_HOOK(0x724359, DXMouse_TooltipManager_GetMousePosition, 0xA) {
+ GET(ToolTipManager*, pThis, ESI);
+ pThis->CurrentMousePosition = DXMouse::Instance->Get_Mouse_Point();
+ R->EBX(&pThis->CurrentMousePosition);
+ return R->Origin() + 0x15;
+}
diff --git a/src/Render/Hooks.Options.cpp b/src/Render/Hooks.Options.cpp
new file mode 100644
index 0000000000..e25f51c332
--- /dev/null
+++ b/src/Render/Hooks.Options.cpp
@@ -0,0 +1,15 @@
+#include
+
+#include "Options.h"
+
+#include
+
+DEFINE_HOOK(0x6BC141, DXRender_LoadConfigFromRA2MD, 0x7)
+{
+ auto& config = DXRenderOptions::Config();
+ config.PreserveAspectRatio = CCINIClass::INI_RA2MD.ReadBool("DXRender", "PreserveAspectRatio", config.PreserveAspectRatio);
+ config.WindowedBorder = CCINIClass::INI_RA2MD.ReadBool("DXRender", "WindowedBorder", config.WindowedBorder);
+ config.StartFullscreen = CCINIClass::INI_RA2MD.ReadBool("DXRender", "StartFullscreen", config.StartFullscreen);
+ config.PauseGameWhenLoseFocus = CCINIClass::INI_RA2MD.ReadBool("DXRender", "PauseGameWhenLoseFocus", config.PauseGameWhenLoseFocus);
+ return 0;
+}
diff --git a/src/Render/Hooks.Surface.cpp b/src/Render/Hooks.Surface.cpp
new file mode 100644
index 0000000000..068cba82ac
--- /dev/null
+++ b/src/Render/Hooks.Surface.cpp
@@ -0,0 +1,14 @@
+#include "Surface.h"
+
+#include
+#include
+
+DEFINE_FUNCTION_JUMP(LJMP, 0x4BA770, DXSurface::CreatePrimary);
+
+DEFINE_JUMP(LJMP, 0x77747A, 0x777575); // Skip Restore_Check
+
+static DXSurface* __fastcall _DXSurface_CTOR(DXSurface* surface, void*, int width, int height, bool system_mem, bool enable_3d) {
+ return new(surface) DXSurface(width, height);
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x4BA5A0, _DXSurface_CTOR);
+
diff --git a/src/Render/Hooks.cpp b/src/Render/Hooks.cpp
new file mode 100644
index 0000000000..5697bb3de8
--- /dev/null
+++ b/src/Render/Hooks.cpp
@@ -0,0 +1,217 @@
+#include "Functions.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef CALL
+#undef CALL
+#endif
+
+// Main window creation
+DEFINE_FUNCTION_JUMP(LJMP, 0x777C30, RenderDX::CreateMainWindow);
+
+static BOOL WINAPI _ClientToScreen(HWND hWnd, LPPOINT lpPoint) {
+ return TRUE;
+}
+
+// Disable ClientToScreen
+DEFINE_PATCH_TYPED(void*, 0x7E14B8, _ClientToScreen);
+
+// But these 3 need to use the real ClientToScreen so that dialogs are where they should be.
+static void __fastcall CenterWindowIn(HWND window, HWND parent) {
+ RECT rcl;
+ ::GetClientRect(parent, &rcl);
+
+ if (parent == Game::hWnd) {
+ rcl.right = Drawing::RenderWidth;
+ rcl.bottom = Drawing::RenderHeight;
+ }
+
+ ::ClientToScreen(parent, reinterpret_cast(&rcl));
+ ::ClientToScreen(parent, reinterpret_cast(&rcl.right));
+ rcl.right -= rcl.left;
+ rcl.bottom -= rcl.top;
+
+ RECT rect;
+ ::GetClientRect(window, &rect);
+ ::ClientToScreen(window, reinterpret_cast(&rect));
+ ::ClientToScreen(window, reinterpret_cast(&rect.right));
+ rect.right -= rect.left;
+ rect.bottom -= rect.top;
+ int x = (rcl.right - rect.right + 1) / 2;
+ int y = (rcl.bottom - rect.bottom + 1) / 2;
+
+ x = std::max(x, 0);
+ y = std::max(y, 0);
+
+ ::SetWindowPos(window, nullptr, x, y, -1, -1, SWP_NOSIZE | SWP_NOZORDER);
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x777080, CenterWindowIn);
+
+static BOOL __fastcall ODMoveDialog(HWND window, int x, int y) {
+ int xpos;
+ int ypos;
+
+ RECT rect1;
+ rect1.left = 0;
+ rect1.top = 0;
+ rect1.right = *reinterpret_cast(0x8A00A4);
+ rect1.bottom = *reinterpret_cast(0x8A00A8);
+
+ ::ClientToScreen(Game::hWnd, reinterpret_cast(&rect1));
+ ::ClientToScreen(Game::hWnd, reinterpret_cast(&rect1.right));
+
+ RECT rect2;
+ ::GetWindowRect(window, &rect2);
+
+ rect2.right -= rect2.left;
+ rect2.bottom -= rect2.top;
+
+ if (x == -1) {
+ xpos = rect2.left - rect1.left;
+ }
+ else {
+ xpos = x;
+ }
+ rect2.left = xpos;
+
+ if (y == -1) {
+ ypos = rect2.top - rect1.top;
+ }
+ else {
+ ypos = y;
+ }
+ rect2.top = ypos;
+
+ return ::MoveWindow(window, rect2.left, rect2.top, rect2.right, rect2.bottom, FALSE);
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x623170, ODMoveDialog);
+
+static BOOL __fastcall WinDialog_GetRectangle(HWND hWnd, LPRECT rect) {
+ BOOL result = ::GetWindowRect(hWnd, rect);
+ if (result) {
+ RECT client;
+ ::GetClientRect(Game::hWnd, &client);
+ ::ClientToScreen(Game::hWnd, reinterpret_cast(&client));
+ rect->left -= client.left;
+ rect->right -= client.left;
+ rect->top -= client.top;
+ rect->bottom -= client.top;
+ }
+ return result;
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x775690, WinDialog_GetRectangle);
+
+// However this WinDialog_GetRectangle is used for drawing offset, so we need to use the original window rect
+static BOOL __fastcall _GetWindowRect(HWND hWnd, LPRECT rect) {
+ return ::GetWindowRect(hWnd, rect);
+}
+DEFINE_FUNCTION_JUMP(CALL, 0x610E77, _GetWindowRect);
+
+// All controls inside the window is repositioned by this function, fix it up too
+static BOOL __fastcall OD_MoveIngameWindowControls(HWND hWnd) {
+ if (!SessionClass::Instance.CurrentlyInGame)
+ return FALSE;
+
+ auto parent = ::GetParent(hWnd);
+
+ RECT rect;
+ RECT parentRect;
+ if (!parent || !::GetWindowRect(hWnd, &rect) || !::GetWindowRect(parent, &parentRect))
+ return FALSE;
+
+ int x = rect.left - parentRect.left + (parentRect.right - parentRect.left - 800) / 2;
+ int y = rect.top - parentRect.top + (parentRect.bottom - parentRect.top - 600) / 2;
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ return ::MoveWindow(hWnd, x, y, rect.right - rect.left, rect.bottom - rect.top, FALSE);
+}
+DEFINE_FUNCTION_JUMP(LJMP, 0x60B7A0, OD_MoveIngameWindowControls);
+
+DEFINE_JUMP(LJMP, 0x4A4830, 0x4A4848); // Skip Wait_Blit
+
+DEFINE_JUMP(LJMP, 0x4A4780, 0x4A4825); // Skip Set_DD_Palette
+
+// Rendering preps.
+DEFINE_FUNCTION_JUMP(LJMP, 0x533FD0, RenderDX::AllocateSurfaces);
+DEFINE_FUNCTION_JUMP(LJMP, 0x4A42F0, RenderDX::SetVideoMode);
+DEFINE_FUNCTION_JUMP(LJMP, 0x4A44F0, RenderDX::ResetVideoMode);
+DEFINE_FUNCTION_JUMP(LJMP, 0x560BF0, RenderDX::ChangeDisplayMode);
+
+// Update the window surface when the game updates its PrimarySurface
+DEFINE_HOOK(0x4F4B7E, DXRender_UpdateScreen_GScreenClass_Blit, 0x5) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ return 0;
+}
+
+DEFINE_HOOK(0x5D233A, DXRender_UpdateScreen_MSEngine_Blit_Rects, 0x5) {
+ const auto eflags = R->EFLAGS();
+ // not JLE
+ const auto zf = eflags & 0x40;
+ const auto sf = eflags & 0x80;
+ const auto of = eflags & 0x800;
+ if (!(zf || sf != of)) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ }
+ return 0;
+}
+
+DEFINE_HOOK(0x5D1F15, DXRender_UpdateScreen_MSEngine_Frame_Update, 0x5) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ return 0;
+}
+
+DEFINE_HOOK(0x690228, DXRender_UpdateScreen_ScoreClass_Call_Back_Delay, 0x6) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ return 0;
+}
+
+DEFINE_NAKED_HOOK(0x5C0477, DXRender_UpdateScreen_Movie_Blit_To_Screen) {
+ __asm {
+ call dword ptr[edx + 8]
+ mov ecx, dword ptr ds:[0x887308]
+ call RenderDX::UpdateScreen
+ pop edi
+ pop esi
+ pop ebx
+ add esp, 0x20
+ ret
+ }
+}
+
+// Windows controls
+static LRESULT CALLBACK OwnerDraw_Window_Procedure_(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ auto result = reinterpret_cast(0x610CA0)(hwnd, msg, wparam, lparam);
+ if (msg == WM_PAINT) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ }
+ return result;
+}
+DEFINE_PATCH_TYPED(void*, 0x60FF06, OwnerDraw_Window_Procedure_);
+
+DEFINE_HOOK_AGAIN(0x611FB0, DXRender_UpdateScreen_OwnerDraw_Window, 0x6);
+DEFINE_HOOK(0x61187D, DXRender_UpdateScreen_OwnerDraw_Window, 0xA) {
+ RenderDX::UpdateScreen(DSurface::Primary);
+ return 0;
+}
+
+DEFINE_HOOK(0x7776B5, MainWindowProc_WMPAINT, 0x6) {
+ RenderDX::MainProcHandlePaint();
+ return 0x7779B5;
+}
+
+// Call Set_Video_Mode even when windowed.
+DEFINE_JUMP(LJMP, 0x6BD9D9, 0x6BDA61);
+DEFINE_JUMP(LJMP, 0x6BDB16, 0x6BDB6D);
+
+// Disable DirectDraw.
+DEFINE_JUMP(LJMP, 0x4A3FD0, 0x4A4019); // Skip Prep_Direct_Draw
+DEFINE_FUNCTION_JUMP(LJMP, 0x4A4900, RenderDX::EnumDisplayModes);
diff --git a/src/Render/Mouse.cpp b/src/Render/Mouse.cpp
new file mode 100644
index 0000000000..5e20eacebf
--- /dev/null
+++ b/src/Render/Mouse.cpp
@@ -0,0 +1,332 @@
+#include "Mouse.h"
+
+#include
+#include
+#include
+
+#include "Functions.h"
+
+#include
+
+#include
+
+DXMouse::DXMouse(Surface* surface, HWND hwnd) {}
+
+DXMouse::~DXMouse() {
+ Delete_Cursor_Image();
+ if (Cursor) {
+ ::DestroyCursor(Cursor);
+ Cursor = nullptr;
+ }
+}
+
+void DXMouse::Set_Cursor(Point2D const& hotspot, SHPStruct const* cursor, int shape) {
+ if (cursor == nullptr || shape < 0 || shape >= cursor->Frames) {
+ Delete_Cursor_Image();
+ Set_System_Cursor();
+ return;
+ }
+
+ if (MouseShape == cursor && ShapeNumber == shape) {
+ return; // No change needed
+ }
+
+ if (cursor != MouseShape) {
+ Delete_Cursor_Image();
+ Convert_Custor_Image(cursor);
+ }
+
+ MouseShape = cursor;
+ ShapeNumber = shape;
+
+ const auto& info = CursorInfo[shape];
+
+ Hotspot = hotspot;
+ Point2D scaled_hotspot;
+ scaled_hotspot.X = std::clamp(Hotspot.X * Get_Cursor_Scale(), 0, info.Width - 1);
+ scaled_hotspot.Y = std::clamp(Hotspot.Y * Get_Cursor_Scale(), 0, info.Height - 1);
+
+ Replace_Cursor(Build_Cursor(info, scaled_hotspot.X, scaled_hotspot.Y));
+}
+
+bool DXMouse::Is_Hidden() const {
+ return !IsVisible;
+}
+
+void DXMouse::Hide_Mouse() {
+ Debug::Log("Hiding mouse cursor\n");
+
+ if (!IsVisible)
+ return;
+
+ ::SetCursor(nullptr);
+ IsVisible = false;
+}
+
+void DXMouse::Show_Mouse() {
+ Debug::Log("Showing mouse cursor\n");
+
+ if (IsVisible)
+ return;
+
+ ::SetCursor(Cursor);
+ IsVisible = true;
+}
+
+void DXMouse::Release_Mouse() {
+ if (!IsCaptured)
+ return;
+
+ ::ClipCursor(nullptr);
+ IsCaptured = false;
+}
+
+void DXMouse::Capture_Mouse() {
+ if (IsCaptured)
+ return;
+
+ RECT client_rect;
+ ::GetClientRect(Game::hWnd, &client_rect);
+ ::MapWindowPoints(Game::hWnd, nullptr, reinterpret_cast(&client_rect), 2);
+ ::ClipCursor(&client_rect);
+
+ IsCaptured = true;
+}
+
+bool DXMouse::Is_Captured() const {
+ return IsCaptured;
+}
+
+void DXMouse::Conditional_Hide_Mouse(RectangleStruct region) {
+ Hide_Mouse();
+}
+
+void DXMouse::Conditional_Show_Mouse() {
+ Show_Mouse();
+}
+
+int DXMouse::Get_Mouse_State() const {
+ return IsVisible ? 0 : -1;
+}
+
+int DXMouse::Get_Mouse_X() const {
+ return MouseX;
+}
+
+int DXMouse::Get_Mouse_Y() const {
+ return MouseY;
+}
+
+Point2D DXMouse::Get_Mouse_Point() const {
+ return Point2D { MouseX, MouseY };
+}
+
+void DXMouse::Set_Mouse_Point(int x, int y) {
+ MouseX = x;
+ MouseY = y;
+}
+
+// Hardware cursor drawing is handled by the OS, so these functions are no-ops.
+void DXMouse::Draw_Mouse(Surface* scr, bool issidebarsurface) {}
+
+void DXMouse::Erase_Mouse(Surface* scr, bool issidebarsurface) {}
+
+// Coordinate conversion is not needed when using hardware cursor, so this is a no-op.
+void DXMouse::Convert_Coordinate(int& x, int& y) const {}
+
+void DXMouse::Process_Mouse() {
+ // Update mouse position via GetCursorPos and ScreenToClient
+ if (!Unsorted::GameInFocus)
+ return;
+
+ POINT pt;
+ if (!::GetCursorPos(&pt)) {
+ return;
+ }
+
+ if (!::ScreenToClient(Game::hWnd, &pt)) {
+ return;
+ }
+
+ if (RenderDX::ShouldScale()) {
+ MouseX = RenderDX::ClientToRenderX(pt.x);
+ MouseY = RenderDX::ClientToRenderY(pt.y);
+ }
+ else {
+ MouseX = pt.x;
+ MouseY = pt.y;
+ }
+
+}
+
+void DXMouse::Recalc_Capture_Region() {
+ if (Is_Captured()) {
+ Release_Mouse();
+ Capture_Mouse();
+ }
+}
+
+void DXMouse::Set_Cached_Cursor() {
+ if (IsVisible)
+ ::SetCursor(Cursor);
+ else
+ ::SetCursor(nullptr);
+}
+
+void DXMouse::Rebuild_Cursor_Image() {
+ SHPStruct const* shape = MouseShape;
+ int number = ShapeNumber;
+
+ Delete_Cursor_Image();
+ Set_Cursor(Hotspot, shape, number);
+}
+
+void DXMouse::Delete_Cursor_Image() {
+ CursorInfo.clear();
+
+ MouseShape = nullptr;
+ ShapeNumber = 0;
+}
+
+void DXMouse::Convert_Custor_Image(SHPStruct const* cursor) {
+ if (!cursor || cursor->Frames <= 0)
+ return;
+
+ for (int i = 0; i < 256; ++i) {
+ const auto color = static_cast(FileSystem::MOUSE_PAL->PaletteData)[i];
+ auto clr = ColorStruct { color };
+ MousePalette[i] = ((i == 0 ? 0 : 255) << 24) | (clr.R << 16) | (clr.G << 8) | clr.B;
+ }
+
+ CursorInfo.resize(cursor->Frames);
+ for (int i = 0; i < cursor->Frames; ++i)
+ Shape_To_Cursor(cursor, i, CursorInfo[i]);
+}
+
+void DXMouse::Shape_To_Cursor(SHPStruct const* cursor, int frame, CursorData& result) {
+ int width = cursor->Width;
+ int height = cursor->Height;
+
+ std::vector original_colors;
+ original_colors.resize(width * height);
+
+ int scaled_width = static_cast(width * Get_Cursor_Scale());
+ int scaled_height = static_cast(height * Get_Cursor_Scale());
+
+ BITMAPV5HEADER bi {};
+ bi.bV5Size = sizeof(BITMAPV5HEADER);
+ bi.bV5Width = scaled_width;
+ bi.bV5Height = -scaled_height; // Negative height for top-down bitmap
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5RedMask = 0x00FF0000;
+ bi.bV5GreenMask = 0x0000FF00;
+ bi.bV5BlueMask = 0x000000FF;
+ bi.bV5AlphaMask = 0xFF000000;
+
+ HDC hdc = ::GetDC(nullptr);
+ void* dst = nullptr;
+ HBITMAP bitmap = ::CreateDIBSection(hdc, reinterpret_cast(&bi), DIB_RGB_COLORS, &dst, nullptr, 0);
+ ::ReleaseDC(nullptr, hdc);
+
+ if (!dst || !bitmap) {
+ return;
+ }
+
+ const auto* src = static_cast(cursor->GetPixels(frame));
+ const auto r = cursor->GetFrameBounds(frame);
+
+ if (cursor->HasCompression(frame)) {
+ const uint8_t* psrc = src;
+ for (int y = 0; y < r.Height; ++y) {
+ uint32_t* dst_row = original_colors.data() + (r.Y + y) * width + r.X;
+ int len = psrc[0] | (psrc[1] << 8);
+ int pos = 0;
+ for (int k = 2; k < len; ++k) {
+ uint8_t b = psrc[k];
+ if (b == 0) {
+ uint8_t count = psrc[++k];
+ for (int i = 0; i < count; ++i) {
+ dst_row[pos++] = MousePalette[0];
+ }
+ }
+ else
+ dst_row[pos++] = MousePalette[b];
+ }
+ psrc += len;
+ }
+ }
+ else {
+ for (int y = 0; y < r.Height; ++y) {
+ uint32_t* dst_row = original_colors.data() + (r.Y + y) * width + r.X;
+ const uint8_t* src_row = src + y * r.Width;
+ for (int x = 0; x < r.Width; ++x) {
+ const auto color = MousePalette[src_row[x]];
+ dst_row[x] = color;
+ }
+ }
+ }
+
+ Scale_Bitmap_Image(original_colors.data(), width, height, static_cast(dst), scaled_width, scaled_height);
+
+ HBITMAP mask = ::CreateBitmap(scaled_width, scaled_height, 1, 1, nullptr);
+
+ result.Width = scaled_width;
+ result.Height = scaled_height;
+ result.Color = bitmap;
+ result.Mask = mask;
+}
+
+void DXMouse::Scale_Bitmap_Image(const uint32_t* src_ptr, int src_w, int src_h, uint32_t* dst, int dst_w, int dst_h) {
+ // Using nearest neighbor scaling for simplicity
+ const uint64_t inc_y = (static_cast(src_h) << 16) / dst_h;
+ const uint64_t inc_x = (static_cast(src_w) << 16) / dst_w;
+
+ uint64_t pos_y = inc_y / 2;
+
+ for (int y = 0; y < dst_h; ++y) {
+ const uint64_t src_y = pos_y >> 16;
+ const uint32_t* src_row = src_ptr + src_y * src_w;
+
+ pos_y += inc_y;
+
+ uint64_t pos_x = inc_x / 2;
+
+ for (int x = 0; x < dst_w; ++x) {
+ const uint64_t src_x = pos_x >> 16;
+ pos_x += inc_x;
+ *dst++ = src_row[src_x];
+ }
+ }
+}
+
+
+void DXMouse::Replace_Cursor(HCURSOR cursor) {
+ auto old_cursor = std::exchange(Cursor, cursor);
+ ::SetCursor(Cursor);
+ if (old_cursor) {
+ ::DestroyCursor(old_cursor);
+ }
+}
+
+void DXMouse::Set_System_Cursor() { Replace_Cursor(::LoadCursorA(nullptr, IDC_ARROW)); }
+
+HCURSOR DXMouse::Build_Cursor(const CursorData& data, int hotspot_x, int hotspot_y) {
+ ICONINFO ii {};
+ ii.fIcon = FALSE;
+ ii.xHotspot = static_cast(hotspot_x);
+ ii.yHotspot = static_cast(hotspot_y);
+ ii.hbmColor = data.Color;
+ ii.hbmMask = data.Mask;
+
+ return static_cast(::CreateIconIndirect(&ii));
+}
+
+int DXMouse::Get_Cursor_Scale() {
+ if (!RenderDX::ShouldScale()) {
+ return 1;
+ }
+
+ return std::max(1, static_cast(std::round(1.0f / RenderDX::GetYScale())));
+}
diff --git a/src/Render/Mouse.h b/src/Render/Mouse.h
new file mode 100644
index 0000000000..26a508134f
--- /dev/null
+++ b/src/Render/Mouse.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include
+
+#include
+
+#include
+
+struct SHPStruct;
+class Surface;
+
+class Mouse {
+public:
+ virtual ~Mouse() {}
+ virtual void Set_Cursor(Point2D const& hotspot, SHPStruct const* cursor, int shape) = 0;
+ virtual bool Is_Hidden() const = 0;
+ virtual void Hide_Mouse() = 0;
+ virtual void Show_Mouse() = 0;
+ virtual void Release_Mouse() = 0;
+ virtual void Capture_Mouse() = 0;
+ virtual bool Is_Captured() const = 0;
+ virtual void Conditional_Hide_Mouse(RectangleStruct region) = 0;
+ virtual void Conditional_Show_Mouse() = 0;
+ virtual int Get_Mouse_State() const = 0;
+ virtual int Get_Mouse_X() const = 0;
+ virtual int Get_Mouse_Y() const = 0;
+ virtual Point2D Get_Mouse_Point() const = 0;
+ virtual void Set_Mouse_Point(int x, int y) = 0;
+ virtual void Draw_Mouse(Surface* scr, bool issidebarsurface = false) = 0;
+ virtual void Erase_Mouse(Surface* scr, bool issidebarsurface = false) = 0;
+ virtual void Convert_Coordinate(int& x, int& y) const = 0;
+};
+
+class DXMouse : public Mouse {
+public:
+ DEFINE_REFERENCE(DXMouse*, Instance, 0x887640u)
+
+ DXMouse(Surface* surface, HWND hwnd);
+
+ virtual ~DXMouse() override;
+ virtual void Set_Cursor(Point2D const& hotspot, SHPStruct const* cursor, int shape) override;
+ virtual bool Is_Hidden() const override;
+ virtual void Hide_Mouse() override;
+ virtual void Show_Mouse() override;
+ virtual void Release_Mouse() override;
+ virtual void Capture_Mouse() override;
+ virtual bool Is_Captured() const override;
+ virtual void Conditional_Hide_Mouse(RectangleStruct region) override;
+ virtual void Conditional_Show_Mouse() override;
+ virtual int Get_Mouse_State() const override;
+ virtual int Get_Mouse_X() const override;
+ virtual int Get_Mouse_Y() const override;
+ virtual Point2D Get_Mouse_Point() const override;
+ virtual void Set_Mouse_Point(int x, int y) override;
+ virtual void Draw_Mouse(Surface* scr, bool issidebarsurface = false) override;
+ virtual void Erase_Mouse(Surface* scr, bool issidebarsurface = false) override;
+ virtual void Convert_Coordinate(int& x, int& y) const override;
+
+ void Process_Mouse();
+ void Recalc_Capture_Region();
+ void Set_Cached_Cursor();
+
+ void Rebuild_Cursor_Image();
+private:
+ SHPStruct const* MouseShape { nullptr };
+ int ShapeNumber { 0 };
+
+ DWORD MousePalette[256] { 0 };
+
+ struct CursorData {
+ ~CursorData() {
+ if (Color) {
+ ::DeleteObject(Color);
+ }
+ if (Mask) {
+ ::DeleteObject(Mask);
+ }
+ }
+
+ int Width { 0 };
+ int Height { 0 };
+ HBITMAP Color { nullptr };
+ HBITMAP Mask { nullptr };
+ };
+ std::vector CursorInfo;
+
+ Point2D Hotspot { 0,0 };
+ HCURSOR Cursor { nullptr };
+
+ bool IsCaptured { false };
+ bool IsVisible { true };
+
+ int MouseX { 0 };
+ int MouseY { 0 };
+
+ void Delete_Cursor_Image();
+ void Convert_Custor_Image(SHPStruct const* cursor);
+ void Shape_To_Cursor(SHPStruct const* cursor, int frame, CursorData& result);
+ void Scale_Bitmap_Image(const uint32_t* src_ptr, int src_w, int src_h, uint32_t* dst, int dst_w, int dst_h);
+ void Replace_Cursor(HCURSOR cursor);
+ void Set_System_Cursor();
+ HCURSOR Build_Cursor(const CursorData& data, int hotspot_x, int hotspot_y);
+
+ static int Get_Cursor_Scale();
+
+};
diff --git a/src/Render/Options.cpp b/src/Render/Options.cpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/Render/Options.h b/src/Render/Options.h
new file mode 100644
index 0000000000..e46ac1cfbb
--- /dev/null
+++ b/src/Render/Options.h
@@ -0,0 +1,15 @@
+#pragma once
+
+struct DXRenderOptions
+{
+ static DXRenderOptions& Config()
+ {
+ static DXRenderOptions instance;
+ return instance;
+ }
+
+ bool PreserveAspectRatio { true };
+ bool WindowedBorder { true };
+ bool StartFullscreen { true };
+ bool PauseGameWhenLoseFocus { true };
+};
diff --git a/src/Render/Renderer.cpp b/src/Render/Renderer.cpp
new file mode 100644
index 0000000000..0508521930
--- /dev/null
+++ b/src/Render/Renderer.cpp
@@ -0,0 +1,1231 @@
+#include "Renderer.h"
+
+#include
+
+#include
+
+#include
+
+#include "Functions.h"
+#include "Options.h"
+
+#include
+
+DXRenderer& DXRenderer::Instance() {
+ static DXRenderer instance;
+ return instance;
+}
+
+static LONG_PTR GetConfiguredWindowedStyle(LONG_PTR style, bool visible) {
+ if (DXRenderOptions::Config().WindowedBorder)
+ style = (style & ~WS_POPUP) | WS_OVERLAPPEDWINDOW;
+ else
+ style = (style & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU)) | WS_POPUP;
+
+ if (visible)
+ style |= WS_VISIBLE;
+ else
+ style &= ~WS_VISIBLE;
+
+ return style;
+}
+
+static LONG_PTR GetConfiguredWindowedExStyle(LONG_PTR exStyle) {
+ if (DXRenderOptions::Config().WindowedBorder)
+ return exStyle;
+
+ return exStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
+}
+
+static bool GetMonitorRect(HMONITOR monitor, RECT& monitorRect) {
+ if (!monitor)
+ return false;
+
+ MONITORINFO monitorInfo {};
+ monitorInfo.cbSize = sizeof(monitorInfo);
+ if (!::GetMonitorInfoA(monitor, &monitorInfo))
+ return false;
+
+ monitorRect = monitorInfo.rcMonitor;
+ return true;
+}
+
+static bool GetPrimaryMonitorRect(RECT& monitorRect) {
+ POINT point { 0, 0 };
+ return GetMonitorRect(::MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY), monitorRect);
+}
+
+static bool GetNearestMonitorRect(HWND hwnd, RECT& monitorRect) {
+ return GetMonitorRect(::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), monitorRect);
+}
+
+static void CenterRectInMonitor(RECT& rect, const RECT& monitorRect) {
+ const int width = rect.right - rect.left;
+ const int height = rect.bottom - rect.top;
+
+ rect.left = monitorRect.left + (monitorRect.right - monitorRect.left - width) / 2;
+ rect.top = monitorRect.top + (monitorRect.bottom - monitorRect.top - height) / 2;
+ rect.right = rect.left + width;
+ rect.bottom = rect.top + height;
+}
+
+bool DXRenderer::CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height, WNDPROC proc) {
+ ::InitCommonControls();
+
+ WNDCLASSA wc {};
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = proc;
+ wc.hInstance = instance;
+ wc.hIcon = ::LoadIconA(instance, MAKEINTRESOURCEA(93));
+ wc.hCursor = ::LoadCursorA(nullptr, IDC_ARROW);
+ wc.lpszClassName = reinterpret_cast(0x849F48);
+ if (!::RegisterClassA(&wc)) {
+ Debug::Log("[RenderDX] Failed to register window class\n");
+ return false;
+ }
+
+ LONG_PTR style = GetConfiguredWindowedStyle(WS_OVERLAPPEDWINDOW, false);
+ LONG_PTR exStyle = GetConfiguredWindowedExStyle(0);
+ RECT rect = { 0, 0, width, height };
+ ::AdjustWindowRectEx(&rect, static_cast(style), FALSE, static_cast(exStyle));
+ bool centerWindow = false;
+ RECT monitorRect {};
+ if (GetPrimaryMonitorRect(monitorRect)) {
+ CenterRectInMonitor(rect, monitorRect);
+ centerWindow = true;
+ }
+
+ int window_width = rect.right - rect.left;
+ int window_height = rect.bottom - rect.top;
+ int window_x = centerWindow ? rect.left : CW_USEDEFAULT;
+ int window_y = centerWindow ? rect.top : CW_USEDEFAULT;
+
+ Game::hWnd = ::CreateWindowExA(static_cast(exStyle), wc.lpszClassName, wc.lpszClassName, static_cast(style), window_x, window_y, window_width, window_height, nullptr, nullptr, instance, nullptr);
+
+ if (!Game::hWnd) {
+ Debug::Log("[RenderDX] Failed to create main window\n");
+ return false;
+ }
+
+ // Disable clipping because we draw Win32 child windows as part of the main window
+ style = GetWindowLongPtrA(Game::hWnd, GWL_STYLE);
+ style &= ~(WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
+ SetWindowLongPtrA(Game::hWnd, GWL_STYLE, style);
+
+ Hwnd = Game::hWnd;
+ WindowWidth = window_width;
+ WindowHeight = window_height;
+
+ if (DXRenderOptions::Config().StartFullscreen)
+ ToggleFullscreen();
+
+ ::ShowWindow(Game::hWnd, cmd_show);
+ ::UpdateWindow(Game::hWnd);
+
+ Game::hIMC = ::ImmGetContext(Game::hWnd);
+ ::ImmAssociateContext(Game::hWnd, nullptr);
+
+ ::RegisterHotKey(Game::hWnd, 1, MOD_ALT | MOD_CONTROL | MOD_SHIFT, 'M');
+
+ // Gain focus for the game window to ensure it receives input
+ ::SetForegroundWindow(Game::hWnd);
+ Unsorted::GameInFocus = true;
+
+ if (!DXRenderer::Instance().LoadImports()) {
+ Debug::Log("[RenderDX] Failed to load required libraries\n");
+ return false;
+ }
+
+ return true;
+}
+
+void DXRenderer::DestroyMainWindow() {
+ if (!Hwnd)
+ return;
+
+ ::DestroyWindow(Hwnd);
+ Hwnd = nullptr;
+
+ DXRenderer::Instance().UnloadImports();
+}
+
+bool DXRenderer::IsRendererReady() {
+ return true;
+}
+
+bool DXRenderer::CreateRenderer(int width, int height, int bits_per_pixel) {
+ if (bits_per_pixel != 16) {
+ Debug::Log("[RenderDX] Unsupported bits per pixel: %d\n", bits_per_pixel);
+ return false;
+ }
+
+ RenderWidth = width;
+ RenderHeight = height;
+ if (WindowWidth <= 0)
+ WindowWidth = width;
+ if (WindowHeight <= 0)
+ WindowHeight = height;
+
+ UpdateViewportAndScissor();
+
+ if (!CreateDevice()) {
+ Debug::Log("[RenderDX] Failed to create D3D12 device\n");
+ return false;
+ }
+
+ if (!CreateCommandQueue()) {
+ Debug::Log("[RenderDX] Failed to create command queue\n");
+ return false;
+ }
+
+ if (!CreateSwapChain()) {
+ Debug::Log("[RenderDX] Failed to create swap chain\n");
+ return false;
+ }
+
+ if (!CreateRtvHeap()) {
+ Debug::Log("[RenderDX] Failed to create RTV descriptor heap\n");
+ return false;
+ }
+
+ if (!CreateRenderTargetViews()) {
+ Debug::Log("[RenderDX] Failed to create render target views\n");
+ return false;
+ }
+
+ if (!CreateSrvHeap()) {
+ Debug::Log("[RenderDX] Failed to create SRV descriptor heap\n");
+ return false;
+ }
+
+ if (!CreateSurfacePipeline()) {
+ Debug::Log("[RenderDX] Failed to create surface pipeline\n");
+ return false;
+ }
+
+ if (!CreateCommandObjects()) {
+ Debug::Log("[RenderDX] Failed to create command objects\n");
+ return false;
+ }
+
+ if (!CreateFenceObjects()) {
+ Debug::Log("[RenderDX] Failed to create fence objects\n");
+ return false;
+ }
+
+ if (!CreateFixedSurfaceGpuResources()) {
+ Debug::Log("[RenderDX] Failed to create fixed surface GPU resources\n");
+ return false;
+ }
+
+ return true;
+}
+
+void DXRenderer::DestroyRenderer() {
+ if (SurfaceTexture) {
+ SurfaceTexture.Reset();
+ }
+
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (SurfaceUploadBuffers[i] && SurfaceUploadMapped[i]) {
+ SurfaceUploadBuffers[i]->Unmap(0, nullptr);
+ SurfaceUploadMapped[i] = nullptr;
+ }
+ }
+
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (SurfaceUploadBuffers[i]) {
+ SurfaceUploadBuffers[i].Reset();
+ }
+ }
+
+ if (Fence) {
+ Fence.Reset();
+ }
+ if (FenceEvent) {
+ ::CloseHandle(FenceEvent);
+ FenceEvent = nullptr;
+ }
+
+ if (CommandList) {
+ CommandList.Reset();
+ }
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (CommandAllocators[i]) {
+ CommandAllocators[i].Reset();
+ }
+ }
+
+ if (PipelineState) {
+ PipelineState.Reset();
+ }
+ if (RootSignature) {
+ RootSignature.Reset();
+ }
+
+ if (SrvHeap) {
+ SrvHeap.Reset();
+ }
+
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (RenderTargets[i])
+ RenderTargets[i].Reset();
+ }
+
+ if (RtvHeap) {
+ RtvHeap.Reset();
+ }
+ RtvDescriptorSize = 0;
+
+ if (SwapChain) {
+ BOOL fullscreenState = FALSE;
+ Microsoft::WRL::ComPtr pTarget;
+ if (SUCCEEDED(SwapChain->GetFullscreenState(&fullscreenState, &pTarget)) && fullscreenState)
+ SwapChain->SetFullscreenState(FALSE, nullptr);
+
+ SwapChain.Reset();
+ }
+ FrameIndex = 0;
+
+ if (CommandQueue) {
+ CommandQueue.Reset();
+ }
+
+ if (Device) {
+ Device.Reset();
+ }
+ if (Factory) {
+ Factory.Reset();
+ }
+}
+
+bool DXRenderer::ResizeWindow(int width, int height) {
+ WindowWidth = width;
+ WindowHeight = height;
+
+ UpdateViewportAndScissor();
+ RenderDX::UpdateScale();
+
+ if (!Device || !SwapChain) {
+ return true; // No swap chain to resize, not an error.
+ }
+
+ if (!WaitForGpu()) {
+ Debug::Log("[RenderDX] Failed to wait for GPU before resizing swap chain.\n");
+ return false;
+ }
+
+ for (auto& target : RenderTargets) {
+ target.Reset();
+ }
+
+ if (FAILED(SwapChain->ResizeBuffers(kFrameCount, WindowWidth, WindowHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0))) {
+ Debug::Log("[RenderDX] Failed to resize swap chain buffers.\n");
+ return false;
+ }
+
+ FrameIndex = SwapChain->GetCurrentBackBufferIndex();
+ if (!CreateRenderTargetViews())
+ return false;
+
+ const UINT64 newFenceValue = Fence->GetCompletedValue() + 1;
+ FenceValues.fill(newFenceValue);
+
+ Debug::Log("[RenderDX] Swap chain resized successfully to %ux%u.\n", WindowWidth, WindowHeight);
+
+ return true;
+}
+
+void DXRenderer::ToggleFullscreen() {
+ Debug::Log("[RenderDX] Toggling fullscreen mode.\n");
+
+ if (!Hwnd)
+ return;
+
+ if (!Windowed) {
+ if (!HasWindowedState) {
+ Debug::Log("[RenderDX] Cannot restore windowed mode, no saved window state.\n");
+ return;
+ }
+
+ Windowed = true;
+
+ ::SetWindowLongPtrA(Hwnd, GWL_STYLE, GetConfiguredWindowedStyle(WindowedStyle, true));
+ ::SetWindowLongPtrA(Hwnd, GWL_EXSTYLE, GetConfiguredWindowedExStyle(WindowedExStyle));
+
+ const int width = WindowedRect.right - WindowedRect.left;
+ const int height = WindowedRect.bottom - WindowedRect.top;
+ int windowX = WindowedRect.left;
+ int windowY = WindowedRect.top;
+
+ if (!DXRenderOptions::Config().WindowedBorder) {
+ RECT monitorRect {};
+ if (GetNearestMonitorRect(Hwnd, monitorRect)) {
+ RECT centeredRect = { 0, 0, width, height };
+ CenterRectInMonitor(centeredRect, monitorRect);
+ WindowedRect = centeredRect;
+ windowX = centeredRect.left;
+ windowY = centeredRect.top;
+ }
+ }
+
+ ::SetWindowPos(Hwnd, nullptr, windowX, windowY, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+ Debug::Log("[RenderDX] Borderless fullscreen disabled.\n");
+ return;
+ }
+
+ if (!::GetWindowRect(Hwnd, &WindowedRect)) {
+ Debug::Log("[RenderDX] Failed to save window rectangle before entering borderless fullscreen.\n");
+ return;
+ }
+
+ WindowedStyle = GetConfiguredWindowedStyle(::GetWindowLongPtrA(Hwnd, GWL_STYLE), true);
+ WindowedExStyle = GetConfiguredWindowedExStyle(::GetWindowLongPtrA(Hwnd, GWL_EXSTYLE));
+ HasWindowedState = true;
+
+ RECT monitorRect {};
+ if (!GetNearestMonitorRect(Hwnd, monitorRect)) {
+ Debug::Log("[RenderDX] Failed to get monitor rectangle for borderless fullscreen.\n");
+ return;
+ }
+
+ const LONG_PTR borderlessStyle = (WindowedStyle & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU)) | WS_POPUP | WS_VISIBLE;
+ const LONG_PTR borderlessExStyle = WindowedExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
+
+ Windowed = false;
+
+ ::SetWindowLongPtrA(Hwnd, GWL_STYLE, borderlessStyle);
+ ::SetWindowLongPtrA(Hwnd, GWL_EXSTYLE, borderlessExStyle);
+
+ const int width = monitorRect.right - monitorRect.left;
+ const int height = monitorRect.bottom - monitorRect.top;
+ ::SetWindowPos(Hwnd, HWND_TOP, monitorRect.left, monitorRect.top, width, height, SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
+
+ Debug::Log("[RenderDX] Borderless fullscreen enabled.\n");
+}
+
+bool DXRenderer::UploadSurfaceToTexture(void* surface_data, int source_pitch) {
+ const int sourceRowBytes = RenderWidth * static_cast(sizeof(std::uint16_t));
+ if (source_pitch < sourceRowBytes) {
+ Debug::Log("[RenderDX] Source pitch %d is smaller than required row bytes %d.\n", source_pitch, sourceRowBytes);
+ return false;
+ }
+
+ if (!PopulateCommandListForCPUSurface(surface_data, source_pitch))
+ return false;
+
+ ID3D12CommandList* list[] = { CommandList.Get() };
+ CommandQueue->ExecuteCommandLists(1, list);
+ return true;
+}
+
+void DXRenderer::SetRenderScale(bool scale) {
+ if (ScaleRender == scale)
+ return;
+
+ ScaleRender = scale;
+ UpdateViewportAndScissor();
+ RenderDX::UpdateScale();
+}
+
+bool DXRenderer::Present() {
+ if (FAILED(SwapChain->Present(0, 0))) {
+ Debug::Log("[RenderDX] Failed to present swap chain.\n");
+ return false;
+ }
+
+ return MoveToNextFrame();
+}
+
+void DXRenderer::MoveWindow(int x, int y, int width, int height) {
+ if (Windowed && !DXRenderOptions::Config().WindowedBorder) {
+ RECT monitorRect {};
+ if (GetNearestMonitorRect(Hwnd, monitorRect)) {
+ RECT centeredRect = { 0, 0, width, height };
+ CenterRectInMonitor(centeredRect, monitorRect);
+ x = centeredRect.left;
+ y = centeredRect.top;
+ }
+ }
+
+ ::MoveWindow(Hwnd, x, y, width, height, TRUE);
+ WindowWidth = width;
+ WindowHeight = height;
+ UpdateViewportAndScissor();
+ RenderDX::UpdateScale();
+}
+
+bool DXRenderer::IsWindowed() const {
+ return Windowed;
+}
+
+int DXRenderer::GetWindowWidth() const {
+ return WindowWidth;
+}
+
+int DXRenderer::GetWindowHeight() const {
+ return WindowHeight;
+}
+
+float DXRenderer::GetViewportX() const {
+ return RenderViewportX;
+}
+
+float DXRenderer::GetViewportY() const {
+ return RenderViewportY;
+}
+
+float DXRenderer::GetViewportWidth() const {
+ return RenderViewportWidth;
+}
+
+float DXRenderer::GetViewportHeight() const {
+ return RenderViewportHeight;
+}
+
+DXRenderer::DXRenderer() {}
+
+DXRenderer::~DXRenderer() {}
+
+bool DXRenderer::LoadImports() {
+ D3D12Lib = ::LoadLibraryW(L"d3d12.dll");
+ if (!D3D12Lib) {
+ Debug::Log("[RenderDX] Failed to load d3d12.dll.\n");
+ return false;
+ }
+
+#if DXRENDER_DEBUG
+ FP_D3D12GetDebugInterface = reinterpret_cast(::GetProcAddress(D3D12Lib, "D3D12GetDebugInterface"));
+ if (!FP_D3D12GetDebugInterface) {
+ Debug::Log("[RenderDX] Failed to get address of D3D12GetDebugInterface.\n");
+ return false;
+ }
+#endif
+ FP_D3D12CreateDevice = reinterpret_cast(::GetProcAddress(D3D12Lib, "D3D12CreateDevice"));
+ if (!FP_D3D12CreateDevice) {
+ Debug::Log("[RenderDX] Failed to get address of D3D12CreateDevice.\n");
+ return false;
+ }
+ FP_D3D12SerializeRootSignature = reinterpret_cast(::GetProcAddress(D3D12Lib, "D3D12SerializeRootSignature"));
+ if (!FP_D3D12SerializeRootSignature) {
+ Debug::Log("[RenderDX] Failed to get address of D3D12SerializeRootSignature.\n");
+ return false;
+ }
+
+ DXGILib = ::LoadLibraryW(L"dxgi.dll");
+ if (!DXGILib) {
+ Debug::Log("[RenderDX] Failed to load dxgi.dll.\n");
+ return false;
+ }
+ FP_CreateDXGIFactory2 = reinterpret_cast(::GetProcAddress(DXGILib, "CreateDXGIFactory2"));
+ if (!FP_CreateDXGIFactory2) {
+ Debug::Log("[RenderDX] Failed to get address of CreateDXGIFactory2.\n");
+ return false;
+ }
+
+ D3DCompilerLib = ::LoadLibraryW(L"d3dcompiler_47.dll");
+ if (!D3DCompilerLib) {
+ Debug::Log("[RenderDX] Failed to load d3dcompiler_47.dll.\n");
+ return false;
+ }
+ FP_D3DCompile = reinterpret_cast(::GetProcAddress(D3DCompilerLib, "D3DCompile"));
+ if (!FP_D3DCompile) {
+ Debug::Log("[RenderDX] Failed to get address of D3DCompile.\n");
+ return false;
+ }
+
+ return true;
+}
+
+void DXRenderer::UnloadImports() {
+ if (D3DCompilerLib) {
+ ::FreeLibrary(D3DCompilerLib);
+ FP_D3DCompile = nullptr;
+ }
+ if (DXGILib) {
+ ::FreeLibrary(DXGILib);
+ FP_CreateDXGIFactory2 = nullptr;
+ }
+ if (D3D12Lib) {
+ ::FreeLibrary(D3D12Lib);
+#if DXRENDER_DEBUG
+ FP_D3D12GetDebugInterface = nullptr;
+#endif
+ FP_D3D12CreateDevice = nullptr;
+ FP_D3D12SerializeRootSignature = nullptr;
+ }
+}
+
+bool DXRenderer::CreateDevice() {
+ UINT dxgiFactoryFlags = 0;
+#if DXRENDER_DEBUG
+ Microsoft::WRL::ComPtr debugController;
+ if (SUCCEEDED(FP_D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
+ debugController->EnableDebugLayer();
+ }
+ dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
+#endif
+ if (FAILED(FP_CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&Factory)))) {
+ return false;
+ }
+ constexpr bool kUseWarpDevice = false;
+ if (kUseWarpDevice) {
+ Microsoft::WRL::ComPtr warpAdapter;
+ if (FAILED(Factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)))) {
+ Debug::Log("[RenderDX] Failed to create WARP adapter.\n");
+ return false;
+ }
+ if (FAILED(FP_D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
+ Debug::Log("[RenderDX] Failed to create WARP adapter.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] D3D12 WARP device created successfully.\n");
+ }
+ else {
+ Microsoft::WRL::ComPtr hardwareAdapter;
+ for (UINT adapterIndex = 0; Factory->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&hardwareAdapter)) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ hardwareAdapter->GetDesc1(&desc);
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
+ continue;
+ }
+ if (SUCCEEDED(FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
+ Debug::Log("[RenderDX] D3D12 device created successfully on adapter: %ls\n", desc.Description);
+ break;
+ }
+ }
+ if (!Device) {
+ return false;
+ }
+
+ Debug::Log("[RenderDX] D3D12 device created successfully.\n");
+ }
+
+ return true;
+}
+
+bool DXRenderer::CreateCommandQueue() {
+ D3D12_COMMAND_QUEUE_DESC queueDesc {};
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
+ queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+ queueDesc.NodeMask = 0;
+
+ if (FAILED(Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&CommandQueue)))) {
+ Debug::Log("[RenderDX] Failed to create command queue.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] Command queue created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateSwapChain() {
+ DXGI_SWAP_CHAIN_DESC1 desc {};
+ desc.Width = WindowWidth;
+ desc.Height = WindowHeight;
+ desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ desc.Stereo = FALSE;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = kFrameCount;
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
+ desc.Flags = 0;
+
+ Microsoft::WRL::ComPtr swapChain1;
+ if (FAILED(Factory->CreateSwapChainForHwnd(CommandQueue.Get(), Hwnd, &desc, nullptr, nullptr, &swapChain1))) {
+ Debug::Log("[RenderDX] Failed to create swap chain.\n");
+ return false;
+ }
+
+ if (FAILED(Factory->MakeWindowAssociation(Hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN))) {
+ Debug::Log("[RenderDX] Failed to set window association.\n");
+ return false;
+ }
+
+ if (FAILED(swapChain1->QueryInterface(IID_PPV_ARGS(&SwapChain)))) {
+ Debug::Log("[RenderDX] Failed to query IDXGISwapChain3 interface.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] Swap chain created successfully.\n");
+ FrameIndex = SwapChain->GetCurrentBackBufferIndex();
+ return true;
+}
+
+bool DXRenderer::CreateRtvHeap() {
+ D3D12_DESCRIPTOR_HEAP_DESC desc {};
+ desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+ desc.NumDescriptors = kFrameCount;
+ desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ desc.NodeMask = 0;
+
+ if (FAILED(Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&RtvHeap)))) {
+ Debug::Log("[RenderDX] Failed to create RTV descriptor heap.\n");
+ return false;
+ }
+
+ RtvDescriptorSize = Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ Debug::Log("[RenderDX] RTV descriptor heap created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateRenderTargetViews() {
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = RtvHeap->GetCPUDescriptorHandleForHeapStart();
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (FAILED(SwapChain->GetBuffer(i, IID_PPV_ARGS(&RenderTargets[i])))) {
+ Debug::Log("[RenderDX] Failed to get back buffer %u.\n", i);
+ return false;
+ }
+ Device->CreateRenderTargetView(RenderTargets[i].Get(), nullptr, rtvHandle);
+ rtvHandle.ptr += RtvDescriptorSize;
+ }
+
+ Debug::Log("[RenderDX] Render target views created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateSrvHeap() {
+ D3D12_DESCRIPTOR_HEAP_DESC desc {};
+ desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ desc.NumDescriptors = 1; // For surface texture SRV
+ desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ desc.NodeMask = 0;
+
+ if (FAILED(Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&SrvHeap)))) {
+ Debug::Log("[RenderDX] Failed to create SRV descriptor heap.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] SRV descriptor heap created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateSurfacePipeline() {
+ D3D12_DESCRIPTOR_RANGE srvRange {};
+ srvRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ srvRange.NumDescriptors = 1;
+ srvRange.BaseShaderRegister = 0;
+ srvRange.RegisterSpace = 0;
+ srvRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ D3D12_ROOT_PARAMETER rootParam {};
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParam.DescriptorTable.NumDescriptorRanges = 1;
+ rootParam.DescriptorTable.pDescriptorRanges = &srvRange;
+ rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ D3D12_STATIC_SAMPLER_DESC sampler {};
+ sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
+ sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.MipLODBias = 0.0f;
+ sampler.MaxAnisotropy = 1;
+ sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+ sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
+ sampler.MinLOD = 0.0f;
+ sampler.MaxLOD = D3D12_FLOAT32_MAX;
+ sampler.ShaderRegister = 0;
+ sampler.RegisterSpace = 0;
+ sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+
+ D3D12_ROOT_SIGNATURE_DESC rootSigDesc {};
+ rootSigDesc.NumParameters = 1;
+ rootSigDesc.pParameters = &rootParam;
+ rootSigDesc.NumStaticSamplers = 1;
+ rootSigDesc.pStaticSamplers = &sampler;
+ rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+ Microsoft::WRL::ComPtr rootSigBlob;
+ Microsoft::WRL::ComPtr errorBlob;
+
+ HRESULT hr = FP_D3D12SerializeRootSignature(
+ &rootSigDesc,
+ D3D_ROOT_SIGNATURE_VERSION_1,
+ &rootSigBlob,
+ &errorBlob
+ );
+
+ if (FAILED(hr)) {
+ if (errorBlob)
+ Debug::Log("[RenderDX] Root signature serialization error: %s\n", static_cast(errorBlob->GetBufferPointer()));
+ else
+ Debug::Log("[RenderDX] Unknown root signature serialization error.\n");
+ return false;
+ }
+
+ if (FAILED(Device->CreateRootSignature(0, rootSigBlob->GetBufferPointer(), rootSigBlob->GetBufferSize(), IID_PPV_ARGS(&RootSignature)))) {
+ Debug::Log("[RenderDX] Failed to create root signature.\n");
+ return false;
+ }
+
+ static constexpr const char* shaderSource = R"(
+Texture2D gSurface : register(t0);
+SamplerState gSampler : register(s0);
+
+struct VSOut
+{
+ float4 position : SV_Position;
+ float2 uv : TEXCOORD0;
+};
+
+VSOut VSMain(uint vertexId : SV_VertexID)
+{
+ VSOut o;
+
+ // Full-screen triangle vertices and UVs
+ float2 positions[3] =
+ {
+ float2(-1.0f, -1.0f),
+ float2(-1.0f, 3.0f),
+ float2( 3.0f, -1.0f)
+ };
+
+ float2 uvs[3] =
+ {
+ float2(0.0f, 1.0f),
+ float2(0.0f, -1.0f),
+ float2(2.0f, 1.0f)
+ };
+
+ o.position = float4(positions[vertexId], 0.0f, 1.0f);
+ o.uv = uvs[vertexId];
+
+ return o;
+}
+
+float4 PSMain(VSOut input) : SV_Target0
+{
+ return gSurface.Sample(gSampler, input.uv);
+}
+)";
+
+ auto vertexShader = CompileShader(shaderSource, "VSMain", "vs_5_0");
+ auto pixelShader = CompileShader(shaderSource, "PSMain", "ps_5_0");
+
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc {};
+ psoDesc.pRootSignature = RootSignature.Get();
+ psoDesc.VS.pShaderBytecode = vertexShader->GetBufferPointer();
+ psoDesc.VS.BytecodeLength = vertexShader->GetBufferSize();
+ psoDesc.PS.pShaderBytecode = pixelShader->GetBufferPointer();
+ psoDesc.PS.BytecodeLength = pixelShader->GetBufferSize();
+ psoDesc.BlendState.AlphaToCoverageEnable = FALSE;
+ psoDesc.BlendState.IndependentBlendEnable = FALSE;
+ D3D12_RENDER_TARGET_BLEND_DESC rtBlend {};
+ rtBlend.BlendEnable = FALSE;
+ rtBlend.LogicOpEnable = FALSE;
+ rtBlend.SrcBlend = D3D12_BLEND_ONE;
+ rtBlend.DestBlend = D3D12_BLEND_ZERO;
+ rtBlend.BlendOp = D3D12_BLEND_OP_ADD;
+ rtBlend.SrcBlendAlpha = D3D12_BLEND_ONE;
+ rtBlend.DestBlendAlpha = D3D12_BLEND_ZERO;
+ rtBlend.BlendOpAlpha = D3D12_BLEND_OP_ADD;
+ rtBlend.LogicOp = D3D12_LOGIC_OP_NOOP;
+ rtBlend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
+ psoDesc.BlendState.RenderTarget[0] = rtBlend;
+ psoDesc.SampleMask = UINT_MAX;
+ psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
+ psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
+ psoDesc.RasterizerState.FrontCounterClockwise = FALSE;
+ psoDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+ psoDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+ psoDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+ psoDesc.RasterizerState.DepthClipEnable = TRUE;
+ psoDesc.RasterizerState.MultisampleEnable = FALSE;
+ psoDesc.RasterizerState.AntialiasedLineEnable = FALSE;
+ psoDesc.RasterizerState.ForcedSampleCount = 0;
+ psoDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+ psoDesc.DepthStencilState.DepthEnable = FALSE;
+ psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
+ psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+ psoDesc.DepthStencilState.StencilEnable = FALSE;
+ psoDesc.DepthStencilState.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
+ psoDesc.DepthStencilState.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
+ psoDesc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
+ psoDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
+ psoDesc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
+ psoDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+ psoDesc.DepthStencilState.BackFace = psoDesc.DepthStencilState.FrontFace;
+ psoDesc.InputLayout = {};
+ psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ psoDesc.NumRenderTargets = 1;
+ psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
+ psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
+ psoDesc.SampleDesc.Count = 1;
+ psoDesc.SampleDesc.Quality = 0;
+
+ if (FAILED(Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&PipelineState)))) {
+ Debug::Log("[RenderDX] Failed to create pipeline state.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] Pipeline state created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateCommandObjects() {
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (FAILED(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&CommandAllocators[i])))) {
+ Debug::Log("[RenderDX] Failed to create command allocator %u.\n", i);
+ return false;
+ }
+ }
+
+ if (FAILED(Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, CommandAllocators[FrameIndex].Get(), PipelineState.Get(), IID_PPV_ARGS(&CommandList)))) {
+ Debug::Log("[RenderDX] Failed to create command list.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] Command objects created successfully.\n");
+ CommandList->Close(); // Command list needs to be closed before reset in the render loop.
+ return true;
+}
+
+bool DXRenderer::CreateFenceObjects() {
+ FenceValues.fill(0);
+
+ if (FAILED(Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Fence)))) {
+ Debug::Log("[RenderDX] Failed to create fence.\n");
+ return false;
+ }
+
+ FenceValues[FrameIndex] = 1;
+ FenceEvent = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+
+ if (!FenceEvent) {
+ Debug::Log("[RenderDX] Failed to create fence event.\n");
+ return false;
+ }
+
+ Debug::Log("[RenderDX] Fence objects created successfully.\n");
+ return true;
+}
+
+bool DXRenderer::CreateFixedSurfaceGpuResources() {
+ const UINT sourceRowBytes = RenderWidth * sizeof(std::uint16_t);
+ SurfaceUploadRowPitch = (sourceRowBytes + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1); // Align up
+ SurfaceUploadBufferSize = static_cast(SurfaceUploadRowPitch) * static_cast(RenderHeight);
+
+ D3D12_HEAP_PROPERTIES defaultHeap {};
+ defaultHeap.Type = D3D12_HEAP_TYPE_DEFAULT;
+ defaultHeap.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ defaultHeap.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ defaultHeap.CreationNodeMask = 1;
+ defaultHeap.VisibleNodeMask = 1;
+
+ D3D12_RESOURCE_DESC textureDesc {};
+ textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ textureDesc.Alignment = 0;
+ textureDesc.Width = RenderWidth;
+ textureDesc.Height = RenderHeight;
+ textureDesc.DepthOrArraySize = 1;
+ textureDesc.MipLevels = 1;
+ textureDesc.Format = DXGI_FORMAT_B5G6R5_UNORM;
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.SampleDesc.Quality = 0;
+ textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ SurfaceTextureState = D3D12_RESOURCE_STATE_COPY_DEST;
+
+ if (FAILED(Device->CreateCommittedResource(
+ &defaultHeap,
+ D3D12_HEAP_FLAG_NONE,
+ &textureDesc,
+ SurfaceTextureState,
+ nullptr,
+ IID_PPV_ARGS(&SurfaceTexture)
+ ))) {
+ Debug::Log("[RenderDX] Failed to create surface texture resource.\n");
+ return false;
+ }
+
+ D3D12_HEAP_PROPERTIES uploadHeap {};
+ uploadHeap.Type = D3D12_HEAP_TYPE_UPLOAD;
+ uploadHeap.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ uploadHeap.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ uploadHeap.CreationNodeMask = 1;
+ uploadHeap.VisibleNodeMask = 1;
+
+ D3D12_RESOURCE_DESC uploadDesc {};
+ uploadDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ uploadDesc.Alignment = 0;
+ uploadDesc.Width = SurfaceUploadBufferSize;
+ uploadDesc.Height = 1;
+ uploadDesc.DepthOrArraySize = 1;
+ uploadDesc.MipLevels = 1;
+ uploadDesc.Format = DXGI_FORMAT_UNKNOWN;
+ uploadDesc.SampleDesc.Count = 1;
+ uploadDesc.SampleDesc.Quality = 0;
+ uploadDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ for (UINT i = 0; i < kFrameCount; ++i) {
+ if (FAILED(Device->CreateCommittedResource(
+ &uploadHeap,
+ D3D12_HEAP_FLAG_NONE,
+ &uploadDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr,
+ IID_PPV_ARGS(&SurfaceUploadBuffers[i])
+ ))) {
+ Debug::Log("[RenderDX] Failed to create surface upload buffer %u.\n", i);
+ return false;
+ }
+
+ D3D12_RANGE readRange {};
+ readRange.Begin = 0;
+ readRange.End = 0;
+
+ if (FAILED(SurfaceUploadBuffers[i]->Map(0, &readRange, reinterpret_cast(&SurfaceUploadMapped[i])))) {
+ Debug::Log("[RenderDX] Failed to map surface upload buffer %u.\n", i);
+ return false;
+ }
+ }
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc {};
+ srvDesc.Format = DXGI_FORMAT_B5G6R5_UNORM;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.Texture2D.MostDetailedMip = 0;
+ srvDesc.Texture2D.MipLevels = 1;
+ srvDesc.Texture2D.PlaneSlice = 0;
+ srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
+
+ Device->CreateShaderResourceView(
+ SurfaceTexture.Get(),
+ &srvDesc,
+ SrvHeap->GetCPUDescriptorHandleForHeapStart()
+ );
+
+ return true;
+}
+
+void DXRenderer::UpdateViewportAndScissor() {
+ if (ScaleRender) {
+ RenderViewportX = 0.0f;
+ RenderViewportY = 0.0f;
+ RenderViewportWidth = static_cast(WindowWidth);
+ RenderViewportHeight = static_cast(WindowHeight);
+
+ if (DXRenderOptions::Config().PreserveAspectRatio && RenderWidth > 0 && RenderHeight > 0 && WindowWidth > 0 && WindowHeight > 0) {
+ const float scale = std::min(
+ static_cast(WindowWidth) / static_cast(RenderWidth),
+ static_cast(WindowHeight) / static_cast(RenderHeight)
+ );
+
+ RenderViewportWidth = static_cast(RenderWidth) * scale;
+ RenderViewportHeight = static_cast(RenderHeight) * scale;
+ RenderViewportX = (static_cast(WindowWidth) - RenderViewportWidth) * 0.5f;
+ RenderViewportY = (static_cast(WindowHeight) - RenderViewportHeight) * 0.5f;
+ }
+
+ Viewport.TopLeftX = RenderViewportX;
+ Viewport.TopLeftY = RenderViewportY;
+ Viewport.Width = RenderViewportWidth;
+ Viewport.Height = RenderViewportHeight;
+ Viewport.MinDepth = 0.0f;
+ Viewport.MaxDepth = 1.0f;
+
+ ScissorRect.left = static_cast(RenderViewportX);
+ ScissorRect.top = static_cast(RenderViewportY);
+ ScissorRect.right = static_cast(RenderViewportX + RenderViewportWidth);
+ ScissorRect.bottom = static_cast(RenderViewportY + RenderViewportHeight);
+ }
+ else {
+ // Just render the surface at its native resolution in the top-left corner of the viewport.
+ RenderViewportX = 0.0f;
+ RenderViewportY = 0.0f;
+ RenderViewportWidth = static_cast(RenderWidth);
+ RenderViewportHeight = static_cast(RenderHeight);
+
+ Viewport.TopLeftX = RenderViewportX;
+ Viewport.TopLeftY = RenderViewportY;
+ Viewport.Width = RenderViewportWidth;
+ Viewport.Height = RenderViewportHeight;
+ Viewport.MinDepth = 0.0f;
+ Viewport.MaxDepth = 1.0f;
+
+ ScissorRect.left = 0;
+ ScissorRect.top = 0;
+ ScissorRect.right = static_cast(RenderWidth);
+ ScissorRect.bottom = static_cast(RenderHeight);
+ }
+}
+
+bool DXRenderer::WaitForGpu() {
+ if (!CommandQueue || !Fence || !FenceEvent) {
+ return true; // If we don't have the necessary objects, we can't wait, but also can't report an error.
+ }
+
+ const UINT64 currentFenceValue = FenceValues[FrameIndex];
+ if (FAILED(CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
+ Debug::Log("[RenderDX] Failed to signal command queue for GPU synchronization.\n");
+ return false;
+ }
+
+ if (FAILED(Fence->SetEventOnCompletion(currentFenceValue, FenceEvent))) {
+ Debug::Log("[RenderDX] Failed to set event on fence completion for GPU synchronization.\n");
+ return false;
+ }
+
+ DWORD waitResult = ::WaitForSingleObjectEx(FenceEvent, INFINITE, FALSE);
+ if (waitResult != WAIT_OBJECT_0) {
+ Debug::Log("[RenderDX] Wait for GPU synchronization event failed with error code: %lu\n", GetLastError());
+ return false;
+ }
+
+ ++FenceValues[FrameIndex];
+ return true;
+}
+
+Microsoft::WRL::ComPtr DXRenderer::CompileShader(std::string_view source, std::string_view entryPoint, std::string_view target) {
+ Microsoft::WRL::ComPtr shaderBlob;
+ Microsoft::WRL::ComPtr errorBlob;
+
+ UINT compileFlags = 0;
+ HRESULT hr = FP_D3DCompile(source.data(), source.length(), nullptr, nullptr, nullptr, entryPoint.data(), target.data(), compileFlags, 0, &shaderBlob, &errorBlob);
+
+ if (FAILED(hr)) {
+ if (errorBlob)
+ Debug::Log("[RenderDX] Shader compilation error: %s\n", static_cast(errorBlob->GetBufferPointer()));
+ else
+ Debug::Log("[RenderDX] Unknown shader compilation error.\n");
+ }
+
+ return shaderBlob;
+}
+
+bool DXRenderer::PopulateCommandListForCPUSurface(const void* pixels, int source_pitch) {
+ if (FAILED(CommandAllocators[FrameIndex]->Reset())) {
+ Debug::Log("[RenderDX] Failed to reset command allocator for populating command list.\n");
+ return false;
+ }
+
+ if (FAILED(CommandList->Reset(CommandAllocators[FrameIndex].Get(), PipelineState.Get()))) {
+ Debug::Log("[RenderDX] Failed to reset command list for populating commands.\n");
+ return false;
+ }
+
+ UploadSurfaceToGpu(pixels, source_pitch);
+
+ CommandList->RSSetViewports(1, &Viewport);
+ CommandList->RSSetScissorRects(1, &ScissorRect);
+
+ D3D12_RESOURCE_BARRIER backBufferToRenderTarget {};
+ backBufferToRenderTarget.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ backBufferToRenderTarget.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ backBufferToRenderTarget.Transition.pResource = RenderTargets[FrameIndex].Get();
+ backBufferToRenderTarget.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ backBufferToRenderTarget.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+ backBufferToRenderTarget.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ CommandList->ResourceBarrier(1, &backBufferToRenderTarget);
+
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = RtvHeap->GetCPUDescriptorHandleForHeapStart();
+ rtvHandle.ptr += static_cast(FrameIndex) * static_cast(RtvDescriptorSize);
+ CommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
+ const float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ CommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
+ CommandList->SetDescriptorHeaps(1, SrvHeap.GetAddressOf());
+ CommandList->SetGraphicsRootSignature(RootSignature.Get());
+ CommandList->SetGraphicsRootDescriptorTable(0, SrvHeap->GetGPUDescriptorHandleForHeapStart());
+ CommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ CommandList->DrawInstanced(3, 1, 0, 0);
+
+ D3D12_RESOURCE_BARRIER backBufferToPresent {};
+ backBufferToPresent.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ backBufferToPresent.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ backBufferToPresent.Transition.pResource = RenderTargets[FrameIndex].Get();
+ backBufferToPresent.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ backBufferToPresent.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ backBufferToPresent.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+
+ CommandList->ResourceBarrier(1, &backBufferToPresent);
+
+ if (FAILED(CommandList->Close())) {
+ Debug::Log("[RenderDX] Failed to close command list after populating commands.\n");
+ return false;
+ }
+
+ return true;
+}
+
+void DXRenderer::UploadSurfaceToGpu(const void* pixels, int source_pitch) {
+ auto dstBase = SurfaceUploadMapped[FrameIndex];
+ const auto* srcBase = static_cast(pixels);
+ const UINT sourceRowBytes = RenderWidth * static_cast(sizeof(std::uint16_t));
+ for (int y = 0; y < RenderHeight; ++y) {
+ auto* dstRow = dstBase + static_cast(y) * SurfaceUploadRowPitch;
+ const auto* srcRow = srcBase + static_cast(y) * source_pitch;
+ std::memcpy(dstRow, srcRow, sourceRowBytes);
+ }
+
+ TransitionSurfaceTexture(SurfaceTextureState, D3D12_RESOURCE_STATE_COPY_DEST);
+
+ D3D12_TEXTURE_COPY_LOCATION dstLocation {};
+ dstLocation.pResource = SurfaceTexture.Get();
+ dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstLocation.SubresourceIndex = 0;
+
+ D3D12_TEXTURE_COPY_LOCATION srcLocation {};
+ srcLocation.pResource = SurfaceUploadBuffers[FrameIndex].Get();
+ srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ srcLocation.PlacedFootprint.Offset = 0;
+ srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_B5G6R5_UNORM;
+ srcLocation.PlacedFootprint.Footprint.Width = RenderWidth;
+ srcLocation.PlacedFootprint.Footprint.Height = RenderHeight;
+ srcLocation.PlacedFootprint.Footprint.Depth = 1;
+ srcLocation.PlacedFootprint.Footprint.RowPitch = SurfaceUploadRowPitch;
+
+ CommandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr);
+
+ TransitionSurfaceTexture(SurfaceTextureState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+}
+
+void DXRenderer::TransitionSurfaceTexture(D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) {
+ if (before == after) {
+ return;
+ }
+
+ D3D12_RESOURCE_BARRIER barrier {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = SurfaceTexture.Get();
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ barrier.Transition.StateBefore = before;
+ barrier.Transition.StateAfter = after;
+
+ CommandList->ResourceBarrier(1, &barrier);
+
+ SurfaceTextureState = after;
+}
+
+bool DXRenderer::MoveToNextFrame() {
+ const UINT64 currentFenceValue = FenceValues[FrameIndex];
+ if (FAILED(CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
+ Debug::Log("[RenderDX] Failed to signal command queue for moving to next frame.\n");
+ return false;
+ }
+
+ FrameIndex = SwapChain->GetCurrentBackBufferIndex();
+ if (Fence->GetCompletedValue() < FenceValues[FrameIndex]) {
+ if (FAILED(Fence->SetEventOnCompletion(FenceValues[FrameIndex], FenceEvent))) {
+ Debug::Log("[RenderDX] Failed to set event on fence completion for moving to next frame.\n");
+ return false;
+ }
+ DWORD waitResult = ::WaitForSingleObjectEx(FenceEvent, INFINITE, FALSE);
+ if (waitResult != WAIT_OBJECT_0) {
+ Debug::Log("[RenderDX] Wait for fence event failed while moving to next frame with error code: %lu\n", GetLastError());
+ return false;
+ }
+ }
+
+ FenceValues[FrameIndex] = currentFenceValue + 1;
+ return true;
+}
diff --git a/src/Render/Renderer.h b/src/Render/Renderer.h
new file mode 100644
index 0000000000..791df077f3
--- /dev/null
+++ b/src/Render/Renderer.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#ifdef DEBUG
+#define DXRENDER_DEBUG 0
+#else
+#define DXRENDER_DEBUG 1
+#endif
+
+class DXRenderer {
+public:
+ static DXRenderer& Instance();
+
+ bool CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height, WNDPROC proc);
+ void DestroyMainWindow();
+
+ bool IsRendererReady();
+ bool CreateRenderer(int width, int height, int bits_per_pixel);
+ void DestroyRenderer();
+ bool ResizeWindow(int width, int height);
+
+ void ToggleFullscreen();
+
+ bool UploadSurfaceToTexture(void* surface_data, int source_pitch);
+ void SetRenderScale(bool scale);
+ bool Present();
+
+ void MoveWindow(int x, int y, int width, int height);
+ bool IsWindowed() const;
+
+ int GetWindowWidth() const;
+ int GetWindowHeight() const;
+ float GetViewportX() const;
+ float GetViewportY() const;
+ float GetViewportWidth() const;
+ float GetViewportHeight() const;
+
+private:
+ DXRenderer();
+ ~DXRenderer();
+
+ bool LoadImports();
+ void UnloadImports();
+
+ bool CreateDevice();
+ bool CreateCommandQueue();
+ bool CreateSwapChain();
+ bool CreateRtvHeap();
+ bool CreateRenderTargetViews();
+ bool CreateSrvHeap();
+ bool CreateSurfacePipeline();
+ bool CreateCommandObjects();
+ bool CreateFenceObjects();
+ bool CreateFixedSurfaceGpuResources();
+
+ void UpdateViewportAndScissor();
+
+ bool WaitForGpu();
+ Microsoft::WRL::ComPtr CompileShader(std::string_view source, std::string_view entryPoint, std::string_view target);
+ bool PopulateCommandListForCPUSurface(const void* pixels, int source_pitch);
+ void UploadSurfaceToGpu(const void* pixels, int source_pitch);
+ void TransitionSurfaceTexture(D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after);
+ bool MoveToNextFrame();
+
+ HMODULE D3D12Lib { nullptr };
+#if DXRENDER_DEBUG
+ decltype(&D3D12GetDebugInterface) FP_D3D12GetDebugInterface { nullptr };
+#endif
+ decltype(&D3D12CreateDevice) FP_D3D12CreateDevice { nullptr };
+ decltype(&D3D12SerializeRootSignature) FP_D3D12SerializeRootSignature { nullptr };
+
+ HMODULE DXGILib;
+ decltype(&CreateDXGIFactory2) FP_CreateDXGIFactory2 { nullptr };
+
+ HMODULE D3DCompilerLib { nullptr };
+ decltype(&D3DCompile) FP_D3DCompile { nullptr };
+
+ HWND Hwnd { nullptr };
+ int WindowWidth { 0 };
+ int WindowHeight { 0 };
+ float RenderViewportX { 0.0f }; // Current render viewport left in client coordinates.
+ float RenderViewportY { 0.0f }; // Current render viewport top in client coordinates.
+ float RenderViewportWidth { 0.0f }; // Current render viewport width in client coordinates.
+ float RenderViewportHeight { 0.0f }; // Current render viewport height in client coordinates.
+ RECT WindowedRect {}; // Saved window rectangle before borderless fullscreen.
+ LONG_PTR WindowedStyle { 0 }; // Saved window style before borderless fullscreen.
+ LONG_PTR WindowedExStyle { 0 }; // Saved extended window style before borderless fullscreen.
+ int RenderWidth { 0 };
+ int RenderHeight { 0 };
+ UINT RenderPitch { 0 };
+ bool ScaleRender { true };
+ bool Windowed { true };
+ bool HasWindowedState { false }; // Whether windowed placement has been saved.
+
+ UINT FrameIndex { 0 };
+ UINT RtvDescriptorSize { 0 };
+
+ Microsoft::WRL::ComPtr Factory;
+ Microsoft::WRL::ComPtr Device;
+ Microsoft::WRL::ComPtr CommandQueue;
+ Microsoft::WRL::ComPtr SwapChain;
+ Microsoft::WRL::ComPtr RtvHeap;
+
+ static constexpr UINT kFrameCount = 2;
+ std::array, kFrameCount> RenderTargets {};
+
+ std::array, kFrameCount> CommandAllocators {};
+ Microsoft::WRL::ComPtr CommandList;
+
+ Microsoft::WRL::ComPtr Fence;
+ std::array FenceValues {};
+ HANDLE FenceEvent;
+
+ D3D12_VIEWPORT Viewport {};
+ D3D12_RECT ScissorRect {};
+
+ Microsoft::WRL::ComPtr SurfaceTexture;
+ D3D12_RESOURCE_STATES SurfaceTextureState { D3D12_RESOURCE_STATE_COPY_DEST };
+
+ std::array, kFrameCount> SurfaceUploadBuffers {};
+ std::array SurfaceUploadMapped {};
+
+ UINT SurfaceUploadRowPitch { 0 };
+ UINT64 SurfaceUploadBufferSize { 0 };
+
+ Microsoft::WRL::ComPtr SrvHeap;
+ Microsoft::WRL::ComPtr RootSignature;
+ Microsoft::WRL::ComPtr PipelineState;
+};
diff --git a/src/Render/Surface.cpp b/src/Render/Surface.cpp
new file mode 100644
index 0000000000..6ffc70bbac
--- /dev/null
+++ b/src/Render/Surface.cpp
@@ -0,0 +1,286 @@
+#include "Surface.h"
+
+#include
+
+#include
+#include
+
+class DXSurfaceImpl
+{
+public:
+ DXSurfaceImpl(int width, int height);
+ ~DXSurfaceImpl();
+
+ int Pitch;
+ std::unique_ptr Buffer;
+};
+
+void DXSurface::CTOR(int width, int height)
+{
+ this->Width = width;
+ this->Height = height;
+ this->LockLevel = 0;
+ this->BytesPerPixel = 2;
+ ImplRef() = new DXSurfaceImpl(width, height);
+}
+
+void DXSurface::DTOR()
+{
+ if (ImplRef())
+ {
+ delete ImplRef();
+ ImplRef() = nullptr;
+ }
+}
+
+DXSurface::DXSurface(int width, int height) : DSurface { noinit_t{} }
+{
+ CTOR(width, height);
+}
+
+DXSurface::~DXSurface()
+{
+ DTOR();
+}
+
+bool DXSurface::CopyFromWhole(Surface* pSrc, bool trans, bool same_copy_cpu)
+{
+ JMP_THIS(0x7BBAF0);
+}
+
+bool DXSurface::CopyFromPart(RectangleStruct* pClipRect, Surface* pSrc, RectangleStruct* pSrcRect, bool trans, bool same_copy_cpu)
+{
+ JMP_THIS(0x7BBB90);
+}
+
+bool DXSurface::CopyFrom(RectangleStruct* pClipRect, RectangleStruct* pClipRect2, Surface* pSrc, RectangleStruct* pDestRect, RectangleStruct* pSrcRect, bool trans, bool same_copy_cpu)
+{
+ JMP_THIS(0x7BBCF0);
+}
+
+bool DXSurface::FillRectEx(RectangleStruct* pClipRect, RectangleStruct* pFillRect, COLORREF nColor)
+{
+ JMP_THIS(0x7BB050);
+}
+
+bool DXSurface::FillRect(RectangleStruct* pFillRect, COLORREF nColor)
+{
+ JMP_THIS(0x7BB020);
+}
+
+bool DXSurface::Fill(COLORREF nColor)
+{
+ JMP_THIS(0x7BBAB0);
+}
+
+bool DXSurface::FillRectTrans(RectangleStruct* pClipRect, ColorStruct* pColor, int nOpacity)
+{
+ JMP_THIS(0x4BB830);
+}
+
+bool DXSurface::DrawEllipse(int XOff, int YOff, int CenterX, int CenterY, RectangleStruct Rect, COLORREF nColor)
+{
+ JMP_THIS(0x7BB350);
+}
+
+bool DXSurface::SetPixel(Point2D* pPoint, COLORREF nColor)
+{
+ JMP_THIS(0x7BAEB0);
+}
+
+COLORREF DXSurface::GetPixel(Point2D* pPoint)
+{
+ JMP_THIS(0x7BAE60);
+}
+
+bool DXSurface::DrawLineEx(RectangleStruct* pClipRect, Point2D* pStart, Point2D* pEnd, COLORREF nColor)
+{
+ JMP_THIS(0x7BA610);
+}
+
+bool DXSurface::DrawLine(Point2D* pStart, Point2D* pEnd, COLORREF nColor)
+{
+ JMP_THIS(0x7BA5E0);
+}
+
+bool DXSurface::DrawLineColor(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, COLORREF nColor, int startZ, int endZ, bool bUnk)
+{
+ JMP_THIS(0x4BFD30);
+}
+
+bool DXSurface::DrawMultiplyingLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, DWORD dwMultiplier, DWORD dwUnk1, DWORD dwUnk2, bool bUnk)
+{
+ JMP_THIS(0x4BBCA0);
+}
+
+bool DXSurface::DrawSubtractiveLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, ColorStruct* pColor, DWORD dwUnk1, DWORD dwUnk2, bool bUnk1, bool bUnk2, bool bUkn3, bool bUkn4, float fUkn)
+{
+ JMP_THIS(0x4BC750);
+}
+
+bool DXSurface::DrawRGBMultiplyingLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, ColorStruct* pColor, float Intensity, int zSource, int zTarget)
+{
+ JMP_THIS(0x4BDF00);
+}
+
+bool DXSurface::PlotLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, bool(__fastcall* fpDrawCallback)(int*))
+{
+ JMP_THIS(0x7BAB90);
+}
+
+bool DXSurface::DrawDashedLine(Point2D* pStart, Point2D* pEnd, int nColor, bool* Pattern, int nOffset)
+{
+ JMP_THIS(0x7BA8C0);
+}
+
+bool DXSurface::DrawDashedLine_(Point2D* pStart, Point2D* pEnd, int nColor, bool* Pattern, int nOffset, bool bUkn)
+{
+ JMP_THIS(0x4C0750);
+}
+
+bool DXSurface::DrawLine_(Point2D* pStart, Point2D* pEnd, int nColor, bool bUnk)
+{
+ JMP_THIS(0x4C0E30);
+}
+
+bool DXSurface::DrawRectEx(RectangleStruct* pClipRect, RectangleStruct* pDrawRect, int nColor)
+{
+ JMP_THIS(0x7BADC0);
+}
+
+bool DXSurface::DrawRect(RectangleStruct* pDrawRect, DWORD dwColor)
+{
+ JMP_THIS(0x7BAD90);
+}
+
+void* DXSurface::Lock(int X, int Y)
+{
+ if (X >= 0 && Y >= 0)
+ {
+ ++LockLevel;
+ return Impl()->Buffer.get() + Y * Impl()->Pitch + X * GetBytesPerPixel();
+ }
+ return nullptr;
+}
+
+bool DXSurface::Unlock()
+{
+ if (LockLevel > 0)
+ {
+ --LockLevel;
+ return true;
+ }
+
+ return false;
+}
+
+bool DXSurface::CanLock(DWORD dwUkn1, DWORD dwUkn2)
+{
+ return true;
+}
+
+bool DXSurface::vt_entry_68(DWORD dwUnk1, DWORD dwUnk2)
+{
+ return true;
+}
+
+bool DXSurface::IsLocked()
+{
+ return false;
+}
+
+int DXSurface::GetBytesPerPixel()
+{
+ return 2;
+}
+
+int DXSurface::GetPitch()
+{
+ return Impl()->Pitch;
+}
+
+RectangleStruct* DXSurface::GetRect(RectangleStruct* pRect)
+{
+ *pRect = { 0, 0, GetWidth(), GetHeight() };
+ return pRect;
+}
+
+int DXSurface::GetWidth()
+{
+ return Width;
+}
+
+int DXSurface::GetHeight()
+{
+ return Height;
+}
+
+bool DXSurface::IsDSurface()
+{
+ return true;
+}
+
+bool DXSurface::PutPixelClip(Point2D* pPoint, short nUkn, RectangleStruct* pRect)
+{
+ JMP_THIS(0x7BAF90);
+}
+
+short DXSurface::GetPixelClip(Point2D* pPoint, RectangleStruct* pRect)
+{
+ JMP_THIS(0x7BAF10);
+}
+
+bool DXSurface::DrawGradientLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, ColorStruct* pStartColor, ColorStruct* pEndColor, float fStep, int nColor)
+{
+ JMP_THIS(0x4BF750);
+}
+
+bool DXSurface::CanBlit()
+{
+ return true;
+}
+
+void* DXSurface::GetBuffer()
+{
+ return Impl()->Buffer.get();
+}
+
+DXSurfaceImpl::DXSurfaceImpl(int width, int height)
+{
+ const int sourceRowBytes = width * 2; // 2 bytes per pixel for 16-bit color
+ Pitch = (sourceRowBytes + 256 - 1) & ~(256 - 1); // Align up to D3D12_TEXTURE_DATA_PITCH_ALIGNMENT
+ Buffer.reset(new BYTE[Pitch * height]);
+}
+
+DXSurfaceImpl::~DXSurfaceImpl() {}
+
+static __forceinline unsigned int Build_Hicolor_Pixel(unsigned int r, unsigned int g, unsigned int b)
+{
+ return (r >> Drawing::RedShiftRight << Drawing::RedShiftLeft) |
+ (g >> Drawing::GreenShiftRight << Drawing::GreenShiftLeft) |
+ (b >> Drawing::BlueShiftRight << Drawing::BlueShiftLeft);
+}
+
+DXSurface* __fastcall DXSurface::CreatePrimary()
+{
+ Drawing::AllowSoftwareBlitFills = false;
+ Drawing::AllowSoftwareBlitStretch = false;
+
+ Debug::Log("[RenderDX] D3D12 surface created as primary surface.\n");
+
+ auto surface = new DXSurface(DSurface::WindowBounds.Width, DSurface::WindowBounds.Height);
+
+ // RGB565 color shifts
+ Drawing::RedShiftLeft = 11;
+ Drawing::RedShiftRight = 3;
+ Drawing::GreenShiftLeft = 5;
+ Drawing::GreenShiftRight = 2;
+ Drawing::BlueShiftLeft = 0;
+ Drawing::BlueShiftRight = 3;
+ Drawing::ColorMode = RGBMode::RGB565;
+ Drawing::HalfbrightMask = static_cast(Build_Hicolor_Pixel(127, 127, 127));
+ Drawing::QuarterbrightMask = static_cast(Build_Hicolor_Pixel(63, 63, 63));
+ Drawing::EighthbrightMask = static_cast(Build_Hicolor_Pixel(31, 31, 31));
+
+ return surface;
+}
diff --git a/src/Render/Surface.h b/src/Render/Surface.h
new file mode 100644
index 0000000000..51da806d2f
--- /dev/null
+++ b/src/Render/Surface.h
@@ -0,0 +1,99 @@
+#pragma once
+
+#include
+
+// CPU render
+class DXSurfaceImpl;
+class DXSurface : public DSurface {
+private:
+ DXSurfaceImpl* Impl() const {
+ return reinterpret_cast(Buffer);
+ }
+
+ DXSurfaceImpl*& ImplRef() {
+ return reinterpret_cast(Buffer);
+ }
+
+public:
+ static DXSurface* __fastcall CreatePrimary();
+
+ void CTOR(int width, int height);
+ void DTOR();
+
+ DXSurface(int width, int height);
+
+ virtual ~DXSurface() override;
+
+ //Surface
+ virtual bool CopyFromWhole(Surface* pSrc, bool trans, bool same_copy_cpu) override;
+
+ virtual bool CopyFromPart(
+ RectangleStruct* pClipRect, //ignored and retrieved again...
+ Surface* pSrc,
+ RectangleStruct* pSrcRect, //desired source rect of pSrc ?
+ bool trans,
+ bool same_copy_cpu) override;
+
+ virtual bool CopyFrom(
+ RectangleStruct* pClipRect,
+ RectangleStruct* pClipRect2, //again? hmm
+ Surface* pSrc,
+ RectangleStruct* pDestRect, //desired dest rect of pSrc ? (stretched? clipped?)
+ RectangleStruct* pSrcRect, //desired source rect of pSrc ?
+ bool trans,
+ bool same_copy_cpu) override;
+
+ virtual bool FillRectEx(RectangleStruct* pClipRect, RectangleStruct* pFillRect, COLORREF nColor) override;
+ virtual bool FillRect(RectangleStruct* pFillRect, COLORREF nColor) override;
+ virtual bool Fill(COLORREF nColor) override;
+ virtual bool FillRectTrans(RectangleStruct* pClipRect, ColorStruct* pColor, int Opacity) override;
+ virtual bool DrawEllipse(int XOff, int YOff, int CenterX, int CenterY, RectangleStruct Rect, COLORREF nColor) override;
+ virtual bool SetPixel(Point2D* pPoint, COLORREF nColor) override;
+ virtual COLORREF GetPixel(Point2D* pPoint) override;
+ virtual bool DrawLineEx(RectangleStruct* pClipRect, Point2D* pStart, Point2D* pEnd, COLORREF nColor) override;
+ virtual bool DrawLine(Point2D* pStart, Point2D* pEnd, COLORREF nColor) override;
+ virtual bool DrawLineColor(
+ RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, COLORREF nColor,
+ int startZ, int endZ, bool bUnk) override;
+
+ virtual bool DrawMultiplyingLine(
+ RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, DWORD dwMultiplier,
+ DWORD dwUnk1, DWORD dwUnk2, bool bUnk) override;
+
+ virtual bool DrawSubtractiveLine(
+ RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, ColorStruct* pColor,
+ DWORD dwUnk1, DWORD dwUnk2, bool bUnk1, bool bUnk2,
+ bool bUkn3, bool bUkn4, float fUkn) override;
+
+ virtual bool DrawRGBMultiplyingLine(
+ RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, ColorStruct* pColor,
+ float Intensity, int zSource, int zTarget) override;
+
+ virtual bool PlotLine(
+ RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd, bool(__fastcall* fpDrawCallback)(int*)) override;
+ virtual bool DrawDashedLine(
+ Point2D* pStart, Point2D* pEnd, int nColor, bool* Pattern, int nOffset) override;
+ virtual bool DrawDashedLine_(
+ Point2D* pStart, Point2D* pEnd, int nColor, bool* Pattern, int nOffset, bool bUkn) override;
+ virtual bool DrawLine_(Point2D* pStart, Point2D* pEnd, int nColor, bool bUnk) override;
+ virtual bool DrawRectEx(RectangleStruct* pClipRect, RectangleStruct* pDrawRect, int nColor) override;
+ virtual bool DrawRect(RectangleStruct* pDrawRect, DWORD dwColor) override;
+ virtual void* Lock(int X, int Y) override;
+ virtual bool Unlock() override;
+ virtual bool CanLock(DWORD dwUkn1 = 0, DWORD dwUkn2 = 0) override;
+ virtual bool vt_entry_68(DWORD dwUnk1, DWORD dwUnk2) override;
+ virtual bool IsLocked() override;
+ virtual int GetBytesPerPixel() override;
+ virtual int GetPitch() override;
+ virtual RectangleStruct* GetRect(RectangleStruct* pRect) override;
+ virtual int GetWidth() override;
+ virtual int GetHeight() override;
+ virtual bool IsDSurface() override;
+ virtual bool PutPixelClip(Point2D* pPoint, short nUkn, RectangleStruct* pRect) override;
+ virtual short GetPixelClip(Point2D* pPoint, RectangleStruct* pRect) override;
+ virtual bool DrawGradientLine(RectangleStruct* pRect, Point2D* pStart, Point2D* pEnd,
+ ColorStruct* pStartColor, ColorStruct* pEndColor, float fStep, int nColor) override;
+ virtual bool CanBlit() override;
+
+ void* GetBuffer();
+};
From 576ce823258ade3de3aeaf3d9f8ddd268b418921 Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sat, 16 May 2026 21:42:12 +0800
Subject: [PATCH 02/21] Enhance D3D12/DXGI error logging with HRESULTs and
Win32 error codes
---
src/Render/Renderer.cpp | 131 ++++++++++++++++++++++++----------------
1 file changed, 78 insertions(+), 53 deletions(-)
diff --git a/src/Render/Renderer.cpp b/src/Render/Renderer.cpp
index 0508521930..57c8156156 100644
--- a/src/Render/Renderer.cpp
+++ b/src/Render/Renderer.cpp
@@ -558,26 +558,29 @@ void DXRenderer::UnloadImports() {
}
bool DXRenderer::CreateDevice() {
+ HRESULT hr = S_OK;
UINT dxgiFactoryFlags = 0;
#if DXRENDER_DEBUG
Microsoft::WRL::ComPtr debugController;
- if (SUCCEEDED(FP_D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
+ if (SUCCEEDED(hr = FP_D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
+ Debug::Log("[RenderDX] D3D12 debug layer enabled.\n");
debugController->EnableDebugLayer();
}
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
#endif
- if (FAILED(FP_CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&Factory)))) {
+ if (FAILED(hr = FP_CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&Factory)))) {
+ Debug::Log("[RenderDX] Failed to create DXGI factory: 0x%08X\n", static_cast(hr));
return false;
}
constexpr bool kUseWarpDevice = false;
if (kUseWarpDevice) {
Microsoft::WRL::ComPtr warpAdapter;
- if (FAILED(Factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)))) {
- Debug::Log("[RenderDX] Failed to create WARP adapter.\n");
+ if (FAILED(hr = Factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)))) {
+ Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
return false;
}
- if (FAILED(FP_D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
- Debug::Log("[RenderDX] Failed to create WARP adapter.\n");
+ if (FAILED(hr = FP_D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
+ Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
return false;
}
@@ -591,7 +594,7 @@ bool DXRenderer::CreateDevice() {
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
continue;
}
- if (SUCCEEDED(FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
+ if (SUCCEEDED(hr = FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
Debug::Log("[RenderDX] D3D12 device created successfully on adapter: %ls\n", desc.Description);
break;
}
@@ -607,14 +610,16 @@ bool DXRenderer::CreateDevice() {
}
bool DXRenderer::CreateCommandQueue() {
+ HRESULT hr = S_OK;
+
D3D12_COMMAND_QUEUE_DESC queueDesc {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 0;
- if (FAILED(Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&CommandQueue)))) {
- Debug::Log("[RenderDX] Failed to create command queue.\n");
+ if (FAILED(hr = Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&CommandQueue)))) {
+ Debug::Log("[RenderDX] Failed to create command queue: 0x%08X\n", static_cast(hr));
return false;
}
@@ -623,6 +628,8 @@ bool DXRenderer::CreateCommandQueue() {
}
bool DXRenderer::CreateSwapChain() {
+ HRESULT hr = S_OK;
+
DXGI_SWAP_CHAIN_DESC1 desc {};
desc.Width = WindowWidth;
desc.Height = WindowHeight;
@@ -638,18 +645,18 @@ bool DXRenderer::CreateSwapChain() {
desc.Flags = 0;
Microsoft::WRL::ComPtr swapChain1;
- if (FAILED(Factory->CreateSwapChainForHwnd(CommandQueue.Get(), Hwnd, &desc, nullptr, nullptr, &swapChain1))) {
- Debug::Log("[RenderDX] Failed to create swap chain.\n");
+ if (FAILED(hr = Factory->CreateSwapChainForHwnd(CommandQueue.Get(), Hwnd, &desc, nullptr, nullptr, &swapChain1))) {
+ Debug::Log("[RenderDX] Failed to create swap chain: 0x%08X\n", static_cast(hr));
return false;
}
- if (FAILED(Factory->MakeWindowAssociation(Hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN))) {
- Debug::Log("[RenderDX] Failed to set window association.\n");
+ if (FAILED(hr = Factory->MakeWindowAssociation(Hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN))) {
+ Debug::Log("[RenderDX] Failed to set window association: 0x%08X\n", static_cast(hr));
return false;
}
- if (FAILED(swapChain1->QueryInterface(IID_PPV_ARGS(&SwapChain)))) {
- Debug::Log("[RenderDX] Failed to query IDXGISwapChain3 interface.\n");
+ if (FAILED(hr = swapChain1->QueryInterface(IID_PPV_ARGS(&SwapChain)))) {
+ Debug::Log("[RenderDX] Failed to query IDXGISwapChain3 interface: 0x%08X\n", static_cast(hr));
return false;
}
@@ -659,14 +666,16 @@ bool DXRenderer::CreateSwapChain() {
}
bool DXRenderer::CreateRtvHeap() {
+ HRESULT hr = S_OK;
+
D3D12_DESCRIPTOR_HEAP_DESC desc {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
desc.NumDescriptors = kFrameCount;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 0;
- if (FAILED(Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&RtvHeap)))) {
- Debug::Log("[RenderDX] Failed to create RTV descriptor heap.\n");
+ if (FAILED(hr = Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&RtvHeap)))) {
+ Debug::Log("[RenderDX] Failed to create RTV descriptor heap: 0x%08X\n", static_cast(hr));
return false;
}
@@ -676,10 +685,12 @@ bool DXRenderer::CreateRtvHeap() {
}
bool DXRenderer::CreateRenderTargetViews() {
+ HRESULT hr = S_OK;
+
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = RtvHeap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < kFrameCount; ++i) {
- if (FAILED(SwapChain->GetBuffer(i, IID_PPV_ARGS(&RenderTargets[i])))) {
- Debug::Log("[RenderDX] Failed to get back buffer %u.\n", i);
+ if (FAILED(hr = SwapChain->GetBuffer(i, IID_PPV_ARGS(&RenderTargets[i])))) {
+ Debug::Log("[RenderDX] Failed to get back buffer %u: 0x%08X\n", i, static_cast(hr));
return false;
}
Device->CreateRenderTargetView(RenderTargets[i].Get(), nullptr, rtvHandle);
@@ -691,14 +702,16 @@ bool DXRenderer::CreateRenderTargetViews() {
}
bool DXRenderer::CreateSrvHeap() {
+ HRESULT hr = S_OK;
+
D3D12_DESCRIPTOR_HEAP_DESC desc {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
desc.NumDescriptors = 1; // For surface texture SRV
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
desc.NodeMask = 0;
- if (FAILED(Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&SrvHeap)))) {
- Debug::Log("[RenderDX] Failed to create SRV descriptor heap.\n");
+ if (FAILED(hr = Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&SrvHeap)))) {
+ Debug::Log("[RenderDX] Failed to create SRV descriptor heap: 0x%08X\n", static_cast(hr));
return false;
}
@@ -760,8 +773,8 @@ bool DXRenderer::CreateSurfacePipeline() {
return false;
}
- if (FAILED(Device->CreateRootSignature(0, rootSigBlob->GetBufferPointer(), rootSigBlob->GetBufferSize(), IID_PPV_ARGS(&RootSignature)))) {
- Debug::Log("[RenderDX] Failed to create root signature.\n");
+ if (FAILED(hr = Device->CreateRootSignature(0, rootSigBlob->GetBufferPointer(), rootSigBlob->GetBufferSize(), IID_PPV_ARGS(&RootSignature)))) {
+ Debug::Log("[RenderDX] Failed to create root signature: 0x%08X\n", static_cast(hr));
return false;
}
@@ -860,8 +873,8 @@ float4 PSMain(VSOut input) : SV_Target0
psoDesc.SampleDesc.Count = 1;
psoDesc.SampleDesc.Quality = 0;
- if (FAILED(Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&PipelineState)))) {
- Debug::Log("[RenderDX] Failed to create pipeline state.\n");
+ if (FAILED(hr = Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&PipelineState)))) {
+ Debug::Log("[RenderDX] Failed to create pipeline state: 0x%08X\n", static_cast(hr));
return false;
}
@@ -870,15 +883,17 @@ float4 PSMain(VSOut input) : SV_Target0
}
bool DXRenderer::CreateCommandObjects() {
+ HRESULT hr = S_OK;
+
for (UINT i = 0; i < kFrameCount; ++i) {
- if (FAILED(Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&CommandAllocators[i])))) {
- Debug::Log("[RenderDX] Failed to create command allocator %u.\n", i);
+ if (FAILED(hr = Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&CommandAllocators[i])))) {
+ Debug::Log("[RenderDX] Failed to create command allocator %u: 0x%08X\n", i, static_cast(hr));
return false;
}
}
- if (FAILED(Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, CommandAllocators[FrameIndex].Get(), PipelineState.Get(), IID_PPV_ARGS(&CommandList)))) {
- Debug::Log("[RenderDX] Failed to create command list.\n");
+ if (FAILED(hr = Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, CommandAllocators[FrameIndex].Get(), PipelineState.Get(), IID_PPV_ARGS(&CommandList)))) {
+ Debug::Log("[RenderDX] Failed to create command list: 0x%08X\n", static_cast(hr));
return false;
}
@@ -888,10 +903,12 @@ bool DXRenderer::CreateCommandObjects() {
}
bool DXRenderer::CreateFenceObjects() {
+ HRESULT hr = S_OK;
+
FenceValues.fill(0);
- if (FAILED(Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Fence)))) {
- Debug::Log("[RenderDX] Failed to create fence.\n");
+ if (FAILED(hr = Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Fence)))) {
+ Debug::Log("[RenderDX] Failed to create fence: 0x%08X\n", static_cast(hr));
return false;
}
@@ -899,7 +916,7 @@ bool DXRenderer::CreateFenceObjects() {
FenceEvent = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!FenceEvent) {
- Debug::Log("[RenderDX] Failed to create fence event.\n");
+ Debug::Log("[RenderDX] Failed to create fence event: 0x%08X\n", ::GetLastError());
return false;
}
@@ -908,6 +925,8 @@ bool DXRenderer::CreateFenceObjects() {
}
bool DXRenderer::CreateFixedSurfaceGpuResources() {
+ HRESULT hr = S_OK;
+
const UINT sourceRowBytes = RenderWidth * sizeof(std::uint16_t);
SurfaceUploadRowPitch = (sourceRowBytes + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1); // Align up
SurfaceUploadBufferSize = static_cast(SurfaceUploadRowPitch) * static_cast(RenderHeight);
@@ -934,7 +953,7 @@ bool DXRenderer::CreateFixedSurfaceGpuResources() {
SurfaceTextureState = D3D12_RESOURCE_STATE_COPY_DEST;
- if (FAILED(Device->CreateCommittedResource(
+ if (FAILED(hr = Device->CreateCommittedResource(
&defaultHeap,
D3D12_HEAP_FLAG_NONE,
&textureDesc,
@@ -942,7 +961,7 @@ bool DXRenderer::CreateFixedSurfaceGpuResources() {
nullptr,
IID_PPV_ARGS(&SurfaceTexture)
))) {
- Debug::Log("[RenderDX] Failed to create surface texture resource.\n");
+ Debug::Log("[RenderDX] Failed to create surface texture resource: 0x%08X\n", static_cast(hr));
return false;
}
@@ -967,7 +986,7 @@ bool DXRenderer::CreateFixedSurfaceGpuResources() {
uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
for (UINT i = 0; i < kFrameCount; ++i) {
- if (FAILED(Device->CreateCommittedResource(
+ if (FAILED(hr = Device->CreateCommittedResource(
&uploadHeap,
D3D12_HEAP_FLAG_NONE,
&uploadDesc,
@@ -975,7 +994,7 @@ bool DXRenderer::CreateFixedSurfaceGpuResources() {
nullptr,
IID_PPV_ARGS(&SurfaceUploadBuffers[i])
))) {
- Debug::Log("[RenderDX] Failed to create surface upload buffer %u.\n", i);
+ Debug::Log("[RenderDX] Failed to create surface upload buffer %u: 0x%08X\n", i, static_cast(hr));
return false;
}
@@ -984,7 +1003,7 @@ bool DXRenderer::CreateFixedSurfaceGpuResources() {
readRange.End = 0;
if (FAILED(SurfaceUploadBuffers[i]->Map(0, &readRange, reinterpret_cast(&SurfaceUploadMapped[i])))) {
- Debug::Log("[RenderDX] Failed to map surface upload buffer %u.\n", i);
+ Debug::Log("[RenderDX] Failed to map surface upload buffer %u: 0x%08X\n", i, static_cast(hr));
return false;
}
}
@@ -1060,24 +1079,26 @@ void DXRenderer::UpdateViewportAndScissor() {
}
bool DXRenderer::WaitForGpu() {
+ HRESULT hr = S_OK;
+
if (!CommandQueue || !Fence || !FenceEvent) {
return true; // If we don't have the necessary objects, we can't wait, but also can't report an error.
}
const UINT64 currentFenceValue = FenceValues[FrameIndex];
- if (FAILED(CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
- Debug::Log("[RenderDX] Failed to signal command queue for GPU synchronization.\n");
+ if (FAILED(hr = CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
+ Debug::Log("[RenderDX] Failed to signal command queue for GPU synchronization: 0x%08X\n", static_cast(hr));
return false;
}
- if (FAILED(Fence->SetEventOnCompletion(currentFenceValue, FenceEvent))) {
- Debug::Log("[RenderDX] Failed to set event on fence completion for GPU synchronization.\n");
+ if (FAILED(hr = Fence->SetEventOnCompletion(currentFenceValue, FenceEvent))) {
+ Debug::Log("[RenderDX] Failed to set event on fence completion for GPU synchronization: 0x%08X\n", static_cast(hr));
return false;
}
DWORD waitResult = ::WaitForSingleObjectEx(FenceEvent, INFINITE, FALSE);
if (waitResult != WAIT_OBJECT_0) {
- Debug::Log("[RenderDX] Wait for GPU synchronization event failed with error code: %lu\n", GetLastError());
+ Debug::Log("[RenderDX] Wait for GPU synchronization event failed with error code: 0x%08X\n", ::GetLastError());
return false;
}
@@ -1103,13 +1124,15 @@ Microsoft::WRL::ComPtr DXRenderer::CompileShader(std::string_view sour
}
bool DXRenderer::PopulateCommandListForCPUSurface(const void* pixels, int source_pitch) {
- if (FAILED(CommandAllocators[FrameIndex]->Reset())) {
- Debug::Log("[RenderDX] Failed to reset command allocator for populating command list.\n");
+ HRESULT hr = S_OK;
+
+ if (FAILED(hr = CommandAllocators[FrameIndex]->Reset())) {
+ Debug::Log("[RenderDX] Failed to reset command allocator for populating command list: 0x%08X\n", static_cast(hr));
return false;
}
- if (FAILED(CommandList->Reset(CommandAllocators[FrameIndex].Get(), PipelineState.Get()))) {
- Debug::Log("[RenderDX] Failed to reset command list for populating commands.\n");
+ if (FAILED(hr = CommandList->Reset(CommandAllocators[FrameIndex].Get(), PipelineState.Get()))) {
+ Debug::Log("[RenderDX] Failed to reset command list for populating commands: 0x%08X\n", static_cast(hr));
return false;
}
@@ -1148,8 +1171,8 @@ bool DXRenderer::PopulateCommandListForCPUSurface(const void* pixels, int source
CommandList->ResourceBarrier(1, &backBufferToPresent);
- if (FAILED(CommandList->Close())) {
- Debug::Log("[RenderDX] Failed to close command list after populating commands.\n");
+ if (FAILED(hr = CommandList->Close())) {
+ Debug::Log("[RenderDX] Failed to close command list after populating commands: 0x%08X\n", static_cast(hr));
return false;
}
@@ -1207,21 +1230,23 @@ void DXRenderer::TransitionSurfaceTexture(D3D12_RESOURCE_STATES before, D3D12_RE
}
bool DXRenderer::MoveToNextFrame() {
+ HRESULT hr = S_OK;
+
const UINT64 currentFenceValue = FenceValues[FrameIndex];
- if (FAILED(CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
- Debug::Log("[RenderDX] Failed to signal command queue for moving to next frame.\n");
+ if (FAILED(hr = CommandQueue->Signal(Fence.Get(), currentFenceValue))) {
+ Debug::Log("[RenderDX] Failed to signal command queue for moving to next frame: 0x%08X\n", static_cast(hr));
return false;
}
FrameIndex = SwapChain->GetCurrentBackBufferIndex();
if (Fence->GetCompletedValue() < FenceValues[FrameIndex]) {
- if (FAILED(Fence->SetEventOnCompletion(FenceValues[FrameIndex], FenceEvent))) {
- Debug::Log("[RenderDX] Failed to set event on fence completion for moving to next frame.\n");
+ if (FAILED(hr = Fence->SetEventOnCompletion(FenceValues[FrameIndex], FenceEvent))) {
+ Debug::Log("[RenderDX] Failed to set event on fence completion for moving to next frame: 0x%08X\n", static_cast(hr));
return false;
}
DWORD waitResult = ::WaitForSingleObjectEx(FenceEvent, INFINITE, FALSE);
if (waitResult != WAIT_OBJECT_0) {
- Debug::Log("[RenderDX] Wait for fence event failed while moving to next frame with error code: %lu\n", GetLastError());
+ Debug::Log("[RenderDX] Wait for fence event failed while moving to next frame with error code: 0x%08X\n", ::GetLastError());
return false;
}
}
From 9d577620de81018d41be9956fefd643149ec98e5 Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sat, 16 May 2026 22:24:36 +0800
Subject: [PATCH 03/21] Disable bink movie render temporary
---
src/Render/Hooks.Surface.cpp | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/Render/Hooks.Surface.cpp b/src/Render/Hooks.Surface.cpp
index 068cba82ac..182a966e0d 100644
--- a/src/Render/Hooks.Surface.cpp
+++ b/src/Render/Hooks.Surface.cpp
@@ -12,3 +12,16 @@ static DXSurface* __fastcall _DXSurface_CTOR(DXSurface* surface, void*, int widt
}
DEFINE_FUNCTION_JUMP(LJMP, 0x4BA5A0, _DXSurface_CTOR);
+static int __stdcall _BinkDDSurfaceType(void*)
+{
+ return 10; // BINKSURFACE565
+}
+DEFINE_PATCH_TYPED(void*, 0x7E15A8, _BinkDDSurfaceType);
+
+static int __stdcall _BinkCopyToBuffer(void* bnk, void* dest, int destpitch, unsigned int destheight, unsigned int destx, unsigned int desty, unsigned int flags)
+{
+ // Skip the bink movie render for now to avoid the crash. The movie will be rendered as black screen, but at least it won't crash.
+ // So we can test the other features without worrying about the movie rendering. We will try to implement the movie rendering later.
+ return 0;
+}
+DEFINE_PATCH_TYPED(void*, 0x7E15B8, _BinkCopyToBuffer);
From 685c9f4773ed00c13625b272b4c4dd2d89327880 Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sat, 16 May 2026 22:36:51 +0800
Subject: [PATCH 04/21] Fix wrong resolution detection bpp
---
src/Render/Functions.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Render/Functions.cpp b/src/Render/Functions.cpp
index da3c6cb5ad..c5708b93ab 100644
--- a/src/Render/Functions.cpp
+++ b/src/Render/Functions.cpp
@@ -503,7 +503,7 @@ void __fastcall RenderDX::ResetScale() {
ViewportY = 0.0f;
}
-int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD bitdepth) {
+int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD) {
std::vector> modes;
DEVMODE devmode{};
DWORD mode_index = 0;
@@ -513,7 +513,7 @@ int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, D
const DWORD h = devmode.dmPelsHeight;
const DWORD bpp = devmode.dmBitsPerPel;
- if (w >= minw && h >= minh && w <= maxw && h <= maxh && bpp == bitdepth) {
+ if (w >= minw && h >= minh && w <= maxw && h <= maxh && bpp == 32) {
modes.emplace_back(static_cast(w), static_cast(h));
}
}
From 4ce614566849940bb4d5a889a2dfd248a4a935f0 Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sat, 16 May 2026 22:37:04 +0800
Subject: [PATCH 05/21] Fix wrong PrimarySurface creation width and height
---
src/Render/Surface.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Render/Surface.cpp b/src/Render/Surface.cpp
index 6ffc70bbac..11d56e2cbf 100644
--- a/src/Render/Surface.cpp
+++ b/src/Render/Surface.cpp
@@ -268,7 +268,7 @@ DXSurface* __fastcall DXSurface::CreatePrimary()
Debug::Log("[RenderDX] D3D12 surface created as primary surface.\n");
- auto surface = new DXSurface(DSurface::WindowBounds.Width, DSurface::WindowBounds.Height);
+ auto surface = new DXSurface(Drawing::RenderWidth, Drawing::RenderHeight);
// RGB565 color shifts
Drawing::RedShiftLeft = 11;
From 1f6a77c6e29e12982470d49e8f3486e05de6ded2 Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sun, 17 May 2026 12:37:25 +0800
Subject: [PATCH 06/21] Refine D3D12 device creation with preferred adapter and
WARP fallbacks
Prioritize hardware adapter selection using `IDXGIFactory6::EnumAdapterByGpuPreference` for optimal performance. Fall back to `IDXGIFactory4::EnumAdapters1` if `IDXGIFactory6` is unavailable. If no hardware device can be created, attempt to create a WARP device as a last resort. This improves robustness and compatibility across various systems.
---
src/Render/Renderer.cpp | 63 ++++++++++++++++++++++++++++-------------
src/Render/Renderer.h | 2 +-
2 files changed, 45 insertions(+), 20 deletions(-)
diff --git a/src/Render/Renderer.cpp b/src/Render/Renderer.cpp
index 57c8156156..a5907e4fa6 100644
--- a/src/Render/Renderer.cpp
+++ b/src/Render/Renderer.cpp
@@ -572,41 +572,66 @@ bool DXRenderer::CreateDevice() {
Debug::Log("[RenderDX] Failed to create DXGI factory: 0x%08X\n", static_cast(hr));
return false;
}
- constexpr bool kUseWarpDevice = false;
- if (kUseWarpDevice) {
- Microsoft::WRL::ComPtr warpAdapter;
- if (FAILED(hr = Factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)))) {
- Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
- return false;
- }
- if (FAILED(hr = FP_D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
- Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
- return false;
- }
- Debug::Log("[RenderDX] D3D12 WARP device created successfully.\n");
+ Microsoft::WRL::ComPtr factory6;
+ if (FAILED(hr = Factory.As(&factory6)))
+ {
+ Debug::Log("[RenderDX] Failed to query IDXGIFactory6 interface: 0x%08X\nFalling back to EnumAdapters1()", static_cast(hr));
+ Microsoft::WRL::ComPtr hardwareAdapter;
+ for (UINT adapterIndex = 0; Factory->EnumAdapters1(adapterIndex, &hardwareAdapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex)
+ {
+ DXGI_ADAPTER_DESC1 desc;
+ hardwareAdapter->GetDesc1(&desc);
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ {
+ continue;
+ }
+ if (SUCCEEDED(hr = FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device))))
+ {
+ Debug::Log("[RenderDX] D3D12 device created successfully on adapter: %ls\n", desc.Description);
+ break;
+ }
+ }
}
- else {
+ else
+ {
+ Debug::Log("[RenderDX] IDXGIFactory6 interface is available. Using EnumAdapterByGpuPreference to select the adapter.\n");
Microsoft::WRL::ComPtr hardwareAdapter;
- for (UINT adapterIndex = 0; Factory->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&hardwareAdapter)) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ for (UINT adapterIndex = 0; factory6->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&hardwareAdapter)) != DXGI_ERROR_NOT_FOUND; ++adapterIndex)
+ {
DXGI_ADAPTER_DESC1 desc;
hardwareAdapter->GetDesc1(&desc);
- if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ {
continue;
}
- if (SUCCEEDED(hr = FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device)))) {
+ if (SUCCEEDED(hr = FP_D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device))))
+ {
Debug::Log("[RenderDX] D3D12 device created successfully on adapter: %ls\n", desc.Description);
break;
}
}
- if (!Device) {
+ }
+ if (!Device)
+ {
+ Debug::Log("[RenderDX] Failed to create D3D12 device on a hardware adapter. Attempting to create WARP device.\n");
+
+ Microsoft::WRL::ComPtr warpAdapter;
+ if (FAILED(hr = Factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter))))
+ {
+ Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
+ return false;
+ }
+ if (FAILED(hr = FP_D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&Device))))
+ {
+ Debug::Log("[RenderDX] Failed to create WARP adapter: 0x%08X\n", static_cast(hr));
return false;
}
- Debug::Log("[RenderDX] D3D12 device created successfully.\n");
+ Debug::Log("[RenderDX] D3D12 WARP device created successfully.\n");
}
- return true;
+ return Device != nullptr;
}
bool DXRenderer::CreateCommandQueue() {
diff --git a/src/Render/Renderer.h b/src/Render/Renderer.h
index 791df077f3..cb89f04fe7 100644
--- a/src/Render/Renderer.h
+++ b/src/Render/Renderer.h
@@ -108,7 +108,7 @@ class DXRenderer {
UINT FrameIndex { 0 };
UINT RtvDescriptorSize { 0 };
- Microsoft::WRL::ComPtr Factory;
+ Microsoft::WRL::ComPtr Factory;
Microsoft::WRL::ComPtr Device;
Microsoft::WRL::ComPtr CommandQueue;
Microsoft::WRL::ComPtr SwapChain;
From ef8a00390a1dadbc401edaa845ab5eb9d8afb27b Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sun, 17 May 2026 23:32:43 +0800
Subject: [PATCH 07/21] Add AGENTS.md
---
AGENTS.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 AGENTS.md
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000000..4d54f91a34
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1 @@
+.github/copilot-instructions.md
From 8b0ddb1376ac7da3361302c13e5545558a7f41fd Mon Sep 17 00:00:00 2001
From: secsome <302702960@qq.com>
Date: Sun, 17 May 2026 23:57:15 +0800
Subject: [PATCH 08/21] Switch to DirectX11 for compability
---
Phobos.vcxproj | 5 +-
src/Render/Functions.cpp | 225 +++----
src/Render/Functions.h | 10 +-
src/Render/Hooks.Mouse.cpp | 29 +-
src/Render/Hooks.Options.cpp | 9 +-
src/Render/Hooks.Surface.cpp | 22 +-
src/Render/Hooks.cpp | 139 ++--
src/Render/Mouse.cpp | 276 ++++----
src/Render/Mouse.h | 126 ++--
src/Render/Options.cpp | 0
src/Render/Options.h | 8 +-
src/Render/Renderer.SurfacePS.h | 81 +++
src/Render/Renderer.SurfaceVS.h | 145 +++++
src/Render/Renderer.cpp | 1048 +++++++++++--------------------
src/Render/Renderer.h | 120 ++--
src/Render/Surface.cpp | 269 ++++----
src/Render/Surface.h | 49 +-
17 files changed, 1181 insertions(+), 1380 deletions(-)
delete mode 100644 src/Render/Options.cpp
create mode 100644 src/Render/Renderer.SurfacePS.h
create mode 100644 src/Render/Renderer.SurfaceVS.h
diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index 3ea12b25cf..d218024677 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -271,7 +271,6 @@
-
@@ -401,6 +400,8 @@
+
+
@@ -427,4 +428,4 @@
-
\ No newline at end of file
+
diff --git a/src/Render/Functions.cpp b/src/Render/Functions.cpp
index c5708b93ab..ef25fe802a 100644
--- a/src/Render/Functions.cpp
+++ b/src/Render/Functions.cpp
@@ -15,9 +15,10 @@
#include
#include
+#include
#include
-bool __fastcall RenderDX::AllocateSurfaces(const RectangleStruct& hidden_rect, const RectangleStruct& composite_rect, const RectangleStruct& tile_rect, const RectangleStruct& sidebar_rect, bool hidden_first) {
+bool __fastcall RenderDX::AllocateSurfaces(const RectangleStruct& hiddenRect, const RectangleStruct& compositeRect, const RectangleStruct& tileRect, const RectangleStruct& sidebarRect, bool hiddenFirst) {
Debug::Log("[RenderDX] Allocating new surfaces\n");
if (DSurface::Alternate) {
@@ -50,47 +51,47 @@ bool __fastcall RenderDX::AllocateSurfaces(const RectangleStruct& hidden_rect, c
DSurface::Sidebar = nullptr;
}
- if (hidden_first && hidden_rect.Width > 0 && hidden_rect.Height > 0) {
- DSurface::Hidden = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ if (hiddenFirst && hiddenRect.Width > 0 && hiddenRect.Height > 0) {
+ DSurface::Hidden = GameCreate(hiddenRect.Width, hiddenRect.Height);
DSurface::Hidden->Fill(0);
- Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hiddenRect.Width, hiddenRect.Height);
}
- if (composite_rect.Width > 0 && composite_rect.Height > 0) {
- DSurface::Composite = GameCreate(composite_rect.Width, composite_rect.Height);
+ if (compositeRect.Width > 0 && compositeRect.Height > 0) {
+ DSurface::Composite = GameCreate(compositeRect.Width, compositeRect.Height);
DSurface::Composite->Fill(0);
- Debug::Log("[RenderDX] CompositeSurface (%dx%d)\n", composite_rect.Width, composite_rect.Height);
+ Debug::Log("[RenderDX] CompositeSurface (%dx%d)\n", compositeRect.Width, compositeRect.Height);
}
- if (tile_rect.Width > 0 && tile_rect.Height > 0) {
- DSurface::Tile = GameCreate(tile_rect.Width, tile_rect.Height);
+ if (tileRect.Width > 0 && tileRect.Height > 0) {
+ DSurface::Tile = GameCreate(tileRect.Width, tileRect.Height);
DSurface::Tile->Fill(0);
- Debug::Log("[RenderDX] TileSurface (%dx%d)\n", tile_rect.Width, tile_rect.Height);
+ Debug::Log("[RenderDX] TileSurface (%dx%d)\n", tileRect.Width, tileRect.Height);
}
- if (sidebar_rect.Width > 0 && sidebar_rect.Height > 0) {
- DSurface::Sidebar = GameCreate(sidebar_rect.Width, sidebar_rect.Height);
+ if (sidebarRect.Width > 0 && sidebarRect.Height > 0) {
+ DSurface::Sidebar = GameCreate(sidebarRect.Width, sidebarRect.Height);
DSurface::Sidebar->Fill(0);
- Debug::Log("[RenderDX] SidebarSurface (%dx%d)\n", sidebar_rect.Width, sidebar_rect.Height);
+ Debug::Log("[RenderDX] SidebarSurface (%dx%d)\n", sidebarRect.Width, sidebarRect.Height);
}
- if (!hidden_first && hidden_rect.Width > 0 && hidden_rect.Height > 0) {
- DSurface::Hidden = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ if (!hiddenFirst && hiddenRect.Width > 0 && hiddenRect.Height > 0) {
+ DSurface::Hidden = GameCreate(hiddenRect.Width, hiddenRect.Height);
DSurface::Hidden->Fill(0);
- Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ Debug::Log("[RenderDX] HiddenSurface (%dx%d)\n", hiddenRect.Width, hiddenRect.Height);
}
- if (hidden_rect.Width > 0 && hidden_rect.Height > 0) {
- DSurface::Alternate = GameCreate(hidden_rect.Width, hidden_rect.Height);
+ if (hiddenRect.Width > 0 && hiddenRect.Height > 0) {
+ DSurface::Alternate = GameCreate(hiddenRect.Width, hiddenRect.Height);
DSurface::Alternate->Fill(0);
- Debug::Log("[RenderDX] AlternateSurface (%dx%d)\n", hidden_rect.Width, hidden_rect.Height);
+ Debug::Log("[RenderDX] AlternateSurface (%dx%d)\n", hiddenRect.Width, hiddenRect.Height);
}
return true;
}
-bool __fastcall RenderDX::SetVideoMode(HWND, int width, int height, int bits_per_pixel) {
- Debug::Log("[RenderDX] Setting video mode to %dx%d@%d\n", width, height, bits_per_pixel);
+bool __fastcall RenderDX::SetVideoMode(HWND, int width, int height, int bitsPerPixel) {
+ Debug::Log("[RenderDX] Setting video mode to %dx%d@%d\n", width, height, bitsPerPixel);
if (!DXRenderer::Instance().IsRendererReady()) {
Debug::Log("[RenderDX] Renderer is not ready\n");
@@ -98,14 +99,14 @@ bool __fastcall RenderDX::SetVideoMode(HWND, int width, int height, int bits_per
}
ResetVideoMode();
- if (!DXRenderer::Instance().CreateRenderer(width, height, bits_per_pixel)) {
+ if (!DXRenderer::Instance().CreateRenderer(width, height, bitsPerPixel)) {
Debug::Log("[RenderDX] Failed to create renderer\n");
return false;
}
Drawing::RenderWidth = width;
Drawing::RenderHeight = height;
- Drawing::RenderBitsPerPixel = bits_per_pixel;
+ Drawing::RenderBitsPerPixel = bitsPerPixel;
RenderDX::UpdateScale();
@@ -133,9 +134,9 @@ static void RecalcMouseWindowRegion(bool rebuildCursor) {
if (!DXMouse::Instance)
return;
- DXMouse::Instance->Recalc_Capture_Region();
+ DXMouse::Instance->RecalcCaptureRegion();
if (rebuildCursor)
- DXMouse::Instance->Rebuild_Cursor_Image();
+ DXMouse::Instance->RebuildCursorImage();
}
static void ApplyWindowResize(int width, int height) {
@@ -143,8 +144,8 @@ static void ApplyWindowResize(int width, int height) {
RecalcMouseWindowRegion(true);
}
-static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
- switch (msg) {
+static LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ switch (message) {
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
@@ -161,13 +162,13 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
{
// Scale mouse inputs before they are processed by SDL or the game.
if (RenderDX::ShouldScale()) {
- int x = GET_X_LPARAM(lparam);
- int y = GET_Y_LPARAM(lparam);
+ int x = GET_X_LPARAM(lParam);
+ int y = GET_Y_LPARAM(lParam);
x = RenderDX::ClientToRenderX(x);
y = RenderDX::ClientToRenderY(y);
- lparam = MAKELPARAM(x, y);
+ lParam = MAKELPARAM(x, y);
}
break;
}
@@ -175,7 +176,7 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
case WM_MOVE:
{
if (DXMouse::Instance) {
- DXMouse::Instance->Recalc_Capture_Region();
+ DXMouse::Instance->RecalcCaptureRegion();
}
return 0; // handled
}
@@ -198,7 +199,7 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
int height = DeferredWindowHeight;
RECT clientRect {};
- if (::GetClientRect(hwnd, &clientRect)) {
+ if (::GetClientRect(hWnd, &clientRect)) {
width = clientRect.right - clientRect.left;
height = clientRect.bottom - clientRect.top;
}
@@ -216,9 +217,9 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
case WM_SIZE:
{
- const int width = LOWORD(lparam);
- const int height = HIWORD(lparam);
- if (wparam == SIZE_MINIMIZED || width == 0 || height == 0) {
+ const int width = LOWORD(lParam);
+ const int height = HIWORD(lParam);
+ if (wParam == SIZE_MINIMIZED || width == 0 || height == 0) {
DeferredWindowResize = false;
RecalcMouseWindowRegion(false);
}
@@ -228,7 +229,7 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
DeferredWindowHeight = height;
RecalcMouseWindowRegion(false);
}
- else {
+ else {
ApplyWindowResize(width, height);
}
@@ -238,11 +239,11 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
case WM_SYSKEYDOWN:
{
// Handle Alt+Enter for fullscreen toggle
- if (wparam == VK_RETURN && (lparam & (1 << 29))) {
+ if (wParam == VK_RETURN && (lParam & (1 << 29))) {
DXRenderer::Instance().ToggleFullscreen();
if (DXMouse::Instance) {
- DXMouse::Instance->Recalc_Capture_Region();
- DXMouse::Instance->Rebuild_Cursor_Image();
+ DXMouse::Instance->RecalcCaptureRegion();
+ DXMouse::Instance->RebuildCursorImage();
}
return 0; // handled
}
@@ -252,9 +253,9 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
case WM_SETCURSOR:
{
// Prevent the system from setting the cursor when it's over our window, since we handle it ourselves.
- if (LOWORD(lparam) == HTCLIENT) {
+ if (LOWORD(lParam) == HTCLIENT) {
if (DXMouse::Instance)
- DXMouse::Instance->Set_Cached_Cursor();
+ DXMouse::Instance->SetCachedCursor();
return TRUE; // handled
}
break;
@@ -262,19 +263,19 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
case WM_ACTIVATEAPP:
{
- if (DXRenderOptions::Config().PauseGameWhenLoseFocus)
+ if (RenderOptions::Config().PauseGameWhenLoseFocus)
break; // goto the original window procedure to allow the game to pause when losing focus
- if (hwnd == Game::hWnd) {
- Unsorted::ScenarioInit = true; // game is always active
- if (wparam) {
+ if (hWnd == Game::hWnd) {
+ Unsorted::GameInFocus = true; // game is always active
+ if (wParam) {
Debug::Log("[RenderDX] Game window activated\n");
if (DXMouse::Instance)
- DXMouse::Instance->Capture_Mouse();
+ DXMouse::Instance->CaptureMouse();
}
else {
Debug::Log("[RenderDX] Game window deactivated\n");
if (DXMouse::Instance)
- DXMouse::Instance->Release_Mouse();
+ DXMouse::Instance->ReleaseMouse();
}
}
return 0; // handled - prevent the game from pausing when the window is deactivated
@@ -282,12 +283,12 @@ static LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
}
// Call original window procedure for default processing
- return reinterpret_cast(0x7775C0)(hwnd, msg, wparam, lparam);
+ return reinterpret_cast(0x7775C0)(hWnd, message, wParam, lParam);
}
-void __fastcall RenderDX::CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height) {
+void __fastcall RenderDX::CreateMainWindow(HINSTANCE instance, int cmdShow, int width, int height) {
Debug::Log("[RenderDX] Creating main window\n");
- if (!DXRenderer::Instance().CreateMainWindow(instance, cmd_show, width, height, MainWindowProc)) {
+ if (!DXRenderer::Instance().CreateMainWindow(instance, cmdShow, width, height, MainWindowProc)) {
Debug::Log("[RenderDX] Failed to create main window\n");
::MessageBoxA(nullptr, "Failed to create main window", "Error", MB_ICONERROR);
::ExitProcess(0xC0DEBEEF);
@@ -299,8 +300,8 @@ void __fastcall RenderDX::DestroyMainWindow() {
DXRenderer::Instance().DestroyMainWindow();
}
-bool __fastcall RenderDX::UpdateScreen(Surface* surface) {
- if (!surface) {
+bool __fastcall RenderDX::UpdateScreen(Surface* pSurface) {
+ if (!pSurface) {
Debug::Log("[RenderDX] UpdateScreen called with null surface\n");
return false;
}
@@ -309,13 +310,13 @@ bool __fastcall RenderDX::UpdateScreen(Surface* surface) {
DXRenderer::Instance().SetRenderScale(shouldScale);
// Retrieve the game surface data
- if (void* pixels = surface->Lock(0, 0)) {
- if (!DXRenderer::Instance().UploadSurfaceToTexture(pixels, surface->GetPitch())) {
+ if (void* pPixels = pSurface->Lock(0, 0)) {
+ if (!DXRenderer::Instance().UploadSurfaceToTexture(pPixels, pSurface->GetPitch())) {
Debug::Log("[RenderDX] Failed to upload surface to texture\n");
- surface->Unlock();
+ pSurface->Unlock();
return false;
}
- surface->Unlock();
+ pSurface->Unlock();
}
static bool scaled = ShouldScale();
@@ -324,7 +325,7 @@ bool __fastcall RenderDX::UpdateScreen(Surface* surface) {
if (scaled != shouldScale) {
scaled = shouldScale;
if (DXMouse::Instance)
- DXMouse::Instance->Rebuild_Cursor_Image();
+ DXMouse::Instance->RebuildCursorImage();
}
DXRenderer::Instance().Present();
@@ -336,62 +337,62 @@ bool __fastcall RenderDX::ShouldScale() {
return Unsorted::SpecialDialog == 0 && Unsorted::WSDialogCount == 0;
}
-static void RebuildDisplayState(const RectangleStruct& view_rect) {
- auto temp = view_rect;
- temp.X = GameOptionsClass::Instance.SidebarMode ? 0 : 168;
- temp.Y = 16;
- temp.Width -= 168;
- temp.Height -= 16;
+static void RebuildDisplayState(const RectangleStruct& viewRect) {
+ auto sidebarRect = viewRect;
+ sidebarRect.X = GameOptionsClass::Instance.SidebarMode ? 0 : 168;
+ sidebarRect.Y = 16;
+ sidebarRect.Width -= 168;
+ sidebarRect.Height -= 16;
- DSurface::ViewBounds = view_rect;
- Drawing::RenderWidth = view_rect.Width;
- Drawing::RenderHeight = view_rect.Height;
+ DSurface::ViewBounds = viewRect;
+ Drawing::RenderWidth = viewRect.Width;
+ Drawing::RenderHeight = viewRect.Height;
DSurface::Primary = DXSurface::CreatePrimary();
RenderDX::AllocateSurfaces(
- view_rect,
- RectangleStruct { 0,0,temp.Width,view_rect.Height },
- RectangleStruct { 0,0,temp.Width,view_rect.Height },
- RectangleStruct { 0,0,168,view_rect.Height },
+ viewRect,
+ RectangleStruct { 0, 0, sidebarRect.Width, viewRect.Height },
+ RectangleStruct { 0, 0, sidebarRect.Width, viewRect.Height },
+ RectangleStruct { 0, 0, 168, viewRect.Height },
false
);
DSurface::Temp = DSurface::Hidden;
if (DXMouse::Instance) {
- DXMouse::Instance->Rebuild_Cursor_Image();
+ DXMouse::Instance->RebuildCursorImage();
}
- SidebarClass::Instance.Set_View_Dimensions(temp);
+ SidebarClass::Instance.Set_View_Dimensions(sidebarRect);
SidebarClass::Instance.Init_IO();
SidebarClass::Instance.Activate(1);
SidebarClass::Instance.InitGUI();
SidebarClass::Instance.MarkNeedsRedraw(2); // REDRAW_ALL
- DXMouse::Instance->Show_Mouse();
+ DXMouse::Instance->ShowMouse();
}
bool __fastcall RenderDX::ChangeDisplayMode(int width, int height) {
Debug::Log("[RenderDX] Changing display mode to %dx%d\n", width, height);
// Save current window position
- RectangleStruct old_rect = DSurface::ViewBounds;
- if (old_rect.Width <= 0 || old_rect.Height <= 0) {
+ RectangleStruct oldRect = DSurface::ViewBounds;
+ if (oldRect.Width <= 0 || oldRect.Height <= 0) {
if (Drawing::RenderWidth > 0 && Drawing::RenderHeight > 0) {
Debug::Log("[RenderDX] Current view bounds are invalid, using RenderWidth/RenderHeight\n");
- old_rect = RectangleStruct { 0, 0, Drawing::RenderWidth, Drawing::RenderHeight };
+ oldRect = RectangleStruct { 0, 0, Drawing::RenderWidth, Drawing::RenderHeight };
}
}
- const int old_render_width = Drawing::RenderWidth;
- const int old_render_height = Drawing::RenderHeight;
- const int old_render_bpp = Drawing::RenderBitsPerPixel;
+ const int oldRenderWidth = Drawing::RenderWidth;
+ const int oldRenderHeight = Drawing::RenderHeight;
+ const int oldRenderBpp = Drawing::RenderBitsPerPixel;
- int old_window_x = 0;
- int old_window_y = 0;
- int old_window_width = DXRenderer::Instance().GetWindowWidth();
- int old_window_height = DXRenderer::Instance().GetWindowHeight();
+ int oldWindowX = 0;
+ int oldWindowY = 0;
+ int oldWindowWidth = DXRenderer::Instance().GetWindowWidth();
+ int oldWindowHeight = DXRenderer::Instance().GetWindowHeight();
- DXMouse::Instance->Hide_Mouse();
+ DXMouse::Instance->HideMouse();
// Delete the old primary surface
if (DSurface::Primary) {
@@ -401,53 +402,53 @@ bool __fastcall RenderDX::ChangeDisplayMode(int width, int height) {
}
if (DXRenderer::Instance().IsWindowed()) {
- int window_width = width;
- int window_height = height;
+ int windowWidth = width;
+ int windowHeight = height;
RECT temp;
::GetWindowRect(Game::hWnd, &temp);
- old_window_x = temp.left;
- old_window_y = temp.top;
- old_window_width = temp.right - temp.left;
- old_window_height = temp.bottom - temp.top;
+ oldWindowX = temp.left;
+ oldWindowY = temp.top;
+ oldWindowWidth = temp.right - temp.left;
+ oldWindowHeight = temp.bottom - temp.top;
+
+ int centerX = oldWindowX + oldWindowWidth / 2;
+ int centerY = oldWindowY + oldWindowHeight / 2;
- int center_x = old_window_x + old_window_width / 2;
- int center_y = old_window_y + old_window_height / 2;
-
- int new_x = center_x - window_width / 2;
- int new_y = center_y - window_height / 2;
+ int newX = centerX - windowWidth / 2;
+ int newY = centerY - windowHeight / 2;
- DXRenderer::Instance().MoveWindow(new_x, new_y, window_width, window_height);
+ DXRenderer::Instance().MoveWindow(newX, newY, windowWidth, windowHeight);
- Debug::Log("[RenderDX] Moved window to (%d, %d) with size %dx%d\n", new_x, new_y, window_width, window_height);
+ Debug::Log("[RenderDX] Moved window to (%d, %d) with size %dx%d\n", newX, newY, windowWidth, windowHeight);
}
// Recreate all intermediates
if (!SetVideoMode(Game::hWnd, width, height, 16)) {
if (DXRenderer::Instance().IsWindowed()) {
- DXRenderer::Instance().MoveWindow(old_window_x, old_window_y, old_window_width, old_window_height);
- Debug::Log("[RenderDX] Restore window to (%d, %d) with size %dx%d\n", old_window_x, old_window_y, old_window_width, old_window_height);
+ DXRenderer::Instance().MoveWindow(oldWindowX, oldWindowY, oldWindowWidth, oldWindowHeight);
+ Debug::Log("[RenderDX] Restore window to (%d, %d) with size %dx%d\n", oldWindowX, oldWindowY, oldWindowWidth, oldWindowHeight);
}
- if (old_rect.X > 0 && old_rect.Y > 0 && old_render_width > 0 && old_render_height > 0) {
+ if (oldRect.X > 0 && oldRect.Y > 0 && oldRenderWidth > 0 && oldRenderHeight > 0) {
Debug::Log("[RenderDX] Restoring old display mode.\n");
- if (!SetVideoMode(Game::hWnd, old_render_width, old_render_height, old_render_bpp)) {
+ if (!SetVideoMode(Game::hWnd, oldRenderWidth, oldRenderHeight, oldRenderBpp)) {
Debug::Log("[RenderDX] Failed to restore old display mode.\n");
- DXMouse::Instance->Show_Mouse();
+ DXMouse::Instance->ShowMouse();
return false;
}
- RebuildDisplayState(old_rect);
+ RebuildDisplayState(oldRect);
}
else {
Debug::Log("[RenderDX] Old view bounds are invalid, cannot restore\n");
}
- DXMouse::Instance->Show_Mouse();
+ DXMouse::Instance->ShowMouse();
return false;
}
- RectangleStruct new_view_rect = { 0, 0, width, height };
- RebuildDisplayState(new_view_rect);
+ RectangleStruct newViewRect = { 0, 0, width, height };
+ RebuildDisplayState(newViewRect);
Debug::Log("[RenderDX]: ViewBounds: %dx%d\n", width, height);
Debug::Log("[RenderDX] Mode change complete.\n");
@@ -503,17 +504,17 @@ void __fastcall RenderDX::ResetScale() {
ViewportY = 0.0f;
}
-int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD) {
+int* __fastcall RenderDX::EnumDisplayModes(DWORD minWidth, DWORD minHeight, DWORD maxWidth, DWORD maxHeight, DWORD) {
std::vector> modes;
DEVMODE devmode{};
- DWORD mode_index = 0;
+ DWORD modeIndex = 0;
- while (::EnumDisplaySettingsA(nullptr, mode_index++, &devmode)) {
+ while (::EnumDisplaySettingsA(nullptr, modeIndex++, &devmode)) {
const DWORD w = devmode.dmPelsWidth;
const DWORD h = devmode.dmPelsHeight;
const DWORD bpp = devmode.dmBitsPerPel;
- if (w >= minw && h >= minh && w <= maxw && h <= maxh && bpp == 32) {
+ if (w >= minWidth && h >= minHeight && w <= maxWidth && h <= maxHeight && bpp == 32) {
modes.emplace_back(static_cast(w), static_cast(h));
}
}
@@ -543,14 +544,14 @@ int* __fastcall RenderDX::EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, D
void __fastcall RenderDX::MainProcHandlePaint() {
if (DXMouse::Instance && DSurface::Primary && DSurface::Hidden && DSurface::Composite) {
if (Unsorted::ScenarioStarted) {
- GScreenClass::UpdatePrimarySurface(DXMouse::Instance->Is_Captured(), DSurface::Composite, nullptr);
+ GScreenClass::UpdatePrimarySurface(DXMouse::Instance->IsCaptured(), DSurface::Composite, nullptr);
SidebarClass::Instance.BlitSidebar(true);
}
else if (Game::IsMoviePlaying()) {
Game::BlitMovie();
}
else {
- GScreenClass::UpdatePrimarySurface(DXMouse::Instance->Is_Captured(), DSurface::Hidden, nullptr);
+ GScreenClass::UpdatePrimarySurface(DXMouse::Instance->IsCaptured(), DSurface::Hidden, nullptr);
}
}
}
diff --git a/src/Render/Functions.h b/src/Render/Functions.h
index 8f97794811..6d679a00e8 100644
--- a/src/Render/Functions.h
+++ b/src/Render/Functions.h
@@ -6,12 +6,12 @@ class Surface;
class RenderDX {
public:
- static bool __fastcall AllocateSurfaces(const RectangleStruct& hidden_rect, const RectangleStruct& composite_rect, const RectangleStruct& tile_rect, const RectangleStruct& sidebar_rect, bool hidden_first);
- static bool __fastcall SetVideoMode(HWND, int width, int height, int bits_per_pixel);
+ static bool __fastcall AllocateSurfaces(const RectangleStruct& hiddenRect, const RectangleStruct& compositeRect, const RectangleStruct& tileRect, const RectangleStruct& sidebarRect, bool hiddenFirst);
+ static bool __fastcall SetVideoMode(HWND, int width, int height, int bitsPerPixel);
static void __fastcall ResetVideoMode();
- static void __fastcall CreateMainWindow(HINSTANCE instance, int cmd_show, int width, int height);
+ static void __fastcall CreateMainWindow(HINSTANCE instance, int cmdShow, int width, int height);
static void __fastcall DestroyMainWindow();
- static bool __fastcall UpdateScreen(Surface* surface);
+ static bool __fastcall UpdateScreen(Surface* pSurface);
static bool __fastcall ShouldScale();
static bool __fastcall ChangeDisplayMode(int width, int height);
static float __fastcall GetXScale();
@@ -20,6 +20,6 @@ class RenderDX {
static int __fastcall ClientToRenderY(int y);
static void __fastcall UpdateScale();
static void __fastcall ResetScale();
- static int* __fastcall EnumDisplayModes(DWORD minw, DWORD minh, DWORD maxw, DWORD maxh, DWORD bitdepth);
+ static int* __fastcall EnumDisplayModes(DWORD minWidth, DWORD minHeight, DWORD maxWidth, DWORD maxHeight, DWORD bitDepth);
static void __fastcall MainProcHandlePaint();
};
diff --git a/src/Render/Hooks.Mouse.cpp b/src/Render/Hooks.Mouse.cpp
index c60153197c..e0a4ae9b44 100644
--- a/src/Render/Hooks.Mouse.cpp
+++ b/src/Render/Hooks.Mouse.cpp
@@ -1,6 +1,5 @@
#include "Mouse.h"
-#include
#include
#include
@@ -9,7 +8,7 @@
#include
-DEFINE_HOOK(0x6BDEF9, WinMain_CreateWWMouse, 0x5) {
+DEFINE_HOOK(0x6BDEF9, WinMainCreateWWMouse, 0x5) {
DXMouse::Instance = GameCreate(DSurface::Primary, Game::hWnd);
R->EAX(DXMouse::Instance);
return 0x6BDF25;
@@ -18,25 +17,25 @@ DEFINE_HOOK(0x6BDEF9, WinMain_CreateWWMouse, 0x5) {
static HANDLE MouseThread;
static std::binary_semaphore MouseThreadSemaphore { 0 };
-static DWORD WINAPI _MouseThread(LPVOID) {
+static DWORD WINAPI MouseThreadProc(LPVOID) {
while (!MouseThreadSemaphore.try_acquire_for(std::chrono::milliseconds(10))) {
if (DXMouse::Instance)
- DXMouse::Instance->Process_Mouse();
+ DXMouse::Instance->ProcessMouse();
}
return 0;
}
-static void __fastcall _DXMouse_StartMouseThread() {
- MouseThread = ::CreateThread(nullptr, 0, _MouseThread, nullptr, 0, nullptr);
+static void __fastcall DXMouseStartMouseThread() {
+ MouseThread = ::CreateThread(nullptr, 0, MouseThreadProc, nullptr, 0, nullptr);
if (!MouseThread) {
MouseThreadSemaphore.release();
return;
}
::SetThreadPriority(MouseThread, THREAD_PRIORITY_TIME_CRITICAL);
}
-DEFINE_FUNCTION_JUMP(LJMP, 0x7B84F0, _DXMouse_StartMouseThread);
+DEFINE_FUNCTION_JUMP(LJMP, 0x7B84F0, DXMouseStartMouseThread);
-static void __fastcall _DXMouse_EndMouseThread() {
+static void __fastcall DXMouseEndMouseThread() {
MouseThreadSemaphore.release();
if (MouseThread) {
::WaitForSingleObject(MouseThread, INFINITE);
@@ -44,17 +43,17 @@ static void __fastcall _DXMouse_EndMouseThread() {
MouseThread = nullptr;
}
}
-DEFINE_FUNCTION_JUMP(LJMP, 0x7B86B0, _DXMouse_EndMouseThread);
+DEFINE_FUNCTION_JUMP(LJMP, 0x7B86B0, DXMouseEndMouseThread);
-static void __fastcall _DXMouse_ProcessMouse(DXMouse* This) {
- This->Process_Mouse();
+static void __fastcall DXMouseProcessMouse(DXMouse* pThis) {
+ pThis->ProcessMouse();
}
-DEFINE_FUNCTION_JUMP(LJMP, 0x7BA090, _DXMouse_ProcessMouse);
+DEFINE_FUNCTION_JUMP(LJMP, 0x7BA090, DXMouseProcessMouse);
-DEFINE_HOOK_AGAIN(0x72429E, DXMouse_TooltipManager_GetMousePosition, 0xA);
-DEFINE_HOOK(0x724359, DXMouse_TooltipManager_GetMousePosition, 0xA) {
+DEFINE_HOOK_AGAIN(0x72429E, DXMouseTooltipManagerGetMousePosition, 0xA);
+DEFINE_HOOK(0x724359, DXMouseTooltipManagerGetMousePosition, 0xA) {
GET(ToolTipManager*, pThis, ESI);
- pThis->CurrentMousePosition = DXMouse::Instance->Get_Mouse_Point();
+ pThis->CurrentMousePosition = DXMouse::Instance->GetMousePoint();
R->EBX(&pThis->CurrentMousePosition);
return R->Origin() + 0x15;
}
diff --git a/src/Render/Hooks.Options.cpp b/src/Render/Hooks.Options.cpp
index e25f51c332..0880487c65 100644
--- a/src/Render/Hooks.Options.cpp
+++ b/src/Render/Hooks.Options.cpp
@@ -1,12 +1,11 @@
-#include
-
#include "Options.h"
+#include
+
#include
-DEFINE_HOOK(0x6BC141, DXRender_LoadConfigFromRA2MD, 0x7)
-{
- auto& config = DXRenderOptions::Config();
+DEFINE_HOOK(0x6BC141, RenderConfig_LoadRA2MD, 0x7) {
+ auto& config = RenderOptions::Config();
config.PreserveAspectRatio = CCINIClass::INI_RA2MD.ReadBool("DXRender", "PreserveAspectRatio", config.PreserveAspectRatio);
config.WindowedBorder = CCINIClass::INI_RA2MD.ReadBool("DXRender", "WindowedBorder", config.WindowedBorder);
config.StartFullscreen = CCINIClass::INI_RA2MD.ReadBool("DXRender", "StartFullscreen", config.StartFullscreen);
diff --git a/src/Render/Hooks.Surface.cpp b/src/Render/Hooks.Surface.cpp
index 182a966e0d..aadf56bf59 100644
--- a/src/Render/Hooks.Surface.cpp
+++ b/src/Render/Hooks.Surface.cpp
@@ -1,27 +1,17 @@
#include "Surface.h"
-#include
#include
DEFINE_FUNCTION_JUMP(LJMP, 0x4BA770, DXSurface::CreatePrimary);
-DEFINE_JUMP(LJMP, 0x77747A, 0x777575); // Skip Restore_Check
+DEFINE_JUMP(LJMP, 0x77747A, 0x777575); // Skip Restore_Check.
-static DXSurface* __fastcall _DXSurface_CTOR(DXSurface* surface, void*, int width, int height, bool system_mem, bool enable_3d) {
- return new(surface) DXSurface(width, height);
+static DXSurface* __fastcall DXSurfaceCtor(DXSurface* pSurface, void*, int width, int height, bool systemMem, bool enable3D) {
+ return new(pSurface) DXSurface(width, height);
}
-DEFINE_FUNCTION_JUMP(LJMP, 0x4BA5A0, _DXSurface_CTOR);
+DEFINE_FUNCTION_JUMP(LJMP, 0x4BA5A0, DXSurfaceCtor);
-static int __stdcall _BinkDDSurfaceType(void*)
-{
+static int __stdcall BinkDDSurfaceType(void*) {
return 10; // BINKSURFACE565
}
-DEFINE_PATCH_TYPED(void*, 0x7E15A8, _BinkDDSurfaceType);
-
-static int __stdcall _BinkCopyToBuffer(void* bnk, void* dest, int destpitch, unsigned int destheight, unsigned int destx, unsigned int desty, unsigned int flags)
-{
- // Skip the bink movie render for now to avoid the crash. The movie will be rendered as black screen, but at least it won't crash.
- // So we can test the other features without worrying about the movie rendering. We will try to implement the movie rendering later.
- return 0;
-}
-DEFINE_PATCH_TYPED(void*, 0x7E15B8, _BinkCopyToBuffer);
+DEFINE_PATCH_TYPED(void*, 0x7E15A8, BinkDDSurfaceType);
diff --git a/src/Render/Hooks.cpp b/src/Render/Hooks.cpp
index 5697bb3de8..02e66529f3 100644
--- a/src/Render/Hooks.cpp
+++ b/src/Render/Hooks.cpp
@@ -1,41 +1,37 @@
#include "Functions.h"
-#include
#include
#include
+#include
#include
#include
-#include