Skip to content

Instantly share code, notes, and snippets.

@namandixit
Created October 8, 2024 04:01
Show Gist options
  • Save namandixit/8022b5db34871fd75cc9c8bf95ab154b to your computer and use it in GitHub Desktop.
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/
/*
* 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);
}
/*
* 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"
/*
* 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