401 lines
14 KiB
Rust
401 lines
14 KiB
Rust
use crate::descriptor_heap::{CpuStagingHeap, RenderTargetHeap};
|
|
use crate::error::FilterChainError;
|
|
use crate::resource::{OutlivesFrame, ResourceHandleStrategy};
|
|
use crate::texture::{D3D12OutputView, InputTexture};
|
|
use crate::util::d3d12_get_closest_format;
|
|
use crate::{error, util};
|
|
use d3d12_descriptor_heap::D3D12DescriptorHeap;
|
|
use gpu_allocator::d3d12::{
|
|
Allocator, Resource, ResourceCategory, ResourceCreateDesc, ResourceStateOrBarrierLayout,
|
|
ResourceType,
|
|
};
|
|
use gpu_allocator::MemoryLocation;
|
|
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
|
|
use librashader_presets::Scale2D;
|
|
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
|
|
use parking_lot::Mutex;
|
|
use std::mem::ManuallyDrop;
|
|
use std::sync::Arc;
|
|
use windows::Win32::Graphics::Direct3D12::{
|
|
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_BOX,
|
|
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, D3D12_FEATURE_DATA_FORMAT_SUPPORT,
|
|
D3D12_FORMAT_SUPPORT1_MIP, D3D12_FORMAT_SUPPORT1_RENDER_TARGET,
|
|
D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE, D3D12_FORMAT_SUPPORT1_TEXTURE2D,
|
|
D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD, D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE,
|
|
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_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RTV_DIMENSION_TEXTURE2D,
|
|
D3D12_SHADER_RESOURCE_VIEW_DESC, D3D12_SHADER_RESOURCE_VIEW_DESC_0,
|
|
D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_TEX2D_RTV, D3D12_TEX2D_SRV, D3D12_TEXTURE_COPY_LOCATION,
|
|
D3D12_TEXTURE_COPY_LOCATION_0, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
|
|
};
|
|
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct OwnedImage {
|
|
pub(crate) handle: ManuallyDrop<Resource>,
|
|
pub(crate) resource: ManuallyDrop<ID3D12Resource>,
|
|
pub(crate) size: Size<u32>,
|
|
pub(crate) format: DXGI_FORMAT,
|
|
pub(crate) max_mipmap: u16,
|
|
device: ID3D12Device,
|
|
allocator: Arc<Mutex<Allocator>>,
|
|
}
|
|
|
|
static CLEAR: &[f32; 4] = &[0.0, 0.0, 0.0, 0.0];
|
|
|
|
impl OwnedImage {
|
|
pub fn get_format_support(
|
|
device: &ID3D12Device,
|
|
format: DXGI_FORMAT,
|
|
mipmap: bool,
|
|
) -> DXGI_FORMAT {
|
|
let mut format_support = D3D12_FEATURE_DATA_FORMAT_SUPPORT {
|
|
Format: format,
|
|
Support1: D3D12_FORMAT_SUPPORT1_TEXTURE2D
|
|
| D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE
|
|
| D3D12_FORMAT_SUPPORT1_RENDER_TARGET,
|
|
..Default::default()
|
|
};
|
|
|
|
if mipmap {
|
|
format_support.Support1 |= D3D12_FORMAT_SUPPORT1_MIP;
|
|
format_support.Support2 |=
|
|
D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE;
|
|
}
|
|
|
|
d3d12_get_closest_format(device, format_support)
|
|
}
|
|
|
|
pub fn new(
|
|
device: &ID3D12Device,
|
|
allocator: &Arc<Mutex<Allocator>>,
|
|
size: Size<u32>,
|
|
format: DXGI_FORMAT,
|
|
mipmap: bool,
|
|
) -> error::Result<OwnedImage> {
|
|
let miplevels = if mipmap {
|
|
size.calculate_miplevels()
|
|
} else {
|
|
1
|
|
};
|
|
let mut desc = D3D12_RESOURCE_DESC {
|
|
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
|
Alignment: 0,
|
|
Width: size.width as u64,
|
|
Height: size.height,
|
|
DepthOrArraySize: 1,
|
|
MipLevels: miplevels as u16,
|
|
Format: format.into(),
|
|
SampleDesc: DXGI_SAMPLE_DESC {
|
|
Count: 1,
|
|
Quality: 0,
|
|
},
|
|
Layout: Default::default(),
|
|
Flags: D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
|
|
};
|
|
|
|
let mut format_support = D3D12_FEATURE_DATA_FORMAT_SUPPORT {
|
|
Format: desc.Format,
|
|
Support1: D3D12_FORMAT_SUPPORT1_TEXTURE2D
|
|
| D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE
|
|
| D3D12_FORMAT_SUPPORT1_RENDER_TARGET,
|
|
..Default::default()
|
|
};
|
|
|
|
if mipmap {
|
|
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
format_support.Support1 |= D3D12_FORMAT_SUPPORT1_MIP;
|
|
format_support.Support2 |=
|
|
D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE;
|
|
}
|
|
|
|
desc.Format = d3d12_get_closest_format(device, format_support);
|
|
|
|
let allocator_resource = allocator.lock().create_resource(&ResourceCreateDesc {
|
|
name: "ownedimage",
|
|
memory_location: MemoryLocation::GpuOnly,
|
|
resource_category: ResourceCategory::RtvDsvTexture,
|
|
resource_desc: &desc,
|
|
castable_formats: &[],
|
|
clear_value: None,
|
|
initial_state_or_layout: ResourceStateOrBarrierLayout::ResourceState(
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
),
|
|
resource_type: &ResourceType::Placed,
|
|
})?;
|
|
|
|
// let mut resource: Option<ID3D12Resource> = None;
|
|
// unsafe {
|
|
// device.CreateCommittedResource(
|
|
// &D3D12_HEAP_PROPERTIES {
|
|
// Type: D3D12_HEAP_TYPE_DEFAULT,
|
|
// CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
|
// MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
|
|
// CreationNodeMask: 1,
|
|
// VisibleNodeMask: 1,
|
|
// },
|
|
// D3D12_HEAP_FLAG_NONE,
|
|
// &desc,
|
|
// D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
// None,
|
|
// &mut resource,
|
|
// )?;
|
|
// }
|
|
// assume_d3d12_init!(resource, "CreateCommittedResource");
|
|
|
|
let resource = ManuallyDrop::new(allocator_resource.resource().clone());
|
|
Ok(OwnedImage {
|
|
handle: ManuallyDrop::new(allocator_resource),
|
|
resource,
|
|
size,
|
|
format: desc.Format,
|
|
device: device.clone(),
|
|
max_mipmap: miplevels as u16,
|
|
allocator: Arc::clone(&allocator),
|
|
})
|
|
}
|
|
|
|
/// SAFETY: self must fit the source image
|
|
/// source must be in D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
|
|
pub unsafe fn copy_from(
|
|
&self,
|
|
cmd: &ID3D12GraphicsCommandList,
|
|
input: &InputTexture,
|
|
) -> error::Result<()> {
|
|
let barriers = [
|
|
util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
|
|
&input.resource,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
|
),
|
|
util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
|
|
&self.resource,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
|
),
|
|
];
|
|
|
|
unsafe {
|
|
cmd.ResourceBarrier(&barriers);
|
|
|
|
let dst = D3D12_TEXTURE_COPY_LOCATION {
|
|
pResource: OutlivesFrame::obtain(&self.resource),
|
|
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
|
|
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
|
|
SubresourceIndex: 0,
|
|
},
|
|
};
|
|
|
|
let src = D3D12_TEXTURE_COPY_LOCATION {
|
|
pResource: OutlivesFrame::obtain(&input.resource),
|
|
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
|
|
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
|
|
SubresourceIndex: 0,
|
|
},
|
|
};
|
|
|
|
cmd.CopyTextureRegion(
|
|
&dst,
|
|
0,
|
|
0,
|
|
0,
|
|
&src,
|
|
Some(&D3D12_BOX {
|
|
left: 0,
|
|
top: 0,
|
|
front: 0,
|
|
right: input.size.width,
|
|
bottom: input.size.height,
|
|
back: 1,
|
|
}),
|
|
);
|
|
}
|
|
|
|
let barriers = [
|
|
util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
|
|
&input.resource,
|
|
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
|
),
|
|
util::d3d12_get_resource_transition_subresource::<OutlivesFrame, _>(
|
|
&self.resource,
|
|
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
|
),
|
|
];
|
|
|
|
unsafe {
|
|
cmd.ResourceBarrier(&barriers);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn clear(
|
|
&self,
|
|
cmd: &ID3D12GraphicsCommandList,
|
|
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
|
|
) -> error::Result<()> {
|
|
unsafe {
|
|
util::d3d12_resource_transition::<OutlivesFrame, _>(
|
|
cmd,
|
|
&self.resource,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
);
|
|
|
|
let rtv = self.create_render_target_view(heap)?;
|
|
|
|
cmd.ClearRenderTargetView(*rtv.descriptor.as_ref(), CLEAR, None);
|
|
|
|
util::d3d12_resource_transition::<OutlivesFrame, _>(
|
|
cmd,
|
|
&self.resource,
|
|
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn create_shader_resource_view(
|
|
&self,
|
|
heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
|
|
filter: FilterMode,
|
|
wrap_mode: WrapMode,
|
|
) -> error::Result<InputTexture> {
|
|
let descriptor = heap.allocate_descriptor()?;
|
|
|
|
unsafe {
|
|
let srv_desc = D3D12_SHADER_RESOURCE_VIEW_DESC {
|
|
Format: self.format.into(),
|
|
ViewDimension: D3D12_SRV_DIMENSION_TEXTURE2D,
|
|
Shader4ComponentMapping: D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
|
|
Anonymous: D3D12_SHADER_RESOURCE_VIEW_DESC_0 {
|
|
Texture2D: D3D12_TEX2D_SRV {
|
|
MipLevels: u32::MAX,
|
|
..Default::default()
|
|
},
|
|
},
|
|
};
|
|
|
|
self.device.CreateShaderResourceView(
|
|
self.handle.resource(),
|
|
Some(&srv_desc),
|
|
*descriptor.as_ref(),
|
|
);
|
|
}
|
|
|
|
Ok(InputTexture::new::<OutlivesFrame, _>(
|
|
&self.resource,
|
|
descriptor,
|
|
self.size,
|
|
self.format,
|
|
filter,
|
|
wrap_mode,
|
|
))
|
|
}
|
|
|
|
pub(crate) fn create_render_target_view(
|
|
&self,
|
|
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
|
|
) -> error::Result<D3D12OutputView> {
|
|
let descriptor = heap.allocate_descriptor()?;
|
|
|
|
unsafe {
|
|
let rtv_desc = D3D12_RENDER_TARGET_VIEW_DESC {
|
|
Format: self.format.into(),
|
|
ViewDimension: D3D12_RTV_DIMENSION_TEXTURE2D,
|
|
Anonymous: D3D12_RENDER_TARGET_VIEW_DESC_0 {
|
|
Texture2D: D3D12_TEX2D_RTV {
|
|
MipSlice: 0,
|
|
..Default::default()
|
|
},
|
|
},
|
|
};
|
|
|
|
self.device.CreateRenderTargetView(
|
|
self.handle.resource(),
|
|
Some(&rtv_desc),
|
|
*descriptor.as_ref(),
|
|
);
|
|
}
|
|
|
|
Ok(D3D12OutputView::new(
|
|
descriptor,
|
|
self.size,
|
|
self.format.into(),
|
|
))
|
|
}
|
|
|
|
pub fn scale(
|
|
&mut self,
|
|
scaling: Scale2D,
|
|
format: ImageFormat,
|
|
viewport_size: &Size<u32>,
|
|
source_size: &Size<u32>,
|
|
original_size: &Size<u32>,
|
|
mipmap: bool,
|
|
) -> error::Result<Size<u32>> {
|
|
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
|
|
let format = Self::get_format_support(&self.device, format.into(), mipmap);
|
|
|
|
if self.size != size
|
|
|| (mipmap && self.max_mipmap == 1)
|
|
|| (!mipmap && self.max_mipmap != 1)
|
|
|| format != self.format
|
|
{
|
|
let mut new = OwnedImage::new(&self.device, &self.allocator, size, format, mipmap)?;
|
|
std::mem::swap(self, &mut new);
|
|
}
|
|
Ok(size)
|
|
}
|
|
}
|
|
|
|
impl ScaleFramebuffer for OwnedImage {
|
|
type Error = FilterChainError;
|
|
type Context = ();
|
|
|
|
fn scale(
|
|
&mut self,
|
|
scaling: Scale2D,
|
|
format: ImageFormat,
|
|
viewport_size: &Size<u32>,
|
|
source_size: &Size<u32>,
|
|
original_size: &Size<u32>,
|
|
should_mipmap: bool,
|
|
_context: &Self::Context,
|
|
) -> Result<Size<u32>, Self::Error> {
|
|
self.scale(
|
|
scaling,
|
|
format,
|
|
viewport_size,
|
|
source_size,
|
|
original_size,
|
|
should_mipmap,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Drop for OwnedImage {
|
|
fn drop(&mut self) {
|
|
// let go of the handle
|
|
unsafe {
|
|
ManuallyDrop::drop(&mut self.resource);
|
|
}
|
|
let resource = unsafe { ManuallyDrop::take(&mut self.handle) };
|
|
if let Err(e) = self.allocator.lock().free_resource(resource) {
|
|
println!("librashader-runtime-d3d12: [warn] failed to deallocate owned image buffer memory {e}")
|
|
}
|
|
}
|
|
}
|