package d3d11_backend import "core:log" import d3d11 "vendor:directx/d3d11" import dxgi "vendor:directx/dxgi" import bk ".." // --- Lifecycle --- shutdown_d3d11 :: proc() { if g_d3d == nil {return} // Flush pending commands if g_d3d.ctx != nil { g_d3d.ctx->Flush() // Unbind all state to avoid release-order issues g_d3d.ctx->OMSetRenderTargets(0, nil, nil) g_d3d.ctx->IASetInputLayout(nil) g_d3d.ctx->VSSetShader(nil, nil, 0) g_d3d.ctx->PSSetShader(nil, nil, 0) g_d3d.ctx->CSSetShader(nil, nil, 0) } // Destroy all active samplers for &entry in g_d3d.samplers { if entry.active && entry.state != nil { entry.state->Release() entry.active = false } } // Destroy all active framebuffers for &entry in g_d3d.framebuffers { if entry.active { // RTV and DSV are owned by texture entries, not duplicated here entry.active = false } } // Destroy render passes (metadata only, nothing to release) for &entry in g_d3d.render_passes { entry.active = false } // Destroy all active descriptors (metadata only) for &entry in g_d3d.descriptors { entry.active = false } // Destroy all active shaders for &entry in g_d3d.shaders { if entry.active { if entry.vs != nil {entry.vs->Release()} if entry.ps != nil {entry.ps->Release()} if entry.cs != nil {entry.cs->Release()} if entry.vs_blob != nil {entry.vs_blob->Release()} entry.active = false } } // Destroy all active pipelines for &entry in g_d3d.pipelines { if entry.active { if entry.vs != nil {entry.vs->Release()} if entry.ps != nil {entry.ps->Release()} if entry.cs != nil {entry.cs->Release()} if entry.input_layout != nil {entry.input_layout->Release()} if entry.rasterizer_state != nil {entry.rasterizer_state->Release()} if entry.blend_state != nil {entry.blend_state->Release()} if entry.depth_stencil_state != nil {entry.depth_stencil_state->Release()} entry.active = false } } // Destroy all active textures for &entry in g_d3d.textures { if entry.active { if entry.srv != nil {entry.srv->Release()} if entry.rtv != nil {entry.rtv->Release()} if entry.dsv != nil {entry.dsv->Release()} if entry.texture != nil {entry.texture->Release()} entry.active = false } } // Destroy all active buffers for &entry in g_d3d.buffers { if entry.active { if entry.buffer != nil {entry.buffer->Release()} entry.active = false } } // Destroy core objects if g_d3d.current_rasterizer_state != nil { g_d3d.current_rasterizer_state->Release() } if g_d3d.push_constant_buf != nil { g_d3d.push_constant_buf->Release() } release_backbuffer_and_depth(g_d3d) if g_d3d.swap_chain != nil {g_d3d.swap_chain->Release()} if g_d3d.ctx != nil {g_d3d.ctx->Release()} if g_d3d.device != nil {g_d3d.device->Release()} free(g_d3d) g_d3d = nil bk.backend_initialized = false } wait_idle_d3d11 :: proc() { if g_d3d != nil && g_d3d.ctx != nil { g_d3d.ctx->Flush() } } // --- Frame lifecycle --- begin_frame_d3d11 :: proc(clear_color: [4]f32) -> (bk.Frame_Context, bool) { if g_d3d == nil {return {}, false} g_d3d.frame_active = true g_d3d.clear_color = clear_color return bk.Frame_Context{frame_index = g_d3d.current_frame}, true } end_frame_d3d11 :: proc(ctx: bk.Frame_Context) -> bool { if g_d3d == nil || !g_d3d.frame_active {return false} // Present with vsync (SyncInterval = 1) result := g_d3d.swap_chain->Present(1, {}) g_d3d.frame_active = false g_d3d.render_pass_active = false g_d3d.current_frame = (g_d3d.current_frame + 1) % bk.MAX_FRAMES_IN_FLIGHT if result == dxgi.STATUS_OCCLUDED { return false } if result < 0 { log.errorf("gpu/d3d11: Present failed: 0x%08X", u32(result)) // Could be a device lost situation -- handle resize return true } return false } on_resize_d3d11 :: proc(width, height: u32) { if g_d3d == nil {return} if width == 0 || height == 0 {return} // Flush pending work g_d3d.ctx->Flush() // Unbind render targets before resizing g_d3d.ctx->OMSetRenderTargets(0, nil, nil) // Release old backbuffer and depth release_backbuffer_and_depth(g_d3d) // Resize swap chain buffers (0 = keep existing buffer count) result := g_d3d.swap_chain->ResizeBuffers(0, width, height, .UNKNOWN, {}) if result < 0 { log.errorf("gpu/d3d11: ResizeBuffers failed: 0x%08X", u32(result)) return } g_d3d.width = width g_d3d.height = height // Recreate backbuffer RTV create_backbuffer_rtv(g_d3d) // Recreate depth buffer create_depth_buffer(g_d3d, width, height) } get_extent_d3d11 :: proc() -> bk.Extent { if g_d3d == nil {return {}} return bk.Extent{width = g_d3d.width, height = g_d3d.height} } // --- Render pass commands --- begin_render_pass_d3d11 :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) { if g_d3d == nil {return} fb_entry, fb_ok := framebuffer_entry(desc.framebuffer) if !fb_ok {return} // Set render targets if fb_entry.rtv_count > 0 { g_d3d.ctx->OMSetRenderTargets(fb_entry.rtv_count, &fb_entry.rtvs[0], fb_entry.dsv) for i in 0.. 0 else desc.clear_color g_d3d.ctx->ClearRenderTargetView(fb_entry.rtvs[i], &cc) } } else { g_d3d.ctx->OMSetRenderTargets(0, nil, fb_entry.dsv) } if fb_entry.dsv != nil { clear_flags := d3d11.CLEAR_FLAGS{.DEPTH} if pass, pass_ok := render_pass_entry(desc.pass); pass_ok && pass.desc.has_stencil { clear_flags += {.STENCIL} } g_d3d.ctx->ClearDepthStencilView(fb_entry.dsv, clear_flags, desc.clear_depth, u8(desc.clear_stencil)) } // Set viewport viewport := d3d11.VIEWPORT { TopLeftX = 0, TopLeftY = 0, Width = f32(desc.width), Height = f32(desc.height), MinDepth = 0, MaxDepth = 1, } g_d3d.ctx->RSSetViewports(1, &viewport) g_d3d.render_pass_active = true } begin_default_pass_d3d11 :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) { if g_d3d == nil {return} // Bind backbuffer + depth g_d3d.ctx->OMSetRenderTargets(1, &g_d3d.backbuffer_rtv, g_d3d.depth_dsv) // Clear cc := clear_color g_d3d.ctx->ClearRenderTargetView(g_d3d.backbuffer_rtv, &cc) if g_d3d.depth_dsv != nil { g_d3d.ctx->ClearDepthStencilView(g_d3d.depth_dsv, {.DEPTH}, 1.0, 0) } // Set default viewport viewport := d3d11.VIEWPORT { TopLeftX = 0, TopLeftY = 0, Width = f32(g_d3d.width), Height = f32(g_d3d.height), MinDepth = 0, MaxDepth = 1, } g_d3d.ctx->RSSetViewports(1, &viewport) // Set default scissor (full screen) scissor := d3d11.RECT { left = 0, top = 0, right = i32(g_d3d.width), bottom = i32(g_d3d.height), } g_d3d.ctx->RSSetScissorRects(1, &scissor) g_d3d.render_pass_active = true } end_render_pass_d3d11 :: proc(ctx: bk.Frame_Context) { if g_d3d == nil {return} g_d3d.render_pass_active = false // D3D11 has no explicit render pass end -- nothing to do } set_viewport_d3d11 :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) { if g_d3d == nil {return} viewport := d3d11.VIEWPORT { TopLeftX = x, TopLeftY = y, Width = w, Height = h, MinDepth = 0, MaxDepth = 1, } g_d3d.ctx->RSSetViewports(1, &viewport) } set_scissor_d3d11 :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) { if g_d3d == nil {return} scissor := d3d11.RECT { left = x, top = y, right = x + i32(w), bottom = y + i32(h), } g_d3d.ctx->RSSetScissorRects(1, &scissor) } set_depth_bias_d3d11 :: proc(ctx: bk.Frame_Context, constant, slope: f32) { if g_d3d == nil {return} // D3D11 requires recreating the rasterizer state to change depth bias desc := g_d3d.current_rasterizer_desc desc.DepthBias = i32(constant) desc.SlopeScaledDepthBias = slope desc.DepthBiasClamp = 0 rs: ^d3d11.IRasterizerState result := g_d3d.device->CreateRasterizerState(&desc, &rs) if result < 0 {return} if g_d3d.current_rasterizer_state != nil { g_d3d.current_rasterizer_state->Release() } g_d3d.current_rasterizer_state = rs g_d3d.current_rasterizer_desc = desc g_d3d.ctx->RSSetState(rs) } // --- Sync queries --- get_default_render_pass_d3d11 :: proc() -> bk.Render_Pass_Handle { if g_d3d == nil {return bk.NULL_RENDER_PASS} return g_d3d.default_render_pass } get_depth_format_d3d11 :: proc() -> bk.Format { if g_d3d == nil {return .Undefined} return from_dxgi_format(g_d3d.depth_format) }