rt(d3d12): make images with OwnedFramebuffer provenance use ManuallyDrop

This commit is contained in:
chyyran 2024-09-28 22:10:32 -04:00 committed by Ronny Chan
parent 7edff0ae35
commit c57e502b78
9 changed files with 233 additions and 138 deletions

View file

@ -131,13 +131,18 @@ impl FrameResiduals {
self.mipmaps.extend(handles) self.mipmaps.extend(handles)
} }
pub fn dispose_resource(&mut self, resource: ManuallyDrop<Option<ID3D12Resource>>) { pub unsafe fn dispose_resource(&mut self, resource: ManuallyDrop<Option<ID3D12Resource>>) {
self.resources.push(resource) self.resources.push(resource)
} }
/// Disposition only handles transition barriers, but it is not unsafe because /// Disposition only handles transition barriers.
/// other things just leak and leakign is not unsafe, ///
pub fn dispose_barriers(&mut self, barrier: impl IntoIterator<Item = D3D12_RESOURCE_BARRIER>) { /// **Safety:** It is only safe to dispose a barrier created with resource strategy IncrementRef.
///
pub unsafe fn dispose_barriers(
&mut self,
barrier: impl IntoIterator<Item = D3D12_RESOURCE_BARRIER>,
) {
self.resource_barriers.extend(barrier); self.resource_barriers.extend(barrier);
} }
@ -230,6 +235,7 @@ mod compile {
} }
} }
use crate::resource::OutlivesFrame;
use compile::{compile_passes_dxil, compile_passes_hlsl, DxilShaderPassMeta, HlslShaderPassMeta}; use compile::{compile_passes_dxil, compile_passes_hlsl, DxilShaderPassMeta, HlslShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters; use librashader_runtime::parameters::RuntimeParameters;
@ -321,7 +327,8 @@ impl FilterChainD3D12 {
let mut staging_heap = unsafe { let mut staging_heap = unsafe {
D3D12DescriptorHeap::new( D3D12DescriptorHeap::new(
device, device,
(MAX_BINDINGS_COUNT as usize) * shader_count // add one, because technically the input image doesn't need to count
(1 + MAX_BINDINGS_COUNT as usize) * shader_count
+ MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS + MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS
+ lut_count, + lut_count,
) )
@ -391,7 +398,7 @@ impl FilterChainD3D12 {
common: FilterCommon { common: FilterCommon {
d3d12: device.clone(), d3d12: device.clone(),
samplers, samplers,
allocator: allocator, allocator,
output_textures, output_textures,
feedback_textures, feedback_textures,
luts, luts,
@ -460,7 +467,10 @@ impl FilterChainD3D12 {
gc.dispose_mipmap_handles(residual_mipmap); gc.dispose_mipmap_handles(residual_mipmap);
gc.dispose_mipmap_gen(mipmap_gen); gc.dispose_mipmap_gen(mipmap_gen);
unsafe {
gc.dispose_barriers(residual_barrier); gc.dispose_barriers(residual_barrier);
}
Ok(luts) Ok(luts)
} }
@ -653,7 +663,7 @@ impl FilterChainD3D12 {
); );
} }
unsafe { unsafe {
back.copy_from(cmd, input, &mut self.residuals)?; back.copy_from(cmd, input)?;
} }
self.history_framebuffers.push_front(back); self.history_framebuffers.push_front(back);
} }
@ -669,6 +679,8 @@ impl FilterChainD3D12 {
/// librashader **will not** create a resource barrier for the final pass. The output image will /// librashader **will not** create a resource barrier for the final pass. The output image will
/// remain in `D3D12_RESOURCE_STATE_RENDER_TARGET` after all shader passes. The caller must transition /// remain in `D3D12_RESOURCE_STATE_RENDER_TARGET` after all shader passes. The caller must transition
/// the output image to the final resource state. /// the output image to the final resource state.
///
/// The input and output images must stay alive until the command list is submitted and work is complete.
pub unsafe fn frame( pub unsafe fn frame(
&mut self, &mut self,
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
@ -689,7 +701,7 @@ impl FilterChainD3D12 {
if let Some(options) = options { if let Some(options) = options {
if options.clear_history { if options.clear_history {
for framebuffer in &mut self.history_framebuffers { for framebuffer in &mut self.history_framebuffers {
framebuffer.clear(cmd, &mut self.rtv_heap, &mut self.residuals)?; framebuffer.clear(cmd, &mut self.rtv_heap)?;
} }
} }
} }
@ -787,13 +799,12 @@ impl FilterChainD3D12 {
)?; )?;
} }
self.residuals util::d3d12_resource_transition::<OutlivesFrame, _>(
.dispose_barriers(util::d3d12_resource_transition(
cmd, cmd,
&target.handle.resource(), &target.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
)); );
let view = target.create_render_target_view(&mut self.rtv_heap)?; let view = target.create_render_target_view(&mut self.rtv_heap)?;
let out = RenderTarget::identity(&view)?; let out = RenderTarget::identity(&view)?;
@ -811,21 +822,21 @@ impl FilterChainD3D12 {
QuadType::Offscreen, QuadType::Offscreen,
)?; )?;
self.residuals util::d3d12_resource_transition::<OutlivesFrame, _>(
.dispose_barriers(util::d3d12_resource_transition(
cmd, cmd,
&target.handle.resource(), &target.resource,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
)); );
if target.max_mipmap > 1 && !self.disable_mipmaps { if target.max_mipmap > 1 && !self.disable_mipmaps {
let (residuals, residual_barriers) = self.common.mipmap_gen.mipmapping_context( // barriers don't get disposed because the context is OutlivesFrame
let (residuals, _residual_barriers) = self.common.mipmap_gen.mipmapping_context(
cmd, cmd,
&mut self.mipmap_heap, &mut self.mipmap_heap,
|ctx| { |ctx| {
ctx.generate_mipmaps( ctx.generate_mipmaps::<OutlivesFrame, _>(
&target.handle.resource(), &target.resource,
target.max_mipmap, target.max_mipmap,
target.size, target.size,
target.format.into(), target.format.into(),
@ -835,7 +846,6 @@ impl FilterChainD3D12 {
)?; )?;
self.residuals.dispose_mipmap_handles(residuals); self.residuals.dispose_mipmap_handles(residuals);
self.residuals.dispose_barriers(residual_barriers);
} }
self.residuals.dispose_output(view.descriptor); self.residuals.dispose_output(view.descriptor);
@ -862,13 +872,12 @@ impl FilterChainD3D12 {
)?; )?;
} }
self.residuals util::d3d12_resource_transition::<OutlivesFrame, _>(
.dispose_barriers(util::d3d12_resource_transition(
cmd, cmd,
&feedback_target.handle.resource(), &feedback_target.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
)); );
let view = feedback_target.create_render_target_view(&mut self.rtv_heap)?; let view = feedback_target.create_render_target_view(&mut self.rtv_heap)?;
let out = RenderTarget::viewport_with_output(&view, viewport); let out = RenderTarget::viewport_with_output(&view, viewport);
@ -885,13 +894,12 @@ impl FilterChainD3D12 {
QuadType::Final, QuadType::Final,
)?; )?;
self.residuals util::d3d12_resource_transition::<OutlivesFrame, _>(
.dispose_barriers(util::d3d12_resource_transition(
cmd, cmd,
&feedback_target.handle.resource(), &feedback_target.resource,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
)); );
} }
if pass.pipeline.format != viewport.output.format { if pass.pipeline.format != viewport.output.format {

View file

@ -1,6 +1,6 @@
use crate::descriptor_heap::{CpuStagingHeap, RenderTargetHeap}; use crate::descriptor_heap::{CpuStagingHeap, RenderTargetHeap};
use crate::error::FilterChainError; use crate::error::FilterChainError;
use crate::filter_chain::FrameResiduals; use crate::resource::{OutlivesFrame, ResourceHandleStrategy};
use crate::texture::{D3D12OutputView, InputTexture}; use crate::texture::{D3D12OutputView, InputTexture};
use crate::util::d3d12_get_closest_format; use crate::util::d3d12_get_closest_format;
use crate::{error, util}; use crate::{error, util};
@ -17,12 +17,13 @@ use parking_lot::Mutex;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::sync::Arc; use std::sync::Arc;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
ID3D12Device, ID3D12GraphicsCommandList, D3D12_BOX, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_BOX,
D3D12_FEATURE_DATA_FORMAT_SUPPORT, D3D12_FORMAT_SUPPORT1_MIP, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, D3D12_FEATURE_DATA_FORMAT_SUPPORT,
D3D12_FORMAT_SUPPORT1_RENDER_TARGET, D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE, D3D12_FORMAT_SUPPORT1_MIP, D3D12_FORMAT_SUPPORT1_RENDER_TARGET,
D3D12_FORMAT_SUPPORT1_TEXTURE2D, D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD, D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE, D3D12_FORMAT_SUPPORT1_TEXTURE2D,
D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE, D3D12_RENDER_TARGET_VIEW_DESC, D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD, D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE,
D3D12_RENDER_TARGET_VIEW_DESC_0, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_DESC, D3D12_RENDER_TARGET_VIEW_DESC, D3D12_RENDER_TARGET_VIEW_DESC_0,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
@ -36,6 +37,7 @@ use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct OwnedImage { pub(crate) struct OwnedImage {
pub(crate) handle: ManuallyDrop<Resource>, pub(crate) handle: ManuallyDrop<Resource>,
pub(crate) resource: ManuallyDrop<ID3D12Resource>,
pub(crate) size: Size<u32>, pub(crate) size: Size<u32>,
pub(crate) format: DXGI_FORMAT, pub(crate) format: DXGI_FORMAT,
pub(crate) max_mipmap: u16, pub(crate) max_mipmap: u16,
@ -113,7 +115,7 @@ impl OwnedImage {
desc.Format = d3d12_get_closest_format(device, format_support); desc.Format = d3d12_get_closest_format(device, format_support);
let resource = allocator.lock().create_resource(&ResourceCreateDesc { let allocator_resource = allocator.lock().create_resource(&ResourceCreateDesc {
name: "ownedimage", name: "ownedimage",
memory_location: MemoryLocation::GpuOnly, memory_location: MemoryLocation::GpuOnly,
resource_category: ResourceCategory::RtvDsvTexture, resource_category: ResourceCategory::RtvDsvTexture,
@ -145,8 +147,10 @@ impl OwnedImage {
// } // }
// assume_d3d12_init!(resource, "CreateCommittedResource"); // assume_d3d12_init!(resource, "CreateCommittedResource");
let resource = ManuallyDrop::new(allocator_resource.resource().clone());
Ok(OwnedImage { Ok(OwnedImage {
handle: ManuallyDrop::new(resource), handle: ManuallyDrop::new(allocator_resource),
resource,
size, size,
format: desc.Format, format: desc.Format,
device: device.clone(), device: device.clone(),
@ -161,17 +165,16 @@ impl OwnedImage {
&self, &self,
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
input: &InputTexture, input: &InputTexture,
gc: &mut FrameResiduals,
) -> error::Result<()> { ) -> error::Result<()> {
let barriers = [ let barriers = [
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
&input.resource, &input.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
), ),
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
&self.handle.resource(), &self.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
@ -180,10 +183,9 @@ impl OwnedImage {
unsafe { unsafe {
cmd.ResourceBarrier(&barriers); cmd.ResourceBarrier(&barriers);
gc.dispose_barriers(barriers);
let dst = D3D12_TEXTURE_COPY_LOCATION { let dst = D3D12_TEXTURE_COPY_LOCATION {
pResource: ManuallyDrop::new(Some(self.handle.resource().clone())), pResource: OutlivesFrame::obtain(&self.resource),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 { Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0, SubresourceIndex: 0,
@ -191,7 +193,7 @@ impl OwnedImage {
}; };
let src = D3D12_TEXTURE_COPY_LOCATION { let src = D3D12_TEXTURE_COPY_LOCATION {
pResource: ManuallyDrop::new(Some(input.resource.clone())), pResource: OutlivesFrame::obtain(&input.resource),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 { Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0, SubresourceIndex: 0,
@ -213,20 +215,17 @@ impl OwnedImage {
back: 1, back: 1,
}), }),
); );
gc.dispose_resource(dst.pResource);
gc.dispose_resource(src.pResource);
} }
let barriers = [ let barriers = [
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
&input.resource, &input.resource,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
), ),
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
&self.handle.resource(), &self.resource,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
@ -237,8 +236,6 @@ impl OwnedImage {
cmd.ResourceBarrier(&barriers); cmd.ResourceBarrier(&barriers);
} }
gc.dispose_barriers(barriers);
Ok(()) Ok(())
} }
@ -246,25 +243,26 @@ impl OwnedImage {
&self, &self,
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>, heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
gc: &mut FrameResiduals,
) -> error::Result<()> { ) -> error::Result<()> {
gc.dispose_barriers(util::d3d12_resource_transition( unsafe {
util::d3d12_resource_transition::<OutlivesFrame, _>(
cmd, cmd,
&self.handle.resource(), &self.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
)); );
let rtv = self.create_render_target_view(heap)?; let rtv = self.create_render_target_view(heap)?;
unsafe { cmd.ClearRenderTargetView(*rtv.descriptor.as_ref(), CLEAR, None) } cmd.ClearRenderTargetView(*rtv.descriptor.as_ref(), CLEAR, None);
gc.dispose_barriers(util::d3d12_resource_transition( util::d3d12_resource_transition::<OutlivesFrame, _>(
cmd, cmd,
&self.handle.resource(), &self.resource,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
)); );
}
Ok(()) Ok(())
} }
@ -297,8 +295,8 @@ impl OwnedImage {
); );
} }
Ok(InputTexture::new( Ok(InputTexture::new::<OutlivesFrame, _>(
self.handle.resource().clone(), &self.resource,
descriptor, descriptor,
self.size, self.size,
self.format, self.format,
@ -390,6 +388,10 @@ impl ScaleFramebuffer for OwnedImage {
impl Drop for OwnedImage { impl Drop for OwnedImage {
fn drop(&mut self) { fn drop(&mut self) {
// let go of the handle
unsafe {
ManuallyDrop::drop(&mut self.resource);
}
let resource = unsafe { ManuallyDrop::take(&mut self.handle) }; let resource = unsafe { ManuallyDrop::take(&mut self.handle) };
if let Err(e) = self.allocator.lock().free_resource(resource) { if let Err(e) = self.allocator.lock().free_resource(resource) {
println!("librashader-runtime-d3d12: [warn] failed to deallocate owned image buffer memory {e}") println!("librashader-runtime-d3d12: [warn] failed to deallocate owned image buffer memory {e}")

View file

@ -176,12 +176,12 @@ impl LutTexture {
let resource = ManuallyDrop::new(allocator_resource.resource().clone()); let resource = ManuallyDrop::new(allocator_resource.resource().clone());
let upload = ManuallyDrop::new(allocator_upload.resource().clone()); let upload = ManuallyDrop::new(allocator_upload.resource().clone());
gc.dispose_barriers(d3d12_resource_transition( d3d12_resource_transition::<OutlivesFrame, _>(
cmd, cmd,
&allocator_resource.resource(), &resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_DEST,
)); );
d3d12_update_subresources::<OutlivesFrame>( d3d12_update_subresources::<OutlivesFrame>(
cmd, cmd,
@ -194,15 +194,15 @@ impl LutTexture {
gc, gc,
)?; )?;
gc.dispose_barriers(d3d12_resource_transition( d3d12_resource_transition::<OutlivesFrame, _>(
cmd, cmd,
&resource, &resource,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
)); );
let view = InputTexture::new( let view = InputTexture::new::<OutlivesFrame, _>(
allocator_resource.resource().clone(), &resource,
descriptor, descriptor,
source.size, source.size,
ImageFormat::R8G8B8A8Unorm.into(), ImageFormat::R8G8B8A8Unorm.into(),
@ -223,8 +223,8 @@ impl LutTexture {
pub fn generate_mipmaps(&self, gen_mips: &mut MipmapGenContext) -> error::Result<()> { pub fn generate_mipmaps(&self, gen_mips: &mut MipmapGenContext) -> error::Result<()> {
if let Some(miplevels) = self.miplevels { if let Some(miplevels) = self.miplevels {
gen_mips.generate_mipmaps( gen_mips.generate_mipmaps::<OutlivesFrame, _>(
&self.allocator_resource.resource(), &self.resource,
miplevels, miplevels,
self.view.size, self.view.size,
ImageFormat::R8G8B8A8Unorm.into(), ImageFormat::R8G8B8A8Unorm.into(),
@ -384,8 +384,8 @@ fn update_subresources<S: ResourceHandleStrategy<ManuallyDrop<ID3D12Resource>>>(
cmd.CopyTextureRegion(&dest_location, 0, 0, 0, &source_location, None); cmd.CopyTextureRegion(&dest_location, 0, 0, 0, &source_location, None);
S::cleanup(gc, dest_location.pResource); S::cleanup_handler(|| gc.dispose_resource(dest_location.pResource));
S::cleanup(gc, source_location.pResource); S::cleanup_handler(|| gc.dispose_resource(source_location.pResource));
} }
} }
Ok(required_size) Ok(required_size)

View file

@ -1,4 +1,5 @@
use crate::descriptor_heap::ResourceWorkHeap; use crate::descriptor_heap::ResourceWorkHeap;
use crate::resource::{ObtainResourceHandle, ResourceHandleStrategy};
use crate::util::dxc_validate_shader; use crate::util::dxc_validate_shader;
use crate::{error, util}; use crate::{error, util};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
@ -6,12 +7,13 @@ use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot};
use librashader_common::Size; use librashader_common::Size;
use librashader_runtime::scaling::MipmapSize; use librashader_runtime::scaling::MipmapSize;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use windows::Win32::Graphics::Direct3D::Dxc::{ use windows::Win32::Graphics::Direct3D::Dxc::{
CLSID_DxcLibrary, CLSID_DxcValidator, DxcCreateInstance, CLSID_DxcLibrary, CLSID_DxcValidator, DxcCreateInstance,
}; };
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
ID3D12DescriptorHeap, ID3D12Device, ID3D12GraphicsCommandList, ID3D12PipelineState, ID3D12DescriptorHeap, ID3D12Device, ID3D12GraphicsCommandList, ID3D12PipelineState,
ID3D12Resource, ID3D12RootSignature, D3D12_COMPUTE_PIPELINE_STATE_DESC, ID3D12RootSignature, D3D12_COMPUTE_PIPELINE_STATE_DESC,
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0,
D3D12_RESOURCE_BARRIER_TYPE_UAV, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_BARRIER_TYPE_UAV, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_UAV_BARRIER, D3D12_SHADER_BYTECODE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_UAV_BARRIER, D3D12_SHADER_BYTECODE,
@ -62,9 +64,9 @@ impl<'a> MipmapGenContext<'a> {
/// Generate a set of mipmaps for the resource. /// Generate a set of mipmaps for the resource.
/// This is a "cheap" action and only dispatches a compute shader. /// This is a "cheap" action and only dispatches a compute shader.
pub fn generate_mipmaps( pub fn generate_mipmaps<S: ResourceHandleStrategy<T>, T: ObtainResourceHandle>(
&mut self, &mut self,
resource: &ID3D12Resource, resource: &T,
miplevels: u16, miplevels: u16,
size: Size<u32>, size: Size<u32>,
format: DXGI_FORMAT, format: DXGI_FORMAT,
@ -72,9 +74,13 @@ impl<'a> MipmapGenContext<'a> {
unsafe { unsafe {
let (residuals_heap, residual_barriers) = self let (residuals_heap, residual_barriers) = self
.gen .gen
.generate_mipmaps(self.cmd, resource, miplevels, size, format, self.heap)?; .generate_mipmaps::<S, T>(self.cmd, resource, miplevels, size, format, self.heap)?;
// heap slots always need to be disposed
self.residuals.extend(residuals_heap); self.residuals.extend(residuals_heap);
self.residual_barriers.extend(residual_barriers);
// barriers need to be disposed if the handle strategy is incrementref
S::cleanup_handler(|| self.residual_barriers.extend(residual_barriers));
} }
Ok(()) Ok(())
@ -178,10 +184,10 @@ impl D3D12MipmapGen {
/// SAFETY: /// SAFETY:
/// - handle must be a CPU handle to an SRV /// - handle must be a CPU handle to an SRV
/// - work_heap must have enough descriptors to fit all miplevels. /// - work_heap must have enough descriptors to fit all miplevels.
unsafe fn generate_mipmaps( unsafe fn generate_mipmaps<S: ResourceHandleStrategy<T>, T: ObtainResourceHandle>(
&self, &self,
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
resource: &ID3D12Resource, resource: &T,
miplevels: u16, miplevels: u16,
size: Size<u32>, size: Size<u32>,
format: DXGI_FORMAT, format: DXGI_FORMAT,
@ -206,7 +212,7 @@ impl D3D12MipmapGen {
}; };
self.device self.device
.CreateShaderResourceView(resource, Some(&srv_desc), *srv.as_ref()); .CreateShaderResourceView(resource.handle(), Some(&srv_desc), *srv.as_ref());
} }
let mut heap_slots = Vec::with_capacity(miplevels as usize); let mut heap_slots = Vec::with_capacity(miplevels as usize);
@ -227,7 +233,7 @@ impl D3D12MipmapGen {
unsafe { unsafe {
self.device.CreateUnorderedAccessView( self.device.CreateUnorderedAccessView(
resource, resource.handle(),
None, None,
Some(&desc), Some(&desc),
*descriptor.as_ref(), *descriptor.as_ref(),
@ -251,13 +257,13 @@ impl D3D12MipmapGen {
let mipmap_params = bytemuck::bytes_of(&mipmap_params); let mipmap_params = bytemuck::bytes_of(&mipmap_params);
let barriers = [ let barriers = [
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<S, _>(
resource, resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
i - 1, i - 1,
), ),
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<S, _>(
resource, resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
@ -267,7 +273,8 @@ impl D3D12MipmapGen {
unsafe { unsafe {
cmd.ResourceBarrier(&barriers); cmd.ResourceBarrier(&barriers);
residual_barriers.extend(barriers);
S::cleanup_handler(|| residual_barriers.extend(barriers));
cmd.SetComputeRootDescriptorTable(1, *heap_slots[i as usize].as_ref()); cmd.SetComputeRootDescriptorTable(1, *heap_slots[i as usize].as_ref());
cmd.SetComputeRoot32BitConstants( cmd.SetComputeRoot32BitConstants(
@ -285,7 +292,7 @@ impl D3D12MipmapGen {
} }
let uav_barrier = ManuallyDrop::new(D3D12_RESOURCE_UAV_BARRIER { let uav_barrier = ManuallyDrop::new(D3D12_RESOURCE_UAV_BARRIER {
pResource: ManuallyDrop::new(Some(resource.clone())), pResource: unsafe { S::obtain(resource) },
}); });
let barriers = [ let barriers = [
@ -294,13 +301,13 @@ impl D3D12MipmapGen {
Anonymous: D3D12_RESOURCE_BARRIER_0 { UAV: uav_barrier }, Anonymous: D3D12_RESOURCE_BARRIER_0 { UAV: uav_barrier },
..Default::default() ..Default::default()
}, },
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<S, _>(
resource, resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
i, i,
), ),
util::d3d12_get_resource_transition_subresource( util::d3d12_get_resource_transition_subresource::<S, _>(
resource, resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
@ -312,7 +319,7 @@ impl D3D12MipmapGen {
cmd.ResourceBarrier(&barriers); cmd.ResourceBarrier(&barriers);
} }
residual_barriers.extend(barriers) S::cleanup_handler(|| residual_barriers.extend(barriers));
} }
Ok((heap_slots, residual_barriers)) Ok((heap_slots, residual_barriers))

View file

@ -1,37 +1,64 @@
use crate::filter_chain::FrameResiduals;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::Deref; use std::ops::Deref;
use windows::Win32::Graphics::Direct3D12::ID3D12Resource; use windows::Win32::Graphics::Direct3D12::ID3D12Resource;
pub trait ResourceHandleStrategy<H> { pub trait ResourceHandleStrategy<H> {
const NEEDS_CLEANUP: bool;
unsafe fn obtain(handle: &H) -> ManuallyDrop<Option<ID3D12Resource>>; unsafe fn obtain(handle: &H) -> ManuallyDrop<Option<ID3D12Resource>>;
unsafe fn cleanup(gc: &mut FrameResiduals, handle: ManuallyDrop<Option<ID3D12Resource>>);
/// Run a clean-up handler if the resource type needs clean up.
fn cleanup_handler<F: FnOnce()>(cleanup_fn: F) {
if Self::NEEDS_CLEANUP {
cleanup_fn();
}
}
} }
pub trait ObtainResourceHandle {
fn handle(&self) -> &ID3D12Resource;
}
impl ObtainResourceHandle for ID3D12Resource {
fn handle(&self) -> &ID3D12Resource {
&self
}
}
impl ObtainResourceHandle for ManuallyDrop<ID3D12Resource> {
fn handle(&self) -> &ID3D12Resource {
self.deref()
}
}
// this isn't actually used anymore but keep it around just in case.
pub struct IncrementRefcount; pub struct IncrementRefcount;
impl ResourceHandleStrategy<ManuallyDrop<ID3D12Resource>> for IncrementRefcount { impl ResourceHandleStrategy<ManuallyDrop<ID3D12Resource>> for IncrementRefcount {
const NEEDS_CLEANUP: bool = true;
unsafe fn obtain( unsafe fn obtain(
handle: &ManuallyDrop<ID3D12Resource>, handle: &ManuallyDrop<ID3D12Resource>,
) -> ManuallyDrop<Option<ID3D12Resource>> { ) -> ManuallyDrop<Option<ID3D12Resource>> {
ManuallyDrop::new(Some(handle.deref().clone())) ManuallyDrop::new(Some(handle.deref().clone()))
} }
}
unsafe fn cleanup(gc: &mut FrameResiduals, handle: ManuallyDrop<Option<ID3D12Resource>>) { impl ResourceHandleStrategy<ID3D12Resource> for IncrementRefcount {
gc.dispose_resource(handle); const NEEDS_CLEANUP: bool = true;
unsafe fn obtain(handle: &ID3D12Resource) -> ManuallyDrop<Option<ID3D12Resource>> {
ManuallyDrop::new(Some(handle.clone()))
} }
} }
pub struct OutlivesFrame; pub struct OutlivesFrame;
impl ResourceHandleStrategy<ManuallyDrop<ID3D12Resource>> for OutlivesFrame { impl ResourceHandleStrategy<ManuallyDrop<ID3D12Resource>> for OutlivesFrame {
const NEEDS_CLEANUP: bool = false;
unsafe fn obtain( unsafe fn obtain(
handle: &ManuallyDrop<ID3D12Resource>, handle: &ManuallyDrop<ID3D12Resource>,
) -> ManuallyDrop<Option<ID3D12Resource>> { ) -> ManuallyDrop<Option<ID3D12Resource>> {
unsafe { std::mem::transmute_copy(handle) } unsafe { std::mem::transmute_copy(handle) }
} }
unsafe fn cleanup(_gc: &mut FrameResiduals, _handle: ManuallyDrop<Option<ID3D12Resource>>) {
// Since the lifetime is ensured for the lifetime of the filter chain strategy, do nothing
}
} }

View file

@ -1,6 +1,8 @@
use crate::descriptor_heap::{CpuStagingHeap, RenderTargetHeap}; use crate::descriptor_heap::{CpuStagingHeap, RenderTargetHeap};
use crate::resource::ResourceHandleStrategy;
use d3d12_descriptor_heap::D3D12DescriptorHeapSlot; use d3d12_descriptor_heap::D3D12DescriptorHeapSlot;
use librashader_common::{FilterMode, GetSize, Size, WrapMode}; use librashader_common::{FilterMode, GetSize, Size, WrapMode};
use std::mem::ManuallyDrop;
use windows::Win32::Graphics::Direct3D12::{ID3D12Resource, D3D12_CPU_DESCRIPTOR_HANDLE}; use windows::Win32::Graphics::Direct3D12::{ID3D12Resource, D3D12_CPU_DESCRIPTOR_HANDLE};
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT; use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
@ -17,6 +19,12 @@ pub(crate) enum InputDescriptor {
Raw(D3D12_CPU_DESCRIPTOR_HANDLE), Raw(D3D12_CPU_DESCRIPTOR_HANDLE),
} }
impl InputDescriptor {
fn is_raw(&self) -> bool {
matches!(self, InputDescriptor::Raw(_))
}
}
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum OutputDescriptor { pub(crate) enum OutputDescriptor {
Owned(D3D12DescriptorHeapSlot<RenderTargetHeap>), Owned(D3D12DescriptorHeapSlot<RenderTargetHeap>),
@ -80,19 +88,19 @@ impl D3D12OutputView {
} }
} }
#[derive(Clone)]
pub struct InputTexture { pub struct InputTexture {
pub(crate) resource: ID3D12Resource, pub(crate) resource: ManuallyDrop<ID3D12Resource>,
pub(crate) descriptor: InputDescriptor, pub(crate) descriptor: InputDescriptor,
pub(crate) size: Size<u32>, pub(crate) size: Size<u32>,
pub(crate) format: DXGI_FORMAT, pub(crate) format: DXGI_FORMAT,
pub(crate) wrap_mode: WrapMode, pub(crate) wrap_mode: WrapMode,
pub(crate) filter: FilterMode, pub(crate) filter: FilterMode,
drop_flag: bool,
} }
impl InputTexture { impl InputTexture {
pub fn new( pub fn new<S: ResourceHandleStrategy<T>, T>(
resource: ID3D12Resource, resource: &T,
handle: D3D12DescriptorHeapSlot<CpuStagingHeap>, handle: D3D12DescriptorHeapSlot<CpuStagingHeap>,
size: Size<u32>, size: Size<u32>,
format: DXGI_FORMAT, format: DXGI_FORMAT,
@ -101,12 +109,18 @@ impl InputTexture {
) -> InputTexture { ) -> InputTexture {
let srv = InputDescriptor::Owned(handle); let srv = InputDescriptor::Owned(handle);
InputTexture { InputTexture {
resource, // SAFETY: `new` is only used for owned textures. We know this because
// we also hold `handle`, so the texture is at least
// as valid for the lifetime of handle.
// Also, resource is non-null by construction.
// Option<T> and <T> have the same layout.
resource: unsafe { std::mem::transmute(S::obtain(resource)) },
descriptor: srv, descriptor: srv,
size, size,
format, format,
wrap_mode, wrap_mode,
filter, filter,
drop_flag: S::NEEDS_CLEANUP,
} }
} }
@ -118,12 +132,42 @@ impl InputTexture {
) -> InputTexture { ) -> InputTexture {
let desc = unsafe { image.resource.GetDesc() }; let desc = unsafe { image.resource.GetDesc() };
InputTexture { InputTexture {
resource: image.resource, resource: ManuallyDrop::new(image.resource.clone()),
descriptor: InputDescriptor::Raw(image.descriptor), descriptor: InputDescriptor::Raw(image.descriptor),
size: Size::new(desc.Width as u32, desc.Height), size: Size::new(desc.Width as u32, desc.Height),
format: desc.Format, format: desc.Format,
wrap_mode, wrap_mode,
filter, filter,
drop_flag: true,
}
}
}
impl Clone for InputTexture {
fn clone(&self) -> Self {
// ensure lifetime for raw resources or if there is a drop flag
if self.descriptor.is_raw() || self.drop_flag {
InputTexture {
resource: ManuallyDrop::clone(&self.resource),
descriptor: self.descriptor.clone(),
size: self.size,
format: self.format,
wrap_mode: self.wrap_mode,
filter: self.filter,
drop_flag: true,
}
} else {
// SAFETY: the parent doesn't have drop flag, so that means
// we don't need to handle drop.
InputTexture {
resource: unsafe { std::mem::transmute_copy(&self.resource) },
descriptor: self.descriptor.clone(),
size: self.size,
format: self.format,
wrap_mode: self.wrap_mode,
filter: self.filter,
drop_flag: false,
}
} }
} }
} }
@ -141,3 +185,11 @@ impl GetSize<u32> for D3D12OutputView {
Ok(self.size) Ok(self.size)
} }
} }
impl Drop for InputTexture {
fn drop(&mut self) {
if self.drop_flag {
unsafe { ManuallyDrop::drop(&mut self.resource) }
}
}
}

View file

@ -7,8 +7,9 @@ use windows::Win32::Graphics::Direct3D::Dxc::{
DXC_CP_UTF8, DXC_CP_UTF8,
}; };
use crate::resource::ResourceHandleStrategy;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_FEATURE_DATA_FORMAT_SUPPORT, ID3D12Device, ID3D12GraphicsCommandList, D3D12_FEATURE_DATA_FORMAT_SUPPORT,
D3D12_FEATURE_FORMAT_SUPPORT, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0, D3D12_FEATURE_FORMAT_SUPPORT, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_FLAG_NONE,
D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_STATES, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_STATES,
@ -174,9 +175,8 @@ pub fn dxc_validate_shader(
} }
} }
#[must_use = "Resource Barrier must be disposed"] pub fn d3d12_get_resource_transition_subresource<S: ResourceHandleStrategy<T>, T>(
pub fn d3d12_get_resource_transition_subresource( resource: &T,
resource: &ID3D12Resource,
before: D3D12_RESOURCE_STATES, before: D3D12_RESOURCE_STATES,
after: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES,
subresource: u32, subresource: u32,
@ -186,7 +186,7 @@ pub fn d3d12_get_resource_transition_subresource(
Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE, Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
Anonymous: D3D12_RESOURCE_BARRIER_0 { Anonymous: D3D12_RESOURCE_BARRIER_0 {
Transition: ManuallyDrop::new(D3D12_RESOURCE_TRANSITION_BARRIER { Transition: ManuallyDrop::new(D3D12_RESOURCE_TRANSITION_BARRIER {
pResource: ManuallyDrop::new(Some(resource.clone())), pResource: unsafe { S::obtain(resource) },
Subresource: subresource, Subresource: subresource,
StateBefore: before, StateBefore: before,
StateAfter: after, StateAfter: after,
@ -195,15 +195,14 @@ pub fn d3d12_get_resource_transition_subresource(
} }
} }
#[must_use = "Resource Barrier must be disposed"]
#[inline(always)] #[inline(always)]
pub fn d3d12_resource_transition( pub fn d3d12_resource_transition<S: ResourceHandleStrategy<T>, T>(
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
resource: &ID3D12Resource, resource: &T,
before: D3D12_RESOURCE_STATES, before: D3D12_RESOURCE_STATES,
after: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES,
) -> [D3D12_RESOURCE_BARRIER; 1] { ) -> [D3D12_RESOURCE_BARRIER; 1] {
d3d12_resource_transition_subresource( d3d12_resource_transition_subresource::<S, T>(
cmd, cmd,
resource, resource,
before, before,
@ -214,14 +213,14 @@ pub fn d3d12_resource_transition(
#[must_use = "Resource Barrier must be disposed"] #[must_use = "Resource Barrier must be disposed"]
#[inline(always)] #[inline(always)]
pub fn d3d12_resource_transition_subresource( fn d3d12_resource_transition_subresource<S: ResourceHandleStrategy<T>, T>(
cmd: &ID3D12GraphicsCommandList, cmd: &ID3D12GraphicsCommandList,
resource: &ID3D12Resource, resource: &T,
before: D3D12_RESOURCE_STATES, before: D3D12_RESOURCE_STATES,
after: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES,
subresource: u32, subresource: u32,
) -> [D3D12_RESOURCE_BARRIER; 1] { ) -> [D3D12_RESOURCE_BARRIER; 1] {
let barrier = [d3d12_get_resource_transition_subresource( let barrier = [d3d12_get_resource_transition_subresource::<S, T>(
resource, resource,
before, before,
after, after,

View file

@ -188,7 +188,7 @@ fn get_hardware_adapter(factory: &IDXGIFactory4) -> Result<IDXGIAdapter1> {
for i in 0.. { for i in 0.. {
let adapter = unsafe { factory.EnumAdapters1(i)? }; let adapter = unsafe { factory.EnumAdapters1(i)? };
let mut desc = unsafe { adapter.GetDesc1()? }; let desc = unsafe { adapter.GetDesc1()? };
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != DXGI_ADAPTER_FLAG_NONE.0 as u32 { if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != DXGI_ADAPTER_FLAG_NONE.0 as u32 {
// Don't select the Basic Render Driver adapter. If you want a // Don't select the Basic Render Driver adapter. If you want a

View file

@ -5,8 +5,8 @@ use crate::hello_triangle::{DXSample, SampleCommandLine};
#[test] #[test]
fn triangle_d3d12() { fn triangle_d3d12() {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new( let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
"../test/shaders_slang/crt/crt-royale.slangp", // "../test/shaders_slang/crt/crt-royale.slangp",
// "../test/basic.slangp", // "../test/basic.slangp",
// "../test/shaders_slang/handheld/console-border/gbc-lcd-grid-v2.slangp", // "../test/shaders_slang/handheld/console-border/gbc-lcd-grid-v2.slangp",
// "../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp", // "../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",