mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-24 07:06:36 +00:00
295 lines
8.1 KiB
C++
295 lines
8.1 KiB
C++
#include "GpDisplayDriverD3D11.h"
|
|
#include "GpWindows.h"
|
|
#include "GpFiber_Win32.h"
|
|
|
|
#include <d3d11.h>
|
|
#include <dxgi1_2.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#pragma comment (lib, "d3d11.lib")
|
|
|
|
void DebugPrintf(const char *fmt, ...)
|
|
{
|
|
char buf[256];
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
vsnprintf_s(buf, 255, fmt, argp);
|
|
OutputDebugString(buf);
|
|
va_end(argp);
|
|
}
|
|
|
|
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_DESTROY:
|
|
{
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
void StartD3DForWindow(HWND hWnd, IDXGISwapChain1*& swapChain)
|
|
{
|
|
DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
|
|
|
|
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
|
|
|
|
swapChainDesc.BufferCount = 2;
|
|
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapChainDesc.SampleDesc.Count = 1;
|
|
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
|
|
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFullscreenDesc;
|
|
|
|
ZeroMemory(&swapChainFullscreenDesc, sizeof(swapChainFullscreenDesc));
|
|
|
|
swapChainFullscreenDesc.Windowed = TRUE;
|
|
swapChainFullscreenDesc.RefreshRate.Numerator = 60;
|
|
swapChainFullscreenDesc.RefreshRate.Denominator = 1;
|
|
|
|
UINT flags = 0;
|
|
const D3D_FEATURE_LEVEL featureLevels[] =
|
|
{
|
|
D3D_FEATURE_LEVEL_9_1
|
|
};
|
|
|
|
flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
|
|
ID3D11Device *device = NULL;
|
|
ID3D11DeviceContext *context = NULL;
|
|
|
|
D3D_FEATURE_LEVEL selectedFeatureLevel;
|
|
|
|
HRESULT result = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, sizeof(featureLevels) / sizeof(featureLevels[0]),
|
|
D3D11_SDK_VERSION, &device, &selectedFeatureLevel, &context);
|
|
|
|
IDXGIDevice2 *dxgiDevice = nullptr;
|
|
result = device->QueryInterface(__uuidof(IDXGIDevice2), reinterpret_cast<void**>(&dxgiDevice));
|
|
|
|
IDXGIAdapter *dxgiAdapter = nullptr;
|
|
result = dxgiDevice->GetAdapter(&dxgiAdapter);
|
|
|
|
IDXGIFactory2 *dxgiFactory = nullptr;
|
|
result = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory));
|
|
|
|
result = dxgiFactory->CreateSwapChainForHwnd(device, hWnd, &swapChainDesc, nullptr, nullptr, &swapChain);
|
|
}
|
|
|
|
bool GpDisplayDriverD3D11::PresentFrameAndSync()
|
|
{
|
|
DXGI_PRESENT_PARAMETERS presentParams;
|
|
|
|
ZeroMemory(&presentParams, sizeof(presentParams));
|
|
|
|
UINT lastPresentCount = 0;
|
|
|
|
if (FAILED(m_swapChain->GetLastPresentCount(&lastPresentCount)))
|
|
return false;
|
|
|
|
if (FAILED(m_swapChain->Present1(1, 0, &presentParams)))
|
|
return false;
|
|
|
|
//DebugPrintf("r: %i\n", static_cast<int>(r));
|
|
|
|
DXGI_FRAME_STATISTICS stats;
|
|
if (FAILED(m_swapChain->GetFrameStatistics(&stats)))
|
|
return false;
|
|
|
|
if (stats.SyncQPCTime.QuadPart != 0)
|
|
{
|
|
if (m_syncTimeBase.QuadPart == 0)
|
|
m_syncTimeBase = stats.SyncQPCTime;
|
|
|
|
LARGE_INTEGER timestamp;
|
|
timestamp.QuadPart = stats.SyncQPCTime.QuadPart - m_syncTimeBase.QuadPart;
|
|
|
|
bool compacted = false;
|
|
if (m_presentHistory.Size() > 0)
|
|
{
|
|
CompactedPresentHistoryItem &lastItem = m_presentHistory[m_presentHistory.Size() - 1];
|
|
LONGLONG timeDelta = timestamp.QuadPart - lastItem.m_timestamp.QuadPart;
|
|
|
|
if (timeDelta < 0)
|
|
timeDelta = 0; // This should never happen
|
|
|
|
if (timeDelta * static_cast<LONGLONG>(m_properties.m_frameTimeLockDenominator) < m_QPFrequency.QuadPart * static_cast<LONGLONG>(m_properties.m_frameTimeLockNumerator))
|
|
{
|
|
lastItem.m_numFrames++;
|
|
compacted = true;
|
|
}
|
|
}
|
|
|
|
if (!compacted)
|
|
{
|
|
if (m_presentHistory.Size() == m_presentHistory.CAPACITY)
|
|
m_presentHistory.RemoveFromStart();
|
|
|
|
CompactedPresentHistoryItem *newItem = m_presentHistory.Append();
|
|
newItem->m_timestamp = timestamp;
|
|
newItem->m_numFrames = 1;
|
|
}
|
|
}
|
|
|
|
if (m_presentHistory.Size() >= 2)
|
|
{
|
|
const size_t presentHistorySizeMinusOne = m_presentHistory.Size() - 1;
|
|
unsigned int numFrames = 0;
|
|
for (size_t i = 0; i < presentHistorySizeMinusOne; i++)
|
|
numFrames += m_presentHistory[i].m_numFrames;
|
|
|
|
LONGLONG timeFrame = m_presentHistory[presentHistorySizeMinusOne].m_timestamp.QuadPart - m_presentHistory[0].m_timestamp.QuadPart;
|
|
|
|
unsigned int cancelledFrames = 0;
|
|
LONGLONG cancelledTime = 0;
|
|
|
|
const int overshootTolerance = 2;
|
|
|
|
for (size_t i = 0; i < presentHistorySizeMinusOne; i++)
|
|
{
|
|
LONGLONG blockTimeframe = m_presentHistory[i + 1].m_timestamp.QuadPart - m_presentHistory[i].m_timestamp.QuadPart;
|
|
unsigned int blockNumFrames = m_presentHistory[i].m_numFrames;
|
|
|
|
if (blockTimeframe * static_cast<LONGLONG>(numFrames) >= timeFrame * static_cast<LONGLONG>(blockNumFrames) * overshootTolerance)
|
|
{
|
|
cancelledTime += blockTimeframe;
|
|
cancelledFrames += blockNumFrames;
|
|
}
|
|
}
|
|
|
|
numFrames -= cancelledFrames;
|
|
timeFrame -= cancelledTime;
|
|
|
|
// timeFrame / numFrames = Frame timestep
|
|
// Unless Frame timestep is within the frame lock range, a.k.a.
|
|
// timeFrame / numFrames / qpFreq >= minFrameTimeNum / minFrameTimeDenom
|
|
|
|
bool isInFrameTimeLock = false;
|
|
if (timeFrame * static_cast<LONGLONG>(m_properties.m_frameTimeLockMinDenominator) >= static_cast<LONGLONG>(numFrames) * static_cast<LONGLONG>(m_properties.m_frameTimeLockMinNumerator) * m_QPFrequency.QuadPart
|
|
&& timeFrame * static_cast<LONGLONG>(m_properties.m_frameTimeLockMaxDenominator) <= static_cast<LONGLONG>(numFrames) * static_cast<LONGLONG>(m_properties.m_frameTimeLockMaxNumerator) * m_QPFrequency.QuadPart)
|
|
{
|
|
isInFrameTimeLock = true;
|
|
}
|
|
|
|
LONGLONG frameTimeStep = m_frameTimeSliceSize;
|
|
if (!isInFrameTimeLock)
|
|
{
|
|
const int MAX_FRAMES_PER_STEP = 4;
|
|
|
|
frameTimeStep = timeFrame / numFrames;
|
|
if (frameTimeStep > m_frameTimeSliceSize * MAX_FRAMES_PER_STEP)
|
|
frameTimeStep = m_frameTimeSliceSize * MAX_FRAMES_PER_STEP;
|
|
}
|
|
|
|
m_frameTimeAccumulated += frameTimeStep;
|
|
while (m_frameTimeAccumulated >= m_frameTimeSliceSize)
|
|
{
|
|
m_properties.m_tickFunc(m_properties.m_tickFuncContext, m_vosFiber);
|
|
m_frameTimeAccumulated -= m_frameTimeSliceSize;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GpDisplayDriverD3D11::Run()
|
|
{
|
|
HWND hWnd;
|
|
WNDCLASSEX wc;
|
|
|
|
LPVOID fiber = ConvertThreadToFiberEx(this, 0);
|
|
if (!fiber)
|
|
return; // ???
|
|
|
|
m_vosFiber = new GpFiber_Win32(fiber);
|
|
|
|
ZeroMemory(&wc, sizeof(wc));
|
|
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = WinProc;
|
|
wc.hInstance = g_gpWindowsGlobals.m_hInstance;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
wc.lpszClassName = "GPD3D11WindowClass";
|
|
|
|
RegisterClassEx(&wc);
|
|
|
|
LONG windowStyle = WS_OVERLAPPEDWINDOW;
|
|
HMENU menus = NULL;
|
|
|
|
// TODO: Fix the resolution here
|
|
RECT wr = { 0, 0, m_windowWidth, m_windowHeight };
|
|
AdjustWindowRect(&wr, windowStyle, menus != NULL);
|
|
|
|
hWnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", L"GlidePort", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, g_gpWindowsGlobals.m_hInstance, NULL);
|
|
|
|
ShowWindow(hWnd, g_gpWindowsGlobals.m_nCmdShow);
|
|
|
|
StartD3DForWindow(hWnd, m_swapChain);
|
|
|
|
LARGE_INTEGER lastTimestamp;
|
|
memset(&lastTimestamp, 0, sizeof(lastTimestamp));
|
|
|
|
MSG msg;
|
|
for (;;)
|
|
{
|
|
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
if (msg.message == WM_QUIT)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
PresentFrameAndSync();
|
|
}
|
|
}
|
|
|
|
// Exit
|
|
ConvertFiberToThread();
|
|
}
|
|
|
|
void GpDisplayDriverD3D11::Shutdown()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
void GpDisplayDriverD3D11::GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *pixelFormat)
|
|
{
|
|
if (width)
|
|
*width = m_windowWidth;
|
|
if (height)
|
|
*height = m_windowHeight;
|
|
if (pixelFormat)
|
|
*pixelFormat = PortabilityLayer::PixelFormat_8BitStandard;
|
|
}
|
|
|
|
GpDisplayDriverD3D11 *GpDisplayDriverD3D11::Create(const GpDisplayDriverProperties &properties)
|
|
{
|
|
return new GpDisplayDriverD3D11(properties);
|
|
}
|
|
|
|
GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties)
|
|
: m_properties(properties)
|
|
, m_frameTimeAccumulated(0)
|
|
, m_windowWidth(640)
|
|
, m_windowHeight(480)
|
|
, m_vosFiber(nullptr)
|
|
{
|
|
memset(&m_syncTimeBase, 0, sizeof(m_syncTimeBase));
|
|
|
|
QueryPerformanceFrequency(&m_QPFrequency);
|
|
|
|
m_frameTimeSliceSize = m_QPFrequency.QuadPart * static_cast<LONGLONG>(properties.m_frameTimeLockNumerator) / static_cast<LONGLONG>(properties.m_frameTimeLockDenominator);
|
|
}
|