mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +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_
 | 
