├── D3d12AfterCrash ├── D3d12AfterCrash.cpp └── D3d12AfterCrash.h ├── DisplaySettingsTest ├── DisplaySettingsTest.c ├── ExampleOutput.txt └── README.md ├── Graphics ├── Graphics card icon.png └── Graphics card icon.svg ├── IncludeList.py ├── IsLog10.c ├── PrintStream ├── PrintStream.cpp ├── PrintStream.hpp └── README.md ├── QueryPerformanceCounterTest.cpp ├── README.md ├── VulkanAfterCrash.h └── fp8_tables.py /D3d12AfterCrash/D3d12AfterCrash.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | D3D12AfterCrash 3 | 4 | Author: Adam Sawicki, http://asawicki.info, adam__REMOVE__@asawicki.info 5 | Version: 1.0.0, 2018-09-12 6 | License: MIT 7 | 8 | For documentation and license, see accompanying file D3d12AfterCrash.h. 9 | */ 10 | #include "D3d12AfterCrash.h" 11 | 12 | namespace D3D12AfterCrash 13 | { 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | // Internal class definitions 17 | 18 | class DeviceImpl : public Device 19 | { 20 | public: 21 | DeviceImpl() : 22 | m_pD3D12Device(nullptr) 23 | { 24 | } 25 | virtual ~DeviceImpl(); 26 | HRESULT Init(const DEVICE_DESC* Desc); 27 | 28 | virtual HRESULT CreateBuffer(const BUFFER_DESC* Desc, Buffer** ppBuffer, UINT** ppData); 29 | 30 | ID3D12Device* GetD3D12Device() { return m_pD3D12Device; } 31 | 32 | private: 33 | ID3D12Device* m_pD3D12Device; 34 | }; 35 | 36 | class BufferImpl : public Buffer 37 | { 38 | public: 39 | BufferImpl() : 40 | m_pBuf(nullptr), 41 | m_BufGpuAddr(0), 42 | m_pBufData(nullptr) 43 | { 44 | } 45 | HRESULT Init(Device* Parent, const BUFFER_DESC* Desc, UINT** ppData); 46 | virtual ~BufferImpl(); 47 | 48 | virtual void WriteMarker( 49 | ID3D12GraphicsCommandList2* pCommandList, 50 | UINT MarkerIndex, 51 | UINT Value, 52 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode); 53 | virtual void WriteMarker( 54 | ID3D12GraphicsCommandList* pCommandList, 55 | UINT MarkerIndex, 56 | UINT Value, 57 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode); 58 | 59 | private: 60 | ID3D12Resource* m_pBuf; 61 | D3D12_GPU_VIRTUAL_ADDRESS m_BufGpuAddr; 62 | UINT* m_pBufData; 63 | }; 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | // Device class implementation 67 | 68 | HRESULT DeviceImpl::Init(const DEVICE_DESC* Desc) 69 | { 70 | m_pD3D12Device = Desc->pD3D12Device; 71 | m_pD3D12Device->AddRef(); 72 | return S_OK; 73 | } 74 | 75 | DeviceImpl::~DeviceImpl() 76 | { 77 | if(m_pD3D12Device) 78 | m_pD3D12Device->Release(); 79 | } 80 | 81 | HRESULT DeviceImpl::CreateBuffer(const BUFFER_DESC* Desc, Buffer** ppBuffer, UINT** ppData) 82 | { 83 | BufferImpl* pBuf = new BufferImpl(); 84 | HRESULT hr = pBuf->Init(this, Desc, ppData); 85 | if(SUCCEEDED(hr)) 86 | { 87 | *ppBuffer = pBuf; 88 | return S_OK; 89 | } 90 | else 91 | { 92 | delete pBuf; 93 | return hr; 94 | } 95 | } 96 | 97 | HRESULT CreateDevice(const DEVICE_DESC* Desc, Device** ppDevice) 98 | { 99 | DeviceImpl* pDev = new DeviceImpl(); 100 | HRESULT hr = pDev->Init(Desc); 101 | if(SUCCEEDED(hr)) 102 | { 103 | *ppDevice = pDev; 104 | return S_OK; 105 | } 106 | else 107 | { 108 | delete pDev; 109 | return hr; 110 | } 111 | } 112 | 113 | //////////////////////////////////////////////////////////////////////////////// 114 | // Buffer class implementation 115 | 116 | HRESULT BufferImpl::Init(Device* Parent, const BUFFER_DESC* Desc, UINT** ppData) 117 | { 118 | D3D12_HEAP_PROPERTIES heapProperties = { D3D12_HEAP_TYPE_READBACK }; 119 | 120 | D3D12_RESOURCE_DESC resourceDesc = { D3D12_RESOURCE_DIMENSION_BUFFER }; 121 | resourceDesc.Width = (Desc->MarkerCount + 3) / 4 * 4 * sizeof(UINT); 122 | resourceDesc.Height = resourceDesc.DepthOrArraySize = resourceDesc.MipLevels = 1; 123 | resourceDesc.SampleDesc.Count = 1; 124 | resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 125 | resourceDesc.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; 126 | 127 | HRESULT hr = ((DeviceImpl*)Parent)->GetD3D12Device()->CreateCommittedResource( 128 | &heapProperties, 129 | D3D12_HEAP_FLAG_NONE, 130 | &resourceDesc, 131 | D3D12_RESOURCE_STATE_COPY_DEST, 132 | nullptr, // pOptimizedClearValue 133 | IID_PPV_ARGS(&m_pBuf)); 134 | if(FAILED(hr)) 135 | return hr; 136 | 137 | m_BufGpuAddr = m_pBuf->GetGPUVirtualAddress(); 138 | 139 | hr = m_pBuf->Map(0, nullptr, (void**)ppData); 140 | 141 | return hr; 142 | } 143 | 144 | BufferImpl::~BufferImpl() 145 | { 146 | if(m_pBuf) 147 | { 148 | D3D12_RANGE emptyRange = { 0, 0 }; 149 | m_pBuf->Unmap(0, &emptyRange); 150 | m_pBuf->Release(); 151 | } 152 | } 153 | 154 | void BufferImpl::WriteMarker( 155 | ID3D12GraphicsCommandList2* pCommandList, 156 | UINT MarkerIndex, 157 | UINT Value, 158 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode) 159 | { 160 | D3D12_WRITEBUFFERIMMEDIATE_PARAMETER param = { m_BufGpuAddr + MarkerIndex * sizeof(UINT), Value }; 161 | pCommandList->WriteBufferImmediate(1, ¶m, &Mode); 162 | } 163 | 164 | void BufferImpl::WriteMarker( 165 | ID3D12GraphicsCommandList* pCommandList, 166 | UINT MarkerIndex, 167 | UINT Value, 168 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode) 169 | { 170 | ID3D12GraphicsCommandList2* commandList2; 171 | pCommandList->QueryInterface(IID_PPV_ARGS(&commandList2)); 172 | // If it crashes here, it means the user has too old version of Windows and ID3D12GraphicsCommandList2 couldn't be obtained. 173 | WriteMarker(commandList2, MarkerIndex, Value, Mode); 174 | commandList2->Release(); 175 | } 176 | 177 | } // namespace D3D12AfterCrash 178 | -------------------------------------------------------------------------------- /D3d12AfterCrash/D3d12AfterCrash.h: -------------------------------------------------------------------------------- 1 | /* 2 | D3D12AfterCrash 3 | 4 | Author: Adam Sawicki, http://asawicki.info, adam__REMOVE__@asawicki.info 5 | Version: 1.0.0, 2018-09-12 6 | License: MIT 7 | 8 | This is a simple C++ library for Direct3D 12 that simplifies writing 9 | 32-bit markers to a buffer that can be read after graphics driver crash and thus 10 | help you find out which specific draw call or other command caused the crash, 11 | pretty much like NVIDIA Aftermath library, but portable to other GPUs. 12 | 13 | How to use it: 14 | 15 | 1. In any CPP file where you want to use the library: 16 | #include "D3D12AfterCrash.h" 17 | 2. Add "D3D12AfterCrash.cpp" file to your project. 18 | 3. Create object of type D3D12AfterCrash::Device using function CreateDevice. 19 | 4. Create one or more D3D12VkAfterCrash::Buffer objects using method 20 | CreateBuffer. 21 | 5. While recording your commands to ID3D12GraphicsCommandList, record also 22 | marker writes using function WriteMarker. 23 | 6. If graphics driver crashes, you receive DXGI_ERROR_DEVICE_REMOVED from 24 | a D3D12 function like Present. After it happened, inspect values under ppData 25 | pointer returned by CreateBuffer to see value of markers successfully 26 | written. 27 | 28 | Objects of this library should be destroyed using normal C++ operator delete. 29 | 30 | Requirements: 31 | 32 | - Windows 10 Fall Creators Update. 33 | If you have too old version of Windows, you will experience crashes because 34 | ID3D12GraphicsCommandList2 interface cannot be obtained. 35 | - Windows SDK in version at least for Windows 10 Fall Creators Update. 36 | If you have too old version of Windows SDK, you will see compilation errors 37 | about identifier ID3D12GraphicsCommandList2 not found. 38 | - Visual Studio 2017 - required by this version of Windows SDK. 39 | 40 | Note: While using this library, you will experience following warning from D3D12 41 | Debug Layer: 42 | 43 | D3D12 WARNING: ID3D12CommandQueue::ExecuteCommandLists: Readback Resource(...) 44 | still has mapped subresorces when executing a command list that performs a copy 45 | operation to the resource.(...) [ EXECUTION WARNING #927: 46 | EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED] 47 | 48 | See also blog post: 49 | http://asawicki.info/news_1690_debugging_d3d12_driver_crash.html 50 | Similar library for Vulkan: 51 | http://asawicki.info/news_1677_debugging_vulkan_driver_crash_-_equivalent_of_nvidia_aftermath.html 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | 55 | Copyright 2018 Adam Sawicki 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy of 58 | this software and associated documentation files (the "Software"), to deal in 59 | the Software without restriction, including without limitation the rights to 60 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 61 | the Software, and to permit persons to whom the Software is furnished to do so, 62 | subject to the following conditions: 63 | 64 | The above copyright notice and this permission notice shall be included in all 65 | copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 68 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 69 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 70 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 71 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 72 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 73 | */ 74 | #pragma once 75 | 76 | #include 77 | 78 | namespace D3D12AfterCrash 79 | { 80 | 81 | struct DEVICE_DESC 82 | { 83 | ID3D12Device* pD3D12Device; 84 | }; 85 | 86 | struct BUFFER_DESC 87 | { 88 | UINT MarkerCount; 89 | }; 90 | 91 | class Device; 92 | class Buffer; 93 | 94 | HRESULT CreateDevice(const DEVICE_DESC* Desc, Device** ppDevice); 95 | 96 | /* 97 | Represents main object of this library initialized for specific ID3D12Device. 98 | */ 99 | class Device 100 | { 101 | public: 102 | virtual ~Device() { } 103 | 104 | /* 105 | Creates and returns buffer object, as well as pointer to its data. This pointer 106 | will (hopefully) remain valid and preserve its content after graphics driver 107 | crash. 108 | */ 109 | virtual HRESULT CreateBuffer(const BUFFER_DESC* Desc, Buffer** ppBuffer, UINT** ppData) = 0; 110 | }; 111 | 112 | /* 113 | Represent buffer in system memory that can hold a number of 32-bit markers. 114 | */ 115 | class Buffer 116 | { 117 | public: 118 | virtual ~Buffer() { } 119 | 120 | /* 121 | Records command to a D3D12 command list that will write 32-bit marker to 122 | specific place in specific buffer. 123 | 124 | Mode can be: 125 | 126 | - D3D12_WRITEBUFFERIMMEDIATE_MODE_DEFAULT - behaves the same as normal 127 | copy-write operations. 128 | - D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_IN - guaranteed to occur after all 129 | preceding commands in the command stream have started. 130 | - D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_OUT - deferred until all previous 131 | commands in the command stream have completed through the GPU pipeline. 132 | */ 133 | virtual void WriteMarker( 134 | ID3D12GraphicsCommandList2* pCommandList, 135 | UINT MarkerIndex, 136 | UINT Value, 137 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode) = 0; 138 | 139 | /* 140 | Convenience function that automatically fetches ID3D12GraphicsCommandList2 141 | interface from old ID3D12GraphicsCommandList. 142 | */ 143 | virtual void WriteMarker( 144 | ID3D12GraphicsCommandList* pCommandList, 145 | UINT MarkerIndex, 146 | UINT Value, 147 | D3D12_WRITEBUFFERIMMEDIATE_MODE Mode) = 0; 148 | }; 149 | 150 | } // namespace D3D12AfterCrash 151 | -------------------------------------------------------------------------------- /DisplaySettingsTest/DisplaySettingsTest.c: -------------------------------------------------------------------------------- 1 | // DisplaySettingsTest 2 | // Author: Adam Sawicki, http://asawicki.info/, adam__REMOVE__@asawicki.info 3 | // Version: 1.0, 2017-03-09 4 | // License: Public Domain 5 | 6 | 7 | // This simple Windows console C program demostrates how to enumerate and change 8 | // display settings using Windows API. 9 | // 10 | // It uses function EnumDisplayDevices to enumerate all available adapters and 11 | // display devices. Their parameters are printed to standard output. 12 | 13 | // If CHANGE_MODE is defined as nonzero, it also searches for a specific display 14 | // mode specified in TARGET_* macros. If found, display settings are temporarily 15 | // changed to that mode using function ChangeDisplaySettingsEx. The program then 16 | // waits for any key press and restores default settings. 17 | 18 | 19 | #define WIN32_LEAN_AND_MEAN 20 | #include 21 | #include // for wprintf() 22 | #include // for _getch() 23 | #include // for _countof() 24 | #include // for assert() 25 | 26 | 27 | // Set to 0 to only print available modes. 28 | // Set to 1 to temporarily change display mode to the parameters specified below. 29 | #define CHANGE_MODE 1 30 | 31 | #define TARGET_ADAPTER_INDEX 0 32 | #define TARGET_WIDTH 1024 33 | #define TARGET_HEIGHT 768 34 | #define TARGET_FREQUENCY_MIN 59 35 | #define TARGET_FREQUENCY_MAX 60 36 | 37 | 38 | static void PrintDisplayDeviceStateFlags(DWORD stateFlags) 39 | { 40 | if(stateFlags & DISPLAY_DEVICE_ACTIVE) 41 | wprintf(L" ACTIVE"); 42 | if(stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) 43 | wprintf(L" MIRRORING_DRIVER"); 44 | if(stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) 45 | wprintf(L" MODESPRUNED"); 46 | if(stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) 47 | wprintf(L" PRIMARY_DEVICE"); 48 | if(stateFlags & DISPLAY_DEVICE_REMOVABLE) 49 | wprintf(L" REMOVABLE"); 50 | if(stateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE) 51 | wprintf(L" VGA_COMPATIBLE"); 52 | } 53 | 54 | static void PrintDisplayDeviceMode(const DEVMODE* mode) 55 | { 56 | wprintf(L"%u x %u, %u%s Hz, %u bpp", 57 | mode->dmPelsWidth, 58 | mode->dmPelsHeight, 59 | mode->dmDisplayFrequency, 60 | (mode->dmDisplayFrequency <= 1 ? L" (Default)" : L""), 61 | mode->dmBitsPerPel); 62 | 63 | // Somehow this flag is documented but not exists in header. 64 | //if(mode->dmDisplayFlags & DM_GRAYSCALE) 65 | // wprintf(L" GRAYSCALE"); 66 | if(mode->dmDisplayFlags & DM_INTERLACED) 67 | wprintf(L" INTERLACED"); 68 | } 69 | 70 | int main() 71 | { 72 | wchar_t targetDeviceName[32] = { 0 }; 73 | DEVMODE targetMode = { 0 }; 74 | BOOL targetModeFound = FALSE; 75 | 76 | DISPLAY_DEVICE adapter = { sizeof(DISPLAY_DEVICE) }; 77 | DWORD adapterIndex = 0; 78 | while(EnumDisplayDevices(NULL, adapterIndex, &adapter, 0)) 79 | { 80 | wprintf(L"Adapter %u:\n", adapterIndex); 81 | 82 | wprintf(L"\tDeviceName = %s\n", adapter.DeviceName); 83 | wprintf(L"\tDeviceString = %s\n", adapter.DeviceString); 84 | 85 | wprintf(L"\tStateFlags ="); 86 | PrintDisplayDeviceStateFlags(adapter.StateFlags); 87 | wprintf(L"\n"); 88 | 89 | DISPLAY_DEVICE displayDevice = { sizeof(DISPLAY_DEVICE) }; 90 | DWORD displayDeviceIndex = 0; 91 | while(EnumDisplayDevices(adapter.DeviceName, displayDeviceIndex, &displayDevice, 0)) 92 | { 93 | wprintf(L"\tDisplay Device %u:\n", displayDeviceIndex); 94 | 95 | wprintf(L"\t\tDeviceName = %s\n", displayDevice.DeviceName); 96 | wprintf(L"\t\tDeviceString = %s\n", displayDevice.DeviceString); 97 | 98 | wprintf(L"\t\tStateFlags ="); 99 | PrintDisplayDeviceStateFlags(displayDevice.StateFlags); 100 | wprintf(L"\n"); 101 | 102 | ++displayDeviceIndex; 103 | } 104 | 105 | DEVMODE mode = { 0 }; 106 | mode.dmSize = sizeof(DEVMODE); 107 | 108 | if(EnumDisplaySettings(adapter.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) 109 | { 110 | wprintf(L"\tDisplay Mode Current: "); 111 | PrintDisplayDeviceMode(&mode); 112 | wprintf(L"\n"); 113 | } 114 | 115 | if(EnumDisplaySettings(adapter.DeviceName, ENUM_REGISTRY_SETTINGS, &mode)) 116 | { 117 | wprintf(L"\tDisplay Mode Registry: "); 118 | PrintDisplayDeviceMode(&mode); 119 | wprintf(L"\n"); 120 | } 121 | 122 | DWORD modeIndex = 0; 123 | while(EnumDisplaySettings(adapter.DeviceName, modeIndex, &mode)) 124 | { 125 | wprintf(L"\tDisplay Mode %u: ", modeIndex); 126 | PrintDisplayDeviceMode(&mode); 127 | wprintf(L"\n"); 128 | 129 | if(!targetModeFound && adapterIndex == TARGET_ADAPTER_INDEX) 130 | { 131 | if(CHANGE_MODE && 132 | mode.dmPelsWidth == TARGET_WIDTH && 133 | mode.dmPelsHeight == TARGET_HEIGHT && 134 | mode.dmDisplayFrequency >= TARGET_FREQUENCY_MIN && 135 | mode.dmDisplayFrequency <= TARGET_FREQUENCY_MAX) 136 | { 137 | wcscpy_s(targetDeviceName, _countof(targetDeviceName), adapter.DeviceName); 138 | targetMode = mode; 139 | targetModeFound = TRUE; 140 | } 141 | } 142 | 143 | ++modeIndex; 144 | } 145 | 146 | ++adapterIndex; 147 | } 148 | 149 | if(targetModeFound) 150 | { 151 | // Change display mode. 152 | LONG r = ChangeDisplaySettingsEx(targetDeviceName, &targetMode, NULL, CDS_FULLSCREEN, NULL); 153 | assert(r == DISP_CHANGE_SUCCESSFUL); 154 | 155 | // Wait for key in console. 156 | _getch(); 157 | 158 | // Restore default display mode. 159 | r = ChangeDisplaySettingsEx(targetDeviceName, NULL, NULL, 0, NULL); 160 | assert(r == DISP_CHANGE_SUCCESSFUL); 161 | } 162 | 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /DisplaySettingsTest/ExampleOutput.txt: -------------------------------------------------------------------------------- 1 | Adapter 0: 2 | DeviceName = \\.\DISPLAY1 3 | DeviceString = AMD Radeon (TM) RX 480 4 | StateFlags = ACTIVE PRIMARY_DEVICE 5 | Display Device 0: 6 | DeviceName = \\.\DISPLAY1\Monitor0 7 | DeviceString = Generic PnP Monitor 8 | StateFlags = ACTIVE 9 | Display Mode Current: 1920 x 1080, 59 Hz, 32 bpp 10 | Display Mode Registry: 1920 x 1080, 59 Hz, 32 bpp 11 | Display Mode 0: 640 x 480, 59 Hz, 32 bpp 12 | Display Mode 1: 640 x 480, 60 Hz, 32 bpp 13 | Display Mode 2: 640 x 480, 75 Hz, 32 bpp 14 | Display Mode 3: 720 x 480, 29 Hz, 32 bpp INTERLACED 15 | Display Mode 4: 720 x 480, 30 Hz, 32 bpp INTERLACED 16 | Display Mode 5: 720 x 480, 59 Hz, 32 bpp 17 | Display Mode 6: 720 x 480, 60 Hz, 32 bpp 18 | Display Mode 7: 720 x 576, 25 Hz, 32 bpp INTERLACED 19 | Display Mode 8: 720 x 576, 50 Hz, 32 bpp 20 | Display Mode 9: 720 x 576, 59 Hz, 32 bpp 21 | Display Mode 10: 720 x 576, 60 Hz, 32 bpp 22 | Display Mode 11: 720 x 576, 59 Hz, 32 bpp 23 | Display Mode 12: 720 x 576, 60 Hz, 32 bpp 24 | Display Mode 13: 720 x 576, 59 Hz, 32 bpp 25 | Display Mode 14: 720 x 576, 60 Hz, 32 bpp 26 | Display Mode 15: 800 x 480, 60 Hz, 32 bpp 27 | Display Mode 16: 800 x 480, 60 Hz, 32 bpp 28 | Display Mode 17: 800 x 480, 60 Hz, 32 bpp 29 | Display Mode 18: 800 x 480, 75 Hz, 32 bpp 30 | Display Mode 19: 800 x 480, 75 Hz, 32 bpp 31 | Display Mode 20: 800 x 480, 75 Hz, 32 bpp 32 | Display Mode 21: 800 x 600, 60 Hz, 32 bpp 33 | Display Mode 22: 800 x 600, 75 Hz, 32 bpp 34 | Display Mode 23: 1024 x 600, 60 Hz, 32 bpp 35 | Display Mode 24: 1024 x 600, 60 Hz, 32 bpp 36 | Display Mode 25: 1024 x 600, 60 Hz, 32 bpp 37 | Display Mode 26: 1024 x 600, 70 Hz, 32 bpp 38 | Display Mode 27: 1024 x 600, 70 Hz, 32 bpp 39 | Display Mode 28: 1024 x 600, 70 Hz, 32 bpp 40 | Display Mode 29: 1024 x 600, 75 Hz, 32 bpp 41 | Display Mode 30: 1024 x 600, 75 Hz, 32 bpp 42 | Display Mode 31: 1024 x 600, 75 Hz, 32 bpp 43 | Display Mode 32: 1024 x 768, 60 Hz, 32 bpp 44 | Display Mode 33: 1024 x 768, 70 Hz, 32 bpp 45 | Display Mode 34: 1024 x 768, 75 Hz, 32 bpp 46 | Display Mode 35: 1152 x 648, 50 Hz, 32 bpp 47 | Display Mode 36: 1152 x 648, 59 Hz, 32 bpp 48 | Display Mode 37: 1152 x 648, 60 Hz, 32 bpp 49 | Display Mode 38: 1152 x 864, 59 Hz, 32 bpp 50 | Display Mode 39: 1152 x 864, 60 Hz, 32 bpp 51 | Display Mode 40: 1152 x 864, 59 Hz, 32 bpp 52 | Display Mode 41: 1152 x 864, 60 Hz, 32 bpp 53 | Display Mode 42: 1152 x 864, 59 Hz, 32 bpp 54 | Display Mode 43: 1152 x 864, 60 Hz, 32 bpp 55 | Display Mode 44: 1152 x 864, 75 Hz, 32 bpp 56 | Display Mode 45: 1280 x 720, 50 Hz, 32 bpp 57 | Display Mode 46: 1280 x 720, 59 Hz, 32 bpp 58 | Display Mode 47: 1280 x 720, 60 Hz, 32 bpp 59 | Display Mode 48: 1280 x 768, 60 Hz, 32 bpp 60 | Display Mode 49: 1280 x 768, 60 Hz, 32 bpp 61 | Display Mode 50: 1280 x 768, 60 Hz, 32 bpp 62 | Display Mode 51: 1280 x 800, 60 Hz, 32 bpp 63 | Display Mode 52: 1280 x 1024, 60 Hz, 32 bpp 64 | Display Mode 53: 1280 x 1024, 75 Hz, 32 bpp 65 | Display Mode 54: 1360 x 768, 60 Hz, 32 bpp 66 | Display Mode 55: 1360 x 768, 60 Hz, 32 bpp 67 | Display Mode 56: 1360 x 768, 60 Hz, 32 bpp 68 | Display Mode 57: 1366 x 768, 60 Hz, 32 bpp 69 | Display Mode 58: 1366 x 768, 60 Hz, 32 bpp 70 | Display Mode 59: 1366 x 768, 60 Hz, 32 bpp 71 | Display Mode 60: 1400 x 1050, 60 Hz, 32 bpp 72 | Display Mode 61: 1440 x 900, 60 Hz, 32 bpp 73 | Display Mode 62: 1600 x 900, 60 Hz, 32 bpp 74 | Display Mode 63: 1680 x 1050, 60 Hz, 32 bpp 75 | Display Mode 64: 1776 x 1000, 24 Hz, 32 bpp 76 | Display Mode 65: 1776 x 1000, 25 Hz, 32 bpp INTERLACED 77 | Display Mode 66: 1776 x 1000, 29 Hz, 32 bpp INTERLACED 78 | Display Mode 67: 1776 x 1000, 30 Hz, 32 bpp INTERLACED 79 | Display Mode 68: 1776 x 1000, 29 Hz, 32 bpp 80 | Display Mode 69: 1776 x 1000, 30 Hz, 32 bpp 81 | Display Mode 70: 1776 x 1000, 50 Hz, 32 bpp 82 | Display Mode 71: 1776 x 1000, 59 Hz, 32 bpp 83 | Display Mode 72: 1776 x 1000, 60 Hz, 32 bpp 84 | Display Mode 73: 1920 x 1080, 23 Hz, 32 bpp 85 | Display Mode 74: 1920 x 1080, 24 Hz, 32 bpp 86 | Display Mode 75: 1920 x 1080, 25 Hz, 32 bpp INTERLACED 87 | Display Mode 76: 1920 x 1080, 29 Hz, 32 bpp INTERLACED 88 | Display Mode 77: 1920 x 1080, 30 Hz, 32 bpp INTERLACED 89 | Display Mode 78: 1920 x 1080, 29 Hz, 32 bpp 90 | Display Mode 79: 1920 x 1080, 30 Hz, 32 bpp 91 | Display Mode 80: 1920 x 1080, 50 Hz, 32 bpp 92 | Display Mode 81: 1920 x 1080, 59 Hz, 32 bpp 93 | Display Mode 82: 1920 x 1080, 60 Hz, 32 bpp 94 | Adapter 1: 95 | DeviceName = \\.\DISPLAY2 96 | DeviceString = AMD Radeon (TM) RX 480 97 | StateFlags = ACTIVE 98 | Display Device 0: 99 | DeviceName = \\.\DISPLAY2\Monitor0 100 | DeviceString = Generic PnP Monitor 101 | StateFlags = ACTIVE 102 | Display Mode Current: 1920 x 1080, 59 Hz, 32 bpp 103 | Display Mode Registry: 1920 x 1080, 59 Hz, 32 bpp 104 | Display Mode 0: 640 x 480, 59 Hz, 32 bpp 105 | Display Mode 1: 640 x 480, 60 Hz, 32 bpp 106 | Display Mode 2: 640 x 480, 75 Hz, 32 bpp 107 | Display Mode 3: 720 x 480, 59 Hz, 32 bpp 108 | Display Mode 4: 720 x 480, 60 Hz, 32 bpp 109 | Display Mode 5: 720 x 576, 50 Hz, 32 bpp 110 | Display Mode 6: 720 x 576, 59 Hz, 32 bpp 111 | Display Mode 7: 720 x 576, 60 Hz, 32 bpp 112 | Display Mode 8: 720 x 576, 59 Hz, 32 bpp 113 | Display Mode 9: 720 x 576, 60 Hz, 32 bpp 114 | Display Mode 10: 720 x 576, 59 Hz, 32 bpp 115 | Display Mode 11: 720 x 576, 60 Hz, 32 bpp 116 | Display Mode 12: 800 x 480, 56 Hz, 32 bpp 117 | Display Mode 13: 800 x 480, 56 Hz, 32 bpp 118 | Display Mode 14: 800 x 480, 56 Hz, 32 bpp 119 | Display Mode 15: 800 x 480, 60 Hz, 32 bpp 120 | Display Mode 16: 800 x 480, 60 Hz, 32 bpp 121 | Display Mode 17: 800 x 480, 60 Hz, 32 bpp 122 | Display Mode 18: 800 x 480, 75 Hz, 32 bpp 123 | Display Mode 19: 800 x 480, 75 Hz, 32 bpp 124 | Display Mode 20: 800 x 480, 75 Hz, 32 bpp 125 | Display Mode 21: 800 x 600, 56 Hz, 32 bpp 126 | Display Mode 22: 800 x 600, 60 Hz, 32 bpp 127 | Display Mode 23: 800 x 600, 75 Hz, 32 bpp 128 | Display Mode 24: 1024 x 600, 60 Hz, 32 bpp 129 | Display Mode 25: 1024 x 600, 60 Hz, 32 bpp 130 | Display Mode 26: 1024 x 600, 60 Hz, 32 bpp 131 | Display Mode 27: 1024 x 600, 75 Hz, 32 bpp 132 | Display Mode 28: 1024 x 600, 75 Hz, 32 bpp 133 | Display Mode 29: 1024 x 600, 75 Hz, 32 bpp 134 | Display Mode 30: 1024 x 768, 60 Hz, 32 bpp 135 | Display Mode 31: 1024 x 768, 75 Hz, 32 bpp 136 | Display Mode 32: 1152 x 648, 50 Hz, 32 bpp 137 | Display Mode 33: 1152 x 648, 59 Hz, 32 bpp 138 | Display Mode 34: 1152 x 648, 60 Hz, 32 bpp 139 | Display Mode 35: 1152 x 864, 59 Hz, 32 bpp 140 | Display Mode 36: 1152 x 864, 60 Hz, 32 bpp 141 | Display Mode 37: 1152 x 864, 59 Hz, 32 bpp 142 | Display Mode 38: 1152 x 864, 60 Hz, 32 bpp 143 | Display Mode 39: 1152 x 864, 59 Hz, 32 bpp 144 | Display Mode 40: 1152 x 864, 60 Hz, 32 bpp 145 | Display Mode 41: 1152 x 864, 75 Hz, 32 bpp 146 | Display Mode 42: 1280 x 720, 50 Hz, 32 bpp 147 | Display Mode 43: 1280 x 720, 59 Hz, 32 bpp 148 | Display Mode 44: 1280 x 720, 60 Hz, 32 bpp 149 | Display Mode 45: 1280 x 768, 60 Hz, 32 bpp 150 | Display Mode 46: 1280 x 768, 60 Hz, 32 bpp 151 | Display Mode 47: 1280 x 768, 60 Hz, 32 bpp 152 | Display Mode 48: 1280 x 768, 75 Hz, 32 bpp 153 | Display Mode 49: 1280 x 768, 75 Hz, 32 bpp 154 | Display Mode 50: 1280 x 768, 75 Hz, 32 bpp 155 | Display Mode 51: 1280 x 1024, 60 Hz, 32 bpp 156 | Display Mode 52: 1280 x 1024, 75 Hz, 32 bpp 157 | Display Mode 53: 1360 x 768, 60 Hz, 32 bpp 158 | Display Mode 54: 1360 x 768, 60 Hz, 32 bpp 159 | Display Mode 55: 1360 x 768, 60 Hz, 32 bpp 160 | Display Mode 56: 1366 x 768, 60 Hz, 32 bpp 161 | Display Mode 57: 1366 x 768, 60 Hz, 32 bpp 162 | Display Mode 58: 1366 x 768, 60 Hz, 32 bpp 163 | Display Mode 59: 1400 x 1050, 60 Hz, 32 bpp 164 | Display Mode 60: 1400 x 1050, 60 Hz, 32 bpp 165 | Display Mode 61: 1400 x 1050, 60 Hz, 32 bpp 166 | Display Mode 62: 1600 x 900, 60 Hz, 32 bpp 167 | Display Mode 63: 1600 x 900, 60 Hz, 32 bpp 168 | Display Mode 64: 1600 x 900, 60 Hz, 32 bpp 169 | Display Mode 65: 1680 x 1050, 60 Hz, 32 bpp 170 | Display Mode 66: 1776 x 1000, 25 Hz, 32 bpp INTERLACED 171 | Display Mode 67: 1776 x 1000, 29 Hz, 32 bpp INTERLACED 172 | Display Mode 68: 1776 x 1000, 30 Hz, 32 bpp INTERLACED 173 | Display Mode 69: 1776 x 1000, 50 Hz, 32 bpp 174 | Display Mode 70: 1776 x 1000, 59 Hz, 32 bpp 175 | Display Mode 71: 1776 x 1000, 60 Hz, 32 bpp 176 | Display Mode 72: 1920 x 1080, 25 Hz, 32 bpp INTERLACED 177 | Display Mode 73: 1920 x 1080, 29 Hz, 32 bpp INTERLACED 178 | Display Mode 74: 1920 x 1080, 30 Hz, 32 bpp INTERLACED 179 | Display Mode 75: 1920 x 1080, 50 Hz, 32 bpp 180 | Display Mode 76: 1920 x 1080, 59 Hz, 32 bpp 181 | Display Mode 77: 1920 x 1080, 60 Hz, 32 bpp 182 | Adapter 2: 183 | DeviceName = \\.\DISPLAY3 184 | DeviceString = AMD Radeon (TM) RX 480 185 | StateFlags = 186 | Display Mode Registry: 0 x 0, 0 (Default) Hz, 0 bpp 187 | Adapter 3: 188 | DeviceName = \\.\DISPLAY4 189 | DeviceString = AMD Radeon (TM) RX 480 190 | StateFlags = 191 | Display Mode Registry: 0 x 0, 0 (Default) Hz, 0 bpp 192 | Adapter 4: 193 | DeviceName = \\.\DISPLAY5 194 | DeviceString = AMD Radeon (TM) RX 480 195 | StateFlags = 196 | Display Mode Registry: 0 x 0, 0 (Default) Hz, 0 bpp 197 | Adapter 5: 198 | DeviceName = \\.\DISPLAY6 199 | DeviceString = AMD Radeon (TM) RX 480 200 | StateFlags = 201 | Display Mode Registry: 0 x 0, 0 (Default) Hz, 0 bpp 202 | -------------------------------------------------------------------------------- /DisplaySettingsTest/README.md: -------------------------------------------------------------------------------- 1 | A simple Windows console C program that demonstrates how to enumerate and change display modes (screen resolution and refresh rate). See my blog post: [How to change display mode using WinAPI?](http://asawicki.info/news_1637_how_to_change_display_mode_using_winapi.html). -------------------------------------------------------------------------------- /Graphics/Graphics card icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawickiap/MISC/8a3ef62b20160f8ffff0087eb645017cbf97ab39/Graphics/Graphics card icon.png -------------------------------------------------------------------------------- /Graphics/Graphics card icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 32 | 37 | 38 | 39 | 61 | 64 | 65 | 67 | 68 | 70 | image/svg+xml 71 | 73 | 74 | 75 | 76 | 77 | 81 | 89 | 94 | 101 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /IncludeList.py: -------------------------------------------------------------------------------- 1 | # IncludeList 2 | # 3 | # Author: Adam Sawicki, http://asawicki.info, adam__REMOVE__@asawicki.info 4 | # Version: 20.11.9 5 | # License: Public Domain 6 | 7 | import argparse 8 | import os.path 9 | import re 10 | 11 | parser = argparse.ArgumentParser(description='Parse #include in given files with additional include directories to find full list of files included.') 12 | parser.add_argument('File', nargs='*', help='file to parse') 13 | parser.add_argument('-I', dest='include', action='append', help='additional include directory') 14 | 15 | reIncludeGlobal = re.compile('^\s*#\s*include\s*<(.+)>\s*$') 16 | reIncludeLocal = re.compile('^\s*#\s*include\s*"(.+)"\s*$') 17 | 18 | def AddInclude(includeAbsolutePath): 19 | global filesToProcess 20 | includeAbsolutePath = os.path.abspath(includeAbsolutePath) # Normalize 21 | filesToProcess.add(includeAbsolutePath) 22 | 23 | def ProcessInclude(currFilePath, includeRelativePath, isGlobal): 24 | global additionalIncludeDirectories 25 | currFileDir = os.path.dirname(currFilePath) 26 | if not isGlobal: 27 | includeAbsolutePath = os.path.join(currFileDir, includeRelativePath) 28 | if os.path.isfile(includeAbsolutePath): 29 | AddInclude(includeAbsolutePath) 30 | return 31 | for includeDir in additionalIncludeDirectories: 32 | includeAbsolutePath = os.path.join(includeDir, includeRelativePath) 33 | if os.path.isfile(includeAbsolutePath): 34 | AddInclude(includeAbsolutePath) 35 | return 36 | if isGlobal: 37 | includeAbsolutePath = os.path.join(currFileDir, includeRelativePath) 38 | if os.path.isfile(includeAbsolutePath): 39 | AddInclude(includeAbsolutePath) 40 | return 41 | if isGlobal: 42 | print('ERROR: Failed #include <{0}>'.format(includeRelativePath)) 43 | else: 44 | print('ERROR: Failed #include "{0}"'.format(includeRelativePath)) 45 | 46 | def ProcessFile(filePath): 47 | global filesToProcess, filesProcessed 48 | file = open(filePath, 'r') 49 | line = file.readline() 50 | while line != '': 51 | match = reIncludeLocal.fullmatch(line) 52 | if match: 53 | ProcessInclude(filePath, match[1], False) 54 | else: 55 | match = reIncludeGlobal.fullmatch(line) 56 | if match: 57 | ProcessInclude(filePath, match[1], True) 58 | line = file.readline() 59 | file.close() 60 | filesProcessed.add(filePath) 61 | 62 | 63 | args = parser.parse_args() 64 | filesToProcess = set() 65 | for path in args.File: 66 | filesToProcess.add(os.path.abspath(path)) # Normalize 67 | filesProcessed = set() 68 | additionalIncludeDirectories = args.include 69 | if not additionalIncludeDirectories: 70 | additionalIncludeDirectories = [] 71 | while len(filesToProcess) > 0: 72 | filePath = filesToProcess.pop() 73 | if filePath not in filesProcessed: 74 | ProcessFile(filePath) 75 | filesProcessedList = list(filesProcessed) 76 | filesProcessedList.sort() 77 | for filePath in filesProcessedList: 78 | print(filePath) 79 | -------------------------------------------------------------------------------- /IsLog10.c: -------------------------------------------------------------------------------- 1 | /* 2 | This is a simple C program demonstrating multiple solutions to a question: 3 | 4 | "Write a function that checks whether an integer number is a power of 10." 5 | 6 | It contains a set of tests. 7 | 8 | Author : Adam Sawicki, http://asawicki.info, 2017-10-04 9 | License : Public Domain 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | int IsLog10_v1(int32_t x) 19 | { 20 | // Convert x to string. 21 | char buf[12]; 22 | itoa(x, buf, 10); 23 | const size_t bufLen = strlen(buf); 24 | 25 | // Check if string contains '1' followed by '0'-s. 26 | if(buf[0] != '1') 27 | return 0; 28 | for(size_t i = 1; i < bufLen; ++i) 29 | { 30 | if(buf[i] != '0') 31 | return 0; 32 | } 33 | return 1; 34 | } 35 | 36 | int IsLog10_v2(int32_t x) 37 | { 38 | // Convert x to double. Calculate log10 of it. 39 | double x_d = (double)x; 40 | double x_log10 = log10(x_d); 41 | // Check if result is integer number - has zero fractional part. 42 | return x_log10 - floor(x_log10) == 0.0; 43 | } 44 | 45 | int IsLog10_v3(int32_t x) 46 | { 47 | if(x <= 0) 48 | return 0; 49 | if(x == 1) 50 | return 1; 51 | if(x % 10 != 0) 52 | return 0; 53 | // Recursion. 54 | return IsLog10_v3(x / 10); 55 | } 56 | 57 | int IsLog10_v4(int32_t x) 58 | { 59 | // Same algorithm as v3, but converted from recursion to a loop. 60 | if(x <= 0) 61 | return 0; 62 | for(;;) 63 | { 64 | if(x == 1) 65 | return 1; 66 | if(x % 10 != 0) 67 | return 0; 68 | x /= 10; 69 | } 70 | } 71 | 72 | int IsLog10_v5(int32_t x) 73 | { 74 | // Just enumerate all possible values. 75 | return 76 | x == 1 || 77 | x == 10 || 78 | x == 100 || 79 | x == 1000 || 80 | x == 10000 || 81 | x == 100000 || 82 | x == 1000000 || 83 | x == 10000000 || 84 | x == 100000000 || 85 | x == 1000000000; 86 | } 87 | 88 | int Test(int32_t x) 89 | { 90 | // Choose version to test. 91 | 92 | //return IsLog10_v1(x); 93 | //return IsLog10_v2(x); 94 | //return IsLog10_v3(x); 95 | //return IsLog10_v4(x); 96 | return IsLog10_v5(x); 97 | } 98 | 99 | int main() 100 | { 101 | const int32_t trueList[] = { 102 | 1, 103 | 10, 104 | 100, 105 | 1000, 106 | 10000, 107 | 100000, 108 | 1000000, 109 | 10000000, 110 | 100000000, 111 | 1000000000, 112 | }; 113 | const size_t trueCount = sizeof(trueList) / sizeof(trueList[0]); 114 | 115 | const int32_t falseList[] = { 116 | 0, 117 | 2, 118 | 3, 119 | 5, 120 | 9, 121 | 11, 122 | 14, 123 | 19, 124 | 20, 125 | 80, 126 | 99, 127 | 101, 128 | 128, 129 | 255, 130 | 256, 131 | 999999999, 132 | 1000000001, 133 | 0x40000000, 134 | 0x40000001, 135 | 0x7FFFFFFE, 136 | 0x7FFFFFFF, 137 | -1, 138 | -2, 139 | -5, 140 | -10, 141 | -100, 142 | -999, 143 | -1000, 144 | -1001, 145 | -999999999, 146 | -1000000000, 147 | -1000000001, 148 | 0x80000000, 149 | }; 150 | const size_t falseCount = sizeof(falseList) / sizeof(falseList[0]); 151 | 152 | int allPassed = 1; 153 | for(size_t i = 0; i < trueCount; ++i) 154 | { 155 | if(!Test(trueList[i])) 156 | { 157 | printf("%d: returned false, should return true.\n", trueList[i]); 158 | allPassed = 0; 159 | } 160 | } 161 | for(size_t i = 0; i < falseCount; ++i) 162 | { 163 | if(Test(falseList[i])) 164 | { 165 | printf("%d: returned true, should return false.\n", falseList[i]); 166 | allPassed = 0; 167 | } 168 | } 169 | if(allPassed) 170 | printf("All tests passed.\n"); 171 | } 172 | -------------------------------------------------------------------------------- /PrintStream/PrintStream.cpp: -------------------------------------------------------------------------------- 1 | // PrintStream.cpp 2 | // Author: Adam Sawicki, www.asawicki.info, adam__REMOVE__@asawicki.info 3 | // Version: 2.0, 2015-12-05 4 | // License: Public Domain 5 | 6 | #include "PrintStream.hpp" 7 | #include 8 | #include 9 | 10 | #ifdef UNICODE 11 | #define TSTRLEN wcslen 12 | #define TPRINTF wprintf 13 | #define TVPRINTF vwprintf 14 | #define TVSPRINTF_S vswprintf_s 15 | #define TVSCPRINTF _vscwprintf 16 | #define TFOPEN_S _wfopen_s 17 | #define TFPRINTF fwprintf 18 | #define TVFPRINTF vfwprintf 19 | #else 20 | #define TSTRLEN strlen 21 | #define TPRINTF printf 22 | #define TVPRINTF vprintf 23 | #define TVSPRINTF_S vsprintf_s 24 | #define TVSCPRINTF _vscprintf 25 | #define TFOPEN_S fopen_s 26 | #define TFPRINTF fprintf 27 | #define TVFPRINTF vfprintf 28 | #endif 29 | 30 | static const size_t SMALL_BUF_SIZE = 256; 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | // CPrintStream 34 | 35 | void CPrintStream::print(const TCHAR* str) 36 | { 37 | print(str, TSTRLEN(str)); 38 | } 39 | 40 | void CPrintStream::print(const TCHAR* str, size_t strLen) 41 | { 42 | if(strLen) 43 | { 44 | // String is null-terminated. 45 | if(str[strLen - 1] == 0) 46 | print(str); 47 | else 48 | { 49 | bool useSmallBuf = strLen < SMALL_BUF_SIZE; 50 | TCHAR smallBuf[SMALL_BUF_SIZE]; 51 | std::vector bigBuf(useSmallBuf ? 0 : strLen + 1); 52 | TCHAR* bufPtr = useSmallBuf ? smallBuf : &bigBuf[0]; 53 | memcpy(bufPtr, str, strLen * sizeof(TCHAR)); 54 | bufPtr[strLen] = 0; 55 | print(bufPtr); 56 | } 57 | } 58 | } 59 | 60 | void CPrintStream::print(const TSTRING& str) 61 | { 62 | print(str.c_str(), str.length()); 63 | } 64 | 65 | void CPrintStream::vprintf(const TCHAR* format, va_list argList) 66 | { 67 | size_t dstLen = (size_t)::TVSCPRINTF(format, argList); 68 | if(dstLen) 69 | { 70 | bool useSmallBuf = dstLen + 1 < SMALL_BUF_SIZE; 71 | TCHAR smallBuf[SMALL_BUF_SIZE]; 72 | std::vector bigBuf(useSmallBuf ? 0 : dstLen + 1); 73 | TCHAR* bufPtr = useSmallBuf ? smallBuf : bigBuf.data(); 74 | ::TVSPRINTF_S(bufPtr, dstLen + 1, format, argList); 75 | print(bufPtr, dstLen); 76 | } 77 | 78 | } 79 | 80 | void CPrintStream::printf(const TCHAR* format, ...) 81 | { 82 | va_list argList; 83 | va_start(argList, format); 84 | vprintf(format, argList); 85 | va_end(argList); 86 | } 87 | 88 | //////////////////////////////////////////////////////////////////////////////// 89 | // CConsolePrintStream 90 | 91 | void CConsolePrintStream::print(const TCHAR* str, size_t strLen) 92 | { 93 | assert(strLen <= INT_MAX); 94 | ::TPRINTF(_T("%.*s"), (int)strLen, str); 95 | } 96 | 97 | void CConsolePrintStream::print(const TCHAR* str) 98 | { 99 | ::TPRINTF(_T("%s"), str); 100 | } 101 | 102 | void CConsolePrintStream::vprintf(const TCHAR* format, va_list argList) 103 | { 104 | ::TVPRINTF(format, argList); 105 | } 106 | 107 | //////////////////////////////////////////////////////////////////////////////// 108 | // CFilePrintStream 109 | 110 | CFilePrintStream::CFilePrintStream() : 111 | m_File(nullptr) 112 | { 113 | } 114 | 115 | CFilePrintStream::CFilePrintStream(const TCHAR* filePath, const TCHAR* mode) : 116 | m_File(nullptr) 117 | { 118 | Open(filePath, mode); 119 | } 120 | 121 | CFilePrintStream::~CFilePrintStream() 122 | { 123 | Close(); 124 | } 125 | 126 | bool CFilePrintStream::Open(const TCHAR* filePath, const TCHAR* mode) 127 | { 128 | Close(); 129 | bool success = TFOPEN_S(&m_File, filePath, mode) == 0; 130 | if(!success) 131 | { 132 | m_File = nullptr; 133 | // Handle error somehow. 134 | assert(0); 135 | } 136 | return success; 137 | } 138 | 139 | void CFilePrintStream::Close() 140 | { 141 | if(m_File) 142 | { 143 | fclose(m_File); 144 | m_File = nullptr; 145 | } 146 | } 147 | 148 | void CFilePrintStream::print(const TCHAR* str, size_t strLen) 149 | { 150 | if(IsOpened()) 151 | { 152 | assert(strLen <= INT_MAX); 153 | ::TFPRINTF(m_File, _T("%.*s"), (int)strLen, str); 154 | } 155 | else 156 | assert(0); 157 | } 158 | 159 | void CFilePrintStream::print(const TCHAR* str) 160 | { 161 | if(IsOpened()) 162 | ::TFPRINTF(m_File, _T("%s"), str); 163 | else 164 | assert(0); 165 | } 166 | 167 | void CFilePrintStream::vprintf(const TCHAR* format, va_list argList) 168 | { 169 | if(IsOpened()) 170 | ::TVFPRINTF(m_File, format, argList); 171 | else 172 | assert(0); 173 | } 174 | 175 | //////////////////////////////////////////////////////////////////////////////// 176 | // CMemoryPrintStream 177 | 178 | void CMemoryPrintStream::print(const TCHAR* str, size_t strLen) 179 | { 180 | m_BufPtr->insert(m_BufPtr->end(), str, str + strLen); 181 | } 182 | 183 | //////////////////////////////////////////////////////////////////////////////// 184 | // CDebugPrintStream 185 | 186 | void CDebugPrintStream::print(const TCHAR* str) 187 | { 188 | OutputDebugString(str); 189 | } 190 | -------------------------------------------------------------------------------- /PrintStream/PrintStream.hpp: -------------------------------------------------------------------------------- 1 | // PrintStream.hpp 2 | // Author: Adam Sawicki, www.asawicki.info, adam__REMOVE__@asawicki.info 3 | // Version: 2.0, 2015-12-05 4 | // License: Public Domain 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef UNICODE 16 | #define TSTRING std::wstring 17 | #else 18 | #define TSTRING std::string 19 | #endif 20 | 21 | // Abstract base class. 22 | // Derived class must implement at least first or second version of print method 23 | // and share inherited ones with: 24 | // using CPrintStream::print; 25 | class CPrintStream 26 | { 27 | public: 28 | virtual ~CPrintStream() { } 29 | // Default implementation copies to a temporary null-terminated string and rediects it to print(str). 30 | virtual void print(const TCHAR* str, size_t strLen); 31 | // Default implementation calculates length and redirects to print(str, strLen). 32 | virtual void print(const TCHAR* str); 33 | // Default implementation redirects to print(str, strLen). 34 | virtual void print(const TSTRING& str); 35 | // Default implementation formats string in memory and redirects it to print(str, strLen). 36 | virtual void vprintf(const TCHAR* format, va_list argList); 37 | // Redirects to print(format, argList). 38 | void printf(const TCHAR* format, ...); 39 | }; 40 | 41 | // Prints to standard output. 42 | class CConsolePrintStream : public CPrintStream 43 | { 44 | public: 45 | using CPrintStream::print; 46 | virtual void print(const TCHAR* str, size_t strLen); 47 | virtual void print(const TCHAR* str); 48 | virtual void vprintf(const TCHAR* format, va_list argList); 49 | }; 50 | 51 | // Prints to file. 52 | class CFilePrintStream : public CPrintStream 53 | { 54 | public: 55 | // Initializes object with empty state. 56 | CFilePrintStream(); 57 | // Opens file during initialization. 58 | CFilePrintStream(const TCHAR* filePath, const TCHAR* mode); 59 | // Automatically closes file. 60 | ~CFilePrintStream(); 61 | 62 | // mode: Like in fopen, e.g. "wb", "a". 63 | bool Open(const TCHAR* filePath, const TCHAR* mode); 64 | void Close(); 65 | bool IsOpened() const { return m_File != nullptr; } 66 | 67 | using CPrintStream::print; 68 | virtual void print(const TCHAR* str, size_t strLen); 69 | virtual void print(const TCHAR* str); 70 | virtual void vprintf(const TCHAR* format, va_list argList); 71 | 72 | private: 73 | FILE* m_File; 74 | }; 75 | 76 | // Appends to internal or external memory buffer. 77 | class CMemoryPrintStream : public CPrintStream 78 | { 79 | public: 80 | typedef std::vector Buf_t; 81 | 82 | CMemoryPrintStream(Buf_t* externalBuf = nullptr) : 83 | m_BufPtr(externalBuf ? externalBuf : &m_InternalBuf) 84 | { 85 | } 86 | 87 | const Buf_t* GetBuf() const { return m_BufPtr; } 88 | Buf_t* GetBuf() { return m_BufPtr; } 89 | void GetAsString(TSTRING& out) const { out.assign(m_BufPtr->begin(), m_BufPtr->end()); } 90 | 91 | using CPrintStream::print; 92 | virtual void print(const TCHAR* str, size_t strLen); 93 | 94 | private: 95 | Buf_t m_InternalBuf; 96 | // Pointer to either m_InternalBuf (if using internal buffer) or external buffer passed to constructor. 97 | // Not null-terminated. 98 | Buf_t* m_BufPtr; 99 | }; 100 | 101 | // Prints to OutputDebugString. 102 | class CDebugPrintStream : public CPrintStream 103 | { 104 | public: 105 | using CPrintStream::print; 106 | virtual void print(const TCHAR* str); 107 | }; 108 | -------------------------------------------------------------------------------- /PrintStream/README.md: -------------------------------------------------------------------------------- 1 | # PrintStream 2 | 3 | A hierarchy of classes that represent abstract concept of a text-based stream that can be printed into, using methods: 4 | 5 | void print(const char* str, size_t strLen); 6 | void print(const char* str); 7 | void print(const std::string& str); 8 | void vprintf(const char* format, va_list argList); 9 | void printf(const char* format, ...); 10 | 11 | Derived classes offer printing to: 12 | 13 | - `CConsolePrintStream` - console (standard output), using functions like `printf`. 14 | - `CFilePrintStream` - file, using functions like `fopen`, `fprintf`. 15 | - `CMemoryPrintStream` - buffer in memory, of type `std::vector`, with conversion to `std::string`. 16 | - `CDebugPrintStream` - debug output, using function `OutputDebugString`. 17 | 18 | The code is tested on Windows, using Visual Studio 2015 Update 1. 19 | 20 | Unicode is supported. Just switch "Character Set" to "Use Unicode Character Set" in Visual Studio project properties and automatically defined macro `UNICODE` will make this code use `wchar_t` instead of `char`, `wprintf` instead of `printf` etc. 21 | 22 | License: Public Domain. 23 | 24 | External dependencies: 25 | 26 | - WinAPI - `` 27 | - Some elements of standard C++ library (STL) 28 | - Some elements of standard C library 29 | -------------------------------------------------------------------------------- /QueryPerformanceCounterTest.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawickiap/MISC/8a3ef62b20160f8ffff0087eb645017cbf97ab39/QueryPerformanceCounterTest.cpp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Miscellaneous small code snippets. 2 | 3 | ## [PrintStream](../../tree/master/PrintStream) 4 | 5 | A hierarchy of classes that represent abstract concept of a text-based stream that can be printed into, using methods like `print(const char* str)`, `printf(const char* format, ....)` etc. Derived classes offer printing to console (standard output), to file, to memory buffer and more. 6 | 7 | ## [DisplaySettingsTest](../../tree/master/DisplaySettingsTest) 8 | 9 | A simple Windows console C program that demonstrates how to enumerate and change display modes (screen resolution and refresh rate). See my blog post: [How to change display mode using WinAPI?](http://asawicki.info/news_1637_how_to_change_display_mode_using_winapi.html). 10 | 11 | ## [IsLog10.c](IsLog10.c) 12 | 13 | Simple C program demonstrating multiple solutions to a question: "Write a function that checks whether an integer number is a power of 10." Contains a set of tests. See my blog post: [How to check if an integer number is a power of 10?](http://www.asawicki.info/news_1660_how_to_check_if_an_integer_number_is_a_power_of_10.html). 14 | 15 | ## [QueryPerformanceCounterTest.cpp](QueryPerformanceCounterTest.cpp) 16 | 17 | Simple C++ console program that tests how long it takes to call WinAPI function `QueryPerformanceCounter`. See my blog post: [When QueryPerformanceCounter call takes long time](http://asawicki.info/news_1667_when_queryperformancecounter_call_takes_long_time.html). 18 | 19 | ## [VulkanAfterCrash.h](VulkanAfterCrash.h) 20 | 21 | Simple, single-header, C++ library for Vulkan that simplifies writing 32-bit markers to a buffer that can be read after graphics driver crash and thus help you find out which specific draw call or other command caused the crash, pretty much like [NVIDIA Aftermath](https://developer.nvidia.com/nvidia-aftermath) library for Direct3D 11/12. See my blog post: [Debugging Vulkan driver crash - equivalent of NVIDIA Aftermath](http://asawicki.info/news_1677_debugging_vulkan_driver_crash_-_equivalent_of_nvidia_aftermath.html). 22 | 23 | ## [IncludeList.py](IncludeList.py) 24 | 25 | Simple Python script that parses given text file to find the list of files included by it using `#include ` or `#include "FileName"`, recursively. Supports `-I` parameter for additional include directories. Supports any programming language that uses C-like preprocessor, e.g. C, C++, HLSL, GLSL. 26 | 27 | Shortcomings: ► Doesn't parse comments or perform full preprocessing, so includes commented out using multiline comment `/* ... */` or macros like `#if 0` are still parsed. ► File names are case-sensitive, so files includes with different capitalization are treated as separate. 28 | -------------------------------------------------------------------------------- /VulkanAfterCrash.h: -------------------------------------------------------------------------------- 1 | /* 2 | VulkanAfterCrash.h 3 | 4 | Author: Adam Sawicki, http://asawicki.info, adam__REMOVE__@asawicki.info 5 | Version: 1.0.1, 2018-05-16 6 | License: MIT 7 | 8 | This is a simple, single-header, C++ library for Vulkan that simplifies writing 9 | 32-bit markers to a buffer that can be read after graphics driver crash and thus 10 | help you find out which specific draw call or other command caused the crash, 11 | pretty much like NVIDIA Aftermath library for Direct3D 11/12. 12 | 13 | How to use it: 14 | 15 | 1. In any CPP file where you want to use the library: 16 | #include "VulkanAfterCrash.h" 17 | 2. In exactly one CPP file, define following macro before that include: 18 | #define VULKAN_AFTER_CRASH_IMPLEMENTATION 19 | 3. Create object of type VkAfterCrash_Device, once for VkDevice. 20 | 4. Create one or more VkAfterCrash_Buffer objects. 21 | 5. While recording your commands to VkCommandBuffer, record also marker writes 22 | using function VkAfterCrash_CmdWriteMarker or 23 | VkAfterCrash_CmdWriteMarkerExtended. 24 | 6. If graphics driver crashes, you receive VK_ERROR_DEVICE_LOST from a Vulkan 25 | function like vkQueueSubmit. After it happened, inspect values under pData 26 | pointer returned by VkAfterCrash_CreateBuffer to see value of markers 27 | successfully written. 28 | 29 | See blog post: 30 | http://asawicki.info/news_1677_debugging_vulkan_driver_crash_-_equivalent_of_nvidia_aftermath.html 31 | Similar library for Direct3D 12: 32 | http://asawicki.info/news_1690_debugging_d3d12_driver_crash.html 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | Copyright 2018 Adam Sawicki 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy of 39 | this software and associated documentation files (the "Software"), to deal in 40 | the Software without restriction, including without limitation the rights to 41 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 42 | the Software, and to permit persons to whom the Software is furnished to do so, 43 | subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 50 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 51 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 52 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 53 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54 | */ 55 | 56 | #ifndef VULKAN_AFTER_CRASH_H 57 | #define VULKAN_AFTER_CRASH_H 58 | 59 | #ifdef __cplusplus 60 | extern "C" { 61 | #endif 62 | 63 | #include 64 | 65 | /* 66 | Represents main object of this library initialized for specific VkDevice. 67 | */ 68 | VK_DEFINE_HANDLE(VkAfterCrash_Device) 69 | 70 | /* 71 | Represent buffer in system memory that can hold a number of 32-bit markers. 72 | */ 73 | VK_DEFINE_HANDLE(VkAfterCrash_Buffer) 74 | 75 | typedef enum VkAfterCrash_DeviceCreateFlagBits { 76 | /* 77 | Use this flag if you found and enabled "VK_AMD_buffer_marker" device extension. 78 | It is required if you want to use function VkAfterCrash_CmdWriteMarkerExtended. 79 | */ 80 | VK_AFTER_CRASH_DEVICE_CREATE_USE_AMD_BUFFER_MARKER_BIT = 0x00000001, 81 | 82 | VK_AFTER_CRASH_DEVICE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF 83 | } VkAfterCrash_DeviceCreateFlagBits; 84 | typedef VkFlags VkAfterCrash_DeviceCreateFlags; 85 | 86 | typedef struct VkAfterCrash_DeviceCreateInfo 87 | { 88 | VkAfterCrash_DeviceCreateFlags flags; 89 | VkPhysicalDevice vkPhysicalDevice; 90 | VkDevice vkDevice; 91 | } VkAfterCrash_DeviceCreateInfo; 92 | 93 | VkResult VkAfterCrash_CreateDevice( 94 | const VkAfterCrash_DeviceCreateInfo* pCreateInfo, 95 | VkAfterCrash_Device* pDevice); 96 | 97 | void VkAfterCrash_DestroyDevice( 98 | VkAfterCrash_Device device); 99 | 100 | typedef struct VkAfterCrash_BufferCreateInfo 101 | { 102 | uint32_t markerCount; 103 | } VkAfterCrash_BufferCreateInfo; 104 | 105 | /* 106 | Creates and returns buffer object, as well as pointer to its data. This pointer 107 | will (hopefully) remain valid and preserve its content after graphics driver 108 | crash. 109 | */ 110 | VkResult VkAfterCrash_CreateBuffer( 111 | VkAfterCrash_Device device, 112 | const VkAfterCrash_BufferCreateInfo* pCreateInfo, 113 | VkAfterCrash_Buffer* pBuffer, 114 | uint32_t** pData); 115 | 116 | void VkAfterCrash_DestroyBuffer( 117 | VkAfterCrash_Buffer buffer); 118 | 119 | /* 120 | Records command to a Vulkan command buffer that will write 32-bit marker to 121 | specific place in specific buffer. 122 | 123 | It must be called outside of render pass. 124 | */ 125 | void VkAfterCrash_CmdWriteMarker( 126 | VkCommandBuffer vkCommandBuffer, 127 | VkAfterCrash_Buffer buffer, 128 | uint32_t markerIndex, 129 | uint32_t value); 130 | 131 | /* 132 | Records command to a Vulkan command buffer that will write 32-bit marker to 133 | specific place in specific buffer, after specific shader stage. 134 | 135 | It can be called inside or outside of render pass. 136 | 137 | It can be used only if VK_AFTER_CRASH_DEVICE_CREATE_USE_AMD_BUFFER_MARKER_BIT 138 | flag was used. 139 | */ 140 | void VkAfterCrash_CmdWriteMarkerExtended( 141 | VkCommandBuffer vkCommandBuffer, 142 | VkAfterCrash_Buffer buffer, 143 | uint32_t markerIndex, 144 | uint32_t value, 145 | VkPipelineStageFlagBits pipelineStage); 146 | 147 | #ifdef __cplusplus 148 | } 149 | #endif 150 | 151 | #endif // #ifndef VULKAN_AFTER_CRASH_H 152 | 153 | // For Visual Studio IntelliSense. 154 | #ifdef __INTELLISENSE__ 155 | #define VULKAN_AFTER_CRASH_IMPLEMENTATION 156 | #endif 157 | 158 | #ifdef VULKAN_AFTER_CRASH_IMPLEMENTATION 159 | #undef VULKAN_AFTER_CRASH_IMPLEMENTATION 160 | 161 | #include 162 | #include 163 | 164 | //////////////////////////////////////////////////////////////////////////////// 165 | // struct VkAfterCrash_Device_T 166 | 167 | struct VkAfterCrash_Device_T 168 | { 169 | public: 170 | VkAfterCrash_Device_T(const VkAfterCrash_DeviceCreateInfo& createInfo); 171 | VkResult Initialize(); 172 | ~VkAfterCrash_Device_T(); 173 | 174 | bool UseAmdBufferMarker() const { return (m_CreateInfo.flags & VK_AFTER_CRASH_DEVICE_CREATE_USE_AMD_BUFFER_MARKER_BIT) != 0; } 175 | VkDevice GetVkDevice() const { return m_CreateInfo.vkDevice; } 176 | PFN_vkCmdWriteBufferMarkerAMD GetVkCmdWriteBufferMarkerAMD() const { return m_vkCmdWriteBufferMarkerAMD; } 177 | bool FindMemoryTypeIndex(uint32_t memTypeBits, uint32_t* pMemTypeIndex) const; 178 | 179 | private: 180 | VkAfterCrash_DeviceCreateInfo m_CreateInfo; 181 | PFN_vkCmdWriteBufferMarkerAMD m_vkCmdWriteBufferMarkerAMD; 182 | }; 183 | 184 | VkAfterCrash_Device_T::VkAfterCrash_Device_T(const VkAfterCrash_DeviceCreateInfo& createInfo) : 185 | m_CreateInfo(createInfo) 186 | { 187 | } 188 | 189 | VkResult VkAfterCrash_Device_T::Initialize() 190 | { 191 | if(UseAmdBufferMarker()) 192 | { 193 | m_vkCmdWriteBufferMarkerAMD = (PFN_vkCmdWriteBufferMarkerAMD)vkGetDeviceProcAddr(GetVkDevice(), "vkCmdWriteBufferMarkerAMD"); 194 | if(m_vkCmdWriteBufferMarkerAMD == nullptr) 195 | return VK_ERROR_FEATURE_NOT_PRESENT; 196 | } 197 | 198 | return VK_SUCCESS; 199 | } 200 | 201 | VkAfterCrash_Device_T::~VkAfterCrash_Device_T() 202 | { 203 | } 204 | 205 | bool VkAfterCrash_Device_T::FindMemoryTypeIndex(uint32_t memTypeBits, uint32_t* pMemTypeIndex) const 206 | { 207 | VkPhysicalDeviceMemoryProperties memProps = {}; 208 | vkGetPhysicalDeviceMemoryProperties(m_CreateInfo.vkPhysicalDevice, &memProps); 209 | 210 | const uint32_t expectedFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 211 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 212 | for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i) 213 | { 214 | if(((1u << i) & memTypeBits) != 0 && 215 | (memProps.memoryTypes[i].propertyFlags & expectedFlags) == expectedFlags) 216 | { 217 | *pMemTypeIndex = i; 218 | return true; 219 | } 220 | } 221 | 222 | *pMemTypeIndex = UINT32_MAX; 223 | return false; 224 | } 225 | 226 | //////////////////////////////////////////////////////////////////////////////// 227 | // struct VkAfterCrash_Buffer_T 228 | 229 | struct VkAfterCrash_Buffer_T 230 | { 231 | public: 232 | VkAfterCrash_Buffer_T( 233 | VkAfterCrash_Device device, 234 | const VkAfterCrash_BufferCreateInfo& createInfo); 235 | VkResult Initialize(); 236 | ~VkAfterCrash_Buffer_T(); 237 | 238 | uint32_t* GetData() const { return m_Data; } 239 | 240 | void CmdWriteMarker( 241 | VkCommandBuffer vkCommandBuffer, 242 | uint32_t markerIndex, 243 | uint32_t value); 244 | void CmdWriteMarkerExtended( 245 | VkCommandBuffer vkCommandBuffer, 246 | uint32_t markerIndex, 247 | uint32_t value, 248 | VkPipelineStageFlagBits pipelineStage); 249 | 250 | private: 251 | VkAfterCrash_Device m_Device; 252 | VkAfterCrash_BufferCreateInfo m_CreateInfo; 253 | VkDeviceMemory m_VkMemory; 254 | uint32_t* m_Data; 255 | VkBuffer m_VkBuffer; 256 | }; 257 | 258 | VkAfterCrash_Buffer_T::VkAfterCrash_Buffer_T( 259 | VkAfterCrash_Device device, 260 | const VkAfterCrash_BufferCreateInfo& createInfo) : 261 | m_Device(device), 262 | m_CreateInfo(createInfo), 263 | m_VkMemory(VK_NULL_HANDLE), 264 | m_Data(nullptr), 265 | m_VkBuffer(VK_NULL_HANDLE) 266 | { 267 | } 268 | 269 | VkResult VkAfterCrash_Buffer_T::Initialize() 270 | { 271 | const VkDevice dev = m_Device->GetVkDevice(); 272 | 273 | VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; 274 | bufCreateInfo.size = m_CreateInfo.markerCount * sizeof(uint32_t); 275 | bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; 276 | 277 | VkResult res = vkCreateBuffer(dev, &bufCreateInfo, nullptr, &m_VkBuffer); 278 | if(res != VK_SUCCESS) 279 | return res; 280 | 281 | VkMemoryRequirements memReq = {}; 282 | vkGetBufferMemoryRequirements(dev, m_VkBuffer, &memReq); 283 | 284 | VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; 285 | allocInfo.allocationSize = memReq.size; 286 | bool ok = m_Device->FindMemoryTypeIndex(memReq.memoryTypeBits, &allocInfo.memoryTypeIndex); 287 | if(!ok) 288 | return VK_ERROR_FORMAT_NOT_SUPPORTED; 289 | res = vkAllocateMemory(dev, &allocInfo, nullptr, &m_VkMemory); 290 | if(res != VK_SUCCESS) 291 | return res; 292 | 293 | res = vkMapMemory(dev, m_VkMemory, 0, VK_WHOLE_SIZE, 0, (void**)&m_Data); 294 | if(res != VK_SUCCESS) 295 | return res; 296 | 297 | res = vkBindBufferMemory(dev, m_VkBuffer, m_VkMemory, 0); 298 | if(res != VK_SUCCESS) 299 | return res; 300 | 301 | return VK_SUCCESS; 302 | } 303 | 304 | VkAfterCrash_Buffer_T::~VkAfterCrash_Buffer_T() 305 | { 306 | const VkDevice dev = m_Device->GetVkDevice(); 307 | 308 | if(m_Data) 309 | vkUnmapMemory(dev, m_VkMemory); 310 | if(m_VkBuffer) 311 | vkDestroyBuffer(dev, m_VkBuffer, nullptr); 312 | if(m_VkMemory) 313 | vkFreeMemory(dev, m_VkMemory, nullptr); 314 | } 315 | 316 | void VkAfterCrash_Buffer_T::CmdWriteMarker( 317 | VkCommandBuffer vkCommandBuffer, 318 | uint32_t markerIndex, 319 | uint32_t value) 320 | { 321 | vkCmdFillBuffer( 322 | vkCommandBuffer, 323 | m_VkBuffer, 324 | markerIndex * sizeof(uint32_t), 325 | sizeof(uint32_t), value); 326 | } 327 | 328 | void VkAfterCrash_Buffer_T::CmdWriteMarkerExtended( 329 | VkCommandBuffer vkCommandBuffer, 330 | uint32_t markerIndex, 331 | uint32_t value, 332 | VkPipelineStageFlagBits pipelineStage) 333 | { 334 | assert(m_Device->UseAmdBufferMarker()); 335 | m_Device->GetVkCmdWriteBufferMarkerAMD()( 336 | vkCommandBuffer, 337 | pipelineStage, 338 | m_VkBuffer, 339 | markerIndex * sizeof(uint32_t), 340 | value); 341 | } 342 | 343 | //////////////////////////////////////////////////////////////////////////////// 344 | // Global functions 345 | 346 | VkResult VkAfterCrash_CreateDevice( 347 | const VkAfterCrash_DeviceCreateInfo* pCreateInfo, 348 | VkAfterCrash_Device* pDevice) 349 | { 350 | assert(pCreateInfo && pDevice); 351 | *pDevice = new VkAfterCrash_Device_T(*pCreateInfo); 352 | VkResult res = (*pDevice)->Initialize(); 353 | if(res != VK_SUCCESS) 354 | { 355 | delete *pDevice; 356 | *pDevice = nullptr; 357 | } 358 | return res; 359 | } 360 | 361 | void VkAfterCrash_DestroyDevice( 362 | VkAfterCrash_Device device) 363 | { 364 | delete device; 365 | } 366 | 367 | VkResult VkAfterCrash_CreateBuffer( 368 | VkAfterCrash_Device device, 369 | const VkAfterCrash_BufferCreateInfo* pCreateInfo, 370 | VkAfterCrash_Buffer* pBuffer, 371 | uint32_t** pData) 372 | { 373 | assert(device && pCreateInfo && pBuffer && pData); 374 | *pBuffer = new VkAfterCrash_Buffer_T(device, *pCreateInfo); 375 | VkResult res = (*pBuffer)->Initialize(); 376 | if(res == VK_SUCCESS) 377 | *pData = (*pBuffer)->GetData(); 378 | else 379 | { 380 | delete *pBuffer; 381 | *pBuffer = nullptr; 382 | *pData = nullptr; 383 | } 384 | return res; 385 | } 386 | 387 | void VkAfterCrash_DestroyBuffer( 388 | VkAfterCrash_Buffer buffer) 389 | { 390 | delete buffer; 391 | } 392 | 393 | void VkAfterCrash_CmdWriteMarker( 394 | VkCommandBuffer vkCommandBuffer, 395 | VkAfterCrash_Buffer buffer, 396 | uint32_t markerIndex, 397 | uint32_t value) 398 | { 399 | assert(vkCommandBuffer && buffer); 400 | buffer->CmdWriteMarker(vkCommandBuffer, markerIndex, value); 401 | } 402 | 403 | void VkAfterCrash_CmdWriteMarkerExtended( 404 | VkCommandBuffer vkCommandBuffer, 405 | VkAfterCrash_Buffer buffer, 406 | uint32_t markerIndex, 407 | uint32_t value, 408 | VkPipelineStageFlagBits pipelineStage) 409 | { 410 | assert(vkCommandBuffer && buffer); 411 | buffer->CmdWriteMarkerExtended(vkCommandBuffer, markerIndex, value, pipelineStage); 412 | } 413 | 414 | #endif // #ifdef VULKAN_AFTER_CRASH_IMPLEMENTATION 415 | -------------------------------------------------------------------------------- /fp8_tables.py: -------------------------------------------------------------------------------- 1 | # FP8 Tables 2 | # 3 | # A script that generates HTML code with tables visualizing all the possible 4 | # values of 8-bit floating-point (FP8) data types used in machine learning. 5 | # Developed to generate tables for the article: 6 | # 7 | # https://asawicki.info/articles/fp8_tables.php 8 | # 9 | # Author: Adam Sawicki, https://asawicki.info, adam__REMOVE__@asawicki.info 10 | # Version: 1.0 11 | # License: Public Domain 12 | 13 | import math 14 | from enum import Enum 15 | 16 | class FP8Type(Enum): 17 | FLOAT8E4M3FN = 1 18 | FLOAT8E4M3FNUZ = 2 19 | FLOAT8E5M2 = 3 20 | FLOAT8E5M2FNUZ = 4 21 | 22 | html_begin = """ 23 | 24 | 25 | 26 | 27 | FP8 Tables 28 | 53 | 54 | 55 | """ 56 | html_end = """ 57 | 58 | """ 59 | 60 | BKG_COLOR_ZERO = '#ff4' 61 | BKG_COLOR_NAN = '#04eff8' 62 | BKG_COLOR_POS_INF = '#4f4' 63 | BKG_COLOR_NEG_INF = '#f44' 64 | 65 | BKG_COLOR_BASE = (255/255, 235/255, 132/255) 66 | BKG_COLOR_MAX_POS = (99/255, 190/255, 123/255) 67 | BKG_COLOR_MAX_NEG = (248/255, 105/255, 107/255) 68 | 69 | FONT_COLOR_SIGN = '#c00' 70 | FONT_COLOR_EXPONENT = '#080' 71 | FONT_COLOR_MANTISSA = '#aaa' 72 | 73 | def interpolate_color(color_tuple_a, color_tuple_b, t): 74 | a_linear = (color_tuple_a[0]**2.2, color_tuple_a[1]**2.2, color_tuple_a[2]**2.2) 75 | b_linear = (color_tuple_b[0]**2.2, color_tuple_b[1]**2.2, color_tuple_b[2]**2.2) 76 | return ((a_linear[0] + (b_linear[0] - a_linear[0]) * t) ** (1/2.2), 77 | (a_linear[1] + (b_linear[1] - a_linear[1]) * t) ** (1/2.2), 78 | (a_linear[2] + (b_linear[2] - a_linear[2]) * t) ** (1/2.2)) 79 | 80 | def interpolate_format_color(color_tuple_a, color_tuple_b, t): 81 | c = interpolate_color(color_tuple_a, color_tuple_b, t) 82 | r = min(max(0, math.floor(c[0] * 255)), 255) 83 | g = min(max(0, math.floor(c[1] * 255)), 255) 84 | b = min(max(0, math.floor(c[2] * 255)), 255) 85 | r = hex(r)[2:].zfill(2) 86 | g = hex(g)[2:].zfill(2) 87 | b = hex(b)[2:].zfill(2) 88 | return f'#{r}{g}{b}' 89 | 90 | def format_color(color_tuple): 91 | r = min(max(0, math.floor(color_tuple[0] * 255)), 255) 92 | g = min(max(0, math.floor(color_tuple[1] * 255)), 255) 93 | b = min(max(0, math.floor(color_tuple[2] * 255)), 255) 94 | r = hex(r)[2:].zfill(2) 95 | g = hex(g)[2:].zfill(2) 96 | b = hex(b)[2:].zfill(2) 97 | return f'#{r}{g}{b}' 98 | 99 | LEGEND = "" + \ 100 | f"
+0Saturated yellow - zero\n" + \ 101 | f"
0.01562Yellow - small numbers, close to zero\n" + \ 102 | f"
448Green - larger positive numbers\n" + \ 103 | f"
-448Red - larger negative numbers\n" + \ 104 | f"
0.001953Gray & italic - denormialized number (no implicit \"1.\")\n" + \ 105 | f"
12Blue & bold - subsequent integer numbers (smaller numbers support fractions, larger ones start jumping every 2, 4, 8...)\n" + \ 106 | f"
NaNTeal - not a number (NaN)\n" + \ 107 | f"
+INFSaturated green - positive infinity\n" + \ 108 | f"
-INFSaturated red - negative infinity\n" + \ 109 | "
\n" 110 | 111 | CAPS = "\n" + \ 112 | "\n" + \ 119 | "0\n" + \ 125 | f"0\n" + \ 130 | f"
 FLOAT8E4M3FNFLOAT8E4M3FNUZFLOAT8E5M2FLOAT8E5M2FNUZ\n" + \ 113 | "
Minimum positive subnormal0.0019530.00097660.00001530.0000076\n" + \ 114 | "
Minimum positive normal0.015620.0078120.0000610.0000305\n" + \ 115 | "
Next value after 11.1251.1251.251.25\n" + \ 116 | "
Maximum integer represented exactly161688\n" + \ 117 | "
Maximum4482405734457344\n" + \ 118 | "
0\n" + \ 120 | f"S 0000 000\n" + \ 121 | f"0 0000 000\n" + \ 122 | f"S 00000 00\n" + \ 123 | f"0 00000 00\n" + \ 124 | f"
NaNS 1111 111\n" + \ 126 | f"1 0000 000\n" + \ 127 | f"S 11111 MM\n" + \ 128 | f"1 00000 00\n" + \ 129 | f"
INF \n" + \ 131 | f" \n" + \ 132 | f"S 11111 00\n" + \ 133 | f" \n" + \ 134 | "
\n" 135 | 136 | 137 | def format_value(value: float) -> str: 138 | if value < 0.001: 139 | s = f'{value:.7f}' 140 | elif value < 0.01: 141 | s = f'{value:.6f}' 142 | elif value < 0.1: 143 | s = f'{value:.5f}' 144 | elif value < 1: 145 | s = f'{value:.4f}' 146 | else: 147 | s = f'{value}' 148 | # When decimal number is present, remove trailing zeros, like "2.5000". 149 | if '.' in s: 150 | while s.endswith('0'): 151 | s = s[:len(s)-1] 152 | # Remove trailing dot, like "2.". 153 | if s.endswith('.'): 154 | s = s[:len(s) - 1] 155 | return s 156 | 157 | def is_ordinal(type: FP8Type, value: float) -> bool: 158 | if type == FP8Type.FLOAT8E4M3FN or type == FP8Type.FLOAT8E4M3FNUZ: 159 | return value >= 8 and value <= 16 160 | else: # E5M2 161 | return value >= 4 and value <= 8 162 | 163 | def write_cell(file, type: FP8Type, byte: int): 164 | sign = byte >> 7 165 | if type == FP8Type.FLOAT8E5M2 or type == FP8Type.FLOAT8E5M2FNUZ: 166 | exponent = (byte >> 2) & 0b11111 167 | exponent_all_ones = exponent == 0b11111 168 | exponent_max = 0b11111 169 | mantissa = byte & 0b11 170 | mantissa_all_ones = mantissa == 0xb11 171 | else: # E4M3. 172 | exponent = (byte >> 3) & 0b1111 173 | exponent_all_ones = exponent == 0b1111 174 | exponent_max = 0b1111 175 | mantissa = byte & 0b111 176 | mantissa_all_ones = mantissa == 0b111 177 | 178 | if exponent == 0 and mantissa == 0: 179 | if type == FP8Type.FLOAT8E4M3FN or type == FP8Type.FLOAT8E5M2: 180 | file.write(f'+0' if sign == 0 else f'-0') 181 | else: # UZ types. 182 | file.write(f'0' if sign == 0 else f'NaN') 183 | return 184 | elif exponent_all_ones: 185 | if mantissa == 0: 186 | if type == FP8Type.FLOAT8E5M2: 187 | file.write(f'+INF' if sign == 0 else f'-INF') 188 | return 189 | elif mantissa_all_ones: 190 | if type == FP8Type.FLOAT8E4M3FN or type == FP8Type.FLOAT8E5M2: 191 | file.write(f'+NaN' if sign == 0 else f'-NaN') 192 | return 193 | else: 194 | if type == FP8Type.FLOAT8E5M2: 195 | file.write(f'+NaN' if sign == 0 else f'-NaN') 196 | return 197 | 198 | class_html = '' 199 | if exponent != 0: 200 | if type == FP8Type.FLOAT8E4M3FN: 201 | value = 2 ** (exponent - 7) * (1 + mantissa * (2 ** -3)) 202 | elif type == FP8Type.FLOAT8E5M2: 203 | value = 2 ** (exponent - 15) * (1 + mantissa * (2 ** -2)) 204 | elif type == FP8Type.FLOAT8E4M3FNUZ: 205 | value = 2 ** (exponent - 8) * (1 + mantissa * (2 ** -3)) 206 | else: # type == FP8Type.FLOAT8E5M2FNUZ 207 | value = 2 ** (exponent - 16) * (1 + mantissa * (2 ** -2)) 208 | if is_ordinal(type, value): 209 | class_html = ' class="ordinal"' 210 | else: 211 | if type == FP8Type.FLOAT8E4M3FN: 212 | value = 2 ** (-6) * mantissa * (2 ** -3) 213 | elif type == FP8Type.FLOAT8E5M2: 214 | value = 2 ** (-14) * mantissa * (2 ** -2) 215 | elif type == FP8Type.FLOAT8E4M3FNUZ: 216 | value = 2 ** (-7) * mantissa * (2 ** -3) 217 | else: # type == FP8Type.FLOAT8E5M2FNUZ 218 | value = 2 ** (-15) * mantissa * (2 ** -2) 219 | class_html = ' class="denorm"' 220 | 221 | sign_char = '' if sign == 0 else '-' 222 | formatted_value = format_value(value) 223 | #bkg_color = '#cfc' if sign == 0 else '#fcc' 224 | if sign == 0: # Positive number. 225 | bkg_color = interpolate_format_color(BKG_COLOR_BASE, BKG_COLOR_MAX_POS, exponent / exponent_max) 226 | else: # Negative number. 227 | bkg_color = interpolate_format_color(BKG_COLOR_BASE, BKG_COLOR_MAX_NEG, exponent / exponent_max) 228 | file.write(f'{sign_char}{formatted_value}') 229 | 230 | def write_for_type(file, type: FP8Type): 231 | file.write("\n") 232 | # Header row - hexadecimal. 233 | file.write("
  \n") 234 | for x in range(16): 235 | text = hex(x)[2:].upper() 236 | file.write(f'-{text}') 237 | file.write("\n") 238 | # Header row - binary. 239 | file.write("
  \n") 240 | for x in range(16): 241 | text = bin(x)[2:].zfill(4) 242 | if type == FP8Type.FLOAT8E5M2 or type == FP8Type.FLOAT8E5M2FNUZ: 243 | file.write(f'-{text[0:2]}{text[2:]}') 244 | else: # E4M3 245 | file.write(f'-{text[0]}{text[1:]}') 246 | file.write("\n") 247 | # Data rows 248 | for y in range(16): 249 | text_hex = hex(y)[2:].upper() 250 | text_bin = bin(y)[2:].zfill(4) 251 | file.write(f"
{text_hex}-{text_bin[0]}{text_bin[1:]}-\n") 252 | for x in range(16): 253 | write_cell(file, type, x | y << 4) 254 | file.write("\n") 255 | 256 | file.write("
\n") 257 | 258 | def main(): 259 | with open('fp8_tables.html', 'w') as file: 260 | file.write(html_begin) 261 | file.write("

FLOAT8E4M3FN

\n") 262 | write_for_type(file, FP8Type.FLOAT8E4M3FN) 263 | file.write("

FLOAT8E4M3FNUZ

\n") 264 | write_for_type(file, FP8Type.FLOAT8E4M3FNUZ) 265 | file.write("

FLOAT8E5M2

\n") 266 | write_for_type(file, FP8Type.FLOAT8E5M2) 267 | file.write("

FLOAT8E5M2FNUZ

\n") 268 | write_for_type(file, FP8Type.FLOAT8E5M2FNUZ) 269 | file.write("

Legend

\n") 270 | file.write(LEGEND) 271 | file.write("

Capabilities and special values

\n") 272 | file.write(CAPS) 273 | file.write(html_end) 274 | 275 | if __name__ == '__main__': 276 | main() 277 | --------------------------------------------------------------------------------