mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
440 lines
13 KiB
C++
440 lines
13 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
/*==========================================================================;
|
|
*
|
|
* Copyright (C) Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: PIX3_win.h
|
|
* Content: PIX include file
|
|
* Don't include this file directly - use pix3.h
|
|
*
|
|
****************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#ifndef _PIX3_H_
|
|
#error Don't include this file directly - use pix3.h
|
|
#endif
|
|
|
|
#ifndef _PIX3_WIN_H_
|
|
#define _PIX3_WIN_H_
|
|
|
|
// PIXEventsThreadInfo is defined in PIXEventsCommon.h
|
|
struct PIXEventsThreadInfo;
|
|
|
|
extern "C" PIXEventsThreadInfo* WINAPI PIXGetThreadInfo() noexcept;
|
|
|
|
#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE)
|
|
// Notifies PIX that an event handle was set as a result of a D3D12 fence being signaled.
|
|
// The event specified must have the same handle value as the handle
|
|
// used in ID3D12Fence::SetEventOnCompletion.
|
|
extern "C" void WINAPI PIXNotifyWakeFromFenceSignal(_In_ HANDLE event);
|
|
|
|
// Notifies PIX that a block of memory was allocated
|
|
extern "C" void WINAPI PIXRecordMemoryAllocationEvent(USHORT allocatorId, void* baseAddress, size_t size, UINT64 metadata);
|
|
|
|
// Notifies PIX that a block of memory was freed
|
|
extern "C" void WINAPI PIXRecordMemoryFreeEvent(USHORT allocatorId, void* baseAddress, size_t size, UINT64 metadata);
|
|
|
|
#else
|
|
|
|
// Eliminate these APIs when not using PIX
|
|
inline void PIXRecordMemoryAllocationEvent(USHORT, void*, size_t, UINT64) {}
|
|
inline void PIXRecordMemoryFreeEvent(USHORT, void*, size_t, UINT64) {}
|
|
|
|
#endif
|
|
|
|
// The following defines denote the different metadata values that have been used
|
|
// by tools to denote how to parse pix marker event data. The first two values
|
|
// are legacy values.
|
|
#define WINPIX_EVENT_UNICODE_VERSION 0
|
|
#define WINPIX_EVENT_ANSI_VERSION 1
|
|
#define WINPIX_EVENT_PIX3BLOB_VERSION 2
|
|
|
|
#define D3D12_EVENT_METADATA WINPIX_EVENT_PIX3BLOB_VERSION
|
|
|
|
__forceinline UINT64 PIXGetTimestampCounter()
|
|
{
|
|
LARGE_INTEGER time = {};
|
|
QueryPerformanceCounter(&time);
|
|
return static_cast<UINT64>(time.QuadPart);
|
|
}
|
|
|
|
enum PIXHUDOptions
|
|
{
|
|
PIX_HUD_SHOW_ON_ALL_WINDOWS = 0x1,
|
|
PIX_HUD_SHOW_ON_TARGET_WINDOW_ONLY = 0x2,
|
|
PIX_HUD_SHOW_ON_NO_WINDOWS = 0x4
|
|
};
|
|
DEFINE_ENUM_FLAG_OPERATORS(PIXHUDOptions);
|
|
|
|
#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && defined(USE_PIX)
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
|
|
|
#include <shlobj.h>
|
|
#include <strsafe.h>
|
|
#include <knownfolders.h>
|
|
#include <shellapi.h>
|
|
|
|
#define PIXERRORCHECK(value) do { \
|
|
if (FAILED(value)) \
|
|
return nullptr; \
|
|
} while(0)
|
|
|
|
namespace PixImpl
|
|
{
|
|
#ifndef PIX3_WIN_UNIT_TEST
|
|
|
|
__forceinline BOOL GetModuleHandleExW(
|
|
DWORD dwFlags,
|
|
LPCWSTR lpModuleName,
|
|
HMODULE* phModule)
|
|
{
|
|
return ::GetModuleHandleExW(dwFlags, lpModuleName, phModule);
|
|
}
|
|
|
|
__forceinline HRESULT SHGetKnownFolderPath(
|
|
REFKNOWNFOLDERID rfid,
|
|
DWORD dwFlags,
|
|
HANDLE hToken,
|
|
PWSTR* ppszPath)
|
|
{
|
|
return ::SHGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
|
|
}
|
|
|
|
__forceinline void CoTaskMemFree(LPVOID pv)
|
|
{
|
|
return ::CoTaskMemFree(pv);
|
|
}
|
|
|
|
__forceinline HANDLE FindFirstFileW(
|
|
LPCWSTR lpFileName,
|
|
LPWIN32_FIND_DATAW lpFindFileData)
|
|
{
|
|
return ::FindFirstFileW(lpFileName, lpFindFileData);
|
|
}
|
|
|
|
__forceinline DWORD GetFileAttributesW(LPCWSTR lpFileName)
|
|
{
|
|
return ::GetFileAttributesW(lpFileName);
|
|
}
|
|
|
|
__forceinline BOOL FindNextFileW(
|
|
HANDLE hFindFile,
|
|
LPWIN32_FIND_DATAW lpFindFileData)
|
|
{
|
|
return ::FindNextFileW(hFindFile, lpFindFileData);
|
|
}
|
|
|
|
__forceinline BOOL FindClose(HANDLE hFindFile)
|
|
{
|
|
return ::FindClose(hFindFile);
|
|
}
|
|
|
|
__forceinline HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, DWORD flags)
|
|
{
|
|
return ::LoadLibraryExW(lpLibFileName, NULL, flags);
|
|
}
|
|
|
|
#endif // !PIX3_WIN_UNIT_TESTS
|
|
|
|
__forceinline void * GetGpuCaptureFunctionPtr(LPCSTR fnName) noexcept
|
|
{
|
|
HMODULE module = GetModuleHandleW(L"WinPixGpuCapturer.dll");
|
|
if (module == NULL)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto fn = (void*)GetProcAddress(module, fnName);
|
|
if (fn == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
|
|
__forceinline void* GetTimingCaptureFunctionPtr(LPCSTR fnName) noexcept
|
|
{
|
|
HMODULE module = GetModuleHandleW(L"WinPixTimingCapturer.dll");
|
|
if (module == NULL)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto fn = (void*)GetProcAddress(module, fnName);
|
|
if (fn == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
|
|
__forceinline HMODULE PIXLoadLatestCapturerLibrary(wchar_t const* capturerDllName, DWORD flags)
|
|
{
|
|
HMODULE libHandle{};
|
|
|
|
if (PixImpl::GetModuleHandleExW(0, capturerDllName, &libHandle))
|
|
{
|
|
return libHandle;
|
|
}
|
|
|
|
LPWSTR programFilesPath = nullptr;
|
|
if (FAILED(PixImpl::SHGetKnownFolderPath(FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFilesPath)))
|
|
{
|
|
PixImpl::CoTaskMemFree(programFilesPath);
|
|
return nullptr;
|
|
}
|
|
|
|
wchar_t pixSearchPath[MAX_PATH];
|
|
|
|
if (FAILED(StringCchCopyW(pixSearchPath, MAX_PATH, programFilesPath)))
|
|
{
|
|
PixImpl::CoTaskMemFree(programFilesPath);
|
|
return nullptr;
|
|
}
|
|
PixImpl::CoTaskMemFree(programFilesPath);
|
|
|
|
PIXERRORCHECK(StringCchCatW(pixSearchPath, MAX_PATH, L"\\Microsoft PIX\\*"));
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
bool foundPixInstallation = false;
|
|
wchar_t newestVersionFound[MAX_PATH];
|
|
wchar_t output[MAX_PATH];
|
|
wchar_t possibleOutput[MAX_PATH];
|
|
|
|
HANDLE hFind = PixImpl::FindFirstFileW(pixSearchPath, &findData);
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
if (((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) &&
|
|
(findData.cFileName[0] != '.'))
|
|
{
|
|
if (!foundPixInstallation || wcscmp(newestVersionFound, findData.cFileName) <= 0)
|
|
{
|
|
// length - 1 to get rid of the wildcard character in the search path
|
|
PIXERRORCHECK(StringCchCopyNW(possibleOutput, MAX_PATH, pixSearchPath, wcslen(pixSearchPath) - 1));
|
|
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, findData.cFileName));
|
|
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, L"\\"));
|
|
PIXERRORCHECK(StringCchCatW(possibleOutput, MAX_PATH, capturerDllName));
|
|
|
|
DWORD result = PixImpl::GetFileAttributesW(possibleOutput);
|
|
|
|
if (result != INVALID_FILE_ATTRIBUTES && !(result & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
foundPixInstallation = true;
|
|
PIXERRORCHECK(StringCchCopyW(newestVersionFound, _countof(newestVersionFound), findData.cFileName));
|
|
PIXERRORCHECK(StringCchCopyW(output, _countof(possibleOutput), possibleOutput));
|
|
}
|
|
}
|
|
}
|
|
} while (PixImpl::FindNextFileW(hFind, &findData) != 0);
|
|
}
|
|
|
|
PixImpl::FindClose(hFind);
|
|
|
|
if (!foundPixInstallation)
|
|
{
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return nullptr;
|
|
}
|
|
|
|
return PixImpl::LoadLibraryExW(output, flags);
|
|
}
|
|
}
|
|
|
|
#undef PIXERRORCHECK
|
|
|
|
__forceinline HMODULE PIXLoadLatestWinPixGpuCapturerLibrary()
|
|
{
|
|
return PixImpl::PIXLoadLatestCapturerLibrary(
|
|
L"WinPixGpuCapturer.dll",
|
|
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
|
}
|
|
|
|
__forceinline HMODULE PIXLoadLatestWinPixTimingCapturerLibrary()
|
|
{
|
|
return PixImpl::PIXLoadLatestCapturerLibrary(
|
|
L"WinPixTimingCapturer.dll",
|
|
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
|
|
}
|
|
|
|
__forceinline HRESULT WINAPI PIXSetTargetWindow(HWND hwnd)
|
|
{
|
|
typedef void(WINAPI* SetGlobalTargetWindowFn)(HWND);
|
|
|
|
auto fn = (SetGlobalTargetWindowFn)PixImpl::GetGpuCaptureFunctionPtr("SetGlobalTargetWindow");
|
|
if (fn == nullptr)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
fn(hwnd);
|
|
return S_OK;
|
|
}
|
|
|
|
__forceinline HRESULT WINAPI PIXGpuCaptureNextFrames(PCWSTR fileName, UINT32 numFrames)
|
|
{
|
|
typedef HRESULT(WINAPI* CaptureNextFrameFn)(PCWSTR, UINT32);
|
|
|
|
auto fn = (CaptureNextFrameFn)PixImpl::GetGpuCaptureFunctionPtr("CaptureNextFrame");
|
|
if (fn == nullptr)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return fn(fileName, numFrames);
|
|
}
|
|
|
|
extern "C" __forceinline HRESULT WINAPI PIXBeginCapture2(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters)
|
|
{
|
|
if (captureFlags == PIX_CAPTURE_GPU)
|
|
{
|
|
typedef HRESULT(WINAPI* BeginProgrammaticGpuCaptureFn)(const PPIXCaptureParameters);
|
|
|
|
auto fn = (BeginProgrammaticGpuCaptureFn)PixImpl::GetGpuCaptureFunctionPtr("BeginProgrammaticGpuCapture");
|
|
if (fn == nullptr)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return fn(captureParameters);
|
|
}
|
|
else if (captureFlags == PIX_CAPTURE_TIMING)
|
|
{
|
|
typedef HRESULT(WINAPI* BeginProgrammaticTimingCaptureFn)(void const*, UINT64);
|
|
|
|
auto fn = (BeginProgrammaticTimingCaptureFn)PixImpl::GetTimingCaptureFunctionPtr("BeginProgrammaticTimingCapture");
|
|
if (fn == nullptr)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return fn(&captureParameters->TimingCaptureParameters, sizeof(captureParameters->TimingCaptureParameters));
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
extern "C" __forceinline HRESULT WINAPI PIXEndCapture(BOOL discard)
|
|
{
|
|
UNREFERENCED_PARAMETER(discard);
|
|
|
|
// We can't tell if the user wants to end a GPU Capture or a Timing Capture.
|
|
// The user shouldn't have both WinPixGpuCapturer and WinPixTimingCapturer loaded in the process though,
|
|
// so we can just look for one of them and call it.
|
|
typedef HRESULT(WINAPI* EndProgrammaticGpuCaptureFn)(void);
|
|
auto gpuFn = (EndProgrammaticGpuCaptureFn)PixImpl::GetGpuCaptureFunctionPtr("EndProgrammaticGpuCapture");
|
|
if (gpuFn != NULL)
|
|
{
|
|
return gpuFn();
|
|
}
|
|
|
|
typedef HRESULT(WINAPI* EndProgrammaticTimingCaptureFn)(BOOL);
|
|
auto timingFn = (EndProgrammaticTimingCaptureFn)PixImpl::GetTimingCaptureFunctionPtr("EndProgrammaticTimingCapture");
|
|
if (timingFn != NULL)
|
|
{
|
|
return timingFn(discard);
|
|
}
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
__forceinline HRESULT WINAPI PIXForceD3D11On12()
|
|
{
|
|
typedef HRESULT (WINAPI* ForceD3D11On12Fn)(void);
|
|
|
|
auto fn = (ForceD3D11On12Fn)PixImpl::GetGpuCaptureFunctionPtr("ForceD3D11On12");
|
|
if (fn == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return fn();
|
|
}
|
|
|
|
__forceinline HRESULT WINAPI PIXSetHUDOptions(PIXHUDOptions hudOptions)
|
|
{
|
|
typedef HRESULT(WINAPI* SetHUDOptionsFn)(PIXHUDOptions);
|
|
|
|
auto fn = (SetHUDOptionsFn)PixImpl::GetGpuCaptureFunctionPtr("SetHUDOptions");
|
|
if (fn == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return fn(hudOptions);
|
|
}
|
|
|
|
__forceinline bool WINAPI PIXIsAttachedForGpuCapture()
|
|
{
|
|
typedef bool(WINAPI* GetIsAttachedToPixFn)(void);
|
|
auto fn = (GetIsAttachedToPixFn)PixImpl::GetGpuCaptureFunctionPtr("GetIsAttachedToPix");
|
|
if (fn == NULL)
|
|
{
|
|
OutputDebugStringW(L"WinPixEventRuntime error: Mismatched header/dll. Please ensure that pix3.h and WinPixGpuCapturer.dll match");
|
|
return false;
|
|
}
|
|
|
|
return fn();
|
|
}
|
|
|
|
__forceinline HINSTANCE WINAPI PIXOpenCaptureInUI(PCWSTR fileName)
|
|
{
|
|
return ShellExecuteW(0, 0, fileName, 0, 0, SW_SHOW);
|
|
}
|
|
|
|
#else
|
|
__forceinline HMODULE PIXLoadLatestWinPixGpuCapturerLibrary()
|
|
{
|
|
return nullptr;
|
|
}
|
|
__forceinline HMODULE PIXLoadLatestWinPixTimingCapturerLibrary()
|
|
{
|
|
return nullptr;
|
|
}
|
|
__forceinline HRESULT WINAPI PIXSetTargetWindow(HWND)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
__forceinline HRESULT WINAPI PIXGpuCaptureNextFrames(PCWSTR, UINT32)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
extern "C" __forceinline HRESULT WINAPI PIXBeginCapture2(DWORD, _In_opt_ const PPIXCaptureParameters)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
extern "C" __forceinline HRESULT WINAPI PIXEndCapture(BOOL)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
__forceinline HRESULT WINAPI PIXForceD3D11On12()
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
__forceinline HRESULT WINAPI PIXSetHUDOptions(PIXHUDOptions)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
__forceinline bool WINAPI PIXIsAttachedForGpuCapture()
|
|
{
|
|
return false;
|
|
}
|
|
__forceinline HINSTANCE WINAPI PIXOpenCaptureInUI(PCWSTR)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif // WINAPI_PARTITION
|
|
|
|
#endif // USE_PIX_SUPPORTED_ARCHITECTURE || USE_PIX
|
|
|
|
#endif //_PIX3_WIN_H_
|