use awsm_renderer_materials::{MaterialAlphaMode, MaterialShaderId};
use wasm_bindgen::JsValue;
use crate::pipeline_scheduler::{
CompileInstallTarget, PassKind, PipelineCompileResolution, PipelineGroupId,
};
use crate::pipelines::compute_pipeline::ComputePipelineCacheKey;
use crate::render_passes::material_classify::shader::cache_key::ShaderCacheKeyMaterialClassify;
use crate::render_passes::material_opaque::edge_pipeline::{
EdgePipelineSlot, EdgeResolvePipelineKeyId,
};
use crate::render_passes::material_opaque::shader::cache_key::{
DynamicShaderInfo, ShaderCacheKeyMaterialOpaque,
};
async fn shader_compile_diagnostic(module: Option<web_sys::GpuShaderModule>) -> Option<String> {
use awsm_renderer_core::shaders::ShaderModuleExt;
let info = module?.get_compilation_info_ext().await.ok()?;
if info.errors.is_empty() {
return None;
}
let mut out = String::new();
for msg in &info.errors {
if !out.is_empty() {
out.push('\n');
}
out.push_str(&format!(
"line {}:{}: {}",
msg.line_num, msg.line_pos, msg.message
));
}
Some(out)
}
impl crate::AwsmRenderer {
pub fn ensure_scene_pipelines(&mut self) -> Result<(), crate::error::AwsmError> {
let active_msaa = self.anti_aliasing.msaa_sample_count;
let active_mipmaps = self.anti_aliasing.mipmap;
let dispatch_hash = self.dynamic_materials.dispatch_hash_cached();
let bucket_count = self.dynamic_materials.bucket_entries_cached().len();
let layout_signature = (dispatch_hash, bucket_count);
let layout_changed = self.last_ensured_bucket_layout != Some(layout_signature);
if layout_changed {
self.relayout_bucket_buffers(bucket_count as u32)?;
self.last_ensured_bucket_layout = Some(layout_signature);
}
let bucket_ids: Vec<MaterialShaderId> = self
.dynamic_materials
.bucket_entries_cached()
.iter()
.map(|e| e.shader_id)
.collect();
for id in &bucket_ids {
if let Err(e) = self.submit_to_scheduler_for_shader_id(*id) {
tracing::warn!(
target: "awsm_renderer::pipeline_readiness",
"ensure_scene_pipelines: submit_to_scheduler({:?}) failed: {:?}", id, e
);
}
}
if layout_changed {
for id in &bucket_ids {
if let Some(mid) = self.pipeline_scheduler.find_material_by_shader_id(*id) {
self.pipeline_scheduler
.mark_material_pending_for_relaunch(PipelineGroupId::Material(mid));
}
}
}
let entries = self.dynamic_materials.bucket_entries_cached().to_vec();
for shader_id in &bucket_ids {
if let Err(e) = self.ensure_bucket_pipelines(
*shader_id,
&entries,
dispatch_hash,
active_msaa,
active_mipmaps,
) {
tracing::warn!(
target: "awsm_renderer::pipeline_readiness",
"ensure_scene_pipelines: bucket({:?}) compile failed: {:?}",
shader_id, e
);
}
}
self.launch_edge_resolve_compile()?;
Ok(())
}
fn relayout_bucket_buffers(
&mut self,
bucket_count: u32,
) -> Result<(), crate::error::AwsmError> {
if self
.material_classify_buffers
.ensure_bucket_count(&self.gpu, bucket_count)?
{
self.bind_groups
.mark_create(crate::bind_groups::BindGroupCreate::MaterialClassifyBuffersResize);
}
if self
.material_bucket_lut
.ensure(&self.gpu, self.dynamic_materials.bucket_entries_cached())?
{
self.bind_groups
.mark_create(crate::bind_groups::BindGroupCreate::MaterialClassifyBuffersResize);
}
if let Some(edge_buffers) = self.material_edge_buffers.as_mut() {
if edge_buffers.ensure_bucket_count(&self.gpu, bucket_count)? {
self.bind_groups.mark_create(
crate::bind_groups::BindGroupCreate::MaterialClassifyBuffersResize,
);
let max_edge_budget = edge_buffers.max_edge_budget;
if let Ok((uniform, _bytes)) =
crate::render_passes::material_opaque::edge_buffers::build_edge_layout_uniform(
&self.gpu,
bucket_count,
max_edge_budget,
)
{
self.material_edge_layout_uniform = Some(uniform);
}
}
}
let mut dropped_keys = self
.render_passes
.material_opaque
.pipelines
.clear_dynamic_pipelines();
dropped_keys.extend(
self.render_passes
.material_opaque
.edge_pipelines
.clear_dynamic_pipelines(),
);
let current_dispatch_hash = self.dynamic_materials.dispatch_hash_cached();
dropped_keys.extend(
self.render_passes
.material_classify
.prune_dynamic_pipeline_cache(current_dispatch_hash),
);
let freed_shaders = self.pipelines.compute.remove_pipeline_keys(&dropped_keys);
for shader_key in &freed_shaders {
self.shaders.remove(*shader_key);
}
let current_bucket_entries = self.dynamic_materials.bucket_entries_cached().to_vec();
let stale_shaders = self
.shaders
.take_stale_dynamic_set_shader_keys(current_dispatch_hash, ¤t_bucket_entries);
if !stale_shaders.is_empty() {
self.pipelines.compute.remove_by_shader_keys(&stale_shaders);
}
Ok(())
}
fn ensure_bucket_pipelines(
&mut self,
shader_id: MaterialShaderId,
entries: &[crate::dynamic_materials::BucketEntry],
dispatch_hash: u64,
active_msaa: Option<u32>,
active_mipmaps: bool,
) -> Result<(), crate::error::AwsmError> {
let Some(mid) = self
.pipeline_scheduler
.find_material_by_shader_id(shader_id)
else {
return Ok(());
};
let group_id = PipelineGroupId::Material(mid);
let Some(generation) = self.pipeline_scheduler.material_generation(mid) else {
return Ok(());
};
if !self.is_launchable_material(shader_id) {
self.pipeline_scheduler.mark_ready(group_id);
return Ok(());
}
let classify_bg = &self.render_passes.material_classify.bind_groups;
let classify_layout_msaa = self.pipeline_layouts.get_key(
&self.gpu,
&self.bind_group_layouts,
crate::pipeline_layouts::PipelineLayoutCacheKey::new(vec![
classify_bg.multisampled_bind_group_layout_key,
]),
)?;
let classify_layout_no_msaa = self.pipeline_layouts.get_key(
&self.gpu,
&self.bind_group_layouts,
crate::pipeline_layouts::PipelineLayoutCacheKey::new(vec![
classify_bg.singlesampled_bind_group_layout_key,
]),
)?;
let opaque_bg = &self.render_passes.material_opaque.bind_groups;
let texture_pool_arrays_len = opaque_bg.texture_pool_arrays_len;
let texture_pool_samplers_len = opaque_bg.texture_pool_sampler_keys.len() as u32;
let opaque_layout_msaa = self.pipeline_layouts.get_key(
&self.gpu,
&self.bind_group_layouts,
crate::pipeline_layouts::PipelineLayoutCacheKey::new(vec![
opaque_bg.multisampled_main_bind_group_layout_key,
opaque_bg.lights_bind_group_layout_key,
opaque_bg.texture_pool_textures_bind_group_layout_key,
opaque_bg.shadows_bind_group_layout_key,
]),
)?;
let opaque_layout_no_msaa = self.pipeline_layouts.get_key(
&self.gpu,
&self.bind_group_layouts,
crate::pipeline_layouts::PipelineLayoutCacheKey::new(vec![
opaque_bg.singlesampled_main_bind_group_layout_key,
opaque_bg.lights_bind_group_layout_key,
opaque_bg.texture_pool_textures_bind_group_layout_key,
opaque_bg.shadows_bind_group_layout_key,
]),
)?;
let reg = self.dynamic_materials.get(shader_id).cloned();
let dynamic_shader = reg.as_ref().map(|reg| DynamicShaderInfo {
shader_includes: reg.shader_includes.resolve(),
struct_decl: awsm_renderer_materials::dynamic_layout::generate_wgsl_struct(
"MaterialData",
®.layout,
),
loader_decl: awsm_renderer_materials::dynamic_layout::generate_wgsl_loader(
"MaterialData",
"material_data_load",
®.layout,
),
wgsl_fragment: reg.wgsl_fragment.clone(),
});
let (base, pbr_features, owns_skybox) = opaque_variant_params(entries, shader_id);
let mut shader_jobs: Vec<crate::shaders::ShaderCacheKey> = Vec::new();
let mut slots: Vec<LaunchSlot> = Vec::new();
shader_jobs.push(
ShaderCacheKeyMaterialClassify {
msaa_sample_count: active_msaa,
bucket_count: entries.len() as u32,
emit_edge_data: active_msaa.is_some() && crate::edge_resolve_supported(&self.gpu),
}
.into(),
);
slots.push(LaunchSlot::Classify { msaa: active_msaa });
let build_opaque = active_msaa.is_none()
&& reg.as_ref().is_none_or(|r| {
matches!(
r.alpha_mode,
MaterialAlphaMode::Opaque | MaterialAlphaMode::Mask { .. }
)
});
if build_opaque {
shader_jobs.push(
ShaderCacheKeyMaterialOpaque {
texture_pool_arrays_len,
texture_pool_samplers_len,
msaa_sample_count: active_msaa,
mipmaps: active_mipmaps,
max_shadow_casters: self.prep_config.clamped_k(),
shader_id,
base,
owns_skybox,
pbr_features,
dispatch_hash,
dynamic_shader: dynamic_shader.clone(),
bucket_entries: entries.to_vec(),
}
.into(),
);
slots.push(LaunchSlot::Opaque {
msaa: active_msaa,
mipmaps: active_mipmaps,
});
}
let resolved_shader_keys = self
.shaders
.ensure_keys_sync_skip_validate(&self.gpu, shader_jobs)?;
let mut compute_jobs: Vec<(LaunchSlot, ComputePipelineCacheKey)> =
Vec::with_capacity(slots.len());
for (shader_key, slot) in resolved_shader_keys.into_iter().zip(slots) {
let layout = match &slot {
LaunchSlot::Classify { msaa } => {
if msaa.is_some() {
classify_layout_msaa
} else {
classify_layout_no_msaa
}
}
LaunchSlot::Opaque { msaa, .. } => {
if msaa.is_some() {
opaque_layout_msaa
} else {
opaque_layout_no_msaa
}
}
};
let cache_key = match &slot {
LaunchSlot::Opaque { .. } => {
ComputePipelineCacheKey::new(shader_key, layout).with_entry_point("cs_opaque")
}
LaunchSlot::Classify { .. } => ComputePipelineCacheKey::new(shader_key, layout),
};
compute_jobs.push((slot, cache_key));
}
let mut promise_jobs: Vec<(LaunchSlot, ComputePipelineCacheKey)> = Vec::new();
let mut skip_keys: Vec<ComputePipelineCacheKey> = Vec::new();
for (slot, cache_key) in &compute_jobs {
if let Some(existing_key) = self.pipelines.compute.cache_lookup(cache_key).copied() {
install_per_pass(self, slot, shader_id, dispatch_hash, existing_key);
} else if self
.pipeline_scheduler
.has_compute_compile_waiter(cache_key)
{
skip_keys.push(cache_key.clone());
} else {
promise_jobs.push((slot.clone(), cache_key.clone()));
}
}
let promise_count = promise_jobs.len();
let prepped_opt = if !promise_jobs.is_empty() {
let cache_keys_only: Vec<ComputePipelineCacheKey> =
promise_jobs.iter().map(|(_, k)| k.clone()).collect();
Some(
crate::pipelines::compute_pipeline::ComputePipelines::ensure_keys_prepare(
&self.gpu,
&self.shaders,
&self.pipeline_layouts,
cache_keys_only,
)?,
)
} else {
None
};
for key in &skip_keys {
self.pipeline_scheduler
.register_compute_compile_waiter(key.clone(), mid);
}
for (_, key) in &promise_jobs {
self.pipeline_scheduler
.register_compute_compile_waiter(key.clone(), mid);
}
if let Some(mut prepped) = prepped_opt {
let promises = std::mem::take(&mut prepped.promises);
for ((slot, cache_key), promise) in promise_jobs.into_iter().zip(promises) {
let target = match &slot {
LaunchSlot::Classify { msaa } => CompileInstallTarget::ClassifyDynamic {
dispatch_hash,
msaa: *msaa,
},
LaunchSlot::Opaque { msaa, mipmaps } => CompileInstallTarget::OpaqueDynamic {
shader_id,
msaa: *msaa,
mipmaps: *mipmaps,
},
};
let id = group_id;
let shader_module = self.shaders.get(cache_key.shader_key).cloned();
let fut: std::pin::Pin<
Box<dyn std::future::Future<Output = PipelineCompileResolution>>,
> = Box::pin(async move {
let result: std::result::Result<web_sys::GpuComputePipeline, JsValue> =
promise.await;
let compile_error = if result.is_err() {
shader_compile_diagnostic(shader_module).await
} else {
None
};
PipelineCompileResolution {
id,
generation,
target,
cache_key,
result,
compile_error,
}
});
self.pipeline_scheduler.push_compile_future_no_count(fut);
}
tracing::info!(
target: "awsm_renderer::pipeline_readiness",
"ensure_bucket_pipelines({:?}): {} sub-pipelines pushed for material({:?})",
shader_id,
promise_count,
mid,
);
}
if self.pipeline_scheduler.pending_subcompile_count(mid) == 0 {
self.pipeline_scheduler.mark_ready(group_id);
}
Ok(())
}
pub(crate) fn launch_edge_resolve_compile(&mut self) -> Result<(), crate::error::AwsmError> {
if self.anti_aliasing.msaa_sample_count.is_none()
|| !crate::edge_resolve_supported(&self.gpu)
{
return Ok(());
}
let color_wgsl = awsm_renderer_core::texture::texture_format_to_wgsl_storage(
self.render_textures.formats.color,
)?;
let bucket_entries = self.dynamic_materials.bucket_entries_cached().to_vec();
let descs = match self
.render_passes
.material_opaque
.edge_pipelines
.build_descriptors(
&self.gpu,
&mut self.pipeline_layouts,
&mut self.bind_group_layouts,
&self.render_passes.material_opaque.bind_groups,
&self.render_passes.material_opaque.edge_bind_group_layouts,
&bucket_entries,
&self.anti_aliasing,
color_wgsl,
Some(&self.dynamic_materials),
self.prep_config.clamped_k(),
)? {
Some(d) => d,
None => return Ok(()),
};
let resolved_shader_keys = self
.shaders
.ensure_keys_sync_skip_validate(&self.gpu, descs.shader_cache_keys)?;
let mut compute_jobs: Vec<(EdgeLaunchSlot, ComputePipelineCacheKey)> =
Vec::with_capacity(descs.slots.len());
for (((shader_key, layout_key), entry_point), slot) in resolved_shader_keys
.into_iter()
.zip(descs.pipeline_layout_keys.iter().copied())
.zip(descs.entry_points.iter().cloned())
.zip(descs.slots.iter().copied())
{
let edge_slot = EdgeLaunchSlot(slot);
let cache_key = match entry_point {
Some(name) => {
ComputePipelineCacheKey::new(shader_key, layout_key).with_entry_point(&name)
}
None => ComputePipelineCacheKey::new(shader_key, layout_key),
};
compute_jobs.push((edge_slot, cache_key));
}
self.render_passes
.material_opaque
.edge_pipelines
.set_desired_edge_keys(compute_jobs.iter().map(|(_, k)| k.clone()));
let mut promise_jobs: Vec<(EdgeLaunchSlot, ComputePipelineCacheKey)> = Vec::new();
let (mut hits, mut inflight_skips) = (0usize, 0usize);
for (slot, cache_key) in &compute_jobs {
if let Some(existing_key) = self.pipelines.compute.cache_lookup(cache_key).copied() {
install_edge_per_pass(self, &slot.0, existing_key);
hits += 1;
} else if self
.render_passes
.material_opaque
.edge_pipelines
.edge_key_in_flight(cache_key)
{
inflight_skips += 1;
tracing::debug!(
target: "awsm_renderer::pipeline_readiness",
"launch_edge_resolve_compile: {:?} skipped as in-flight",
slot.0,
);
} else {
promise_jobs.push((slot.clone(), cache_key.clone()));
}
}
if promise_jobs.is_empty() {
tracing::debug!(
target: "awsm_renderer::pipeline_readiness",
"launch_edge_resolve_compile: 0 pushed ({} cache-hit installs, {} in-flight skips)",
hits,
inflight_skips,
);
return Ok(());
}
let cache_keys_only: Vec<ComputePipelineCacheKey> =
promise_jobs.iter().map(|(_, k)| k.clone()).collect();
let mut prepped =
crate::pipelines::compute_pipeline::ComputePipelines::ensure_keys_prepare(
&self.gpu,
&self.shaders,
&self.pipeline_layouts,
cache_keys_only,
)?;
let promise_jobs_count = promise_jobs.len();
let promises = std::mem::take(&mut prepped.promises);
for ((slot, cache_key), promise) in promise_jobs.into_iter().zip(promises) {
self.render_passes
.material_opaque
.edge_pipelines
.mark_edge_key_in_flight(cache_key.clone());
let target = match slot.0 {
EdgePipelineSlot::Shade(id) => CompileInstallTarget::EdgeResolveShade {
shader_id: id.shader_id,
mipmaps: id.mipmaps,
},
EdgePipelineSlot::FinalBlend => CompileInstallTarget::EdgeResolveFinalBlend,
};
let id = PipelineGroupId::Pass(PassKind::MaterialEdgeResolve);
let generation = 0u32;
let fut: std::pin::Pin<
Box<dyn std::future::Future<Output = PipelineCompileResolution>>,
> = Box::pin(async move {
let result: std::result::Result<web_sys::GpuComputePipeline, JsValue> =
promise.await;
PipelineCompileResolution {
id,
generation,
target,
cache_key,
result,
compile_error: None,
}
});
self.pipeline_scheduler.push_compile_future_no_count(fut);
}
tracing::info!(
target: "awsm_renderer::pipeline_readiness",
"launch_edge_resolve_compile: {} layout-level edge sub-pipelines pushed",
promise_jobs_count,
);
Ok(())
}
pub fn apply_compile_resolution(&mut self) -> bool {
let Some(resolution) = self.pipeline_scheduler.next_compile_resolution() else {
return false;
};
self.apply_compile_resolution_inline(resolution);
true
}
pub(crate) fn apply_compile_resolution_inline(
&mut self,
resolution: PipelineCompileResolution,
) {
let PipelineCompileResolution {
id,
generation,
target,
cache_key,
result,
compile_error,
} = resolution;
match &target {
CompileInstallTarget::EdgeResolveShade { .. }
| CompileInstallTarget::EdgeResolveFinalBlend => {
self.render_passes
.material_opaque
.edge_pipelines
.clear_edge_key_in_flight(&cache_key);
if !self
.render_passes
.material_opaque
.edge_pipelines
.is_edge_key_desired(&cache_key)
{
tracing::debug!(
target: "awsm_renderer::pipeline_readiness",
"apply_compile_resolution: edge resolution no longer desired — dropped (slot {:?})",
edge_slot_from_target(&target),
);
return;
}
match result {
Ok(pipeline) => {
let pipeline_key = self
.pipelines
.compute
.install_resolved_pipeline(pipeline, cache_key);
install_edge_per_pass(self, &edge_slot_from_target(&target), pipeline_key);
}
Err(e) => {
tracing::warn!(
target: "awsm_renderer::pipeline_readiness",
"apply_compile_resolution: edge pipeline compile failed: {:?}", e
);
}
}
return;
}
CompileInstallTarget::ClassifyDynamic { .. }
| CompileInstallTarget::OpaqueDynamic { .. } => {}
}
let waiters = self
.pipeline_scheduler
.take_compute_compile_waiters(&cache_key);
let mid = match id {
PipelineGroupId::Material(mid) => mid,
PipelineGroupId::Pass(_) => {
tracing::warn!(
target: "awsm_renderer::pipeline_readiness",
"apply_compile_resolution: Pass-flavoured resolution not yet supported"
);
for waiter_mid in waiters {
self.pipeline_scheduler.note_subcompile_complete(waiter_mid);
}
return;
}
};
let stale = self
.pipeline_scheduler
.material_generation(mid)
.map(|g| g != generation)
.unwrap_or(true);
if stale {
tracing::debug!(
target: "awsm_renderer::pipeline_readiness",
"apply_compile_resolution: stale resolution dropped (material({:?}), waiters={})",
mid,
waiters.len()
);
for waiter_mid in waiters {
self.pipeline_scheduler.note_subcompile_complete(waiter_mid);
}
return;
}
let pipeline = match result {
Ok(p) => p,
Err(e) => {
let detail =
compile_error.unwrap_or_else(|| format!("{e:?} (no shader compilation info)"));
tracing::warn!(
target: "awsm_renderer::pipeline_readiness",
"apply_compile_resolution: pipeline-creation failed for material({:?}): {}",
mid,
detail,
);
self.pipeline_scheduler
.mark_failed(id, crate::error::AwsmError::MaterialShaderCompile(detail));
for waiter_mid in waiters {
self.pipeline_scheduler.note_subcompile_complete(waiter_mid);
}
return;
}
};
let pipeline_key = self
.pipelines
.compute
.install_resolved_pipeline(pipeline, cache_key);
match &target {
CompileInstallTarget::ClassifyDynamic { .. }
| CompileInstallTarget::OpaqueDynamic { .. } => {
install_per_pass(
self,
&launch_slot_from_target(&target),
shader_id_from_target(&target),
dispatch_hash_from_target(&target),
pipeline_key,
);
}
CompileInstallTarget::EdgeResolveShade { .. }
| CompileInstallTarget::EdgeResolveFinalBlend => {
unreachable!("edge-resolve targets are installed by the layout-level branch")
}
}
for waiter_mid in waiters {
self.pipeline_scheduler.note_subcompile_complete(waiter_mid);
}
}
}
fn opaque_variant_params(
entries: &[crate::dynamic_materials::BucketEntry],
shader_id: MaterialShaderId,
) -> (crate::dynamic_materials::ShadingBase, u32, bool) {
let entry = entries.iter().find(|e| e.shader_id == shader_id);
let base = entry
.map(|e| e.base)
.unwrap_or_else(|| crate::dynamic_materials::ShadingBase::for_shader_id(shader_id));
let pbr_features = entry
.map(|e| e.pbr_features)
.unwrap_or_else(|| awsm_renderer_materials::pbr::PbrFeatures::default().bits());
let owns_skybox = shader_id == MaterialShaderId::SKYBOX;
(base, pbr_features, owns_skybox)
}
#[derive(Clone)]
enum LaunchSlot {
Classify { msaa: Option<u32> },
Opaque { msaa: Option<u32>, mipmaps: bool },
}
fn launch_slot_from_target(t: &CompileInstallTarget) -> LaunchSlot {
match t {
CompileInstallTarget::ClassifyDynamic { msaa, .. } => LaunchSlot::Classify { msaa: *msaa },
CompileInstallTarget::OpaqueDynamic { msaa, mipmaps, .. } => LaunchSlot::Opaque {
msaa: *msaa,
mipmaps: *mipmaps,
},
CompileInstallTarget::EdgeResolveShade { .. }
| CompileInstallTarget::EdgeResolveFinalBlend => {
unreachable!(
"launch_slot_from_target: edge variants route through edge_slot_from_target"
)
}
}
}
fn shader_id_from_target(t: &CompileInstallTarget) -> MaterialShaderId {
match t {
CompileInstallTarget::OpaqueDynamic { shader_id, .. } => *shader_id,
CompileInstallTarget::ClassifyDynamic { .. } => MaterialShaderId::PBR,
CompileInstallTarget::EdgeResolveShade { .. }
| CompileInstallTarget::EdgeResolveFinalBlend => {
unreachable!("shader_id_from_target: edge variants route through edge_slot_from_target")
}
}
}
fn dispatch_hash_from_target(t: &CompileInstallTarget) -> u64 {
match t {
CompileInstallTarget::ClassifyDynamic { dispatch_hash, .. } => *dispatch_hash,
CompileInstallTarget::OpaqueDynamic { .. } => 0,
CompileInstallTarget::EdgeResolveShade { .. }
| CompileInstallTarget::EdgeResolveFinalBlend => {
unreachable!(
"dispatch_hash_from_target: edge variants route through edge_slot_from_target"
)
}
}
}
fn is_live_bucket(renderer: &crate::AwsmRenderer, shader_id: MaterialShaderId) -> bool {
renderer
.dynamic_materials
.bucket_entries_cached()
.iter()
.any(|e| e.shader_id == shader_id)
}
fn install_per_pass(
renderer: &mut crate::AwsmRenderer,
slot: &LaunchSlot,
shader_id: MaterialShaderId,
dispatch_hash: u64,
pipeline_key: crate::pipelines::compute_pipeline::ComputePipelineKey,
) {
use crate::render_passes::material_opaque::pipeline::PipelineKeyId;
if matches!(slot, LaunchSlot::Opaque { .. }) && !is_live_bucket(renderer, shader_id) {
free_displaced_compute_pipeline(renderer, pipeline_key);
return;
}
let displaced = match slot {
LaunchSlot::Classify { msaa } => renderer
.render_passes
.material_classify
.pipeline_cache
.borrow_mut()
.insert((dispatch_hash, *msaa), pipeline_key)
.filter(|old| *old != pipeline_key),
LaunchSlot::Opaque { msaa, mipmaps } => renderer
.render_passes
.material_opaque
.pipelines
.insert_dynamic_pipeline(
PipelineKeyId {
msaa_sample_count: *msaa,
mipmaps: *mipmaps,
shader_id,
},
pipeline_key,
),
};
if let Some(old) = displaced {
free_displaced_compute_pipeline(renderer, old);
}
}
fn free_displaced_compute_pipeline(
renderer: &mut crate::AwsmRenderer,
key: crate::pipelines::compute_pipeline::ComputePipelineKey,
) {
let freed_shaders = renderer.pipelines.compute.remove_pipeline_keys(&[key]);
for shader_key in &freed_shaders {
renderer.shaders.remove(*shader_key);
}
}
#[derive(Clone)]
struct EdgeLaunchSlot(EdgePipelineSlot);
fn install_edge_per_pass(
renderer: &mut crate::AwsmRenderer,
slot: &EdgePipelineSlot,
pipeline_key: crate::pipelines::compute_pipeline::ComputePipelineKey,
) {
if let EdgePipelineSlot::Shade(id) = slot {
if !is_live_bucket(renderer, id.shader_id) {
free_displaced_compute_pipeline(renderer, pipeline_key);
return;
}
}
let edge = &mut renderer.render_passes.material_opaque.edge_pipelines;
let displaced = match slot {
EdgePipelineSlot::Shade(id) => edge.insert_shade_pipeline(*id, pipeline_key),
EdgePipelineSlot::FinalBlend => edge
.final_blend_pipeline_key
.replace(pipeline_key)
.filter(|old| *old != pipeline_key),
};
if let Some(old) = displaced {
free_displaced_compute_pipeline(renderer, old);
}
}
fn edge_slot_from_target(t: &CompileInstallTarget) -> EdgePipelineSlot {
match t {
CompileInstallTarget::EdgeResolveShade { shader_id, mipmaps } => {
EdgePipelineSlot::Shade(EdgeResolvePipelineKeyId {
shader_id: *shader_id,
mipmaps: *mipmaps,
})
}
CompileInstallTarget::EdgeResolveFinalBlend => EdgePipelineSlot::FinalBlend,
_ => unreachable!("edge_slot_from_target called with non-edge variant"),
}
}