From f332d4a9b548e9c7e18c245fd3b90ffc5a94b943 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 8 Jun 2021 02:14:12 -0300
Subject: [PATCH] common/host_shader: Load Windows 10 functions dynamically

Workaround old headers and libraries shipped on MinGW.
---
 src/common/host_memory.cpp | 117 ++++++++++++++++++++++++++++---------
 1 file changed, 88 insertions(+), 29 deletions(-)

diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index c6d65aab9..9ae384f01 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -1,18 +1,10 @@
 #ifdef _WIN32
 
-#ifdef _WIN32_WINNT
-#undef _WIN32_WINNT
-#endif
-#define _WIN32_WINNT 0x0A00 // Windows 10
-
-#include <windows.h>
-
-#include <boost/icl/separate_interval_set.hpp>
-
 #include <iterator>
 #include <unordered_map>
-
-#pragma comment(lib, "mincore.lib")
+#include <boost/icl/separate_interval_set.hpp>
+#include <windows.h>
+#include "common/dynamic_library.h"
 
 #elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
 
@@ -40,38 +32,99 @@ constexpr size_t HugePageSize = 0x200000;
 
 #ifdef _WIN32
 
+// Manually imported for MinGW compatibility
+#ifndef MEM_RESERVE_PLACEHOLDER
+#define MEM_RESERVE_PLACEHOLDER 0x0004000
+#endif
+#ifndef MEM_REPLACE_PLACEHOLDER
+#define MEM_REPLACE_PLACEHOLDER 0x00004000
+#endif
+#ifndef MEM_COALESCE_PLACEHOLDERS
+#define MEM_COALESCE_PLACEHOLDERS 0x00000001
+#endif
+#ifndef MEM_PRESERVE_PLACEHOLDER
+#define MEM_PRESERVE_PLACEHOLDER 0x00000002
+#endif
+
+using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
+    _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
+    _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
+    _In_opt_ PCWSTR Name,
+    _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
+    _In_ ULONG ParameterCount);
+
+using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
+    _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
+    _In_ ULONG AllocationType, _In_ ULONG PageProtection,
+    _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
+    _In_ ULONG ParameterCount);
+
+using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)(
+    _In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
+    _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection,
+    _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
+    _In_ ULONG ParameterCount);
+
+using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
+                                           _In_ ULONG UnmapFlags);
+
+template <typename T>
+static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
+    if (!dll.GetSymbol(name, &pfn)) {
+        LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
+        throw std::bad_alloc{};
+    }
+}
+
 class HostMemory::Impl {
 public:
     explicit Impl(size_t backing_size_, size_t virtual_size_)
-        : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} {
+        : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
+          kernelbase_dll("Kernelbase") {
+        if (!kernelbase_dll.IsOpen()) {
+            LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
+            throw std::bad_alloc{};
+        }
+        GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
+        GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
+        GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
+        GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
+
         // Allocate backing file map
         backing_handle =
-            CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
-                               PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
+            pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
+                                   PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
         if (!backing_handle) {
+            LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
+                         backing_size >> 20);
             throw std::bad_alloc{};
         }
         // Allocate a virtual memory for the backing file map as placeholder
-        backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, backing_size,
-                                                      MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
-                                                      PAGE_NOACCESS, nullptr, 0));
+        backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
+                                                          MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
+                                                          PAGE_NOACCESS, nullptr, 0));
         if (!backing_base) {
             Release();
+            LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
+                         backing_size >> 20);
             throw std::bad_alloc{};
         }
         // Map backing placeholder
-        void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
-                                         MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
+        void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
+                                             MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
         if (ret != backing_base) {
             Release();
+            LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
             throw std::bad_alloc{};
         }
         // Allocate virtual address placeholder
-        virtual_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, virtual_size,
-                                                      MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
-                                                      PAGE_NOACCESS, nullptr, 0));
+        virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
+                                                          MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
+                                                          PAGE_NOACCESS, nullptr, 0));
         if (!virtual_base) {
             Release();
+            LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
+                         virtual_size >> 30);
             throw std::bad_alloc{};
         }
     }
@@ -136,8 +189,8 @@ private:
     void Release() {
         if (!placeholders.empty()) {
             for (const auto& placeholder : placeholders) {
-                if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
-                                      MEM_PRESERVE_PLACEHOLDER)) {
+                if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
+                                          MEM_PRESERVE_PLACEHOLDER)) {
                     LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
                 }
             }
@@ -149,7 +202,7 @@ private:
             }
         }
         if (backing_base) {
-            if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
+            if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
                 LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
             }
             if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
@@ -184,8 +237,8 @@ private:
         const bool split_left = unmap_begin > placeholder_begin;
         const bool split_right = unmap_end < placeholder_end;
 
-        if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin,
-                              MEM_PRESERVE_PLACEHOLDER)) {
+        if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
+                                  MEM_PRESERVE_PLACEHOLDER)) {
             LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
         }
         // If we have to remap memory regions due to partial unmaps, we are in a data race as
@@ -235,8 +288,8 @@ private:
     }
 
     void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
-        if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
-                            length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
+        if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
+                                length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
             LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
         }
     }
@@ -279,6 +332,12 @@ private:
     HANDLE process{};        ///< Current process handle
     HANDLE backing_handle{}; ///< File based backing memory
 
+    DynamicLibrary kernelbase_dll;
+    PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
+    PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
+    PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
+    PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
+
     std::mutex placeholder_mutex;                                 ///< Mutex for placeholders
     boost::icl::separate_interval_set<size_t> placeholders;       ///< Mapped placeholders
     std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset