#ifndef _WIN32 #pragma warning Building DX11 on non - windows is probably a mistake #endif #include #include #include #include "gfx/renderer_api.h" #include "runtime/config.h" #define DONT_DEFINE_RENDERER_GLOBAL #include "gpu.hpp" RT_CVAR_S( rt_Dx11AdapterName, "Name of the adapter that should be used for device creation. Default: \"\" (Use default)", ""); RT_CVAR_I(rt_Dx11VSync, "Enable vsync. Default: 1", 1); RT_CVAR_I(rt_Dx11MaxSubmittedCommandBuffers, "Maximum number of submitted command buffers per frame. Default: 1024", 1024); extern rt_cvar rt_Dx11MaxCommandBuffers; rt_gpu g_gpu; extern "C" void RT_RENDERER_API_FN(RegisterCVars)(void) { rtRegisterCVAR(&rt_Dx11AdapterName); rtRegisterCVAR(&rt_Dx11VSync); rtRegisterCVAR(&rt_Dx11MaxCommandBuffers); } static rt_swap_chain CreateSwapChain(HWND hwnd) { rt_swap_chain swc; DXGI_SWAP_CHAIN_DESC1 desc; desc.Width = 0; // use window width desc.Height = 0; // use window height desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // can't specify _SRGB here when using // DXGI_SWAP_EFFECT_FLIP_* ...; desc.Stereo = FALSE; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 2; desc.Scaling = DXGI_SCALING_STRETCH; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; desc.Flags = 0; if (FAILED(g_gpu.dxgi_factory->CreateSwapChainForHwnd(g_gpu.device.Get(), hwnd, &desc, nullptr, nullptr, &swc.swap_chain))) { rtReportError("dx11", "Failed to create the swap chain."); return swc; } ID3D11Texture2D *frame_buffer; if (FAILED(swc.swap_chain->GetBuffer(0, IID_PPV_ARGS(&frame_buffer)))) { rtReportError("dx11", "Failed to retrieve the backbuffer."); swc.swap_chain.Reset(); return swc; } D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {}; rtv_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; if (FAILED(g_gpu.device->CreateRenderTargetView(frame_buffer, &rtv_desc, &swc.rtv))) { rtReportError("dx11", "Failed to create the render target view for the backbuffer."); swc.swap_chain.Reset(); return swc; } return swc; } static IDXGIAdapter *RetrieveSelectedAdapter(void) { ComPtr factory; if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)))) { return NULL; } UINT i = 0; IDXGIAdapter *adapter; while (factory->EnumAdapters(i, &adapter) == S_OK) { ++i; DXGI_ADAPTER_DESC desc; adapter->GetDesc(&desc); char utf8_desc[256]; rtWStrToUTF8(desc.Description, utf8_desc, 256); if (strncmp(utf8_desc, rt_Dx11AdapterName.s, 256) == 0) return adapter; } return NULL; } extern rt_result InitCommandBufferManagement(); extern void ShutdownCommandBufferManagement(); extern rt_result InitRenderTargetManagement(); extern void ShutdownRenderTargetManagement(); extern rt_result InitBufferManagement(); extern void ShutdownBufferManagement(); extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; UINT device_flags = 0; #ifdef RT_DEBUG device_flags |= D3D11_CREATE_DEVICE_DEBUG; #endif IDXGIAdapter *selected_adapter = RetrieveSelectedAdapter(); ID3D11Device *base_device; ID3D11DeviceContext *base_context; if (FAILED(D3D11CreateDevice(selected_adapter, D3D_DRIVER_TYPE_HARDWARE, nullptr, device_flags, feature_levels, RT_ARRAY_COUNT(feature_levels), D3D11_SDK_VERSION, &base_device, &g_gpu.feature_level, &base_context))) { rtLog("dx11", "Feature level 11.1 creation failed, retrying with feature level 11.0"); if (FAILED(D3D11CreateDevice(selected_adapter, D3D_DRIVER_TYPE_HARDWARE, nullptr, device_flags, &feature_levels[1], RT_ARRAY_COUNT(feature_levels) - 1, D3D11_SDK_VERSION, &base_device, &g_gpu.feature_level, &base_context))) { rtReportError("dx11", "Failed to create the d3d11 device."); return RT_UNKNOWN_ERROR; } } if (FAILED(base_device->QueryInterface(IID_PPV_ARGS(&g_gpu.device)))) { rtReportError("dx11", "Failed to query the D3D11Device1 interface."); return RT_UNKNOWN_ERROR; } if (FAILED(base_context->QueryInterface(IID_PPV_ARGS(&g_gpu.device_context)))) { rtReportError("dx11", "Failed to query the D3D11DeviceContext1 interface."); return RT_UNKNOWN_ERROR; } IDXGIDevice1 *dxgi_device; if (FAILED(g_gpu.device->QueryInterface(&dxgi_device))) { rtReportError("dx11", "Failed to query the DXGIDevice1 interface."); return RT_UNKNOWN_ERROR; } IDXGIAdapter *adapter; if (FAILED(dxgi_device->GetAdapter(&adapter))) { rtReportError("dx11", "Failed to retrieve the dxgi adapter."); return RT_UNKNOWN_ERROR; } if (FAILED(adapter->GetParent(IID_PPV_ARGS(&g_gpu.dxgi_factory)))) { rtReportError("dx11", "Failed to retrieve the dxgi factory."); return RT_UNKNOWN_ERROR; } g_gpu.device->CheckFeatureSupport(D3D11_FEATURE_THREADING, &g_gpu.threading_support, sizeof(g_gpu.threading_support)); g_gpu.swap_chain = CreateSwapChain(info->hWnd); g_gpu.context_lock = rtCreateMutex(); rt_result res = InitCommandBufferManagement(); if (res != RT_SUCCESS) return res; res = InitRenderTargetManagement(); if (res != RT_SUCCESS) return res; res = InitBufferManagement(); if (res != RT_SUCCESS) return res; return RT_SUCCESS; } extern "C" void RT_RENDERER_API_FN(Shutdown)(void) { ShutdownBufferManagement(); ShutdownRenderTargetManagement(); ShutdownCommandBufferManagement(); rtDestroyMutex(g_gpu.context_lock); g_gpu.swap_chain.rtv.Reset(); g_gpu.swap_chain.swap_chain.Reset(); g_gpu.dxgi_factory.Reset(); g_gpu.device.Reset(); } extern "C" unsigned int RT_RENDERER_API_FN(GetMaxFramesInFlight)(void) { // TODO: Verify this. return 1; } extern "C" void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { RT_UNUSED(frame_id); FLOAT clear_color[4] = { 0, 0, 0, 0, }; rtLockMutex(g_gpu.context_lock); g_gpu.device_context->ClearRenderTargetView(g_gpu.swap_chain.rtv.Get(), clear_color); rtUnlockMutex(g_gpu.context_lock); } extern "C" void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { RT_UNUSED(frame_id); rtLockMutex(g_gpu.context_lock); UINT sync_interval = rt_Dx11VSync.i ? 1 : 0; g_gpu.swap_chain.swap_chain->Present(sync_interval, 0); rtUnlockMutex(g_gpu.context_lock); } // Copied from null. Delete once no longer needed extern "C" { #define RETURN_HANDLE_STUB2(type, initial) \ static unsigned int s_next = (initial); \ s_next = (s_next + 1) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \ type h = { \ 1, \ s_next, \ }; \ return h; #define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1) #define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \ static unsigned int s_next = (initial); \ for (uint32_t i = 0; i < (count); ++i) { \ (out)[i].index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \ (out)[i].version = 1; \ } #define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_semaphore_info *info, rt_gpu_semaphore_handle *p_semaphores) { RT_UNUSED(info); RETURN_HANDLE_ARRAY_STUB2(p_semaphores, count, 3) return RT_SUCCESS; } void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *semaphores) { RT_UNUSED(count); RT_UNUSED(semaphores); } /* NOTE(Kevin): It might become necessary to actually track the value, to correctly simulate gpu * behaviour */ uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) { RT_UNUSED(sem); return 0; } rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { return {1, 1}; } rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { return {1, 2}; } }