add build system, vendor deps, init stuff
This commit is contained in:
51
src/main.cpp
Normal file
51
src/main.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "platform/platform.h"
|
||||
#include "renderer/renderer.h"
|
||||
#include "imgui.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
PlatformWindowDesc window_desc = {};
|
||||
PlatformWindow *window = platform_create_window(&window_desc);
|
||||
if (!window)
|
||||
return 1;
|
||||
|
||||
int32_t w, h;
|
||||
platform_get_size(window, &w, &h);
|
||||
|
||||
RendererDesc renderer_desc = {};
|
||||
renderer_desc.window_handle = platform_get_native_handle(window);
|
||||
renderer_desc.width = w;
|
||||
renderer_desc.height = h;
|
||||
Renderer *renderer = renderer_create(&renderer_desc);
|
||||
if (!renderer) {
|
||||
platform_destroy_window(window);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t last_w = w, last_h = h;
|
||||
bool show_demo = true;
|
||||
|
||||
while (platform_poll_events(window)) {
|
||||
platform_get_size(window, &w, &h);
|
||||
if (w != last_w || h != last_h) {
|
||||
renderer_resize(renderer, w, h);
|
||||
last_w = w;
|
||||
last_h = h;
|
||||
}
|
||||
|
||||
if (!renderer_begin_frame(renderer))
|
||||
continue;
|
||||
|
||||
if (show_demo)
|
||||
ImGui::ShowDemoWindow(&show_demo);
|
||||
|
||||
renderer_end_frame(renderer);
|
||||
}
|
||||
|
||||
renderer_destroy(renderer);
|
||||
platform_destroy_window(window);
|
||||
return 0;
|
||||
}
|
||||
18
src/platform/platform.h
Normal file
18
src/platform/platform.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct PlatformWindow;
|
||||
|
||||
struct PlatformWindowDesc {
|
||||
const char *title = "autosample";
|
||||
int32_t width = 1280;
|
||||
int32_t height = 720;
|
||||
};
|
||||
|
||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc);
|
||||
void platform_destroy_window(PlatformWindow *window);
|
||||
bool platform_poll_events(PlatformWindow *window);
|
||||
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h);
|
||||
void *platform_get_native_handle(PlatformWindow *window);
|
||||
135
src/platform/platform_win32.cpp
Normal file
135
src/platform/platform_win32.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "platform/platform.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "imgui_impl_win32.h"
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
struct PlatformWindow {
|
||||
HWND hwnd;
|
||||
bool should_close;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
static PlatformWindow *g_current_window = nullptr;
|
||||
|
||||
static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam))
|
||||
return true;
|
||||
|
||||
switch (msg) {
|
||||
case WM_SIZE:
|
||||
if (g_current_window && wparam != SIZE_MINIMIZED) {
|
||||
g_current_window->width = (int32_t)LOWORD(lparam);
|
||||
g_current_window->height = (int32_t)HIWORD(lparam);
|
||||
}
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
if (g_current_window)
|
||||
g_current_window->should_close = true;
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
case WM_SYSCOMMAND:
|
||||
if ((wparam & 0xfff0) == SC_KEYMENU)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc)
|
||||
{
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_CLASSDC;
|
||||
wc.lpfnWndProc = win32_wndproc;
|
||||
wc.hInstance = GetModuleHandleW(nullptr);
|
||||
wc.lpszClassName = L"autosample_wc";
|
||||
RegisterClassExW(&wc);
|
||||
|
||||
int screen_w = GetSystemMetrics(SM_CXSCREEN);
|
||||
int screen_h = GetSystemMetrics(SM_CYSCREEN);
|
||||
int x = (screen_w - desc->width) / 2;
|
||||
int y = (screen_h - desc->height) / 2;
|
||||
|
||||
RECT rect = { 0, 0, (LONG)desc->width, (LONG)desc->height };
|
||||
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, nullptr, 0);
|
||||
wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, wtitle, wchar_count);
|
||||
|
||||
HWND hwnd = CreateWindowExW(
|
||||
0, wc.lpszClassName, wtitle,
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
x, y,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
nullptr, nullptr, wc.hInstance, nullptr
|
||||
);
|
||||
|
||||
_freea(wtitle);
|
||||
|
||||
if (!hwnd)
|
||||
return nullptr;
|
||||
|
||||
ShowWindow(hwnd, SW_SHOWDEFAULT);
|
||||
UpdateWindow(hwnd);
|
||||
|
||||
PlatformWindow *window = new PlatformWindow();
|
||||
window->hwnd = hwnd;
|
||||
window->should_close = false;
|
||||
window->width = desc->width;
|
||||
window->height = desc->height;
|
||||
|
||||
g_current_window = window;
|
||||
return window;
|
||||
}
|
||||
|
||||
void platform_destroy_window(PlatformWindow *window)
|
||||
{
|
||||
if (!window) return;
|
||||
|
||||
if (window->hwnd) {
|
||||
DestroyWindow(window->hwnd);
|
||||
UnregisterClassW(L"autosample_wc", GetModuleHandleW(nullptr));
|
||||
}
|
||||
|
||||
if (g_current_window == window)
|
||||
g_current_window = nullptr;
|
||||
|
||||
delete window;
|
||||
}
|
||||
|
||||
bool platform_poll_events(PlatformWindow *window)
|
||||
{
|
||||
MSG msg;
|
||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
if (msg.message == WM_QUIT) {
|
||||
window->should_close = true;
|
||||
}
|
||||
}
|
||||
return !window->should_close;
|
||||
}
|
||||
|
||||
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h)
|
||||
{
|
||||
if (w) *w = window->width;
|
||||
if (h) *h = window->height;
|
||||
}
|
||||
|
||||
void *platform_get_native_handle(PlatformWindow *window)
|
||||
{
|
||||
return (void *)window->hwnd;
|
||||
}
|
||||
19
src/renderer/renderer.h
Normal file
19
src/renderer/renderer.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct Renderer;
|
||||
|
||||
struct RendererDesc {
|
||||
void *window_handle = nullptr;
|
||||
int32_t width = 1280;
|
||||
int32_t height = 720;
|
||||
int32_t frame_count = 2;
|
||||
};
|
||||
|
||||
Renderer *renderer_create(RendererDesc *desc);
|
||||
void renderer_destroy(Renderer *renderer);
|
||||
bool renderer_begin_frame(Renderer *renderer);
|
||||
void renderer_end_frame(Renderer *renderer);
|
||||
void renderer_resize(Renderer *renderer, int32_t width, int32_t height);
|
||||
435
src/renderer/renderer_dx12.cpp
Normal file
435
src/renderer/renderer_dx12.cpp
Normal file
@@ -0,0 +1,435 @@
|
||||
#include "renderer/renderer.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_5.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_win32.h"
|
||||
#include "imgui_impl_dx12.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DX12_ENABLE_DEBUG_LAYER
|
||||
#endif
|
||||
|
||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
||||
#include <dxgidebug.h>
|
||||
#pragma comment(lib, "dxguid.lib")
|
||||
#endif
|
||||
|
||||
#define NUM_BACK_BUFFERS 2
|
||||
#define SRV_HEAP_SIZE 64
|
||||
|
||||
struct FrameContext {
|
||||
ID3D12CommandAllocator *command_allocator;
|
||||
UINT64 fence_value;
|
||||
};
|
||||
|
||||
struct SrvHeapAllocator {
|
||||
ID3D12DescriptorHeap *heap;
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE heap_type;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE start_cpu;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE start_gpu;
|
||||
UINT increment;
|
||||
int free_indices[SRV_HEAP_SIZE];
|
||||
int free_count;
|
||||
};
|
||||
|
||||
static void srv_alloc_init(SrvHeapAllocator *a, ID3D12Device *device, ID3D12DescriptorHeap *heap)
|
||||
{
|
||||
a->heap = heap;
|
||||
a->heap_type = heap->GetDesc().Type;
|
||||
a->start_cpu = heap->GetCPUDescriptorHandleForHeapStart();
|
||||
a->start_gpu = heap->GetGPUDescriptorHandleForHeapStart();
|
||||
a->increment = device->GetDescriptorHandleIncrementSize(a->heap_type);
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
|
||||
a->free_count = (int)desc.NumDescriptors;
|
||||
for (int i = 0; i < a->free_count; i++)
|
||||
a->free_indices[i] = a->free_count - 1 - i;
|
||||
}
|
||||
|
||||
static void srv_alloc_alloc(SrvHeapAllocator *a, D3D12_CPU_DESCRIPTOR_HANDLE *out_cpu, D3D12_GPU_DESCRIPTOR_HANDLE *out_gpu)
|
||||
{
|
||||
int idx = a->free_indices[--a->free_count];
|
||||
out_cpu->ptr = a->start_cpu.ptr + (idx * a->increment);
|
||||
out_gpu->ptr = a->start_gpu.ptr + (idx * a->increment);
|
||||
}
|
||||
|
||||
static void srv_alloc_free(SrvHeapAllocator *a, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu)
|
||||
{
|
||||
(void)gpu;
|
||||
int idx = (int)((cpu.ptr - a->start_cpu.ptr) / a->increment);
|
||||
a->free_indices[a->free_count++] = idx;
|
||||
}
|
||||
|
||||
struct Renderer {
|
||||
HWND hwnd;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t frame_count;
|
||||
UINT frame_index;
|
||||
|
||||
ID3D12Device *device;
|
||||
ID3D12CommandQueue *command_queue;
|
||||
IDXGISwapChain3 *swap_chain;
|
||||
HANDLE swap_chain_waitable;
|
||||
bool swap_chain_occluded;
|
||||
bool tearing_support;
|
||||
|
||||
ID3D12DescriptorHeap *rtv_heap;
|
||||
ID3D12DescriptorHeap *srv_heap;
|
||||
SrvHeapAllocator srv_alloc;
|
||||
|
||||
FrameContext frames[NUM_BACK_BUFFERS];
|
||||
ID3D12Resource *render_targets[NUM_BACK_BUFFERS];
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtv_descriptors[NUM_BACK_BUFFERS];
|
||||
|
||||
ID3D12GraphicsCommandList *command_list;
|
||||
ID3D12Fence *fence;
|
||||
HANDLE fence_event;
|
||||
UINT64 fence_last_signaled;
|
||||
};
|
||||
|
||||
static bool create_device(Renderer *r)
|
||||
{
|
||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
||||
ID3D12Debug *debug = nullptr;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
|
||||
debug->EnableDebugLayer();
|
||||
debug->Release();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&r->device)) != S_OK)
|
||||
return false;
|
||||
|
||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
||||
{
|
||||
ID3D12InfoQueue *info_queue = nullptr;
|
||||
if (SUCCEEDED(r->device->QueryInterface(IID_PPV_ARGS(&info_queue)))) {
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
|
||||
info_queue->Release();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_command_queue(Renderer *r)
|
||||
{
|
||||
D3D12_COMMAND_QUEUE_DESC desc = {};
|
||||
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
desc.NodeMask = 1;
|
||||
return r->device->CreateCommandQueue(&desc, IID_PPV_ARGS(&r->command_queue)) == S_OK;
|
||||
}
|
||||
|
||||
static bool create_descriptor_heaps(Renderer *r)
|
||||
{
|
||||
{
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
||||
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||
desc.NumDescriptors = NUM_BACK_BUFFERS;
|
||||
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
desc.NodeMask = 1;
|
||||
if (r->device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&r->rtv_heap)) != S_OK)
|
||||
return false;
|
||||
|
||||
SIZE_T rtv_size = r->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle = r->rtv_heap->GetCPUDescriptorHandleForHeapStart();
|
||||
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
r->rtv_descriptors[i] = handle;
|
||||
handle.ptr += rtv_size;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
|
||||
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||
desc.NumDescriptors = SRV_HEAP_SIZE;
|
||||
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
if (r->device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&r->srv_heap)) != S_OK)
|
||||
return false;
|
||||
|
||||
srv_alloc_init(&r->srv_alloc, r->device, r->srv_heap);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_frame_resources(Renderer *r)
|
||||
{
|
||||
for (int i = 0; i < r->frame_count; i++) {
|
||||
if (r->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
IID_PPV_ARGS(&r->frames[i].command_allocator)) != S_OK)
|
||||
return false;
|
||||
r->frames[i].fence_value = 0;
|
||||
}
|
||||
|
||||
if (r->device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
r->frames[0].command_allocator, nullptr, IID_PPV_ARGS(&r->command_list)) != S_OK)
|
||||
return false;
|
||||
if (r->command_list->Close() != S_OK)
|
||||
return false;
|
||||
|
||||
if (r->device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&r->fence)) != S_OK)
|
||||
return false;
|
||||
|
||||
r->fence_event = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
return r->fence_event != nullptr;
|
||||
}
|
||||
|
||||
static bool create_swap_chain(Renderer *r)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 sd = {};
|
||||
sd.BufferCount = NUM_BACK_BUFFERS;
|
||||
sd.Width = 0;
|
||||
sd.Height = 0;
|
||||
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
||||
sd.Scaling = DXGI_SCALING_STRETCH;
|
||||
sd.Stereo = FALSE;
|
||||
|
||||
IDXGIFactory5 *factory = nullptr;
|
||||
if (CreateDXGIFactory1(IID_PPV_ARGS(&factory)) != S_OK)
|
||||
return false;
|
||||
|
||||
BOOL allow_tearing = FALSE;
|
||||
factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
|
||||
r->tearing_support = (allow_tearing == TRUE);
|
||||
if (r->tearing_support)
|
||||
sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
IDXGISwapChain1 *swap_chain1 = nullptr;
|
||||
if (factory->CreateSwapChainForHwnd(r->command_queue, r->hwnd, &sd, nullptr, nullptr, &swap_chain1) != S_OK) {
|
||||
factory->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (swap_chain1->QueryInterface(IID_PPV_ARGS(&r->swap_chain)) != S_OK) {
|
||||
swap_chain1->Release();
|
||||
factory->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r->tearing_support)
|
||||
factory->MakeWindowAssociation(r->hwnd, DXGI_MWA_NO_ALT_ENTER);
|
||||
|
||||
swap_chain1->Release();
|
||||
factory->Release();
|
||||
|
||||
r->swap_chain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
|
||||
r->swap_chain_waitable = r->swap_chain->GetFrameLatencyWaitableObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void create_render_targets(Renderer *r)
|
||||
{
|
||||
for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
ID3D12Resource *back_buffer = nullptr;
|
||||
r->swap_chain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
|
||||
r->device->CreateRenderTargetView(back_buffer, nullptr, r->rtv_descriptors[i]);
|
||||
r->render_targets[i] = back_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_render_targets(Renderer *r)
|
||||
{
|
||||
for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
if (r->render_targets[i]) {
|
||||
r->render_targets[i]->Release();
|
||||
r->render_targets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_for_pending(Renderer *r)
|
||||
{
|
||||
r->command_queue->Signal(r->fence, ++r->fence_last_signaled);
|
||||
r->fence->SetEventOnCompletion(r->fence_last_signaled, r->fence_event);
|
||||
WaitForSingleObject(r->fence_event, INFINITE);
|
||||
}
|
||||
|
||||
static FrameContext *wait_for_next_frame(Renderer *r)
|
||||
{
|
||||
FrameContext *fc = &r->frames[r->frame_index % r->frame_count];
|
||||
if (r->fence->GetCompletedValue() < fc->fence_value) {
|
||||
r->fence->SetEventOnCompletion(fc->fence_value, r->fence_event);
|
||||
HANDLE waitables[] = { r->swap_chain_waitable, r->fence_event };
|
||||
WaitForMultipleObjects(2, waitables, TRUE, INFINITE);
|
||||
} else {
|
||||
WaitForSingleObject(r->swap_chain_waitable, INFINITE);
|
||||
}
|
||||
return fc;
|
||||
}
|
||||
|
||||
static void init_imgui(Renderer *r)
|
||||
{
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplWin32_Init(r->hwnd);
|
||||
|
||||
ImGui_ImplDX12_InitInfo init_info = {};
|
||||
init_info.Device = r->device;
|
||||
init_info.CommandQueue = r->command_queue;
|
||||
init_info.NumFramesInFlight = r->frame_count;
|
||||
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
|
||||
init_info.SrvDescriptorHeap = r->srv_heap;
|
||||
init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo *info, D3D12_CPU_DESCRIPTOR_HANDLE *out_cpu, D3D12_GPU_DESCRIPTOR_HANDLE *out_gpu) {
|
||||
Renderer *r = (Renderer *)info->UserData;
|
||||
srv_alloc_alloc(&r->srv_alloc, out_cpu, out_gpu);
|
||||
};
|
||||
init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo *info, D3D12_CPU_DESCRIPTOR_HANDLE cpu, D3D12_GPU_DESCRIPTOR_HANDLE gpu) {
|
||||
Renderer *r = (Renderer *)info->UserData;
|
||||
srv_alloc_free(&r->srv_alloc, cpu, gpu);
|
||||
};
|
||||
init_info.UserData = r;
|
||||
ImGui_ImplDX12_Init(&init_info);
|
||||
}
|
||||
|
||||
Renderer *renderer_create(RendererDesc *desc)
|
||||
{
|
||||
Renderer *r = new Renderer();
|
||||
memset(r, 0, sizeof(*r));
|
||||
|
||||
r->hwnd = (HWND)desc->window_handle;
|
||||
r->width = desc->width;
|
||||
r->height = desc->height;
|
||||
r->frame_count = desc->frame_count;
|
||||
if (r->frame_count > NUM_BACK_BUFFERS) r->frame_count = NUM_BACK_BUFFERS;
|
||||
|
||||
if (!create_device(r)) goto fail;
|
||||
if (!create_command_queue(r)) goto fail;
|
||||
if (!create_descriptor_heaps(r)) goto fail;
|
||||
if (!create_frame_resources(r)) goto fail;
|
||||
if (!create_swap_chain(r)) goto fail;
|
||||
create_render_targets(r);
|
||||
|
||||
init_imgui(r);
|
||||
return r;
|
||||
|
||||
fail:
|
||||
renderer_destroy(r);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void renderer_destroy(Renderer *r)
|
||||
{
|
||||
if (!r) return;
|
||||
|
||||
wait_for_pending(r);
|
||||
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
cleanup_render_targets(r);
|
||||
|
||||
if (r->swap_chain) { r->swap_chain->SetFullscreenState(false, nullptr); r->swap_chain->Release(); }
|
||||
if (r->swap_chain_waitable) CloseHandle(r->swap_chain_waitable);
|
||||
for (int i = 0; i < r->frame_count; i++)
|
||||
if (r->frames[i].command_allocator) r->frames[i].command_allocator->Release();
|
||||
if (r->command_queue) r->command_queue->Release();
|
||||
if (r->command_list) r->command_list->Release();
|
||||
if (r->rtv_heap) r->rtv_heap->Release();
|
||||
if (r->srv_heap) r->srv_heap->Release();
|
||||
if (r->fence) r->fence->Release();
|
||||
if (r->fence_event) CloseHandle(r->fence_event);
|
||||
if (r->device) r->device->Release();
|
||||
|
||||
#ifdef DX12_ENABLE_DEBUG_LAYER
|
||||
IDXGIDebug1 *dxgi_debug = nullptr;
|
||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug)))) {
|
||||
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY);
|
||||
dxgi_debug->Release();
|
||||
}
|
||||
#endif
|
||||
|
||||
delete r;
|
||||
}
|
||||
|
||||
bool renderer_begin_frame(Renderer *r)
|
||||
{
|
||||
if ((r->swap_chain_occluded && r->swap_chain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
|
||||
|| IsIconic(r->hwnd))
|
||||
{
|
||||
Sleep(10);
|
||||
return false;
|
||||
}
|
||||
r->swap_chain_occluded = false;
|
||||
|
||||
ImGui_ImplDX12_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderer_end_frame(Renderer *r)
|
||||
{
|
||||
ImGui::Render();
|
||||
|
||||
FrameContext *fc = wait_for_next_frame(r);
|
||||
UINT back_buffer_idx = r->swap_chain->GetCurrentBackBufferIndex();
|
||||
|
||||
fc->command_allocator->Reset();
|
||||
r->command_list->Reset(fc->command_allocator, nullptr);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = r->render_targets[back_buffer_idx];
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
r->command_list->ResourceBarrier(1, &barrier);
|
||||
|
||||
const float clear_color[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
r->command_list->ClearRenderTargetView(r->rtv_descriptors[back_buffer_idx], clear_color, 0, nullptr);
|
||||
r->command_list->OMSetRenderTargets(1, &r->rtv_descriptors[back_buffer_idx], FALSE, nullptr);
|
||||
r->command_list->SetDescriptorHeaps(1, &r->srv_heap);
|
||||
|
||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), r->command_list);
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
r->command_list->ResourceBarrier(1, &barrier);
|
||||
r->command_list->Close();
|
||||
|
||||
r->command_queue->ExecuteCommandLists(1, (ID3D12CommandList *const *)&r->command_list);
|
||||
r->command_queue->Signal(r->fence, ++r->fence_last_signaled);
|
||||
fc->fence_value = r->fence_last_signaled;
|
||||
|
||||
HRESULT hr = r->swap_chain->Present(1, 0);
|
||||
r->swap_chain_occluded = (hr == DXGI_STATUS_OCCLUDED);
|
||||
r->frame_index++;
|
||||
}
|
||||
|
||||
void renderer_resize(Renderer *r, int32_t width, int32_t height)
|
||||
{
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
wait_for_pending(r);
|
||||
cleanup_render_targets(r);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 desc = {};
|
||||
r->swap_chain->GetDesc1(&desc);
|
||||
r->swap_chain->ResizeBuffers(0, (UINT)width, (UINT)height, desc.Format, desc.Flags);
|
||||
|
||||
create_render_targets(r);
|
||||
|
||||
r->width = width;
|
||||
r->height = height;
|
||||
}
|
||||
Reference in New Issue
Block a user