use super::*;
use gpui::{LayerCacheStats, LayerPlan};
struct Slot {
size: [u32; 2],
blur_size: [u32; 2],
textures: Vec<TextureBundle>,
retained: Option<LayerPlan>,
}
#[derive(Default)]
pub(crate) struct Groups {
slots: Vec<Slot>,
plans: Vec<LayerPlan>,
hits: Vec<bool>,
pub stats: LayerCacheStats,
}
impl Groups {
pub fn clear(&mut self) {
self.slots.clear();
self.plans.clear();
self.hits.clear();
}
#[cfg(any(test, feature = "test-support"))]
pub fn texture_count(&self) -> usize {
self.slots.iter().map(|slot| slot.textures.len()).sum()
}
#[cfg(any(test, feature = "test-support"))]
pub fn texture_bytes(&self) -> u64 {
self.slots
.iter()
.flat_map(|slot| &slot.textures)
.map(|texture| texture.extent.width.0 as u64 * texture.extent.height.0 as u64 * 4)
.sum()
}
pub fn prepare(
&mut self,
device: &ID3D11Device,
scene: &Scene,
size: Size<DevicePixels>,
) -> Result<()> {
self.plans = scene.layer_plans(size)?;
self.hits.clear();
for (i, p) in self.plans.iter().enumerate() {
if self.slots.get(i).is_none_or(|s| s.size != p.size) {
let slot = Slot {
size: p.size,
blur_size: p.blur_size,
textures: Vec::new(),
retained: None,
};
if i == self.slots.len() {
self.slots.push(slot)
} else {
self.slots[i] = slot
}
}
let slot = &mut self.slots[i];
if slot.blur_size != p.blur_size {
slot.textures.truncate(2);
slot.blur_size = p.blur_size;
if let Some(old) = &mut slot.retained {
old.blur_radius = 0.;
}
}
let count = if p.blur_radius > 0. { 4 } else { 2 };
if slot.textures.len() != count {
if let Some(old) = &mut slot.retained {
old.blur_radius = 0.;
}
}
slot.textures.truncate(count);
while slot.textures.len() < count {
let extent = if slot.textures.len() < 2 {
p.size
} else {
p.blur_size
};
slot.textures.push(allocate_texture_limited(
device,
gpui::size(
DevicePixels(extent[0] as i32),
DevicePixels(extent[1] as i32),
),
DXGI_FORMAT_B8G8R8A8_UNORM,
4,
4 * 1024 * 1024,
)?);
}
self.hits.push(
self.slots[i]
.retained
.as_ref()
.is_some_and(|old| p.matches(old)),
);
}
self.slots.truncate(self.plans.len());
Ok(())
}
pub fn boundary(&self, index: usize) -> (usize, bool) {
self.plans
.iter()
.enumerate()
.find_map(|(slot, p)| {
if p.begin_index == index {
Some((slot, true))
} else if p.end_index == index {
Some((slot, false))
} else {
None
}
})
.expect("validated layer boundary")
}
pub fn start(&mut self, slot: usize) {
if !self.hits[slot] {
self.slots[slot].retained = None;
}
}
pub fn hit(&self, slot: usize) -> bool {
self.hits[slot]
}
pub fn end(&self, slot: usize) -> usize {
self.plans[slot].end_index
}
pub(super) fn draw(
&self,
pipeline: &CompositorPipeline,
uniform: &ID3D11Buffer,
dc: &ID3D11DeviceContext,
context: &ID3D11DeviceContext,
source: &ID3D11Texture2D,
target: &ID3D11RenderTargetView,
size: Size<DevicePixels>,
slot: usize,
begin: bool,
) -> Result<()> {
let p = &self.plans[slot];
let hit = self.hits[slot];
let textures = &self.slots[slot].textures;
pipeline.bind(dc);
unsafe {
dc.OMSetRenderTargets(None, None);
if begin || !hit {
dc.CopySubresourceRegion(
&textures[usize::from(!begin)].texture,
0,
0,
0,
0,
source,
0,
Some(&D3D11_BOX {
left: p.origin[0],
top: p.origin[1],
front: 0,
right: p.origin[0] + p.size[0],
bottom: p.origin[1] + p.size[1],
back: 1,
}),
);
}
if !begin && p.needs_blur(self.slots[slot].retained.as_ref()) {
for axis in 0..2 {
let params = &p.parameters[axis + 2];
let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
dc.Map(uniform, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut mapped))?;
std::ptr::copy_nonoverlapping(
params.as_ptr().cast::<u8>(),
mapped.pData.cast(),
96,
);
dc.Unmap(uniform, 0);
dc.VSSetConstantBuffers(0, Some(&[Some(uniform.clone())]));
dc.PSSetConstantBuffers(0, Some(&[Some(uniform.clone())]));
dc.OMSetRenderTargets(Some(&[Some(textures[axis + 2].rtv.clone())]), None);
dc.RSSetViewports(Some(&[viewport(textures[axis + 2].extent)]));
dc.PSSetShaderResources(
0,
Some(&[
Some(textures[axis + 1].srv.clone()),
Some(textures[0].srv.clone()),
]),
);
dc.Draw(6, 0);
dc.PSSetShaderResources(0, Some(&[None, None]));
dc.OMSetRenderTargets(None, None);
}
}
if !begin || !hit {
let params = &p.parameters[usize::from(!begin)];
let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
dc.Map(uniform, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut mapped))?;
std::ptr::copy_nonoverlapping(
params.as_ptr().cast::<u8>(),
mapped.pData.cast(),
96,
);
dc.Unmap(uniform, 0);
dc.VSSetConstantBuffers(0, Some(&[Some(uniform.clone())]));
dc.PSSetConstantBuffers(0, Some(&[Some(uniform.clone())]));
dc.OMSetRenderTargets(Some(&[Some(target.clone())]), None);
dc.RSSetViewports(Some(&[viewport(size)]));
dc.PSSetShaderResources(
0,
Some(&[
Some(textures[if p.blur_radius > 0. { 3 } else { 1 }].srv.clone()),
Some(textures[0].srv.clone()),
]),
);
dc.Draw(6, 0);
dc.PSSetShaderResources(0, Some(&[None, None]));
dc.OMSetRenderTargets(None, None);
}
let mut commands = None;
dc.FinishCommandList(false, Some(&mut commands))?;
context.ExecuteCommandList(commands.as_ref().context("group boundary commands")?, true);
pipeline.device.GetDeviceRemovedReason()?;
}
Ok(())
}
pub fn commit(&mut self, executed: &[usize]) {
for &slot in executed {
if self.hits[slot] {
self.stats.hits += 1;
} else {
self.stats.captures += 1;
}
self.slots[slot].retained = Some(self.plans[slot].clone());
}
}
}