librashader/librashader-runtime-d3d12/src/framebuffer.rs

327 lines
11 KiB
Rust
Raw Normal View History

2023-02-06 08:17:23 +11:00
use crate::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap};
use crate::error::{assume_d3d12_init, FilterChainError};
2023-02-06 17:05:19 +11:00
use crate::texture::{D3D12OutputView, InputTexture};
2023-01-31 17:25:45 +11:00
use crate::util::d3d12_get_closest_format;
2023-02-06 10:03:38 +11:00
use crate::{error, util};
2023-02-06 08:17:23 +11:00
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
2023-02-06 08:17:23 +11:00
use std::ops::Deref;
2023-02-06 17:05:19 +11:00
use windows::Win32::Foundation::RECT;
2023-02-06 08:17:23 +11:00
use windows::Win32::Graphics::Direct3D12::{
2023-02-06 10:03:38 +11:00
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_BOX,
D3D12_CPU_PAGE_PROPERTY_UNKNOWN, 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,
2023-02-06 10:34:30 +11:00
D3D12_FORMAT_SUPPORT1_TEXTURE2D, D3D12_HEAP_FLAG_NONE, D3D12_HEAP_PROPERTIES,
D3D12_HEAP_TYPE_DEFAULT, D3D12_MEMORY_POOL_UNKNOWN, D3D12_RENDER_TARGET_VIEW_DESC,
2023-02-07 13:56:24 +11:00
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,
2023-02-06 08:17:23 +11:00
};
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
2023-01-31 17:25:45 +11:00
#[derive(Debug, Clone)]
pub(crate) struct OwnedImage {
2023-02-06 08:17:23 +11:00
pub(crate) handle: ID3D12Resource,
2023-01-31 17:25:45 +11:00
pub(crate) size: Size<u32>,
2023-02-06 10:03:38 +11:00
pub(crate) format: ImageFormat,
2023-02-10 11:44:01 +11:00
pub(crate) max_mipmap: u16,
2023-01-31 17:25:45 +11:00
device: ID3D12Device,
}
2023-02-06 17:05:19 +11:00
static CLEAR: &[f32; 4] = &[0.0, 0.0, 0.0, 0.0];
2023-02-01 09:50:47 +11:00
impl OwnedImage {
pub fn new(
device: &ID3D12Device,
size: Size<u32>,
format: ImageFormat,
mipmap: bool,
) -> error::Result<OwnedImage> {
2023-02-10 13:03:55 +11:00
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,
};
2023-02-01 09:50:47 +11:00
2023-02-10 13:03:55 +11:00
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()
};
2023-02-01 09:50:47 +11:00
2023-02-10 13:03:55 +11:00
if mipmap {
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
format_support.Support1 |= D3D12_FORMAT_SUPPORT1_MIP;
}
2023-02-01 09:50:47 +11:00
2023-02-10 13:03:55 +11:00
desc.Format = d3d12_get_closest_format(device, desc.Format, format_support);
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,
)?;
2023-02-01 09:50:47 +11:00
}
2023-02-10 13:03:55 +11:00
assume_d3d12_init!(resource, "CreateCommittedResource");
Ok(OwnedImage {
handle: resource,
size,
format,
device: device.clone(),
max_mipmap: miplevels as u16,
})
2023-02-01 09:50:47 +11:00
}
2023-02-06 10:03:38 +11:00
/// 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<()> {
2023-02-07 13:56:24 +11:00
let barriers = [
util::d3d12_get_resource_transition_subresource(
&input.resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
),
util::d3d12_get_resource_transition_subresource(
&self.handle,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
),
];
cmd.ResourceBarrier(&barriers);
2023-02-06 10:03:38 +11:00
unsafe {
cmd.CopyTextureRegion(
&D3D12_TEXTURE_COPY_LOCATION {
pResource: windows::core::ManuallyDrop::new(&self.handle),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0,
},
},
0,
0,
0,
&D3D12_TEXTURE_COPY_LOCATION {
pResource: windows::core::ManuallyDrop::new(&input.resource),
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
SubresourceIndex: 0,
},
},
Some(&D3D12_BOX {
left: 0,
top: 0,
front: 0,
right: input.size.width,
bottom: input.size.height,
back: 1,
}),
);
}
2023-02-07 13:56:24 +11:00
let barriers = [
util::d3d12_get_resource_transition_subresource(
&input.resource,
D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
),
util::d3d12_get_resource_transition_subresource(
&self.handle,
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
),
];
cmd.ResourceBarrier(&barriers);
2023-02-06 10:03:38 +11:00
Ok(())
}
2023-02-06 17:05:19 +11:00
pub fn clear(
&self,
cmd: &ID3D12GraphicsCommandList,
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
) -> error::Result<()> {
util::d3d12_resource_transition(
cmd,
&self.handle,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET,
);
let rtv = self.create_render_target_view(heap)?;
let rect = RECT {
left: 0,
top: 0,
right: self.size.width as i32,
bottom: self.size.height as i32,
};
unsafe {
// more efficient if we don't pass the rect but
cmd.ClearRenderTargetView(*rtv.descriptor.as_ref(), CLEAR.as_ptr(), &[rect])
}
util::d3d12_resource_transition(
cmd,
&self.handle,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
);
Ok(())
}
2023-02-06 08:17:23 +11:00
pub(crate) fn create_shader_resource_view(
&self,
heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
filter: FilterMode,
wrap_mode: WrapMode,
) -> error::Result<InputTexture> {
2023-02-01 09:50:47 +11:00
let descriptor = heap.alloc_slot()?;
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 {
2023-02-01 17:25:39 +11:00
MipLevels: u32::MAX,
2023-02-01 09:50:47 +11:00
..Default::default()
},
},
};
2023-02-06 08:17:23 +11:00
self.device.CreateShaderResourceView(
&self.handle,
Some(&srv_desc),
*descriptor.deref().as_ref(),
);
2023-01-31 17:25:45 +11:00
}
2023-02-06 08:17:23 +11:00
Ok(InputTexture::new(
2023-02-06 10:03:38 +11:00
self.handle.clone(),
2023-02-06 08:17:23 +11:00
descriptor,
self.size,
self.format,
filter,
2023-02-06 17:05:19 +11:00
wrap_mode,
2023-02-06 08:17:23 +11:00
))
2023-02-01 09:50:47 +11:00
}
2023-02-06 08:17:23 +11:00
pub(crate) fn create_render_target_view(
&self,
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
2023-02-06 17:05:19 +11:00
) -> error::Result<D3D12OutputView> {
2023-02-01 09:50:47 +11:00
let descriptor = heap.alloc_slot()?;
2023-01-31 17:25:45 +11:00
unsafe {
2023-02-01 09:50:47 +11:00
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()
},
2023-01-31 17:25:45 +11:00
},
2023-02-01 09:50:47 +11:00
};
2023-02-06 08:17:23 +11:00
self.device.CreateRenderTargetView(
&self.handle,
Some(&rtv_desc),
*descriptor.deref().as_ref(),
);
2023-01-31 17:25:45 +11:00
}
2023-02-01 09:50:47 +11:00
Ok(D3D12OutputView::new(
descriptor,
self.size,
self.format.into(),
))
2023-01-31 17:25:45 +11:00
}
2023-02-01 17:25:39 +11:00
2023-02-06 08:17:23 +11:00
pub fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
mipmap: bool,
) -> error::Result<Size<u32>> {
2023-02-01 17:25:39 +11:00
let size = source_size.scale_viewport(scaling, *viewport_size);
if self.size != size
|| (mipmap && self.max_mipmap == 1)
|| (!mipmap && self.max_mipmap != 1)
|| format != self.format
{
2023-02-06 08:17:23 +11:00
let mut new = OwnedImage::new(&self.device, size, format, mipmap)?;
2023-02-01 17:25:39 +11:00
2023-02-06 08:17:23 +11:00
std::mem::swap(self, &mut new);
2023-02-01 17:25:39 +11:00
}
Ok(size)
}
2023-02-06 08:17:23 +11:00
}
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>,
should_mipmap: bool,
_context: &Self::Context,
) -> Result<Size<u32>, Self::Error> {
self.scale(scaling, format, viewport_size, source_size, should_mipmap)
}
}