add build system, vendor deps, init stuff

This commit is contained in:
2026-02-22 17:57:58 -05:00
parent 530439574c
commit aa6065b0bd
26 changed files with 72992 additions and 26 deletions

51
src/main.cpp Normal file
View 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
View 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);

View 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
View 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);

View 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;
}