use super::compute;
use super::frame_table::ContextFrameTable;
use super::types::{
Dx12State, SharedBufferTable, SharedComputePipelineTable, SharedContextFrameTable, SharedContextMap,
SharedLogicalDevice, SharedPipelineTable, SharedRenderTargetTable, SharedSamplerTable, SharedShaderTable,
SharedSubmissionContext, SharedTextureTable,
};
use super::{ContextHandle, DeviceHandle, GpuCommand, GraphCommand, SubmitSync};
use crate::timeline::TimelineValue;
use anyhow::{Context as _, Result};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use windows::Win32::Graphics::Direct3D12::ID3D12Fence;
pub(crate) struct Dx12RecordState<'a> {
pub ld: &'a SharedLogicalDevice,
pub devices: &'a HashMap<DeviceHandle, SharedLogicalDevice>,
pub contexts: &'a SharedContextMap,
pub frame_table: SharedContextFrameTable,
pub buffers: &'a SharedBufferTable,
pub shaders: &'a SharedShaderTable,
pub pipelines: &'a SharedPipelineTable,
pub compute_pipelines: &'a SharedComputePipelineTable,
pub render_targets: &'a SharedRenderTargetTable,
pub textures: &'a SharedTextureTable,
#[allow(dead_code)]
pub samplers: &'a SharedSamplerTable,
}
pub(crate) struct Dx12SubmitScope<'a> {
#[allow(dead_code)]
pub ctx: ContextHandle,
pub device_handle: DeviceHandle,
pub sc: super::types::SharedSubmissionContext,
pub record: Dx12RecordState<'a>,
pub context_fences: &'a Arc<RwLock<HashMap<ContextHandle, super::types::ContextFenceEntry>>>,
pub ctx_fence: ID3D12Fence,
pub device_owner: Option<ContextHandle>,
}
impl<'a> Dx12SubmitScope<'a> {
pub(crate) fn assert_ctx(&self, ctx: ContextHandle) {
debug_assert_eq!(self.ctx, ctx, "ContextSubmitSession invoked with wrong context handle");
}
pub fn ld(&self) -> &'a SharedLogicalDevice {
self.record.ld
}
pub fn devices(&self) -> &'a HashMap<DeviceHandle, SharedLogicalDevice> {
self.record.devices
}
pub fn contexts(&self) -> &'a SharedContextMap {
self.record.contexts
}
pub fn frame_table(&self) -> &ContextFrameTable {
&self.record.frame_table
}
pub fn buffers(&self) -> &'a SharedBufferTable {
self.record.buffers
}
#[allow(dead_code)]
pub fn shaders(&self) -> &'a SharedShaderTable {
self.record.shaders
}
#[allow(dead_code)]
pub fn pipelines(&self) -> &'a SharedPipelineTable {
self.record.pipelines
}
pub fn compute_pipelines(&self) -> &'a SharedComputePipelineTable {
self.record.compute_pipelines
}
pub fn render_targets(&self) -> &'a SharedRenderTargetTable {
self.record.render_targets
}
pub fn textures(&self) -> &'a SharedTextureTable {
self.record.textures
}
#[allow(dead_code)]
pub fn samplers(&self) -> &'a SharedSamplerTable {
self.record.samplers
}
}
pub(crate) fn record_state_from_backend<'a>(
state: &'a Dx12State,
ctx: ContextHandle,
device_handle: DeviceHandle,
) -> Result<Dx12RecordState<'a>> {
let frame_table = Arc::clone(
&state
.contexts
.read()
.unwrap()
.get(&ctx)
.with_context(|| format!("Invalid context handle {ctx}"))?
.lock()
.unwrap()
.frame_table,
);
Ok(Dx12RecordState {
ld: state
.devices
.get(&device_handle)
.with_context(|| format!("Invalid device {device_handle}"))?,
devices: &state.devices,
contexts: &state.contexts,
frame_table,
buffers: &state.buffers,
shaders: &state.shaders,
pipelines: &state.pipelines,
compute_pipelines: &state.compute_pipelines,
render_targets: &state.render_targets,
textures: &state.textures,
samplers: &state.samplers,
})
}
pub(crate) fn record_state_for_legacy_render<'a>(
state: &'a mut Dx12State,
device_handle: DeviceHandle,
) -> Result<Dx12RecordState<'a>> {
let frame_table = super::frame_table::ensure_legacy_frame_table(state, device_handle)?;
Ok(Dx12RecordState {
ld: state
.devices
.get(&device_handle)
.with_context(|| format!("Invalid device {device_handle}"))?,
devices: &state.devices,
contexts: &state.contexts,
frame_table,
buffers: &state.buffers,
shaders: &state.shaders,
pipelines: &state.pipelines,
compute_pipelines: &state.compute_pipelines,
render_targets: &state.render_targets,
textures: &state.textures,
samplers: &state.samplers,
})
}
pub(crate) struct Dx12SubmitSession {
ctx: ContextHandle,
device_handle: DeviceHandle,
sc: SharedSubmissionContext,
ld: SharedLogicalDevice,
devices: Arc<HashMap<DeviceHandle, SharedLogicalDevice>>,
contexts: SharedContextMap,
frame_table: SharedContextFrameTable,
context_fences: Arc<RwLock<HashMap<ContextHandle, super::types::ContextFenceEntry>>>,
buffers: SharedBufferTable,
shaders: SharedShaderTable,
pipelines: SharedPipelineTable,
compute_pipelines: SharedComputePipelineTable,
render_targets: SharedRenderTargetTable,
textures: SharedTextureTable,
samplers: SharedSamplerTable,
device_owner_handle: Option<ContextHandle>,
ctx_fence: ID3D12Fence,
}
impl Dx12SubmitSession {
pub fn clone_from_state(state: &Dx12State, ctx: ContextHandle) -> Result<Arc<Self>> {
let sc = Arc::clone(
state
.contexts
.read()
.unwrap()
.get(&ctx)
.with_context(|| format!("Invalid context handle {ctx}"))?,
);
let (device_handle, frame_table) = {
let sc_guard = sc.lock().unwrap();
(sc_guard.device, Arc::clone(&sc_guard.frame_table))
};
let ld = Arc::clone(
state
.devices
.get(&device_handle)
.with_context(|| format!("Invalid device handle {device_handle}"))?,
);
let devices: HashMap<DeviceHandle, SharedLogicalDevice> = state
.devices
.iter()
.map(|(handle, device)| (*handle, Arc::clone(device)))
.collect();
let device_owner_handle = state.device_owner_handles.get(&device_handle).copied();
let ctx_fence = state
.context_fences
.read()
.unwrap()
.get(&ctx)
.with_context(|| format!("Invalid context handle {ctx}"))?
.1
.clone();
Ok(Arc::new(Self {
ctx,
device_handle,
sc,
ld,
devices: Arc::new(devices),
contexts: Arc::clone(&state.contexts),
frame_table,
context_fences: Arc::clone(&state.context_fences),
buffers: Arc::clone(&state.buffers),
shaders: Arc::clone(&state.shaders),
pipelines: Arc::clone(&state.pipelines),
compute_pipelines: Arc::clone(&state.compute_pipelines),
render_targets: Arc::clone(&state.render_targets),
textures: Arc::clone(&state.textures),
samplers: Arc::clone(&state.samplers),
device_owner_handle,
ctx_fence,
}))
}
fn scope(&self) -> Dx12SubmitScope<'_> {
Dx12SubmitScope {
ctx: self.ctx,
device_handle: self.device_handle,
sc: std::sync::Arc::clone(&self.sc),
record: Dx12RecordState {
ld: &self.ld,
devices: &self.devices,
contexts: &self.contexts,
frame_table: Arc::clone(&self.frame_table),
buffers: &self.buffers,
shaders: &self.shaders,
pipelines: &self.pipelines,
compute_pipelines: &self.compute_pipelines,
render_targets: &self.render_targets,
textures: &self.textures,
samplers: &self.samplers,
},
context_fences: &self.context_fences,
ctx_fence: self.ctx_fence.clone(),
device_owner: self.device_owner_handle,
}
}
}
impl crate::backend::ContextSubmitSession for Dx12SubmitSession {
fn separate_graphics_queue(&self) -> bool {
true
}
fn device_queue_owner(&self, _ctx: ContextHandle) -> Option<ContextHandle> {
self.device_owner_handle
}
fn retains_present_partitions(&self) -> bool {
true
}
fn submit_standalone(
&self,
ctx: ContextHandle,
commands: &[GpuCommand],
sync: Option<&SubmitSync>,
) -> Result<TimelineValue> {
let scope = self.scope();
scope.assert_ctx(ctx);
compute::submit_with_scope(&scope, scope.ctx, commands, sync)
}
fn submit_graph(
&self,
ctx: ContextHandle,
commands: &[GraphCommand],
sync: Option<&SubmitSync>,
) -> Result<TimelineValue> {
let scope = self.scope();
scope.assert_ctx(ctx);
compute::submit_graph_with_scope(&scope, scope.ctx, commands, None, sync)
}
fn submit_graph_and_retain(
&self,
ctx: ContextHandle,
commands: &[GraphCommand],
key: u64,
sync: Option<&SubmitSync>,
) -> Result<TimelineValue> {
let scope = self.scope();
scope.assert_ctx(ctx);
compute::evict_retained_with_scope(&scope, scope.ctx, key);
compute::submit_graph_with_scope(&scope, scope.ctx, commands, Some(key), sync)
}
fn try_resubmit_retained(
&self,
ctx: ContextHandle,
key: u64,
sync: Option<&SubmitSync>,
) -> Result<Option<TimelineValue>> {
let scope = self.scope();
scope.assert_ctx(ctx);
compute::try_resubmit_retained_with_scope(&scope, scope.ctx, key, sync)
}
fn evict_retained(&self, ctx: ContextHandle, key: u64) {
let scope = self.scope();
scope.assert_ctx(ctx);
compute::evict_retained_with_scope(&scope, scope.ctx, key);
}
}