From d898961a7edb83722ec51c75a30a4f0ebda3a48f Mon Sep 17 00:00:00 2001
From: narzoul <narzoul@users.noreply.github.com>
Date: Tue, 29 Nov 2016 00:00:49 +0100
Subject: [PATCH] Restore legacy double/triple buffered v-sync behavior

On recent drivers, double buffered DirectDraw flips no longer wait for the
vertical sync before returning and instead just insert the flip into the
flip queue for later execution. This effectively results in triple buffered
behavior (in the render-ahead sense) and causes up to an extra frame of
latency even if the flip queue size is set to 1.

To restore the legacy double buffered behavior, each flip waits for the
presented frame to leave the flip queue before returning.

To restore the legacy triple buffered behavior, the flip queue size is
forced to 1. This causes the flip to wait if the previous flip is still
pending.
---
 DDrawCompat/D3dDdi/AdapterFuncs.h             |   1 +
 DDrawCompat/D3dDdi/Hooks.cpp                  |  27 +-
 DDrawCompat/D3dDdi/Hooks.h                    |   3 +
 DDrawCompat/D3dDdi/KernelModeThunks.cpp       | 234 ++++++++++++++++++
 DDrawCompat/D3dDdi/KernelModeThunks.h         |  11 +
 DDrawCompat/D3dDdi/Log/AdapterFuncsLog.cpp    |  38 +++
 DDrawCompat/D3dDdi/Log/AdapterFuncsLog.h      |  12 +
 DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.cpp |  15 ++
 DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.h   |   1 +
 .../D3dDdi/Log/KernelModeThunksLog.cpp        | 116 +++++++++
 DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h  |  19 ++
 DDrawCompat/DDraw/RealPrimarySurface.cpp      |   2 +-
 DDrawCompat/DDrawCompat.vcxproj               |   6 +
 DDrawCompat/DDrawCompat.vcxproj.filters       |  18 ++
 14 files changed, 486 insertions(+), 17 deletions(-)
 create mode 100644 DDrawCompat/D3dDdi/KernelModeThunks.cpp
 create mode 100644 DDrawCompat/D3dDdi/KernelModeThunks.h
 create mode 100644 DDrawCompat/D3dDdi/Log/AdapterFuncsLog.cpp
 create mode 100644 DDrawCompat/D3dDdi/Log/AdapterFuncsLog.h
 create mode 100644 DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp
 create mode 100644 DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h

diff --git a/DDrawCompat/D3dDdi/AdapterFuncs.h b/DDrawCompat/D3dDdi/AdapterFuncs.h
index f7006aa..216730a 100644
--- a/DDrawCompat/D3dDdi/AdapterFuncs.h
+++ b/DDrawCompat/D3dDdi/AdapterFuncs.h
@@ -6,6 +6,7 @@
 #include <d3dumddi.h>
 
 #include "Common/CompatVtable.h"
+#include "D3dDdi/Log/AdapterFuncsLog.h"
 #include "D3dDdi/Visitors/AdapterFuncsVisitor.h"
 
 namespace D3dDdi
diff --git a/DDrawCompat/D3dDdi/Hooks.cpp b/DDrawCompat/D3dDdi/Hooks.cpp
index 47d89b3..bd46202 100644
--- a/DDrawCompat/D3dDdi/Hooks.cpp
+++ b/DDrawCompat/D3dDdi/Hooks.cpp
@@ -11,6 +11,7 @@
 #include "Common/Log.h"
 #include "D3dDdi/AdapterCallbacks.h"
 #include "D3dDdi/AdapterFuncs.h"
+#include "D3dDdi/KernelModeThunks.h"
 
 std::ostream& operator<<(std::ostream& os, const D3DDDIARG_OPENADAPTER& data)
 {
@@ -33,21 +34,6 @@ namespace
 	HRESULT APIENTRY openAdapter(D3DDDIARG_OPENADAPTER* pOpenData);
 	void unhookOpenAdapter();
 
-	NTSTATUS APIENTRY d3dKmtQueryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
-	{
-		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTQueryAdapterInfo)(pData);
-		if (SUCCEEDED(result) && KMTQAITYPE_UMDRIVERNAME == pData->Type)
-		{
-			auto info = static_cast<D3DKMT_UMDFILENAMEINFO*>(pData->pPrivateDriverData);
-			if (g_hookedUmdFileName != info->UmdFileName)
-			{
-				unhookOpenAdapter();
-				hookOpenAdapter(info->UmdFileName);
-			}
-		}
-		return result;
-	}
-
 	void hookOpenAdapter(const std::wstring& umdFileName)
 	{
 		g_hookedUmdFileName = umdFileName;
@@ -99,7 +85,16 @@ namespace D3dDdi
 
 	void installHooks()
 	{
-		HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, d3dKmtQueryAdapterInfo);
+		KernelModeThunks::installHooks();
+	}
+
+	void onUmdFileNameQueried(const std::wstring& umdFileName)
+	{
+		if (g_hookedUmdFileName != umdFileName)
+		{
+			unhookOpenAdapter();
+			hookOpenAdapter(umdFileName);
+		}
 	}
 
 	void uninstallHooks()
diff --git a/DDrawCompat/D3dDdi/Hooks.h b/DDrawCompat/D3dDdi/Hooks.h
index 8c7ab30..12eaeff 100644
--- a/DDrawCompat/D3dDdi/Hooks.h
+++ b/DDrawCompat/D3dDdi/Hooks.h
@@ -1,8 +1,11 @@
 #pragma once
 
+#include <string>
+
 namespace D3dDdi
 {
 	UINT getDdiVersion();
 	void installHooks();
+	void onUmdFileNameQueried(const std::wstring& umdFileName);
 	void uninstallHooks();
 }
diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp
new file mode 100644
index 0000000..416ea44
--- /dev/null
+++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp
@@ -0,0 +1,234 @@
+#define CINTERFACE
+
+#include <map>
+
+#include <d3d.h>
+#include <d3dumddi.h>
+#include <../km/d3dkmthk.h>
+
+#include "Common/Log.h"
+#include "Common/Hook.h"
+#include "D3dDdi/Hooks.h"
+#include "D3dDdi/KernelModeThunks.h"
+#include "DDraw/Surfaces/PrimarySurface.h"
+
+namespace
+{
+	struct ContextInfo
+	{
+		D3DKMT_HANDLE device;
+
+		ContextInfo() : device(0) {}
+	};
+
+	struct DeviceInfo
+	{
+		D3DKMT_HANDLE adapter;
+		D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId;
+
+		DeviceInfo() : adapter(0), vidPnSourceId(D3DDDI_ID_UNINITIALIZED) {}
+	};
+
+	std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
+	std::map<D3DKMT_HANDLE, DeviceInfo> g_devices;
+
+	NTSTATUS APIENTRY createDevice(D3DKMT_CREATEDEVICE* pData)
+	{
+		Compat::LogEnter("D3DKMTCreateDevice", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateDevice)(pData);
+		if (SUCCEEDED(result))
+		{
+			g_devices[pData->hDevice].adapter = pData->hAdapter;
+
+			D3DKMT_SETQUEUEDLIMIT limit = {};
+			limit.hDevice = pData->hDevice;
+			limit.Type = D3DKMT_SET_QUEUEDLIMIT_PRESENT;
+			limit.QueuedPresentLimit = 1;
+			CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(&limit);
+		}
+		Compat::LogLeave("D3DKMTCreateDevice", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY createContext(D3DKMT_CREATECONTEXT* pData)
+	{
+		Compat::LogEnter("D3DKMTCreateContext", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateContext)(pData);
+		if (SUCCEEDED(result))
+		{
+			g_contexts[pData->hContext].device = pData->hDevice;
+		}
+		Compat::LogLeave("D3DKMTCreateContext", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY createContextVirtual(D3DKMT_CREATECONTEXTVIRTUAL* pData)
+	{
+		Compat::LogEnter("D3DKMTCreateContextVirtual", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTCreateContextVirtual)(pData);
+		if (SUCCEEDED(result))
+		{
+			g_contexts[pData->hContext].device = pData->hDevice;
+		}
+		Compat::LogLeave("D3DKMTCreateContextVirtual", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData)
+	{
+		Compat::LogEnter("D3DKMTDestroyContext", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyContext)(pData);
+		if (SUCCEEDED(result))
+		{
+			g_contexts.erase(pData->hContext);
+		}
+		Compat::LogLeave("D3DKMTDestroyContext", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY destroyDevice(const D3DKMT_DESTROYDEVICE* pData)
+	{
+		Compat::LogEnter("D3DKMTDestroyDevice", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTDestroyDevice)(pData);
+		if (SUCCEEDED(result))
+		{
+			g_devices.erase(pData->hDevice);
+		}
+		Compat::LogLeave("D3DKMTDestroyDevice", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY present(D3DKMT_PRESENT* pData)
+	{
+		Compat::LogEnter("D3DKMTPresent", pData);
+
+		static UINT presentCount = 0;
+		++presentCount;
+		pData->Flags.PresentCountValid = 1;
+		pData->PresentCount = presentCount;
+
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTPresent)(pData);
+		if (SUCCEEDED(result) &&
+			1 == DDraw::PrimarySurface::getDesc().dwBackBufferCount &&
+			pData->Flags.Flip && pData->FlipInterval != D3DDDI_FLIPINTERVAL_IMMEDIATE)
+		{
+			auto contextIt = g_contexts.find(pData->hContext);
+			auto deviceIt = (contextIt != g_contexts.end())
+				? g_devices.find(contextIt->second.device)
+				: g_devices.find(pData->hDevice);
+			if (deviceIt != g_devices.end())
+			{
+				D3DKMT_WAITFORVERTICALBLANKEVENT vbEvent = {};
+				vbEvent.hAdapter = deviceIt->second.adapter;
+				vbEvent.hDevice = deviceIt->first;
+				vbEvent.VidPnSourceId = deviceIt->second.vidPnSourceId;
+
+				D3DKMT_GETDEVICESTATE deviceState = {};
+				deviceState.hDevice = deviceIt->first;
+				deviceState.StateType = D3DKMT_DEVICESTATE_PRESENT;
+				deviceState.PresentState.VidPnSourceId = deviceIt->second.vidPnSourceId;
+				NTSTATUS stateResult = D3DKMTGetDeviceState(&deviceState);
+				while (SUCCEEDED(stateResult) &&
+					presentCount != deviceState.PresentState.PresentStats.PresentCount)
+				{
+					if (FAILED(D3DKMTWaitForVerticalBlankEvent(&vbEvent)))
+					{
+						Sleep(1);
+					}
+					stateResult = D3DKMTGetDeviceState(&deviceState);
+				}
+			}
+		}
+
+		Compat::LogLeave("D3DKMTPresent", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY queryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* pData)
+	{
+		Compat::LogEnter("D3DKMTQueryAdapterInfo", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTQueryAdapterInfo)(pData);
+		if (SUCCEEDED(result) && KMTQAITYPE_UMDRIVERNAME == pData->Type)
+		{
+			auto info = static_cast<D3DKMT_UMDFILENAMEINFO*>(pData->pPrivateDriverData);
+			D3dDdi::onUmdFileNameQueried(info->UmdFileName);
+		}
+		Compat::LogLeave("D3DKMTQueryAdapterInfo", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY setQueuedLimit(const D3DKMT_SETQUEUEDLIMIT* pData)
+	{
+		Compat::LogEnter("D3DKMTSetQueuedLimit", pData);
+		if (D3DKMT_SET_QUEUEDLIMIT_PRESENT == pData->Type)
+		{
+			const UINT origLimit = pData->QueuedPresentLimit;
+			const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = 1;
+			NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData);
+			const_cast<D3DKMT_SETQUEUEDLIMIT*>(pData)->QueuedPresentLimit = origLimit;
+			Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result;
+			return result;
+		}
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetQueuedLimit)(pData);
+		Compat::LogLeave("D3DKMTSetQueuedLimit", pData) << result;
+		return result;
+	}
+
+	void processSetVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData)
+	{
+		auto& vidPnSourceId = g_devices[pData->hDevice].vidPnSourceId;
+		for (UINT i = 0; i < pData->VidPnSourceCount; ++i)
+		{
+			if (D3DKMT_VIDPNSOURCEOWNER_UNOWNED != pData->pType[i])
+			{
+				vidPnSourceId = pData->pVidPnSourceId[i];
+				return;
+			}
+		}
+		vidPnSourceId = D3DDDI_ID_UNINITIALIZED;
+	}
+
+	NTSTATUS APIENTRY setVidPnSourceOwner(const D3DKMT_SETVIDPNSOURCEOWNER* pData)
+	{
+		Compat::LogEnter("D3DKMTSetVidPnSourceOwner", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetVidPnSourceOwner)(pData);
+		if (SUCCEEDED(result))
+		{
+			processSetVidPnSourceOwner(pData);
+		}
+		Compat::LogLeave("D3DKMTSetVidPnSourceOwner", pData) << result;
+		return result;
+	}
+
+	NTSTATUS APIENTRY setVidPnSourceOwner1(const D3DKMT_SETVIDPNSOURCEOWNER1* pData)
+	{
+		Compat::LogEnter("D3DKMTSetVidPnSourceOwner1", pData);
+		NTSTATUS result = CALL_ORIG_FUNC(D3DKMTSetVidPnSourceOwner1)(pData);
+		if (SUCCEEDED(result))
+		{
+			processSetVidPnSourceOwner(&pData->Version0);
+		}
+		Compat::LogLeave("D3DKMTSetVidPnSourceOwner1", pData) << result;
+		return result;
+	}
+}
+
+namespace D3dDdi
+{
+	namespace KernelModeThunks
+	{
+		void installHooks()
+		{
+			HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext);
+			HOOK_FUNCTION(gdi32, D3DKMTCreateContextVirtual, createContextVirtual);
+			HOOK_FUNCTION(gdi32, D3DKMTCreateDevice, createDevice);
+			HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext);
+			HOOK_FUNCTION(gdi32, D3DKMTDestroyDevice, destroyDevice);
+			HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo);
+			HOOK_FUNCTION(gdi32, D3DKMTPresent, present);
+			HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit);
+			HOOK_FUNCTION(gdi32, D3DKMTSetVidPnSourceOwner, setVidPnSourceOwner);
+			HOOK_FUNCTION(gdi32, D3DKMTSetVidPnSourceOwner1, setVidPnSourceOwner1);
+		}
+	}
+}
diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.h b/DDrawCompat/D3dDdi/KernelModeThunks.h
new file mode 100644
index 0000000..9c49692
--- /dev/null
+++ b/DDrawCompat/D3dDdi/KernelModeThunks.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "D3dDdi/Log/KernelModeThunksLog.h"
+
+namespace D3dDdi
+{
+	namespace KernelModeThunks
+	{
+		void installHooks();
+	}
+}
diff --git a/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.cpp b/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.cpp
new file mode 100644
index 0000000..eb99430
--- /dev/null
+++ b/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.cpp
@@ -0,0 +1,38 @@
+#include "Common/Log.h"
+#include "D3dDdi/Log/AdapterFuncsLog.h"
+
+std::ostream& operator<<(std::ostream& os, const D3DDDI_ALLOCATIONLIST& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hAllocation)
+		<< Compat::hex(data.Value);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DDDI_PATCHLOCATIONLIST& data)
+{
+	return Compat::LogStruct(os)
+		<< data.AllocationIndex
+		<< Compat::hex(data.Value)
+		<< Compat::hex(data.DriverId)
+		<< Compat::hex(data.AllocationOffset)
+		<< Compat::hex(data.PatchOffset)
+		<< Compat::hex(data.SplitOffset);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DDDIARG_CREATEDEVICE& data)
+{
+	return Compat::LogStruct(os)
+		<< data.hDevice
+		<< data.Interface
+		<< data.Version
+		<< data.pCallbacks
+		<< data.pCommandBuffer
+		<< data.CommandBufferSize
+		<< data.pAllocationList
+		<< data.AllocationListSize
+		<< data.pPatchLocationList
+		<< data.PatchLocationListSize
+		<< data.pDeviceFuncs
+		<< Compat::hex(data.Flags.Value)
+		<< Compat::hex(data.CommandBuffer);
+}
diff --git a/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.h b/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.h
new file mode 100644
index 0000000..de05b50
--- /dev/null
+++ b/DDrawCompat/D3dDdi/Log/AdapterFuncsLog.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#define CINTERFACE
+
+#include <ostream>
+
+#include <d3d.h>
+#include <d3dumddi.h>
+
+std::ostream& operator<<(std::ostream& os, const D3DDDI_ALLOCATIONLIST& data);
+std::ostream& operator<<(std::ostream& os, const D3DDDI_PATCHLOCATIONLIST& data);
+std::ostream& operator<<(std::ostream& os, const D3DDDIARG_CREATEDEVICE& data);
diff --git a/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.cpp b/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.cpp
index 5a9d602..9757ba0 100644
--- a/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.cpp
+++ b/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.cpp
@@ -76,6 +76,21 @@ std::ostream& operator<<(std::ostream& os, const D3DDDICB_LOCK2& data)
 		<< data.pData;
 }
 
+std::ostream& operator<<(std::ostream& os, const D3DDDICB_PRESENT& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hSrcAllocation)
+		<< Compat::hex(data.hDstAllocation)
+		<< data.hContext
+		<< data.BroadcastContextCount
+		<< Compat::array(data.BroadcastContext, data.BroadcastContextCount)
+		<< Compat::hex(data.BroadcastSrcAllocation)
+		<< Compat::hex(data.BroadcastDstAllocation)
+		<< data.PrivateDriverDataSize
+		<< data.pPrivateDriverData
+		<< static_cast<UINT>(data.bOptimizeForComposition);
+}
+
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_UNLOCK& data)
 {
 	return Compat::LogStruct(os)
diff --git a/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.h b/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.h
index a286eed..e307640 100644
--- a/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.h
+++ b/DDrawCompat/D3dDdi/Log/DeviceCallbacksLog.h
@@ -14,5 +14,6 @@ std::ostream& operator<<(std::ostream& os, const D3DDDICB_DEALLOCATE& data);
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_DEALLOCATE2& data);
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_LOCK& data);
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_LOCK2& data);
+std::ostream& operator<<(std::ostream& os, const D3DDDICB_PRESENT& data);
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_UNLOCK& data);
 std::ostream& operator<<(std::ostream& os, const D3DDDICB_UNLOCK2& data);
diff --git a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp
new file mode 100644
index 0000000..c887004
--- /dev/null
+++ b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp
@@ -0,0 +1,116 @@
+#include "Common/Log.h"
+#include "D3dDdi/Log/KernelModeThunksLog.h"
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice)
+		<< data.NodeOrdinal
+		<< data.EngineAffinity
+		<< Compat::hex(data.Flags.Value)
+		<< data.pPrivateDriverData
+		<< data.PrivateDriverDataSize
+		<< data.ClientHint
+		<< Compat::hex(data.hContext)
+		<< data.pCommandBuffer
+		<< data.CommandBufferSize
+		<< Compat::array(data.pAllocationList, data.AllocationListSize)
+		<< data.AllocationListSize
+		<< Compat::array(data.pPatchLocationList, data.PatchLocationListSize)
+		<< data.PatchLocationListSize
+		<< Compat::hex(data.CommandBuffer);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXTVIRTUAL& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice)
+		<< data.NodeOrdinal
+		<< data.EngineAffinity
+		<< Compat::hex(data.Flags.Value)
+		<< data.pPrivateDriverData
+		<< data.PrivateDriverDataSize
+		<< data.ClientHint
+		<< Compat::hex(data.hContext);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDEVICE& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hAdapter)
+		<< Compat::hex(*reinterpret_cast<const DWORD*>(&data.Flags))
+		<< Compat::hex(data.hDevice)
+		<< data.pCommandBuffer
+		<< data.CommandBufferSize
+		<< Compat::array(data.pAllocationList, data.AllocationListSize)
+		<< data.AllocationListSize
+		<< Compat::array(data.pPatchLocationList, data.PatchLocationListSize)
+		<< data.PatchLocationListSize;
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYCONTEXT& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hContext);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice)
+		<< data.hWindow
+		<< data.VidPnSourceId
+		<< Compat::hex(data.hSource)
+		<< Compat::hex(data.hDestination)
+		<< Compat::hex(data.Color)
+		<< data.DstRect
+		<< data.SrcRect
+		<< data.SubRectCnt
+		<< Compat::array(data.pSrcSubRects, data.SubRectCnt)
+		<< data.PresentCount
+		<< data.FlipInterval
+		<< Compat::hex(data.Flags.Value)
+		<< data.BroadcastContextCount
+		<< Compat::hex(Compat::array(data.BroadcastContext, data.BroadcastContextCount))
+		<< data.PresentLimitSemaphore
+		<< data.PresentHistoryToken
+		<< data.pPresentRegions
+		<< Compat::hex(data.hAdapter)
+		<< data.Duration
+		<< Compat::hex(Compat::array(data.BroadcastSrcAllocation, data.BroadcastContextCount))
+		<< Compat::hex(Compat::array(data.BroadcastDstAllocation, data.BroadcastContextCount))
+		<< data.PrivateDriverDataSize
+		<< data.pPrivateDriverData
+		<< static_cast<UINT>(data.bOptimizeForComposition);
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETQUEUEDLIMIT& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice)
+		<< data.Type
+		<< data.VidPnSourceId
+		<< data.QueuedPendingFlipLimit;
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER& data)
+{
+	return Compat::LogStruct(os)
+		<< Compat::hex(data.hDevice)
+		<< Compat::array(data.pType, data.VidPnSourceCount)
+		<< Compat::array(data.pVidPnSourceId, data.VidPnSourceCount)
+		<< data.VidPnSourceCount;
+}
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER1& data)
+{
+	return Compat::LogStruct(os)
+		<< data.Version0
+		<< Compat::hex(data.Flags.Value);
+}
diff --git a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h
new file mode 100644
index 0000000..f2af804
--- /dev/null
+++ b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#define CINTERFACE
+
+#include <ostream>
+
+#include <d3d.h>
+#include <d3dumddi.h>
+#include <../km/d3dkmthk.h>
+
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXTVIRTUAL& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDEVICE& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYCONTEXT& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETQUEUEDLIMIT& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER& data);
+std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER1& data);
diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp
index 3314521..bc49532 100644
--- a/DDrawCompat/DDraw/RealPrimarySurface.cpp
+++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp
@@ -213,7 +213,7 @@ namespace DDraw
 		desc.dwSize = sizeof(desc);
 		desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
 		desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
-		desc.dwBackBufferCount = 2;
+		desc.dwBackBufferCount = 1;
 
 		CompatPtr<typename Types<DirectDraw>::TCreatedSurface> surface;
 		HRESULT result = dd->CreateSurface(&dd, &desc, &surface.getRef(), nullptr);
diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj
index e368c76..d990e1f 100644
--- a/DDrawCompat/DDrawCompat.vcxproj
+++ b/DDrawCompat/DDrawCompat.vcxproj
@@ -169,9 +169,12 @@
     <ClInclude Include="D3dDdi\DeviceCallbacks.h" />
     <ClInclude Include="D3dDdi\DeviceFuncs.h" />
     <ClInclude Include="D3dDdi\Hooks.h" />
+    <ClInclude Include="D3dDdi\KernelModeThunks.h" />
     <ClInclude Include="D3dDdi\LockResource.h" />
+    <ClInclude Include="D3dDdi\Log\AdapterFuncsLog.h" />
     <ClInclude Include="D3dDdi\Log\DeviceCallbacksLog.h" />
     <ClInclude Include="D3dDdi\Log\DeviceFuncsLog.h" />
+    <ClInclude Include="D3dDdi\Log\KernelModeThunksLog.h" />
     <ClInclude Include="D3dDdi\Visitors\AdapterCallbacksVisitor.h" />
     <ClInclude Include="D3dDdi\Visitors\AdapterFuncsVisitor.h" />
     <ClInclude Include="D3dDdi\Visitors\DeviceCallbacksVisitor.h" />
@@ -233,9 +236,12 @@
     <ClCompile Include="D3dDdi\DeviceCallbacks.cpp" />
     <ClCompile Include="D3dDdi\DeviceFuncs.cpp" />
     <ClCompile Include="D3dDdi\Hooks.cpp" />
+    <ClCompile Include="D3dDdi\KernelModeThunks.cpp" />
     <ClCompile Include="D3dDdi\LockResource.cpp" />
+    <ClCompile Include="D3dDdi\Log\AdapterFuncsLog.cpp" />
     <ClCompile Include="D3dDdi\Log\DeviceCallbacksLog.cpp" />
     <ClCompile Include="D3dDdi\Log\DeviceFuncsLog.cpp" />
+    <ClCompile Include="D3dDdi\Log\KernelModeThunksLog.cpp" />
     <ClCompile Include="DDraw\ActivateAppHandler.cpp" />
     <ClCompile Include="DDraw\DirectDraw.cpp" />
     <ClCompile Include="DDraw\DirectDrawPalette.cpp" />
diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters
index ffec786..2173d79 100644
--- a/DDrawCompat/DDrawCompat.vcxproj.filters
+++ b/DDrawCompat/DDrawCompat.vcxproj.filters
@@ -291,6 +291,15 @@
     <ClInclude Include="D3dDdi\LockResource.h">
       <Filter>Header Files\D3dDdi</Filter>
     </ClInclude>
+    <ClInclude Include="D3dDdi\Log\AdapterFuncsLog.h">
+      <Filter>Header Files\D3dDdi\Log</Filter>
+    </ClInclude>
+    <ClInclude Include="D3dDdi\KernelModeThunks.h">
+      <Filter>Header Files\D3dDdi</Filter>
+    </ClInclude>
+    <ClInclude Include="D3dDdi\Log\KernelModeThunksLog.h">
+      <Filter>Header Files\D3dDdi\Log</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Gdi\Gdi.cpp">
@@ -440,6 +449,15 @@
     <ClCompile Include="D3dDdi\LockResource.cpp">
       <Filter>Source Files\D3dDdi</Filter>
     </ClCompile>
+    <ClCompile Include="D3dDdi\Log\AdapterFuncsLog.cpp">
+      <Filter>Source Files\D3dDdi\Log</Filter>
+    </ClCompile>
+    <ClCompile Include="D3dDdi\KernelModeThunks.cpp">
+      <Filter>Source Files\D3dDdi</Filter>
+    </ClCompile>
+    <ClCompile Include="D3dDdi\Log\KernelModeThunksLog.cpp">
+      <Filter>Source Files\D3dDdi\Log</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="Dll\DDrawCompat.def">