// Calls D3D11 Api for loading and displaying textures. image_show_texture :: (using id: *Image_Data) { show_d3d_texture(*d3d_texture, scale_factor); } icon_load_texture :: (using id: *Image_Data) { if !g_pd3dDevice then return; desc : D3D11_TEXTURE2D_DESC; memset(*desc, 0, size_of(D3D11_TEXTURE2D_DESC)); desc.Width = cast(u32)width; desc.Height = cast(u32)height; desc.MipLevels = 1; desc.ArraySize = 1; // desc.Format = DXGI_FORMAT.R8G8B8A8_UNORM; desc.Format = DXGI_FORMAT.B8G8R8A8_UNORM; // BGRA, as GDI hands out bitmaps desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE.DEFAULT; desc.BindFlags = D3D11_BIND_FLAG.SHADER_RESOURCE; desc.CPUAccessFlags = 0; // d3d_texture.texture = null; subResource : D3D11_SUBRESOURCE_DATA; memset(*subResource, 0, size_of(D3D11_SUBRESOURCE_DATA)); subResource.pSysMem = id.data; subResource.SysMemPitch = (desc.Width * 4); subResource.SysMemSlicePitch = 0; hr := ID3D11Device_CreateTexture2D(g_pd3dDevice, *desc, *subResource, *d3d_texture.texture); if (hr != S_OK) { print("ID3D11Device_CreateBuffer failed: %\n", hr); return; } srvDesc : D3D11_SHADER_RESOURCE_VIEW_DESC; memset(*srvDesc, 0, size_of(D3D11_SHADER_RESOURCE_VIEW_DESC)); srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION.TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; hr = ID3D11Device_CreateShaderResourceView(g_pd3dDevice, d3d_texture.texture, *srvDesc, *d3d_texture.out_srv); if (hr != S_OK) { print("ID3D11Device_CreateShaderResourceView failed: %\n", hr); IUnknown_Release(d3d_texture.texture); d3d_texture.texture = null; return; } IUnknown_Release(d3d_texture.texture); d3d_texture.texture = null; d3d_texture.out_width = width; d3d_texture.out_height = height; d3d_texture.loaded = true; } show_d3d_texture :: (using d3d_texture: *D3D11_Texture, scale_factor: float32 = 1) { if loaded { // ImGui.Image(Get_ImTextureRef(d3d_texture), // Vector2.{scale_factor * cast(float32)d3d_texture.out_width, scale_factor * cast(float32)d3d_texture.out_height}, Vector2.{0, 0}, // Vector2.{1,1}, Vector4.{1,1,1,1}, Vector4.{0,0,0,0}); } } show_d3d_texture_height :: (using d3d_texture: *D3D11_Texture, new_height: float32) { new_scale_factor := new_height / d3d_texture.out_height; new_width := d3d_texture.out_width * new_scale_factor; if loaded { ImGui.Image(Get_ImTextureRef(d3d_texture), Vector2.{new_width, new_height}, Vector2.{0, 0}, Vector2.{1,1}, Vector4.{1,1,1,1}, Vector4.{0,0,0,0}); } } show_d3d_texture_width :: (using d3d_texture: *D3D11_Texture, new_width: float32) { new_scale_factor := new_width / d3d_texture.out_width; new_height := d3d_texture.out_height * new_scale_factor; if loaded { ImGui.Image(Get_ImTextureRef(d3d_texture), Vector2.{new_width, new_height}, Vector2.{0, 0}, Vector2.{1,1}, Vector4.{1,1,1,1}, Vector4.{0,0,0,0}); } } DXGI_Handle_Vsync :: () { sync_interval: u32 = xx ifx app.vsync_on && app.frame_count > 1 then 1 else 0; hr := IDXGISwapChain_Present(g_pSwapChain, sync_interval, 0); g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); d3d11_check_for_device_loss(hr, "IDXGISwapChain_Present"); } D3D11_HandleWindowResize :: () { if g_device_lost then return; if (g_resizeWidth != 0 && g_resizeHeight != 0) { D3D11_CleanupRenderTarget(); hr := IDXGISwapChain_ResizeBuffers(g_pSwapChain, 0, g_resizeWidth, g_resizeHeight, DXGI_FORMAT.UNKNOWN, 0); g_resizeWidth = 0; g_resizeHeight = 0; if d3d11_check_for_device_loss(hr, "IDXGISwapChain_ResizeBuffers") then return; if !D3D11_CreateRenderTarget() { log_error("[D3D11] No render target after a resize; recreating the device."); g_device_lost = true; } } } // Returns true when hr failed because the device is gone. Recovery then runs from // d3d11_recover_lost_device_if_needed, and no frame is drawn until it succeeds. d3d11_check_for_device_loss :: (hr: HRESULT, call: string) -> bool { if !FAILED(hr) then return false; reason := ID3D11Device_GetDeviceRemovedReason(g_pd3dDevice); if reason == S_OK { // A Present that keeps failing would log every frame for as long as the app runs. d3d11_failures_unlogged += 1; now := current_time_monotonic(); if !d3d11_failure_logged || to_float64_seconds(now - d3d11_failure_logged_at) >= 10 { if d3d11_failures_unlogged > 1 log_error("[D3D11] % failed: % (% failures since the last report)", call, d3d11_hresult_string(hr), d3d11_failures_unlogged); else log_error("[D3D11] % failed: %", call, d3d11_hresult_string(hr)); d3d11_failure_logged = true; d3d11_failure_logged_at = now; d3d11_failures_unlogged = 0; } return false; } if !g_device_lost { log_error("[D3D11] % failed with %; the device was removed (reason %). Recreating it.", call, d3d11_hresult_string(hr), d3d11_hresult_string(reason)); } g_device_lost = true; return true; } // Present is the only call that reports a removed device, but the ImGui backend creates its device // objects in NewFrame and textures in RenderDrawData, and asserts when that fails. So the device is // asked before each; a removal in the moment between the check and the call can still assert. d3d11_device_was_removed :: () -> bool { if g_device_lost then return true; reason := ID3D11Device_GetDeviceRemovedReason(g_pd3dDevice); if reason == S_OK then return false; log_error("[D3D11] The device was removed (reason %). Recreating it.", d3d11_hresult_string(reason)); g_device_lost = true; return true; } D3D11_CleanupDevice :: () { D3D11_CleanupRenderTarget(); D3D11_ReleaseSwapChain(); if (g_pd3dDeviceContext) { IUnknown_Release(g_pd3dDeviceContext); g_pd3dDeviceContext = null; } if (g_pd3dDevice) { IUnknown_Release(g_pd3dDevice); g_pd3dDevice = null; } } D3D11_CleanupRenderTarget :: () { if (g_mainRenderTargetView) { IUnknown_Release(g_mainRenderTargetView); g_mainRenderTargetView = null; } } D3D11_ReleaseSwapChain :: () { if (g_pSwapChain) { IUnknown_Release(g_pSwapChain); g_pSwapChain = null; } } D3D11_InitDevice :: (hwnd: HWND, requested_driver_type: D3D_DRIVER_TYPE) -> bool { driver := requested_driver_type; device_start := current_time_monotonic(); device, device_context, level, hr := d3d11_create_device(driver); if FAILED(hr) { fallback := ifx driver == .HARDWARE then D3D_DRIVER_TYPE.WARP else .HARDWARE; log_error("[D3D11] D3D11CreateDevice(%) failed with %; trying % instead.", driver, d3d11_hresult_string(hr), fallback); driver = fallback; device, device_context, level, hr = d3d11_create_device(driver); if FAILED(hr) { log_error("[D3D11] D3D11CreateDevice(%) failed with %.", driver, d3d11_hresult_string(hr)); return false; } } perf.startup_device_ms = to_float64_seconds(current_time_monotonic() - device_start) * 1000.0; swap_chain_start := current_time_monotonic(); if !d3d11_attach_device(device, device_context, hwnd) { d3d11_release_device(device, device_context); return false; } perf.startup_swap_chain_ms = to_float64_seconds(current_time_monotonic() - swap_chain_start) * 1000.0; g_driver_type = driver; g_feature_level = level; d3d11_update_adapter_name(); return true; } D3D11_CreateRenderTarget :: () -> bool { pBackBuffer: *ID3D11Texture2D; hr := IDXGISwapChain_GetBuffer(g_pSwapChain, 0, *uid(ID3D11Texture2D_UUID), xx *pBackBuffer); if FAILED(hr) { log_error("[D3D11] IDXGISwapChain_GetBuffer failed: %", d3d11_hresult_string(hr)); return false; } defer IUnknown_Release(pBackBuffer); hr = ID3D11Device_CreateRenderTargetView(g_pd3dDevice, pBackBuffer, null, *g_mainRenderTargetView); if FAILED(hr) { log_error("[D3D11] ID3D11Device_CreateRenderTargetView failed: %", d3d11_hresult_string(hr)); return false; } return true; } delete_d3d_texture :: (using d3d_texture: *D3D11_Texture) { if loaded { IUnknown_Release(out_srv); loaded = false; } } Image_Data :: struct { data: *u8; width: s32; height: s32; depth: s32; scale_factor: float32 = 1.0; d3d_texture: D3D11_Texture; } // texture_of :: (using im: *Image_Data) -> ImGui.ImTextureID #expand { // Get_ImTextureRef(*d3d_texture); // } D3D11_Texture :: struct { texture: *ID3D11Texture2D; out_srv: *ID3D11ShaderResourceView; out_width : s32; out_height : s32; loaded := false; } Get_ImTextureRef :: (using d3d_texture: *D3D11_Texture) -> ImGui.ImTextureRef #expand { ref: ImGui.ImTextureRef = { null, cast(ImGui.ImTextureID)out_srv }; return ref; } // A texture from tightly packed RGBA pixels, at most GPU_TEXTURE_MAX_SIDE a side. Its handle is the // shader resource view, which is what the ImGui DX11 backend draws. A zero handle when it failed. gpu_texture_create :: (pixels: *u8, width: s32, height: s32) -> Gpu_Texture { result: Gpu_Texture; if !g_pd3dDevice || width <= 0 || height <= 0 || width > GPU_TEXTURE_MAX_SIDE || height > GPU_TEXTURE_MAX_SIDE return result; desc: D3D11_TEXTURE2D_DESC; desc.Width = cast(u32) width; desc.Height = cast(u32) height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = .R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = .IMMUTABLE; desc.BindFlags = .SHADER_RESOURCE; data: D3D11_SUBRESOURCE_DATA; data.pSysMem = pixels; data.SysMemPitch = cast(u32) width * 4; texture: *ID3D11Texture2D; hr := ID3D11Device_CreateTexture2D(g_pd3dDevice, *desc, *data, *texture); if FAILED(hr) { log_error("[D3D11] CreateTexture2D (%x%) failed: %", width, height, d3d11_hresult_string(hr)); return result; } defer IUnknown_Release(texture); // the view keeps it alive view_desc: D3D11_SHADER_RESOURCE_VIEW_DESC; view_desc.Format = desc.Format; view_desc.ViewDimension = .TEXTURE2D; view_desc.Texture2D.MipLevels = 1; view: *ID3D11ShaderResourceView; hr = ID3D11Device_CreateShaderResourceView(g_pd3dDevice, texture, *view_desc, *view); if FAILED(hr) { log_error("[D3D11] CreateShaderResourceView failed: %", d3d11_hresult_string(hr)); return result; } result.handle = view; result.width = width; result.height = height; return result; } gpu_texture_release :: (texture: *Gpu_Texture) { if texture.handle IUnknown_Release(cast(*ID3D11ShaderResourceView) texture.handle); texture.* = .{}; } // --------------------------------------------------------------------------------------------- // #WarpFirstStartup // // Creating a hardware D3D11 device loads the vendor's user-mode driver, which can take hundreds of // milliseconds. A WARP (software) device can be initialized rapidly and set to work rendering // while the hardware device initializes in the background. So the window starts on WARP while a // worker thread creates the hardware device, and the renderer moves across at a frame boundary // once it is ready. // // main(): D3D11_InitDevice(hwnd, .WARP) first frames render on WARP // d3d11_start_hardware_device_thread() once WARP is up, so the two creations never overlap // main loop: d3d11_swap_to_hardware_if_ready() between the message pump and ImGui.NewFrame() // exit: d3d11_finish_hardware_device_thread() // --------------------------------------------------------------------------------------------- d3d11_hw: D3D11_Hardware_Device_Task; D3D11_Hardware_Device_Task :: struct { thread: *Thread; swap_attempted: bool; // set once, whether the swap succeeded or not; we never retry // Written by the worker, read by the main thread only after thread_is_done() has returned true. device: *ID3D11Device; device_context: *ID3D11DeviceContext; level: D3D_FEATURE_LEVEL; hr: HRESULT = E_FAIL; create_ms: float64; // how long D3D11CreateDevice took on the worker finish_time: Apollo_Time; // when the worker finished } d3d11_start_hardware_device_thread :: () { push_allocator(context.default_allocator); d3d11_hw.thread = New(Thread); if !thread_init(d3d11_hw.thread, d3d11_hardware_device_thread_proc, name = "d3d11 device") { log_error("[D3D11] Could not start the hardware device thread; staying on WARP."); free(d3d11_hw.thread); d3d11_hw.thread = null; return; } thread_start(d3d11_hw.thread); } d3d11_finish_hardware_device_thread :: () { if !d3d11_hw.thread then return; thread_is_done(d3d11_hw.thread, -1); d3d11_free_hardware_device_thread(); d3d11_release_device(d3d11_hw.device, d3d11_hw.device_context); d3d11_hw.device = null; d3d11_hw.device_context = null; } // Must run between the message pump and ImGui.NewFrame(): the swap destroys and recreates every // ImGui platform window, which is only legal outside a frame. d3d11_swap_to_hardware_if_ready :: () { if d3d11_hw.swap_attempted || !d3d11_hw.thread then return; if !thread_is_done(d3d11_hw.thread) then return; d3d11_hw.swap_attempted = true; d3d11_free_hardware_device_thread(); new_device := d3d11_hw.device; new_context := d3d11_hw.device_context; d3d11_hw.device = null; d3d11_hw.device_context = null; if FAILED(d3d11_hw.hr) || !new_device || !new_context { log_error("[D3D11] Hardware device creation failed with %; staying on WARP.", d3d11_hresult_string(d3d11_hw.hr)); d3d11_release_device(new_device, new_context); return; } perf.hw_device_create_ms = d3d11_hw.create_ms; perf.hw_device_ready_ms = to_float64_seconds(d3d11_hw.finish_time - app.startup_time) * 1000.0; // Nothing to gain: WARP was unavailable and D3D11_InitDevice already fell back to hardware. if g_driver_type == .HARDWARE || !d3d11_is_worth_swapping_to(new_device) { d3d11_release_device(new_device, new_context); return; } swap_start := current_time_monotonic(); if !d3d11_replace_device(new_device, new_context, .HARDWARE, d3d11_hw.level) { log_error("[D3D11] Could not bring up a swap chain on the hardware device; staying on WARP."); if !g_pSwapChain { log_error("[D3D11] Lost the renderer entirely while swapping devices. Exiting."); app.should_exit = true; } return; } perf.hw_swap_ms = to_float64_seconds(current_time_monotonic() - swap_start) * 1000.0; log("[D3D11] Swapped to '%'. Device creation took % ms on the worker, ready % ms after launch; the swap took % ms.", perf.adapter_name, FF(perf.hw_device_create_ms, 1), FF(perf.hw_device_ready_ms, 1), FF(perf.hw_swap_ms, 1)); } // Must run between the message pump and ImGui.NewFrame(), for the same reason as // d3d11_swap_to_hardware_if_ready. Retries at most once a second until a device comes up. d3d11_recover_lost_device_if_needed :: () { if !g_device_lost then return; if to_float64_seconds(current_time_monotonic() - g_device_recovery_last_attempt) < 1.0 then return; g_device_recovery_last_attempt = current_time_monotonic(); // A pending hardware device may have been created on the adapter that was just removed. if d3d11_hw.thread && !d3d11_hw.swap_attempted { d3d11_hw.swap_attempted = true; d3d11_finish_hardware_device_thread(); } driver := D3D_DRIVER_TYPE.HARDWARE; device, device_context, level, hr := d3d11_create_device(driver); if FAILED(hr) { log_error("[D3D11] Recreating a hardware device failed with %; trying WARP.", d3d11_hresult_string(hr)); driver = .WARP; device, device_context, level, hr = d3d11_create_device(driver); if FAILED(hr) { log_error("[D3D11] Recreating a WARP device failed with %; retrying in a second.", d3d11_hresult_string(hr)); return; } } if !d3d11_replace_device(device, device_context, driver, level) { log_error("[D3D11] Could not bring up a swap chain on the recreated device; retrying in a second."); return; } g_device_lost = false; log("[D3D11] Recovered from device loss on '%' (%).", perf.adapter_name, g_driver_type); } // Moves the renderer, the swap chain, and both ImGui backends onto new_device, which it takes // ownership of. On failure new_device is released and the old device is put back, though its // swap chain can still fail to come back: check g_pSwapChain. d3d11_replace_device :: (new_device: *ID3D11Device, new_context: *ID3D11DeviceContext, driver: D3D_DRIVER_TYPE, level: D3D_FEATURE_LEVEL) -> bool { // ImplDX11_Shutdown destroys every platform window, including the main viewport's Win32 data, // which only ImplWin32_Init restores; so both backends go down and come back up together. // // ImplWin32_Shutdown calls ClearPlatformHandlers, which also drops the clipboard, open-in-shell // and IME handlers that ImGui installed at context creation and that ImplWin32_Init never // reinstalls. Without copying and restoring them, InputText copy/paste silently breaks for the rest of the run. platform_io := ImGui.GetPlatformIO(); saved_get_clipboard_text := platform_io.Platform_GetClipboardTextFn; saved_set_clipboard_text := platform_io.Platform_SetClipboardTextFn; saved_clipboard_user_data := platform_io.Platform_ClipboardUserData; saved_open_in_shell := platform_io.Platform_OpenInShellFn; saved_open_in_shell_user_data := platform_io.Platform_OpenInShellUserData; saved_set_ime_data := platform_io.Platform_SetImeDataFn; saved_ime_user_data := platform_io.Platform_ImeUserData; ImGui.ImplDX11_Shutdown(); ImGui.ImplWin32_Shutdown(); old_device := g_pd3dDevice; old_context := g_pd3dDeviceContext; // The window can only have one swap chain, so the old one has to be gone before the new one is // created. Flush after ClearState so the deferred destruction actually happens now. D3D11_CleanupRenderTarget(); ID3D11DeviceContext_ClearState(old_context); ID3D11DeviceContext_Flush(old_context); D3D11_ReleaseSwapChain(); replaced := d3d11_attach_device(new_device, new_context, platform.hwnd); if replaced { g_driver_type = driver; g_feature_level = level; d3d11_release_device(old_device, old_context); } else { d3d11_release_device(new_device, new_context); d3d11_attach_device(old_device, old_context, platform.hwnd); } // The new chain was sized to the current client area, so any resize queued before the swap is moot. g_resizeWidth = 0; g_resizeHeight = 0; g_SwapChainOccluded = false; ImGui.ImplWin32_Init(platform.hwnd); ImGui.ImplDX11_Init( cast,no_check(*ImGui.ID3D11Device) g_pd3dDevice, cast,no_check(*ImGui.ID3D11DeviceContext) g_pd3dDeviceContext); imgui_reset_key_down_state(); platform_io.Platform_GetClipboardTextFn = saved_get_clipboard_text; platform_io.Platform_SetClipboardTextFn = saved_set_clipboard_text; platform_io.Platform_ClipboardUserData = saved_clipboard_user_data; platform_io.Platform_OpenInShellFn = saved_open_in_shell; platform_io.Platform_OpenInShellUserData = saved_open_in_shell_user_data; if !platform_io.Platform_SetImeDataFn { platform_io.Platform_SetImeDataFn = saved_set_ime_data; platform_io.Platform_ImeUserData = saved_ime_user_data; } d3d11_update_adapter_name(); // Textures belong to the device they were made on: release any the app made with // icon_load_texture here, and make them again on the new device. Textures made with // gpu_texture_create are made again by whoever holds them when they see this change. if replaced g_texture_generation += 1; return replaced; } #scope_file d3d11_hardware_device_thread_proc :: (thread: *Thread) -> s64 { start := current_time_monotonic(); device, device_context, level, hr := d3d11_create_device(.HARDWARE); finish := current_time_monotonic(); d3d11_hw.device = device; d3d11_hw.device_context = device_context; d3d11_hw.level = level; d3d11_hw.hr = hr; d3d11_hw.create_ms = to_float64_seconds(finish - start) * 1000.0; d3d11_hw.finish_time = finish; return 0; } d3d11_free_hardware_device_thread :: () { push_allocator(context.default_allocator); thread_deinit(d3d11_hw.thread); free(d3d11_hw.thread); d3d11_hw.thread = null; } // Swapping WARP for something that is not actually a GPU buys nothing and costs a visible hitch. d3d11_is_worth_swapping_to :: (device: *ID3D11Device) -> bool { removed := ID3D11Device_GetDeviceRemovedReason(device); if removed != S_OK { log_error("[D3D11] The hardware device was removed before we could swap to it: %", d3d11_hresult_string(removed)); return false; } desc, ok := d3d11_adapter_desc(device); if ok && desc.VendorId == DXGI_VENDOR_ID_MICROSOFT && desc.DeviceId == DXGI_DEVICE_ID_BASIC_RENDER_DRIVER { log("[D3D11] The hardware adapter is the Microsoft Basic Render Driver; staying on WARP."); return false; } return true; } d3d11_create_device :: (driver_type: D3D_DRIVER_TYPE) -> (device: *ID3D11Device, device_context: *ID3D11DeviceContext, level: D3D_FEATURE_LEVEL, hr: HRESULT) { flags: D3D11_CREATE_DEVICE_FLAG; levels: [2]D3D_FEATURE_LEVEL = .[._11_0, ._10_0]; device: *ID3D11Device; device_context: *ID3D11DeviceContext; level: D3D_FEATURE_LEVEL; hr := D3D11CreateDevice(null, driver_type, null, flags, levels.data, xx levels.count, D3D11_SDK_VERSION, *device, *level, *device_context); return device, device_context, level, hr; } d3d11_attach_device :: (device: *ID3D11Device, device_context: *ID3D11DeviceContext, hwnd: HWND) -> bool { g_pd3dDevice = device; g_pd3dDeviceContext = device_context; swap_chain, ok := d3d11_create_swap_chain(device, hwnd); if !ok then return false; g_pSwapChain = swap_chain; if !D3D11_CreateRenderTarget() { D3D11_ReleaseSwapChain(); return false; } return true; } // MakeWindowAssociation is per-factory, so it is redone every time the swap chain is recreated. d3d11_create_swap_chain :: (device: *ID3D11Device, hwnd: HWND) -> (swap_chain: *IDXGISwapChain, success: bool) { factory, have_factory := d3d11_factory_for_device(device); if !have_factory then return null, false; defer IUnknown_Release(factory); sd : DXGI_SWAP_CHAIN_DESC; sd.BufferCount = 2; sd.BufferDesc.Width = 0; // 0x0 sizes the buffers to the window's client area sd.BufferDesc.Height = 0; sd.BufferDesc.Format = .R8G8B8A8_UNORM; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.Flags = xx DXGI_SWAP_CHAIN_FLAG.ALLOW_MODE_SWITCH; sd.BufferUsage = DXGI_USAGE.RENDER_TARGET_OUTPUT; sd.OutputWindow = hwnd; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.Windowed = .TRUE; sd.SwapEffect = DXGI_SWAP_EFFECT.DISCARD; swap_chain: *IDXGISwapChain; hr := IDXGIFactory_CreateSwapChain(factory, device, *sd, *swap_chain); if FAILED(hr) { log_error("[D3D11] IDXGIFactory_CreateSwapChain failed: %", d3d11_hresult_string(hr)); return null, false; } // Disable DXGI's default Alt+Enter fullscreen behavior. // - You are free to leave this enabled, but it will not work properly with multiple viewports. // - This must be done for all windows associated to the device. Our DX11 backend does this automatically for secondary viewports that it creates. IDXGIFactory_MakeWindowAssociation(factory, hwnd, cast(u32) DXGI_MWA.NO_ALT_ENTER); return swap_chain, true; } d3d11_factory_for_device :: (device: *ID3D11Device) -> (factory: *IDXGIFactory, success: bool) { adapter, have_adapter := d3d11_adapter_for_device(device); if !have_adapter then return null, false; defer IUnknown_Release(adapter); factory: *IDXGIFactory; hr := IDXGIObject_GetParent(adapter, *uid(IDXGIFactory_UUID), xx *factory); if FAILED(hr) { log_error("[D3D11] IDXGIObject_GetParent(IDXGIFactory) failed: %", d3d11_hresult_string(hr)); return null, false; } return factory, true; } d3d11_adapter_for_device :: (device: *ID3D11Device) -> (adapter: *IDXGIAdapter, success: bool) { dxgi_device: *IDXGIDevice; hr := IUnknown_QueryInterface(device, *uid(IDXGIDevice_UUID), xx *dxgi_device); if FAILED(hr) { log_error("[D3D11] QueryInterface(IDXGIDevice) failed: %", d3d11_hresult_string(hr)); return null, false; } defer IUnknown_Release(dxgi_device); adapter: *IDXGIAdapter; hr = IDXGIDevice_GetAdapter(dxgi_device, *adapter); if FAILED(hr) { log_error("[D3D11] IDXGIDevice_GetAdapter failed: %", d3d11_hresult_string(hr)); return null, false; } return adapter, true; } d3d11_adapter_desc :: (device: *ID3D11Device) -> (desc: DXGI_ADAPTER_DESC, success: bool) { desc: DXGI_ADAPTER_DESC; adapter, have_adapter := d3d11_adapter_for_device(device); if !have_adapter then return desc, false; defer IUnknown_Release(adapter); if FAILED(IDXGIAdapter_GetDesc(adapter, *desc)) then return desc, false; return desc, true; } d3d11_release_device :: (device: *ID3D11Device, device_context: *ID3D11DeviceContext) { if device_context then IUnknown_Release(device_context); if device then IUnknown_Release(device); } d3d11_update_adapter_name :: () { push_allocator(context.default_allocator); if perf.adapter_name.count > 0 then free(perf.adapter_name); perf.adapter_name = ""; desc, ok := d3d11_adapter_desc(g_pd3dDevice); if ok { name, converted := wide_to_utf8(cast(*u16) desc.Description.data); if converted then perf.adapter_name = name; } perf.renderer_is_software = (g_driver_type != .HARDWARE); } d3d11_hresult_string :: (hr: HRESULT) -> string { return tprint("0x%", FormatInt.{value = cast,no_check(u32) hr, base = 16, minimum_digits = 8}); } DXGI_VENDOR_ID_MICROSOFT :: 0x1414; DXGI_DEVICE_ID_BASIC_RENDER_DRIVER :: 0x8c; #scope_export // ~ Data g_driver_type: D3D_DRIVER_TYPE = .UNKNOWN; g_feature_level: D3D_FEATURE_LEVEL = ._11_0; g_pd3dDevice: *ID3D11Device; g_pd3dDeviceContext: *ID3D11DeviceContext; g_pSwapChain: *IDXGISwapChain; g_mainRenderTargetView: *ID3D11RenderTargetView; g_resizeWidth: u32 = 0; g_resizeHeight: u32 = 0; g_SwapChainOccluded: bool = false; g_device_lost: bool = false; g_device_recovery_last_attempt: Apollo_Time; // d3d11_check_for_device_loss: failures that leave the device in place, reported at most every 10 s. d3d11_failure_logged: bool; d3d11_failure_logged_at: Apollo_Time; d3d11_failures_unlogged: s64; DXGI_PRESENT_TEST :: 0x00000001;