vk/11/12: allow gpu-deferred creation of filter chains
This commit is contained in:
parent
800b6e7b4b
commit
fa6cd87c60
12
README.md
12
README.md
|
@ -152,11 +152,13 @@ static GL_DEFAULT_MVP: &[f32; 16] = &[
|
|||
```
|
||||
|
||||
### Thread safety
|
||||
In general, it is **safe** to create a filter chain instance from a different thread, but drawing filter passes **must be
|
||||
externally synchronized**. The exceptions to filter chain creation are in OpenGL, where creating the filter chain instance
|
||||
is safe **if and only if** the thread local OpenGL context is initialized to the same context as the drawing thread, and
|
||||
in Direct3D 11, where filter chain creation is thread-unsafe if the `ID3D11Device` was created with
|
||||
`D3D11_CREATE_DEVICE_SINGLETHREADED`.
|
||||
In general, it is **safe** to create a filter chain instance from a different thread, but drawing frames requires
|
||||
**externally synchronization** of the filter chain object. Additionally, filter chain creation requires external synchronization
|
||||
of the graphics devices queue where applicable, as loading LUT textures requires submission of commands to the graphics queue,
|
||||
unless the `filter_chain_create_deferred` methods are used.
|
||||
|
||||
OpenGL has an additional restriction where creating the filter chain instance in a different thread is safe **if and only if**
|
||||
the thread local OpenGL context is initialized to the same context as the drawing thread.
|
||||
|
||||
### Writing a librashader Runtime
|
||||
|
||||
|
|
|
@ -90,6 +90,31 @@ impl FilterChainD3D11 {
|
|||
device: &ID3D11Device,
|
||||
preset: ShaderPreset,
|
||||
options: Option<&FilterChainOptionsD3D11>,
|
||||
) -> error::Result<FilterChainD3D11> {
|
||||
let immediate_context = unsafe { device.GetImmediateContext()? };
|
||||
unsafe { Self::load_from_preset_deferred(device, preset, &immediate_context, options) }
|
||||
}
|
||||
|
||||
/// Load a filter chain from a pre-parsed `ShaderPreset`, deferring and GPU-side initialization
|
||||
/// to the caller. This function is therefore requires no external synchronization of the
|
||||
/// immediate context, as long as the immediate context is not used as the input context,
|
||||
/// nor of the device, as long as the device is not single-threaded only.
|
||||
///
|
||||
/// ## Safety
|
||||
/// The provided context must either be immediate, or immediately submitted after this function
|
||||
/// returns, **before drawing frames**, or lookup textures will fail to load and the filter chain
|
||||
/// will be in an invalid state.
|
||||
///
|
||||
/// If the context is deferred, it must be ready for command recording, and have no prior commands
|
||||
/// recorded. No commands shall be recorded after, the caller must immediately call [`FinishCommandList`](https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-finishcommandlist)
|
||||
/// and execute the command list on the immediate context after this function returns.
|
||||
///
|
||||
/// If the context is immediate, then access to the immediate context requires external synchronization.
|
||||
pub unsafe fn load_from_preset_deferred(
|
||||
device: &ID3D11Device,
|
||||
preset: ShaderPreset,
|
||||
ctx: &ID3D11DeviceContext,
|
||||
options: Option<&FilterChainOptionsD3D11>,
|
||||
) -> error::Result<FilterChainD3D11> {
|
||||
let (passes, semantics) = HLSL::compile_preset_passes::<
|
||||
GlslangCompilation,
|
||||
|
@ -104,7 +129,7 @@ impl FilterChainD3D11 {
|
|||
let immediate_context = unsafe { device.GetImmediateContext()? };
|
||||
|
||||
// load luts
|
||||
let luts = FilterChainD3D11::load_luts(device, &immediate_context, &preset.textures)?;
|
||||
let luts = FilterChainD3D11::load_luts(device, &ctx, &preset.textures)?;
|
||||
|
||||
let framebuffer_gen =
|
||||
|| OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false);
|
||||
|
|
|
@ -38,10 +38,11 @@ mod tests {
|
|||
// const FILTER_PATH: &str =
|
||||
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp";
|
||||
// "../test/null.slangp",
|
||||
// const FILTER_PATH: &str = "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp";
|
||||
const FILTER_PATH: &str =
|
||||
"../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp";
|
||||
|
||||
// const FILTER_PATH: &str = "../test/slang-shaders/test/history.slangp";
|
||||
const FILTER_PATH: &str = "../test/slang-shaders/test/feedback.slangp";
|
||||
// const FILTER_PATH: &str = "../test/slang-shaders/test/feedback.slangp";
|
||||
|
||||
// const FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp";
|
||||
const IMAGE_PATH: &str = "../triangle.png";
|
||||
|
|
|
@ -43,7 +43,7 @@ use windows::Win32::Graphics::Direct3D12::{
|
|||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
||||
use windows::Win32::System::Threading::{CreateEventA, ResetEvent, WaitForSingleObject};
|
||||
use windows::Win32::System::Threading::{CreateEventA, WaitForSingleObject};
|
||||
use windows::Win32::System::WindowsProgramming::INFINITE;
|
||||
|
||||
use librashader_runtime::framebuffer::FramebufferInit;
|
||||
|
@ -51,7 +51,7 @@ use librashader_runtime::render_target::RenderTarget;
|
|||
use librashader_runtime::scaling::ScaleFramebuffer;
|
||||
use rayon::prelude::*;
|
||||
|
||||
const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 1024;
|
||||
const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 4096;
|
||||
|
||||
type DxilShaderPassMeta =
|
||||
ShaderPassArtifact<impl CompileReflectShader<DXIL, GlslangCompilation> + Send>;
|
||||
|
@ -99,6 +99,7 @@ pub(crate) struct FilterCommon {
|
|||
pub(crate) struct FrameResiduals {
|
||||
outputs: Vec<OutputDescriptor>,
|
||||
mipmaps: Vec<D3D12DescriptorHeapSlot<ResourceWorkHeap>>,
|
||||
mipmap_luts: Vec<D3D12MipmapGen>,
|
||||
}
|
||||
|
||||
impl FrameResiduals {
|
||||
|
@ -106,9 +107,14 @@ impl FrameResiduals {
|
|||
Self {
|
||||
outputs: Vec::new(),
|
||||
mipmaps: Vec::new(),
|
||||
mipmap_luts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispose_mipmap_gen(&mut self, mipmap: D3D12MipmapGen) {
|
||||
self.mipmap_luts.push(mipmap)
|
||||
}
|
||||
|
||||
pub fn dispose_output(&mut self, descriptor: OutputDescriptor) {
|
||||
self.outputs.push(descriptor)
|
||||
}
|
||||
|
@ -149,6 +155,52 @@ impl FilterChainD3D12 {
|
|||
device: &ID3D12Device,
|
||||
preset: ShaderPreset,
|
||||
options: Option<&FilterChainOptionsD3D12>,
|
||||
) -> error::Result<FilterChainD3D12> {
|
||||
unsafe {
|
||||
// 1 time queue infrastructure for lut uploads
|
||||
let command_pool: ID3D12CommandAllocator =
|
||||
device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?;
|
||||
let cmd: ID3D12GraphicsCommandList =
|
||||
device.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &command_pool, None)?;
|
||||
let queue: ID3D12CommandQueue =
|
||||
device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC {
|
||||
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
Priority: 0,
|
||||
Flags: D3D12_COMMAND_QUEUE_FLAG_NONE,
|
||||
NodeMask: 0,
|
||||
})?;
|
||||
|
||||
let fence_event = CreateEventA(None, false, false, None)?;
|
||||
let fence: ID3D12Fence = device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
|
||||
|
||||
let filter_chain = Self::load_from_preset_deferred(device, preset, &cmd, options)?;
|
||||
|
||||
cmd.Close()?;
|
||||
queue.ExecuteCommandLists(&[cmd.cast()?]);
|
||||
queue.Signal(&fence, 1)?;
|
||||
|
||||
if fence.GetCompletedValue() < 1 {
|
||||
fence.SetEventOnCompletion(1, fence_event)?;
|
||||
WaitForSingleObject(fence_event, INFINITE);
|
||||
CloseHandle(fence_event);
|
||||
}
|
||||
|
||||
Ok(filter_chain)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a filter chain from a pre-parsed `ShaderPreset`, deferring and GPU-side initialization
|
||||
/// to the caller. This function therefore requires no external synchronization of the device queue.
|
||||
///
|
||||
/// ## Safety
|
||||
/// The provided command list must be ready for recording and contain no prior commands.
|
||||
/// The caller is responsible for ending the command list and immediately submitting it to a
|
||||
/// graphics queue. The command list must be completely executed before calling [`frame`](Self::frame).
|
||||
pub unsafe fn load_from_preset_deferred(
|
||||
device: &ID3D12Device,
|
||||
preset: ShaderPreset,
|
||||
cmd: &ID3D12GraphicsCommandList,
|
||||
options: Option<&FilterChainOptionsD3D12>,
|
||||
) -> error::Result<FilterChainD3D12> {
|
||||
let shader_count = preset.shaders.len();
|
||||
let lut_count = preset.textures.len();
|
||||
|
@ -171,18 +223,20 @@ impl FilterChainD3D12 {
|
|||
let draw_quad = DrawQuad::new(device)?;
|
||||
let mut staging_heap = D3D12DescriptorHeap::new(
|
||||
device,
|
||||
(MAX_BINDINGS_COUNT as usize) * shader_count + 2048 + lut_count,
|
||||
(MAX_BINDINGS_COUNT as usize) * shader_count
|
||||
+ MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS
|
||||
+ lut_count,
|
||||
)?;
|
||||
let rtv_heap = D3D12DescriptorHeap::new(
|
||||
device,
|
||||
(MAX_BINDINGS_COUNT as usize) * shader_count + 2048 + lut_count,
|
||||
(MAX_BINDINGS_COUNT as usize) * shader_count
|
||||
+ MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS
|
||||
+ lut_count,
|
||||
)?;
|
||||
|
||||
let luts = FilterChainD3D12::load_luts(device, &mut staging_heap, &preset.textures)?;
|
||||
|
||||
let root_signature = D3D12RootSignature::new(device)?;
|
||||
|
||||
let (texture_heap, sampler_heap, filters, mipmap_heap) = FilterChainD3D12::init_passes(
|
||||
let (texture_heap, sampler_heap, filters, mut mipmap_heap) = FilterChainD3D12::init_passes(
|
||||
device,
|
||||
&root_signature,
|
||||
passes,
|
||||
|
@ -191,6 +245,17 @@ impl FilterChainD3D12 {
|
|||
options.map_or(false, |o| o.force_hlsl_pipeline),
|
||||
)?;
|
||||
|
||||
let mut residuals = FrameResiduals::new();
|
||||
|
||||
let luts = FilterChainD3D12::load_luts(
|
||||
device,
|
||||
cmd,
|
||||
&mut staging_heap,
|
||||
&mut mipmap_heap,
|
||||
&mut residuals,
|
||||
&preset.textures,
|
||||
)?;
|
||||
|
||||
let framebuffer_gen =
|
||||
|| OwnedImage::new(device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, false);
|
||||
let input_gen = || None;
|
||||
|
@ -240,70 +305,39 @@ impl FilterChainD3D12 {
|
|||
sampler_heap,
|
||||
mipmap_heap,
|
||||
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
|
||||
residuals: FrameResiduals::new(),
|
||||
residuals,
|
||||
})
|
||||
}
|
||||
|
||||
fn load_luts(
|
||||
device: &ID3D12Device,
|
||||
heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
|
||||
cmd: &ID3D12GraphicsCommandList,
|
||||
staging_heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
|
||||
mipmap_heap: &mut D3D12DescriptorHeap<ResourceWorkHeap>,
|
||||
trash: &mut FrameResiduals,
|
||||
textures: &[TextureConfig],
|
||||
) -> error::Result<FxHashMap<usize, LutTexture>> {
|
||||
// use separate mipgen to load luts.
|
||||
let mipmap_gen = D3D12MipmapGen::new(device, true)?;
|
||||
let mut work_heap: D3D12DescriptorHeap<ResourceWorkHeap> =
|
||||
D3D12DescriptorHeap::new(device, u16::MAX as usize)?;
|
||||
unsafe {
|
||||
// 1 time queue infrastructure for lut uploads
|
||||
let command_pool: ID3D12CommandAllocator =
|
||||
device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?;
|
||||
let cmd: ID3D12GraphicsCommandList =
|
||||
device.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &command_pool, None)?;
|
||||
let queue: ID3D12CommandQueue =
|
||||
device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC {
|
||||
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
Priority: 0,
|
||||
Flags: D3D12_COMMAND_QUEUE_FLAG_NONE,
|
||||
NodeMask: 0,
|
||||
})?;
|
||||
|
||||
let fence_event = CreateEventA(None, false, false, None)?;
|
||||
let fence: ID3D12Fence = device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
|
||||
let mut residuals = Vec::new();
|
||||
|
||||
let mut luts = FxHashMap::default();
|
||||
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
let image = Image::load(&texture.path, UVDirection::TopLeft)?;
|
||||
|
||||
let (texture, staging) = LutTexture::new(
|
||||
let texture = LutTexture::new(
|
||||
device,
|
||||
heap,
|
||||
&cmd,
|
||||
staging_heap,
|
||||
cmd,
|
||||
&image,
|
||||
texture.filter_mode,
|
||||
texture.wrap_mode,
|
||||
texture.mipmap,
|
||||
)?;
|
||||
luts.insert(index, texture);
|
||||
residuals.push(staging);
|
||||
}
|
||||
|
||||
cmd.Close()?;
|
||||
|
||||
queue.ExecuteCommandLists(&[cmd.cast()?]);
|
||||
queue.Signal(&fence, 1)?;
|
||||
|
||||
// Wait until finished
|
||||
if fence.GetCompletedValue() < 1 {
|
||||
fence.SetEventOnCompletion(1, fence_event)?;
|
||||
WaitForSingleObject(fence_event, INFINITE);
|
||||
ResetEvent(fence_event);
|
||||
}
|
||||
|
||||
cmd.Reset(&command_pool, None)?;
|
||||
|
||||
let residuals = mipmap_gen.mipmapping_context(&cmd, &mut work_heap, |context| {
|
||||
let residual_mipmap = mipmap_gen.mipmapping_context(cmd, mipmap_heap, |context| {
|
||||
for lut in luts.values() {
|
||||
lut.generate_mipmaps(context)?;
|
||||
}
|
||||
|
@ -311,21 +345,11 @@ impl FilterChainD3D12 {
|
|||
Ok::<(), FilterChainError>(())
|
||||
})?;
|
||||
|
||||
//
|
||||
cmd.Close()?;
|
||||
queue.ExecuteCommandLists(&[cmd.cast()?]);
|
||||
queue.Signal(&fence, 2)?;
|
||||
//
|
||||
if fence.GetCompletedValue() < 2 {
|
||||
fence.SetEventOnCompletion(2, fence_event)?;
|
||||
WaitForSingleObject(fence_event, INFINITE);
|
||||
CloseHandle(fence_event);
|
||||
}
|
||||
trash.dispose_mipmap_handles(residual_mipmap);
|
||||
trash.dispose_mipmap_gen(mipmap_gen);
|
||||
|
||||
drop(residuals);
|
||||
Ok(luts)
|
||||
}
|
||||
}
|
||||
|
||||
fn init_passes(
|
||||
device: &ID3D12Device,
|
||||
|
|
|
@ -35,8 +35,9 @@ mod tests {
|
|||
let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
|
||||
// "../test/slang-shaders/crt/crt-lottes.slangp",
|
||||
// "../test/basic.slangp",
|
||||
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp",
|
||||
"../test/slang-shaders/test/feedback.slangp",
|
||||
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp",
|
||||
"../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp",
|
||||
// "../test/slang-shaders/test/feedback.slangp",
|
||||
// "../test/slang-shaders/test/history.slangp",
|
||||
// "../test/slang-shaders/crt/crt-royale.slangp",
|
||||
// "../test/slang-shaders/vhs/VHSPro.slangp",
|
||||
|
|
|
@ -27,6 +27,7 @@ pub struct LutTexture {
|
|||
resource: ID3D12Resource,
|
||||
view: InputTexture,
|
||||
miplevels: Option<u16>,
|
||||
_staging: ID3D12Resource,
|
||||
}
|
||||
|
||||
impl LutTexture {
|
||||
|
@ -38,7 +39,7 @@ impl LutTexture {
|
|||
filter: FilterMode,
|
||||
wrap_mode: WrapMode,
|
||||
mipmap: bool,
|
||||
) -> error::Result<(LutTexture, ID3D12Resource)> {
|
||||
) -> error::Result<LutTexture> {
|
||||
let miplevels = source.size.calculate_miplevels() as u16;
|
||||
let mut desc = D3D12_RESOURCE_DESC {
|
||||
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
||||
|
@ -186,14 +187,12 @@ impl LutTexture {
|
|||
filter,
|
||||
wrap_mode,
|
||||
);
|
||||
Ok((
|
||||
LutTexture {
|
||||
Ok(LutTexture {
|
||||
resource,
|
||||
_staging: upload,
|
||||
view,
|
||||
miplevels: if mipmap { Some(miplevels) } else { None },
|
||||
},
|
||||
upload,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_mipmaps(&self, gen_mips: &mut MipmapGenContext) -> error::Result<()> {
|
||||
|
|
|
@ -4,6 +4,7 @@ use librashader_preprocess::PreprocessError;
|
|||
use librashader_presets::ParsePresetError;
|
||||
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
|
||||
use librashader_runtime::image::ImageError;
|
||||
use std::convert::Infallible;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Cumulative error type for Vulkan filter chains.
|
||||
|
@ -31,5 +32,11 @@ pub enum FilterChainError {
|
|||
AllocationDoesNotExist,
|
||||
}
|
||||
|
||||
impl From<Infallible> for FilterChainError {
|
||||
fn from(_value: Infallible) -> Self {
|
||||
panic!("uninhabited error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Result type for Vulkan filter chains.
|
||||
pub type Result<T> = std::result::Result<T, FilterChainError>;
|
||||
|
|
|
@ -28,6 +28,7 @@ use librashader_runtime::uniforms::UniformStorage;
|
|||
use parking_lot::RwLock;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::Infallible;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -218,27 +219,109 @@ impl Drop for FrameResiduals {
|
|||
|
||||
impl FilterChainVulkan {
|
||||
/// Load the shader preset at the given path into a filter chain.
|
||||
pub fn load_from_path(
|
||||
vulkan: impl TryInto<VulkanObjects, Error = FilterChainError>,
|
||||
pub fn load_from_path<V, E>(
|
||||
vulkan: V,
|
||||
path: impl AsRef<Path>,
|
||||
options: Option<&FilterChainOptionsVulkan>,
|
||||
) -> error::Result<FilterChainVulkan> {
|
||||
) -> error::Result<FilterChainVulkan>
|
||||
where
|
||||
V: TryInto<VulkanObjects, Error = E>,
|
||||
FilterChainError: From<E>,
|
||||
{
|
||||
// load passes from preset
|
||||
let preset = ShaderPreset::try_parse(path)?;
|
||||
Self::load_from_preset(vulkan, preset, options)
|
||||
}
|
||||
|
||||
/// Load a filter chain from a pre-parsed `ShaderPreset`.
|
||||
pub fn load_from_preset(
|
||||
vulkan: impl TryInto<VulkanObjects, Error = FilterChainError>,
|
||||
pub fn load_from_preset<V, E>(
|
||||
vulkan: V,
|
||||
preset: ShaderPreset,
|
||||
options: Option<&FilterChainOptionsVulkan>,
|
||||
) -> error::Result<FilterChainVulkan> {
|
||||
) -> error::Result<FilterChainVulkan>
|
||||
where
|
||||
V: TryInto<VulkanObjects, Error = E>,
|
||||
FilterChainError: From<E>,
|
||||
{
|
||||
let vulkan = vulkan.try_into().map_err(|e| e.into())?;
|
||||
let device = Arc::clone(&vulkan.device);
|
||||
let queue = vulkan.queue.clone();
|
||||
|
||||
let command_pool = unsafe {
|
||||
device.create_command_pool(
|
||||
&vk::CommandPoolCreateInfo::builder()
|
||||
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
|
||||
.build(),
|
||||
None,
|
||||
)?
|
||||
};
|
||||
|
||||
let command_buffer = unsafe {
|
||||
// panic safety: command buffer count = 1
|
||||
device.allocate_command_buffers(
|
||||
&vk::CommandBufferAllocateInfo::builder()
|
||||
.command_pool(command_pool)
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_buffer_count(1)
|
||||
.build(),
|
||||
)?[0]
|
||||
};
|
||||
|
||||
unsafe {
|
||||
device.begin_command_buffer(
|
||||
command_buffer,
|
||||
&vk::CommandBufferBeginInfo::builder()
|
||||
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
|
||||
.build(),
|
||||
)?
|
||||
}
|
||||
|
||||
let filter_chain = unsafe {
|
||||
Self::load_from_preset_deferred::<_, Infallible>(
|
||||
vulkan,
|
||||
preset,
|
||||
command_buffer,
|
||||
options,
|
||||
)?
|
||||
};
|
||||
|
||||
unsafe {
|
||||
device.end_command_buffer(command_buffer)?;
|
||||
|
||||
let buffers = [command_buffer];
|
||||
let submits = [vk::SubmitInfo::builder().command_buffers(&buffers).build()];
|
||||
|
||||
device.queue_submit(queue, &submits, vk::Fence::null())?;
|
||||
device.queue_wait_idle(queue)?;
|
||||
device.free_command_buffers(command_pool, &buffers);
|
||||
device.destroy_command_pool(command_pool, None);
|
||||
}
|
||||
|
||||
Ok(filter_chain)
|
||||
}
|
||||
|
||||
/// Load a filter chain from a pre-parsed `ShaderPreset`, deferring and GPU-side initialization
|
||||
/// to the caller. This function therefore requires no external synchronization of the device queue.
|
||||
///
|
||||
/// ## Safety
|
||||
/// The provided command buffer must be ready for recording and contain no prior commands.
|
||||
/// The caller is responsible for ending the command buffer and immediately submitting it to a
|
||||
/// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
|
||||
pub unsafe fn load_from_preset_deferred<V, E>(
|
||||
vulkan: V,
|
||||
preset: ShaderPreset,
|
||||
cmd: vk::CommandBuffer,
|
||||
options: Option<&FilterChainOptionsVulkan>,
|
||||
) -> error::Result<FilterChainVulkan>
|
||||
where
|
||||
V: TryInto<VulkanObjects, Error = E>,
|
||||
FilterChainError: From<E>,
|
||||
{
|
||||
let (passes, semantics) = SPIRV::compile_preset_passes::<
|
||||
GlslangCompilation,
|
||||
FilterChainError,
|
||||
>(preset.shaders, &preset.textures)?;
|
||||
let device = vulkan.try_into()?;
|
||||
let device = vulkan.try_into().map_err(From::from)?;
|
||||
|
||||
let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight);
|
||||
if frames_in_flight == 0 {
|
||||
|
@ -254,7 +337,7 @@ impl FilterChainVulkan {
|
|||
options.map_or(false, |o| o.use_render_pass),
|
||||
)?;
|
||||
|
||||
let luts = FilterChainVulkan::load_luts(&device, &preset.textures)?;
|
||||
let luts = FilterChainVulkan::load_luts(&device, cmd, &preset.textures)?;
|
||||
let samplers = SamplerSet::new(&device.device)?;
|
||||
|
||||
let framebuffer_gen =
|
||||
|
@ -383,66 +466,21 @@ impl FilterChainVulkan {
|
|||
|
||||
fn load_luts(
|
||||
vulkan: &VulkanObjects,
|
||||
command_buffer: vk::CommandBuffer,
|
||||
textures: &[TextureConfig],
|
||||
) -> error::Result<FxHashMap<usize, LutTexture>> {
|
||||
let mut luts = FxHashMap::default();
|
||||
|
||||
let command_pool = unsafe {
|
||||
vulkan.device.create_command_pool(
|
||||
&vk::CommandPoolCreateInfo::builder()
|
||||
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
|
||||
.build(),
|
||||
None,
|
||||
)?
|
||||
};
|
||||
|
||||
let command_buffer = unsafe {
|
||||
// panic safety: command buffer count = 1
|
||||
vulkan.device.allocate_command_buffers(
|
||||
&vk::CommandBufferAllocateInfo::builder()
|
||||
.command_pool(command_pool)
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_buffer_count(1)
|
||||
.build(),
|
||||
)?[0]
|
||||
};
|
||||
|
||||
unsafe {
|
||||
vulkan.device.begin_command_buffer(
|
||||
command_buffer,
|
||||
&vk::CommandBufferBeginInfo::builder()
|
||||
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
|
||||
.build(),
|
||||
)?
|
||||
}
|
||||
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
let image = Image::load(&texture.path, UVDirection::TopLeft)?;
|
||||
|
||||
let texture = LutTexture::new(vulkan, command_buffer, image, texture)?;
|
||||
luts.insert(index, texture);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
vulkan.device.end_command_buffer(command_buffer)?;
|
||||
|
||||
let buffers = [command_buffer];
|
||||
let submits = [vk::SubmitInfo::builder().command_buffers(&buffers).build()];
|
||||
|
||||
vulkan
|
||||
.device
|
||||
.queue_submit(vulkan.queue, &submits, vk::Fence::null())?;
|
||||
vulkan.device.queue_wait_idle(vulkan.queue)?;
|
||||
|
||||
vulkan.device.free_command_buffers(command_pool, &buffers);
|
||||
|
||||
vulkan.device.destroy_command_pool(command_pool, None);
|
||||
}
|
||||
Ok(luts)
|
||||
}
|
||||
|
||||
// image must be in SHADER_READ_OPTIMAL
|
||||
pub fn push_history(
|
||||
fn push_history(
|
||||
&mut self,
|
||||
input: &VulkanImage,
|
||||
cmd: vk::CommandBuffer,
|
||||
|
|
|
@ -44,8 +44,8 @@ mod tests {
|
|||
let filter = FilterChainVulkan::load_from_path(
|
||||
&base,
|
||||
// "../test/slang-shaders/crt/crt-royale.slangp",
|
||||
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp",
|
||||
"../test/slang-shaders/test/feedback.slangp",
|
||||
"../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV-GLASS.slangp",
|
||||
// "../test/slang-shaders/test/feedback.slancargogp",
|
||||
// "../test/basic.slangp",
|
||||
Some(&FilterChainOptionsVulkan {
|
||||
frames_in_flight: 3,
|
||||
|
|
|
@ -8,10 +8,8 @@ use librashader_runtime::image::{Image, BGRA8};
|
|||
use librashader_runtime::scaling::MipmapSize;
|
||||
|
||||
pub(crate) struct LutTexture {
|
||||
#[allow(dead_code)]
|
||||
memory: VulkanImageMemory,
|
||||
#[allow(dead_code)]
|
||||
staging: VulkanBuffer,
|
||||
_memory: VulkanImageMemory,
|
||||
_staging: VulkanBuffer,
|
||||
pub image: InputImage,
|
||||
}
|
||||
|
||||
|
@ -46,11 +44,6 @@ impl LutTexture {
|
|||
|
||||
let memory = unsafe {
|
||||
let mem_reqs = vulkan.device.get_image_memory_requirements(texture);
|
||||
// let mem_type = util::find_vulkan_memory_type(
|
||||
// &vulkan.memory_properties,
|
||||
// mem_reqs.memory_type_bits,
|
||||
// vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
// )?;
|
||||
VulkanImageMemory::new(&vulkan.device, &vulkan.alloc, mem_reqs, &texture)?
|
||||
};
|
||||
|
||||
|
@ -223,8 +216,8 @@ impl LutTexture {
|
|||
}
|
||||
|
||||
Ok(LutTexture {
|
||||
memory,
|
||||
staging,
|
||||
_memory: memory,
|
||||
_staging: staging,
|
||||
image: InputImage {
|
||||
image_view: texture_view,
|
||||
image: VulkanImage {
|
||||
|
|
Loading…
Reference in a new issue