Created
October 8, 2024 04:01
-
-
Save namandixit/8022b5db34871fd75cc9c8bf95ab154b to your computer and use it in GitHub Desktop.
Nimajjana's old partially-written D3D12 renderer, for future reference (programmed by following this tutorial: https://www.3dgep.com/learning-directx-12-1/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Creator: Naman Dixit | |
* Notice: © Copyright 2023 Naman Dixit | |
*/ | |
pragma_clang("clang diagnostic push") | |
pragma_clang("clang diagnostic ignored \"-Wnonportable-system-include-path\"") | |
pragma_clang("clang diagnostic ignored \"-Wmicrosoft-enum-value\"") | |
pragma_clang("clang diagnostic ignored \"-Wreserved-identifier\"") | |
pragma_clang("clang diagnostic ignored \"-Wnon-virtual-dtor\"") | |
pragma_clang("clang diagnostic ignored \"-Wnested-anon-types\"") | |
#include "thirdparty/AgilitySDK/d3d12.h" | |
pragma_clang("clang diagnostic pop") | |
#include <dxgi1_6.h> // NOTE(naman): Windows 10 guarantees DXGI 1.4, so create that interface even though we include the latest header (to get access to 1.6, if needed) | |
#include <dxgidebug.h> | |
#include <DirectXMath.h> | |
#include <wrl.h> | |
using namespace Microsoft::WRL; | |
pragma_clang("clang diagnostic push") | |
pragma_clang("clang diagnostic ignored \"-Wmissing-variable-declarations\"") | |
extern "C" { __declspec(dllexport) extern const DWORD NvOptimusEnablement; } | |
extern "C" { __declspec(dllexport) extern const DWORD AmdPowerXpressRequestHighPerformance; } | |
extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = D3D12_SDK_VERSION; } | |
extern "C" { __declspec(dllexport) extern const char *D3D12SDKPath = u8".\\d3d12\\"; } | |
pragma_clang("clang diagnostic pop") | |
#include "common/templates.hpp" | |
global_variable constexpr Uint SWAPCHAIN_BACKBUFFER_COUNT = 3; | |
struct GPU { | |
ComPtr<IDXGIAdapter3> adapter; | |
const Char *name; | |
Size video_memory; | |
}; | |
struct D3D12_Command_Queue { | |
struct Command_Allocator { | |
// A Command Allocator allows you to create command lists where you can define the functions you want the GPU to execute for that allocator. | |
ComPtr<ID3D12CommandAllocator> allocator; | |
Uint64 fence_value; | |
}; | |
D3D12_COMMAND_LIST_TYPE list_type; | |
ComPtr<ID3D12Device> device; | |
// A Command Queue allows you to submit groups of commands, known as command lists, together to execute in order, thus allowing a GPU to stay busy and optimize its execution speed. | |
ComPtr<ID3D12CommandQueue> command_queue; | |
ComPtr<ID3D12Fence> fence; | |
Uint64 fence_value = 0; | |
HANDLE fence_event; | |
Queue<Command_Allocator, &GLOBAL_heap_memory_allocator> allocators; | |
// A Command List is an object that can encode a number of commands to be executed by the GPU, be it configuring barriers, setting root signatures, etc. | |
Queue<ComPtr<ID3D12GraphicsCommandList>, &GLOBAL_heap_memory_allocator> lists; | |
}; | |
struct D3D12_Backbuffer_RTV { | |
ComPtr<ID3D12Resource> backbuffer; | |
Uint64 fence_value = 0; | |
}; | |
struct D3D12_Backbuffer { | |
/* Render Target View (Colour Information) */ | |
// Descriptor heaps are objects that handle memory allocation required for storing the descriptions of objects that shaders reference. | |
ComPtr<ID3D12DescriptorHeap> rtv_descriptor_heap; | |
UINT rtv_descriptor_size; | |
Vector<D3D12_Backbuffer_RTV, &GLOBAL_heap_memory_allocator> render_target_views; | |
UINT rtv_index_current; | |
D3D12_RESOURCE_STATES rtv_current_state = D3D12_RESOURCE_STATE_COMMON; | |
/* Depth-Stencil buffer */ | |
ComPtr<ID3D12DescriptorHeap> ds_descriptor_heap; | |
ComPtr<ID3D12Resource> depth_stencil_buffer; | |
}; | |
typedef struct D3D12_Mesh { | |
ComPtr<ID3D12Resource> vertex_buffer; | |
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view; | |
ComPtr<ID3D12Resource> index_buffer; | |
D3D12_INDEX_BUFFER_VIEW index_buffer_view; | |
} D3D12_Mesh; | |
struct D3D12_State { | |
/* Config - User provided *************************/ | |
D3D12_Config config; | |
/* Config - Auto detected *************************/ | |
// "Tearing" support, needed to disable vsync, or to support variable refresh rate. | |
BOOL tearing; // Type has to be BOOL, due to DXGI API constraint | |
/* State Variables : Window size independent ******/ | |
// Factories are the entry point to the DirectX 12 API. They can be used to find adapters which can then create devices and other important data structures. | |
ComPtr<IDXGIFactory4> dxgi_factory_4; | |
Vector<GPU, &GLOBAL_heap_memory_allocator> gpus; | |
Size gpu_current; | |
// A Device allows you to create key data structures such as command queues, allocators, resources like pipelines, buffers, buffer views, shader blobs, heaps, and synchronization primitives. | |
ComPtr<ID3D12Device> device; | |
D3D12_Command_Queue graphics_queue; | |
D3D12_Command_Queue compute_queue; | |
D3D12_Command_Queue copy_queue; | |
/* State Variables : Window size dependent ********/ | |
// Swapchains handle swapping and allocating back buffers to display what you're rendering to a given window. | |
ComPtr<IDXGISwapChain3> swapchain_3; | |
D3D12_Backbuffer swapchain_backbuffer; | |
// Logic | |
Uint64 frame_number = 0; | |
Bool initialized = false; | |
}; | |
struct D3D12_Draw_Commands { | |
ComPtr<ID3D12GraphicsCommandList> list; | |
}; | |
global_variable void (*GLOBAL_jotter_function) (Jot_Channel, Jot_Level, const Char *, Sint, const Char *, const Char *, ...); | |
#define jot(lvl, ...) GLOBAL_jotter_function(Jot_Channel_RENDERER, Jot_Level_##lvl, __FILE__, __LINE__, __func__, __VA_ARGS__) | |
#define jotHRESULT(lvl, ...) JotterHRESULT(hr, Jot_Level_##lvl, __FILE__, __LINE__, __func__, __VA_ARGS__) | |
#define jotLastError(lvl, ...) JotterLastError(Jot_Level_##lvl, __FILE__, __LINE__, __func__, __VA_ARGS__) | |
internal_function | |
void DebugMessageCallback (D3D12_MESSAGE_CATEGORY category, D3D12_MESSAGE_SEVERITY severity, D3D12_MESSAGE_ID id, LPCSTR description, void* userdata) | |
{ | |
D3D12_State *d3d = static_cast<D3D12_State*>(userdata); | |
const Char *category_str = nullptr; | |
switch (category) { | |
case D3D12_MESSAGE_CATEGORY_APPLICATION_DEFINED: category_str = "Application"; break; | |
case D3D12_MESSAGE_CATEGORY_MISCELLANEOUS: category_str = "Miscellaneous"; break; | |
case D3D12_MESSAGE_CATEGORY_INITIALIZATION: category_str = "Initialization"; break; | |
case D3D12_MESSAGE_CATEGORY_CLEANUP: category_str = "Cleanup"; break; | |
case D3D12_MESSAGE_CATEGORY_COMPILATION: category_str = "Compilation"; break; | |
case D3D12_MESSAGE_CATEGORY_STATE_CREATION: category_str = "State Creation"; break; | |
case D3D12_MESSAGE_CATEGORY_STATE_SETTING: category_str = "State Setting"; break; | |
case D3D12_MESSAGE_CATEGORY_STATE_GETTING: category_str = "State Getting"; break; | |
case D3D12_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: category_str = "Resource Manipulation"; break; | |
case D3D12_MESSAGE_CATEGORY_EXECUTION: category_str = "Execution"; break; | |
case D3D12_MESSAGE_CATEGORY_SHADER: category_str = "Shader"; break; | |
} | |
const char *severity_str = nullptr; | |
switch (severity) { | |
case D3D12_MESSAGE_SEVERITY_CORRUPTION: severity_str = "Corruption"; break; | |
case D3D12_MESSAGE_SEVERITY_ERROR: severity_str = "Error"; break; | |
case D3D12_MESSAGE_SEVERITY_WARNING: severity_str = "Warning"; break; | |
case D3D12_MESSAGE_SEVERITY_INFO: severity_str = "Information"; break; | |
case D3D12_MESSAGE_SEVERITY_MESSAGE: severity_str = "Message"; break; | |
} | |
GLOBAL_jotter_function(Jot_Channel_RENDERER, Jot_Level_WARN, | |
__FILE__, 0, "<callback>", | |
"(%s from %s: id %d, frame %zu) %s", severity_str, category_str, static_cast<int>(id), d3d->frame_number, description); | |
return; | |
} | |
internal_function | |
void JotterHRESULT(::HRESULT hr, | |
Jot_Level level, | |
const Char *file, Sint line, const Char *func, | |
const Char *message) | |
{ | |
wchar_t buffer[4096]; | |
DWORD result = ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |
nullptr, static_cast<DWORD>(hr), 0, buffer, elemin(buffer)-1, nullptr); | |
if (result) { | |
Char *cbuffer = winUTF16ToUTF8(buffer, nullptr); | |
GLOBAL_jotter_function(Jot_Channel_RENDERER, level, file, line, func, "%s (%#1lx = %s)", message, hr, cbuffer); | |
memDealloc(&GLOBAL_heap_memory_allocator, cbuffer); | |
} | |
return; | |
} | |
internal_function | |
void JotterLastError(Jot_Level level, | |
const Char *file, Sint line, const Char *func, | |
const Char *message) | |
{ | |
DWORD err = GetLastError(); | |
wchar_t buffer[4096]; | |
DWORD result = ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |
nullptr, err, 0, buffer, elemin(buffer)-1, nullptr); | |
if (result) { | |
Char *cbuffer = winUTF16ToUTF8(buffer, nullptr); | |
GLOBAL_jotter_function(Jot_Channel_RENDERER, level, file, line, func, "%s (%#1ld = %s)", message, err, cbuffer); | |
memDealloc(&GLOBAL_heap_memory_allocator, cbuffer); | |
} | |
return; | |
} | |
internal_function | |
Bool SignalQueueOnCompletionWithValue (D3D12_Command_Queue *cq, Uint64 *fence_value) | |
{ | |
(*fence_value)++; | |
HRESULT hr = cq->command_queue->Signal(cq->fence.Get(), *fence_value); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Couldn't not signal the fence"); | |
return false; | |
} | |
return true; | |
} | |
internal_function | |
Bool HasQueueBeenSignalledWithValue (D3D12_Command_Queue *cq, Uint64 fence_value) | |
{ | |
Bool result = cq->fence->GetCompletedValue() >= fence_value; | |
return result; | |
} | |
internal_function | |
Bool WaitForQueueToBeSignalledWithValue (D3D12_Command_Queue *cq, Uint64 fence_value) | |
{ | |
if (HasQueueBeenSignalledWithValue(cq, fence_value) == false) { | |
HRESULT hr = cq->fence->SetEventOnCompletion(fence_value, cq->fence_event); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "SetEventOnCompletion failed"); | |
return false; | |
} | |
::WaitForSingleObject(cq->fence_event, INFINITE); | |
} | |
return true; | |
} | |
internal_function | |
Bool CommandQueueCreate (D3D12_Command_Queue *cq, ComPtr<ID3D12Device> device, D3D12_COMMAND_LIST_TYPE type) | |
{ | |
HRESULT hr; | |
cq->device = device; | |
cq->list_type = type; | |
{ // Command Queue | |
// NOTE(naman): Command Queue is an abstraction over the hardware resource on which a list | |
// of draw/state commands (Command List) can be executed. Direct Command Queues execute | |
// Direct Command Lists, which are capable of graphics, compute and copy work, and can | |
// contain other Command Lists inside them (called Bundle Command List). | |
D3D12_COMMAND_QUEUE_DESC description = {}; | |
description.Type = type; | |
description.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; | |
description.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; | |
description.NodeMask = 0; | |
hr = cq->device->CreateCommandQueue(&description, IID_PPV_ARGS(&cq->command_queue)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not create ID3D12CommandQueue"); | |
return false; | |
} | |
} | |
{ // Fence | |
hr = cq->device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&cq->fence)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not create ID3D12Fence"); | |
return false; | |
} | |
} | |
{ | |
cq->fence_event = ::CreateEventW(nullptr, FALSE, FALSE, nullptr); | |
if (cq->fence_event == nullptr) { | |
jotLastError(QUIT, "Could not create Fence Event: CreateEventW failed"); | |
return false; | |
} | |
} | |
return true; | |
} | |
[[maybe_unused]] | |
internal_function | |
Bool CommandListStartRecording (D3D12_Command_Queue *cq, ComPtr<ID3D12GraphicsCommandList> *cl_out) | |
{ | |
HRESULT hr; | |
ComPtr<ID3D12CommandAllocator> ca; | |
ComPtr<ID3D12GraphicsCommandList > cl; | |
if (cq->allocators.IsNotEmpty() && HasQueueBeenSignalledWithValue(cq, cq->allocators.PeekFront().fence_value)) { | |
ca = cq->allocators.PopFront().allocator; | |
hr = ca->Reset(); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CommandAllocator->Reset failed"); | |
return false; | |
} | |
} else { | |
hr = cq->device->CreateCommandAllocator(cq->list_type, IID_PPV_ARGS(&ca)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CreateCommandAllocator failed"); | |
return false; | |
} | |
} | |
if (cq->lists.IsNotEmpty()) { | |
cl = cq->lists.PopFront(); | |
hr = cl->Reset(ca.Get(), nullptr); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CommandList->Reset failed"); | |
return false; | |
} | |
} else { | |
hr = cq->device->CreateCommandList(0, cq->list_type, ca.Get(), nullptr, IID_PPV_ARGS(&cl)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CreateCommandList failed"); | |
return false; | |
} | |
} | |
hr = cl->SetPrivateDataInterface(__uuidof(ID3D12CommandAllocator), ca.Get()); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "SetPrivateDataInterface failed"); | |
return false; | |
} | |
*cl_out = cl; | |
return true; | |
} | |
[[maybe_unused]] | |
internal_function | |
Bool CommandListExecuteRecording (D3D12_Command_Queue *cq, ComPtr<ID3D12GraphicsCommandList> cl) | |
{ | |
HRESULT hr; | |
hr = cl->Close(); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not close command list"); | |
return false; | |
} | |
ID3D12CommandAllocator* ca; | |
UINT data_size = sizeof(ca); | |
hr = cl->GetPrivateData(__uuidof(ID3D12CommandAllocator), &data_size, &ca); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Command List's GetPrivateData failed"); | |
return false; | |
} | |
ID3D12CommandList* const cls[] = { | |
cl.Get() | |
}; | |
cq->command_queue->ExecuteCommandLists(elemin(cls), cls); | |
if (SignalQueueOnCompletionWithValue(cq, &cq->fence_value) == false) { | |
jot(QUIT, "Couldn't signal the fence while executing command list"); | |
return false; | |
} | |
D3D12_Command_Queue::Command_Allocator cal = { ca, cq->fence_value }; | |
cq->allocators.PushBack(cal); | |
cq->lists.PushBack(cl); | |
ca->Release(); | |
return true; | |
} | |
internal_function | |
void QueueFlush (D3D12_Command_Queue *cq) | |
{ | |
SignalQueueOnCompletionWithValue(cq, &cq->fence_value); | |
WaitForQueueToBeSignalledWithValue(cq, cq->fence_value); | |
} | |
internal_function | |
Bool UpdateWindowBackbuffer (D3D12_State *d3d, UINT client_width, UINT client_height) | |
{ | |
HRESULT hr; | |
{ // Render Target View | |
/* NOTE(naman): The Output-Merger (OM) stage combines the various types of | |
* output data (pixel shader output values, depth values, and | |
* stencil information) together with the contents of the | |
* currently bound render targets to produce the final pipeline | |
* result. The render target view describes the resource that | |
* receives the final colour computed by the pixel shader stage. | |
*/ | |
d3d->swapchain_backbuffer.rtv_descriptor_size = d3d->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); | |
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = d3d->swapchain_backbuffer.rtv_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); | |
for (Uint i = 0; i < SWAPCHAIN_BACKBUFFER_COUNT; i++) { | |
ComPtr<ID3D12Resource> back_buffer; | |
hr = d3d->swapchain_3->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not create back buffer from swapchain"); | |
return false; | |
} | |
d3d->device->CreateRenderTargetView(back_buffer.Get(), nullptr, rtv_handle); | |
D3D12_Backbuffer_RTV rtv; | |
rtv.backbuffer = back_buffer; | |
d3d->swapchain_backbuffer.render_target_views.Add(rtv); | |
rtv_handle.ptr += d3d->swapchain_backbuffer.rtv_descriptor_size; | |
} | |
} | |
{ // Depth Stencil Buffer | |
QueueFlush(&d3d->graphics_queue); | |
QueueFlush(&d3d->compute_queue); | |
QueueFlush(&d3d->copy_queue); | |
D3D12_CLEAR_VALUE clear_value = {}; | |
clear_value.Format = DXGI_FORMAT_D32_FLOAT; | |
clear_value.DepthStencil = { 1.0f, 0 }; | |
D3D12_HEAP_PROPERTIES heap_properties = {}; | |
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT; | |
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; | |
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; | |
heap_properties.CreationNodeMask = 1; | |
heap_properties.VisibleNodeMask = 1; | |
D3D12_RESOURCE_DESC buffer_descriptor = {}; | |
buffer_descriptor.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; | |
buffer_descriptor.Alignment = 0; | |
buffer_descriptor.Width = static_cast<UINT64>(client_width); | |
buffer_descriptor.Height = static_cast<UINT>(client_height); | |
buffer_descriptor.DepthOrArraySize = 1; | |
buffer_descriptor.MipLevels = 0; | |
buffer_descriptor.Format = DXGI_FORMAT_D32_FLOAT; | |
buffer_descriptor.SampleDesc.Count = 1; | |
buffer_descriptor.SampleDesc.Quality = 0; | |
buffer_descriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; | |
buffer_descriptor.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; | |
hr = d3d->device->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &buffer_descriptor, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, | |
IID_PPV_ARGS(&d3d->swapchain_backbuffer.depth_stencil_buffer)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not create depth buffer for swapchain"); | |
return false; | |
} | |
D3D12_DEPTH_STENCIL_VIEW_DESC dsv = {}; | |
dsv.Format = DXGI_FORMAT_D32_FLOAT; | |
dsv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; | |
dsv.Texture2D.MipSlice = 0; | |
dsv.Flags = D3D12_DSV_FLAG_NONE; | |
d3d->device->CreateDepthStencilView(d3d->swapchain_backbuffer.depth_stencil_buffer.Get(), &dsv, | |
d3d->swapchain_backbuffer.ds_descriptor_heap->GetCPUDescriptorHandleForHeapStart()); | |
} | |
return true; | |
} | |
D3D12_State* d3d12Initialize (HWND window, Uint32 client_width, Uint32 client_height, | |
D3D12_Config config, | |
void (*jotter) (Jot_Channel, Jot_Level, const Char *, Sint, const Char *, const Char *, ...)) | |
{ | |
GLOBAL_jotter_function = jotter; | |
{ | |
auto d3d12core_module = GetModuleHandleW(L"d3d12core.dll"); | |
UINT *loaded_version = reinterpret_cast<UINT*>(GetProcAddress(d3d12core_module, "D3D12SDKVersion")); | |
jot(INFO, "AgilitySDK Compiled Version: %u", D3D12SDKVersion); | |
if (loaded_version) { | |
jot(INFO, "AgilitySDK Loaded Version: %u", *loaded_version); | |
} | |
} | |
D3D12_State *d3d = new (memAlloc(&GLOBAL_heap_memory_allocator, sizeof(*d3d))) D3D12_State(); | |
d3d->config = config; | |
HRESULT hr; | |
if (d3d->config.debug) { // Debug Layers (erros are not critical) | |
// NOTE(naman): Should be called before doing anything else | |
{ // D3D Debug | |
ComPtr<ID3D12Debug> debug_interface; | |
hr = D3D12GetDebugInterface(IID_PPV_ARGS(&debug_interface)); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "D3D12GetDebugInterface failed"); | |
return nullptr; // NOTE(naman): Returning since we want the debug build to fail in debug mode, even though it is not a CRITICAL error | |
} | |
debug_interface->EnableDebugLayer(); | |
} | |
{ // DXGI Debug | |
ComPtr<IDXGIInfoQueue> dxgi_debug_info_queue; | |
hr = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug_info_queue)); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "DXGIGetDebugInterface1 for IDXGIInfoQueue failed"); | |
return nullptr; // NOTE(naman): Returning since we want the debug build to fail here, even though it is not a CRITICAL error | |
} | |
dxgi_debug_info_queue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true); | |
dxgi_debug_info_queue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true); | |
dxgi_debug_info_queue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, true); | |
dxgi_debug_info_queue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO, true); | |
dxgi_debug_info_queue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE, true); | |
} | |
} | |
{ // DXGI Factory | |
UINT create_factory_flags = 0; | |
if (d3d->config.debug) { | |
create_factory_flags = DXGI_CREATE_FACTORY_DEBUG; | |
} | |
hr = CreateDXGIFactory2(create_factory_flags, IID_PPV_ARGS(&d3d->dxgi_factory_4)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CreateDXGIFactory2 failed"); | |
return nullptr; | |
} | |
} | |
{ // Select Adapter (GPU) | |
if (d3d->config.warp) { | |
GPU gpu_warp = {}; | |
gpu_warp.name = "WARP"; | |
hr = d3d->dxgi_factory_4->EnumWarpAdapter(IID_PPV_ARGS(&gpu_warp.adapter)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "EnumWarpAdapter"); | |
return nullptr; | |
} | |
} else { | |
// First, check if DXGI 1.6 is available | |
Bool dxgi_1_6_available = true; | |
ComPtr<IDXGIFactory6> dxgi_factory_6; | |
hr = d3d->dxgi_factory_4.As(&dxgi_factory_6); | |
if (FAILED(hr)) { | |
dxgi_1_6_available = false; | |
jotHRESULT(WARN, "Could not create a IDXGIFactory6 from IDXGIFactory4 (will select GPU based on max memory)"); | |
} | |
// If DXGI 1.6 is available, we we enumerate all GPUs from the best to the | |
// worst, and select the first one. Else, we select the GPU with maximum dedicated | |
// video memory. | |
UINT i = 0; | |
Size max_memory = 0; | |
Size max_memory_index = 0; | |
ComPtr<IDXGIAdapter1> dxgi_adapter_1; // DXGI 1.0 (needed because the DXGI 1.4 EnumAdapters1 returns this) | |
ComPtr<IDXGIAdapter3> dxgi_adapter_3; // DXGI 1.4 | |
while (true) { | |
if (dxgi_1_6_available) { | |
hr = dxgi_factory_6->EnumAdapterByGpuPreference(i, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&dxgi_adapter_3)); | |
} else { | |
hr = d3d->dxgi_factory_4->EnumAdapters1(i, &dxgi_adapter_1); | |
} | |
if (hr == DXGI_ERROR_NOT_FOUND) break; | |
if (dxgi_1_6_available == false) { | |
// Convert the IDXGIAdapter1 to IDXGIAdapter3 | |
hr = dxgi_adapter_1.As(&dxgi_adapter_3); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not get IDXGIAdapter3 from IDXGIAdapter1"); | |
return nullptr; | |
} | |
} | |
DXGI_ADAPTER_DESC2 adapter_desc_2; | |
dxgi_adapter_3->GetDesc2(&adapter_desc_2); | |
// NOTE(naman): "All Direct3D 12 drivers will be Feature Level 11_0 or better." | |
// https://docs.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels | |
if (((adapter_desc_2.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0) && | |
SUCCEEDED(D3D12CreateDevice(dxgi_adapter_3.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr))) { | |
GPU gpu = {}; | |
gpu.video_memory = adapter_desc_2.DedicatedVideoMemory; | |
gpu.adapter = dxgi_adapter_3; | |
if (gpu.video_memory > max_memory) { | |
max_memory = gpu.video_memory; | |
max_memory_index = i; | |
} | |
gpu.name = winUTF16ToUTF8(adapter_desc_2.Description,nullptr); | |
jot(INFO, "Adapter #%u: %zu = %s", i, d3d->gpus.ElemIn(), gpu.name); | |
jot(INFO, "| %30s : %#04x", "Vendor PCI ID", adapter_desc_2.VendorId); | |
jot(INFO, "| %30s : %#04x", "Device PCI ID", adapter_desc_2.DeviceId); | |
jot(INFO, "| %30s : %#08x", "SubSys PCI ID", adapter_desc_2.SubSysId); | |
jot(INFO, "| %30s : %#08x", "Revision PCI ID", adapter_desc_2.Revision); | |
jot(INFO, "| %30s : %4.1f GiB (%zu Bytes)", "Dedicated Video Memory", | |
static_cast<Float64>(adapter_desc_2.DedicatedVideoMemory)/GiB(1.0), adapter_desc_2.DedicatedVideoMemory); | |
jot(INFO, "| %30s : %4.1f GiB (%zu Bytes)", "Dedicated System Memory", | |
static_cast<Float64>(adapter_desc_2.DedicatedSystemMemory)/GiB(1.0), adapter_desc_2.DedicatedSystemMemory); | |
jot(INFO, "| %30s : %4.1f GiB (%zu Bytes)", "Shared System Memory", | |
static_cast<Float64>(adapter_desc_2.SharedSystemMemory)/GiB(1.0), adapter_desc_2.SharedSystemMemory); | |
d3d->gpus.Add(gpu); | |
} else { | |
Char *name = winUTF16ToUTF8(adapter_desc_2.Description, nullptr); | |
jot(INFO, "Adapter #%u: %s [%4x:%04x] (ignoring)", i, name, adapter_desc_2.VendorId, adapter_desc_2.DeviceId); | |
memDealloc(&GLOBAL_heap_memory_allocator, name); | |
} | |
i++; | |
} | |
if (dxgi_1_6_available == false) { | |
d3d->gpu_current = max_memory_index; | |
} | |
} | |
} | |
{ // D3d12 Device | |
jot(INFO, "Creating Direct3d12 device on GPU %zu: %s", d3d->gpu_current, d3d->gpus[d3d->gpu_current].name); | |
hr = D3D12CreateDevice(d3d->gpus[d3d->gpu_current].adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3d->device)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not create ID3D12Device2"); | |
return nullptr; | |
} | |
} | |
if (d3d->config.debug) { | |
ComPtr<ID3D12InfoQueue> device_info_queue; | |
hr = d3d->device.As(&device_info_queue); | |
if (FAILED(hr)) { | |
jotHRESULT(WARN, "Could not get ID3D12InfoQueue from ID3D12Device2 (no debug device available)"); | |
} else { | |
device_info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); | |
device_info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); | |
device_info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); | |
device_info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_INFO, TRUE); | |
device_info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_MESSAGE, TRUE); | |
#if 0 | |
// Suppress whole categories of messages | |
//D3D12_MESSAGE_CATEGORY categories[] = {}; | |
// Suppress messages based on their severity level | |
D3D12_MESSAGE_SEVERITY severities[] = | |
{ | |
D3D12_MESSAGE_SEVERITY_INFO | |
}; | |
// Suppress individual messages by their ID | |
D3D12_MESSAGE_ID deny_ids[] = { | |
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, // I'm really not sure how to avoid this message. | |
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging. | |
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging. | |
}; | |
D3D12_INFO_QUEUE_FILTER filter = {}; | |
//NewFilter.DenyList.NumCategories = _countof(categories); | |
//NewFilter.DenyList.pCategoryList = categories; | |
NewFilter.DenyList.NumSeverities = _countof(severities); | |
NewFilter.DenyList.pSeverityList = severities; | |
NewFilter.DenyList.NumIDs = _countof(deny_ids); | |
NewFilter.DenyList.pIDList = deny_ids; | |
hr = device_info_queue->PushStorageFilter(&filter); | |
if (FAILED(hr)) { | |
jotHRESULT(WARNING, "ID3D12InfoQueue->PushStorageFilter failed"); | |
} | |
#endif | |
// https://microsoft.github.io/DirectX-Specs/d3d/MessageCallback.html | |
ComPtr<ID3D12InfoQueue1> device_info_queue_1; | |
hr = device_info_queue.As(&device_info_queue_1); | |
if (FAILED(hr)) { | |
jotHRESULT(WARN, "Could not get ID3D12InfoQueue1 from ID3D12InfoQueue (no debug message callback available)"); | |
} else { | |
DWORD cookie = {}; | |
hr = device_info_queue_1->RegisterMessageCallback(DebugMessageCallback, D3D12_MESSAGE_CALLBACK_FLAG_NONE, d3d, &cookie); | |
if (FAILED(hr)) { | |
jotHRESULT(WARN, "Could not register debug message callback"); | |
} | |
} | |
} | |
} | |
{ | |
if (CommandQueueCreate(&d3d->graphics_queue, d3d->device, D3D12_COMMAND_LIST_TYPE_DIRECT) == false) { | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CommandQueueCreate failed for DIRECT queue"); | |
return nullptr; | |
} | |
} | |
if (CommandQueueCreate(&d3d->compute_queue, d3d->device, D3D12_COMMAND_LIST_TYPE_COMPUTE) == false) { | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CommandQueueCreate failed for COMPUTE queue"); | |
return nullptr; | |
} | |
} | |
if (CommandQueueCreate(&d3d->copy_queue, d3d->device, D3D12_COMMAND_LIST_TYPE_COPY) == false) { | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CommandQueueCreate failed for COPY queue"); | |
return nullptr; | |
} | |
} | |
} | |
{ // Check for tearing support (needed to disable vsync, or to support variable refresh rate) | |
ComPtr<IDXGIFactory5> dxgi_factory_5; | |
hr = d3d->dxgi_factory_4.As(&dxgi_factory_5); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "Could not get IDXGIFactory5 from IDXGIFactory4 (won't be able to check for tearing)"); | |
} else { | |
hr = dxgi_factory_5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &d3d->tearing, sizeof(d3d->tearing)); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "Could not check for tearing support"); | |
d3d->tearing = false; | |
} else { | |
if (d3d->tearing) { | |
jot(INFO, "Tearing is supported"); | |
} else { | |
jot(INFO, "Tearing is not supported"); | |
} | |
} | |
} | |
} | |
{ // Create Swap Chain | |
DXGI_SWAP_CHAIN_DESC1 swapchain_description = {}; | |
swapchain_description.Width = client_width; | |
swapchain_description.Height = client_height; | |
swapchain_description.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | |
swapchain_description.Stereo = FALSE; | |
swapchain_description.SampleDesc = {}; | |
swapchain_description.SampleDesc.Count = 1; // Number of multi-samples | |
swapchain_description.SampleDesc.Quality = 0; // Quality of multi-samples | |
swapchain_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used | |
swapchain_description.BufferCount = SWAPCHAIN_BACKBUFFER_COUNT; | |
swapchain_description.Scaling = DXGI_SCALING_NONE; | |
swapchain_description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; | |
// swapchain_description.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; | |
// It is recommended to always allow tearing if tearing support is available. | |
swapchain_description.Flags = (d3d->tearing && !d3d->config.vsync) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0U; | |
// NOTE(naman): CreateSwapChainForHwnd requires IDXGISwapChain1 (part of DXGI 1.2, so fine to use for Windows 10 and above) | |
ComPtr<IDXGISwapChain1> swapchain_1; | |
hr = d3d->dxgi_factory_4->CreateSwapChainForHwnd(d3d->graphics_queue.command_queue.Get(), window, &swapchain_description, nullptr, nullptr, &swapchain_1); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "CreateSwapChainForHwnd failed"); | |
return nullptr; | |
} | |
// Don't let DXGI handle any hotkeys | |
hr = d3d->dxgi_factory_4->MakeWindowAssociation(window, | |
DXGI_MWA_NO_WINDOW_CHANGES | | |
DXGI_MWA_NO_ALT_ENTER | | |
DXGI_MWA_NO_PRINT_SCREEN); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "MakeWindowAssociation failed"); | |
} | |
hr = swapchain_1.As(&d3d->swapchain_3); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Can't query IDXGISwapChain3 from IDXGISwapChain1"); | |
return nullptr; | |
} | |
d3d->swapchain_backbuffer.rtv_index_current = d3d->swapchain_3->GetCurrentBackBufferIndex(); | |
} | |
{ // Descriptor heap for Render Target View and Depth Stencil View | |
D3D12_DESCRIPTOR_HEAP_DESC rtv_descriptor_heap_description = {}; | |
rtv_descriptor_heap_description.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; | |
rtv_descriptor_heap_description.NumDescriptors = SWAPCHAIN_BACKBUFFER_COUNT; | |
hr = d3d->device->CreateDescriptorHeap(&rtv_descriptor_heap_description, IID_PPV_ARGS(&d3d->swapchain_backbuffer.rtv_descriptor_heap)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not get create descriptor heap for render target view during initialization"); | |
return nullptr; | |
} | |
// Create the descriptor heap for the depth-stencil view. | |
D3D12_DESCRIPTOR_HEAP_DESC dsv_descriptor_heap_description = {}; | |
dsv_descriptor_heap_description.NumDescriptors = 1; | |
dsv_descriptor_heap_description.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; | |
dsv_descriptor_heap_description.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; | |
hr = d3d->device->CreateDescriptorHeap(&dsv_descriptor_heap_description, IID_PPV_ARGS(&d3d->swapchain_backbuffer.ds_descriptor_heap)); | |
if (FAILED(hr)) { | |
jotHRESULT(QUIT, "Could not get create descriptor heap for depth stencil view during initialization"); | |
return nullptr; | |
} | |
} | |
if (UpdateWindowBackbuffer(d3d, client_width, client_height) == false) { | |
jot(QUIT, "Couldn't update the render target view"); | |
return nullptr; | |
} | |
d3d->initialized = true; | |
return d3d; | |
} | |
void d3d12Finalize (D3D12_State *d3d) | |
{ | |
QueueFlush(&d3d->graphics_queue); | |
QueueFlush(&d3d->compute_queue); | |
QueueFlush(&d3d->copy_queue); | |
d3d->~D3D12_State(); | |
if (d3d->config.debug) { // Report all COM object that are still live | |
HRESULT hr; | |
ComPtr<IDXGIDebug> dxgi_debug; | |
hr = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug)); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "DXGIGetDebugInterface1 for IDXGIDebug failed"); | |
return; | |
} | |
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL); | |
} | |
} | |
[[maybe_unused]] | |
internal_function | |
Bool d3d12Resize(D3D12_State *d3d, UINT client_width, UINT client_height) | |
{ | |
HRESULT hr; | |
// Flush the GPU queue to make sure the swap chain's back buffers | |
// are not being referenced by an in-flight command list. | |
QueueFlush(&d3d->graphics_queue); | |
QueueFlush(&d3d->compute_queue); | |
QueueFlush(&d3d->copy_queue); | |
for (Uint i = 0; i < SWAPCHAIN_BACKBUFFER_COUNT; i++) { | |
d3d->swapchain_backbuffer.render_target_views[i].backbuffer.Reset(); | |
d3d->swapchain_backbuffer.render_target_views[i].fence_value = d3d->swapchain_backbuffer.render_target_views[d3d->swapchain_backbuffer.rtv_index_current].fence_value; | |
} | |
DXGI_SWAP_CHAIN_DESC swapchain_desc = {}; | |
hr = d3d->swapchain_3->GetDesc(&swapchain_desc); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "GetDesc failed"); | |
return false; | |
} | |
hr = d3d->swapchain_3->ResizeBuffers(SWAPCHAIN_BACKBUFFER_COUNT, | |
client_width, client_height, | |
swapchain_desc.BufferDesc.Format, swapchain_desc.Flags); | |
if (FAILED(hr)) { | |
jotHRESULT(FAIL, "ResizeBuffers failed"); | |
return false; | |
} | |
d3d->swapchain_backbuffer.rtv_index_current = d3d->swapchain_3->GetCurrentBackBufferIndex(); | |
if (UpdateWindowBackbuffer(d3d, client_width, client_height) == false) { | |
jot(QUIT, "Couldn't update the render target view while resizing"); | |
return false; | |
} | |
return true; | |
} | |
Uint64 d3d12FrameBegin (D3D12_State *d3d) | |
{ | |
d3d->swapchain_backbuffer.rtv_index_current = d3d->swapchain_3->GetCurrentBackBufferIndex(); | |
WaitForQueueToBeSignalledWithValue(&d3d->graphics_queue, d3d->swapchain_backbuffer.render_target_views[d3d->swapchain_backbuffer.rtv_index_current].fence_value); | |
d3d->frame_number++; | |
return d3d->frame_number; | |
} | |
void d3d12FrameEnd (D3D12_State *d3d) | |
{ | |
d3d->swapchain_backbuffer.render_target_views[d3d->swapchain_backbuffer.rtv_index_current].fence_value = d3d->graphics_queue.fence_value; | |
UINT sync_interval = d3d->config.vsync ? 1 : 0; | |
UINT present_flags = (d3d->tearing && !d3d->config.vsync) ? DXGI_PRESENT_ALLOW_TEARING : 0; | |
d3d->swapchain_3->Present(sync_interval, present_flags); | |
} | |
D3D12_Backbuffer* d3d12SwapchainGetBackbuffer (D3D12_State *d3d) | |
{ | |
D3D12_Backbuffer *backbuffer = dynamic_cast<D3D12_Backbuffer*>(&d3d->swapchain_backbuffer); | |
if (backbuffer) { | |
return backbuffer; | |
} else { | |
return nullptr; | |
} | |
} | |
D3D12_Draw_Commands* d3d12DrawCommandsRecord (D3D12_State *d3d) | |
{ | |
D3D12_Draw_Commands *dcs = new (memAlloc(&GLOBAL_heap_memory_allocator, sizeof(*dcs))) D3D12_Draw_Commands(); | |
if (CommandListStartRecording(&d3d->graphics_queue, &dcs->list) == false) { | |
jot(QUIT, "CommandListStartRecording failed"); | |
return nullptr; | |
} | |
return dcs; | |
} | |
Bool d3d12DrawCommandsRun (D3D12_State *d3d, D3D12_Draw_Commands *dcs) | |
{ | |
if (CommandListExecuteRecording(&d3d->graphics_queue, dcs->list) == false) { | |
jot(QUIT, "CommandListExecuteRecording failed"); | |
return false; | |
} | |
dcs->~D3D12_Draw_Commands(); | |
return true; | |
} | |
void d3d12DrawBackbufferTransitionTo (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *bb, D3D12_Draw_Mode to) | |
{ | |
D3D12_RESOURCE_STATES after = D3D12_RESOURCE_STATE_COMMON; | |
switch (to) { | |
case D3D12_Draw_Mode_DRAW: after = D3D12_RESOURCE_STATE_RENDER_TARGET; break; | |
case D3D12_Draw_Mode_PRESENT: after = D3D12_RESOURCE_STATE_PRESENT; break; | |
} | |
if (bb->rtv_current_state != after) { | |
D3D12_RESOURCE_BARRIER barrier = {}; | |
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; | |
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; | |
barrier.Transition.pResource = bb->render_target_views[bb->rtv_index_current].backbuffer.Get(); | |
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; | |
barrier.Transition.StateBefore = bb->rtv_current_state; | |
barrier.Transition.StateAfter = after; | |
dcs->list->ResourceBarrier(1, &barrier); | |
bb->rtv_current_state = after; | |
} | |
} | |
void d3d12DrawBackbufferClearColour (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *bb, Float32 red, Float32 green, Float32 blue) | |
{ | |
FLOAT clear_colour[] = { red, green, blue, 1.0f }; | |
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle_temp = bb->rtv_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); | |
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = {}; | |
rtv_handle.ptr = (rtv_handle_temp.ptr + (bb->rtv_index_current * bb->rtv_descriptor_size)); | |
dcs->list->ClearRenderTargetView(rtv_handle, clear_colour, 0, nullptr); // Clear the entire resource | |
} | |
void d3d12DrawBackbufferClearDepthStencil (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *bb, Float32 depth) | |
{ | |
D3D12_CPU_DESCRIPTOR_HANDLE dsv = bb->ds_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); | |
dcs->list->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, depth, 0, 0, nullptr); | |
} | |
void d3d12DrawBackbufferClear (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *b, Float32 red, Float32 green, Float32 blue) | |
{ | |
d3d12DrawBackbufferClearColour(dcs, b, red, green, blue); | |
d3d12DrawBackbufferClearDepthStencil(dcs, b, 1.0f); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Creator: Naman Dixit | |
* Notice: © Copyright 2023 Naman Dixit | |
*/ | |
#include "common/env.h" | |
pragma_clang("clang diagnostic push") | |
pragma_clang("clang diagnostic ignored \"-Wreserved-identifier\"") | |
pragma_clang("clang diagnostic ignored \"-Wdocumentation-unknown-command\"") | |
pragma_clang("clang diagnostic ignored \"-Wsign-conversion\"") | |
pragma_clang("clang diagnostic ignored \"-Wmicrosoft-enum-value\"") | |
pragma_clang("clang diagnostic ignored \"-Wold-style-cast\"") | |
#include "thirdparty/SDL3/SDL.h" | |
pragma_clang("clang diagnostic pop") | |
#include "../SDL_addendum.h" | |
#include "../jot.h" | |
#include "common/macro.h" | |
#include "common/memory.h" | |
#include "win32.h" | |
global_variable HANDLE GLOBAL_process_heap = nullptr; | |
header_function | |
MEMORY_ALLOCATOR_FUNCTION(HeapMemoryAllocatorFunction) | |
{ | |
unused_variable(userdata); | |
if (GLOBAL_process_heap == nullptr) GLOBAL_process_heap = ::GetProcessHeap(); | |
pragma_clang("clang diagnostic push"); | |
pragma_clang("clang diagnostic ignored \"-Wunreachable-code-break\""); | |
switch (mode) { | |
case Memory_ALLOCATE: { | |
void *memory = ::HeapAlloc(GLOBAL_process_heap, 0, new_size); | |
return memory; | |
} break; | |
case Memory_REALLOCATE: { | |
void *memory = ::HeapReAlloc(GLOBAL_process_heap, 0, old_ptr, new_size); | |
return memory; | |
} break; | |
case Memory_DEALLOCATE: { | |
::HeapFree(GLOBAL_process_heap, 0, old_ptr); | |
return nullptr; | |
} break; | |
case Memory_DEALLOCATE_ALL: { | |
return nullptr; | |
} break; | |
} | |
pragma_clang("clang diagnostic pop"); | |
return nullptr; | |
} | |
global_variable Memory_Allocator GLOBAL_heap_memory_allocator = {&HeapMemoryAllocatorFunction, MEMORY_ALLOCATION_ALIGNMENT, nullptr}; | |
internal_function | |
Char * winUTF16ToUTF8 (LPWSTR wcstr, Size *cstr_length_out) | |
{ | |
Size cstr_length = static_cast<Size>(WideCharToMultiByte(CP_UTF8, 0, wcstr, -1, nullptr, 0, nullptr, nullptr)); | |
DWORD cstr_size = static_cast<DWORD>(cstr_length) * sizeof(Char); | |
Char *cstr = static_cast<Char *>(memAlloc(&GLOBAL_heap_memory_allocator, cstr_size)); | |
WideCharToMultiByte(CP_UTF8, 0, wcstr, -1, cstr, static_cast<int>(cstr_length), nullptr, nullptr); | |
if (cstr_length_out) *cstr_length_out = cstr_length; | |
return cstr; | |
} | |
[[maybe_unused]] | |
internal_function | |
LPWSTR winUTF8ToUTF16 (Char const *cstr, Size *wcstr_length_out) | |
{ | |
Size wcstr_length = static_cast<Size>(MultiByteToWideChar(CP_UTF8, 0, cstr, -1, nullptr, 0)); | |
DWORD wcstr_size = static_cast<DWORD>(wcstr_length) * sizeof(wchar_t); | |
LPWSTR wcstr = static_cast<LPWSTR>(memAlloc(&GLOBAL_heap_memory_allocator, wcstr_size)); | |
MultiByteToWideChar(CP_UTF8, 0, cstr, -1, wcstr, static_cast<int>(wcstr_length)); | |
if (wcstr_length_out) *wcstr_length_out = wcstr_length; | |
return wcstr; | |
} | |
#include "d3d12.cpp" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Creator: Naman Dixit | |
* Notice: © Copyright 2023 Naman Dixit | |
*/ | |
#if !defined(WIN32_H_INCLUDE_GUARD) | |
# define WIN32_LEAN_AND_MEAN | |
# define NOMINMAX | |
# define NOGDI | |
# include "Windows.h" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef struct D3D12_Config { | |
Bool warp; // WARP software renderer | |
Bool vsync; | |
Bool debug; | |
} D3D12_Config; | |
typedef struct D3D12_State D3D12_State; | |
typedef struct D3D12_Draw_Commands D3D12_Draw_Commands; | |
typedef struct D3D12_Backbuffer D3D12_Backbuffer; | |
typedef enum D3D12_Draw_Mode { | |
D3D12_Draw_Mode_DRAW, | |
D3D12_Draw_Mode_PRESENT, | |
} D3D12_Draw_Mode; | |
// Init/Quit | |
D3D12_State* d3d12Initialize (HWND window, Uint32 client_width, Uint32 client_height, | |
D3D12_Config config, | |
void (*jotter) (Jot_Channel, Jot_Level, const Char *, Sint, const Char *, const Char *, ...)); | |
void d3d12Finalize (D3D12_State *d3d); | |
// FrameBegin | |
Uint64 d3d12FrameBegin (D3D12_State *d3d); | |
void d3d12FrameEnd (D3D12_State *d3d); | |
D3D12_Backbuffer* d3d12SwapchainGetBackbuffer (D3D12_State *d3d); | |
D3D12_Draw_Commands* d3d12DrawCommandsRecord (D3D12_State *d3d); | |
Bool d3d12DrawCommandsRun (D3D12_State *d3d, D3D12_Draw_Commands *dcs); | |
void d3d12DrawBackbufferTransitionTo (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *b, D3D12_Draw_Mode to); | |
void d3d12DrawBackbufferClearColour (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *b, Float32 red, Float32 green, Float32 blue); | |
void d3d12DrawBackbufferClearDepthStencil (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *b, Float32 depth); | |
void d3d12DrawBackbufferClear (D3D12_Draw_Commands *dcs, D3D12_Backbuffer *b, Float32 red, Float32 green, Float32 blue); | |
#ifdef __cplusplus | |
} | |
#endif | |
#define WIN32_H_INCLUDE_GUARD | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment