rt(mtl): implement filter pass and filter chain logic
This commit is contained in:
parent
ba3154b92d
commit
a7b1682a37
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1737,6 +1737,7 @@ dependencies = [
|
||||||
"librashader-reflect",
|
"librashader-reflect",
|
||||||
"librashader-runtime",
|
"librashader-runtime",
|
||||||
"objc2 0.5.0",
|
"objc2 0.5.0",
|
||||||
|
"rayon",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
|
@ -50,6 +50,7 @@ pub struct NagaMslModule {
|
||||||
pub struct NagaMslContext {
|
pub struct NagaMslContext {
|
||||||
pub vertex: NagaMslModule,
|
pub vertex: NagaMslModule,
|
||||||
pub fragment: NagaMslModule,
|
pub fragment: NagaMslModule,
|
||||||
|
pub next_free_binding: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromCompilation<SpirvCompilation, Naga> for MSL {
|
impl FromCompilation<SpirvCompilation, Naga> for MSL {
|
||||||
|
|
|
@ -136,6 +136,8 @@ impl CompileShader<MSL> for NagaReflect {
|
||||||
|
|
||||||
let fragment = write_msl(&self.fragment, frag_options)?;
|
let fragment = write_msl(&self.fragment, frag_options)?;
|
||||||
let vertex = write_msl(&self.vertex, vert_options)?;
|
let vertex = write_msl(&self.vertex, vert_options)?;
|
||||||
|
|
||||||
|
let vertex_binding = self.get_next_binding(0);
|
||||||
Ok(ShaderCompilerOutput {
|
Ok(ShaderCompilerOutput {
|
||||||
vertex: vertex.0,
|
vertex: vertex.0,
|
||||||
fragment: fragment.0,
|
fragment: fragment.0,
|
||||||
|
@ -148,6 +150,7 @@ impl CompileShader<MSL> for NagaReflect {
|
||||||
translation_info: vertex.1,
|
translation_info: vertex.1,
|
||||||
module: self.vertex,
|
module: self.vertex,
|
||||||
},
|
},
|
||||||
|
next_free_binding: vertex_binding
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ rustc-hash = "1.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
array-concat = "0.5.2"
|
array-concat = "0.5.2"
|
||||||
bytemuck = { version = "1.12.3", features = ["derive"] }
|
bytemuck = { version = "1.12.3", features = ["derive"] }
|
||||||
|
rayon = "1.8.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::error;
|
||||||
use crate::error::FilterChainError;
|
use crate::error::FilterChainError;
|
||||||
use icrate::Foundation::NSRange;
|
use icrate::Foundation::NSRange;
|
||||||
use icrate::Metal::{
|
use icrate::Metal::{
|
||||||
|
@ -12,8 +13,14 @@ pub struct MetalBuffer {
|
||||||
size: usize,
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<ProtocolObject<dyn MTLBuffer>> for MetalBuffer {
|
||||||
|
fn as_ref(&self) -> &ProtocolObject<dyn MTLBuffer> {
|
||||||
|
self.buffer.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MetalBuffer {
|
impl MetalBuffer {
|
||||||
pub fn new(device: &ProtocolObject<dyn MTLDevice>, size: usize) -> Result<Self> {
|
pub fn new(device: &ProtocolObject<dyn MTLDevice>, size: usize) -> error::Result<Self> {
|
||||||
let resource_mode = if cfg!(target_os = "ios") {
|
let resource_mode = if cfg!(target_os = "ios") {
|
||||||
MTLResourceStorageModeShared
|
MTLResourceStorageModeShared
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::ffi::c_void;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::error::{FilterChainError, Result};
|
use crate::error::{FilterChainError, Result};
|
||||||
|
use crate::graphics_pipeline::VERTEX_BUFFER_INDEX;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, Default, Zeroable, Pod)]
|
#[derive(Debug, Copy, Clone, Default, Zeroable, Pod)]
|
||||||
|
@ -92,7 +93,7 @@ impl DrawQuad {
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
cmd.setVertexBuffer_offset_atIndex(Some(self.buffer.as_ref()), 0, 0);
|
cmd.setVertexBuffer_offset_atIndex(Some(self.buffer.as_ref()), 0, VERTEX_BUFFER_INDEX);
|
||||||
cmd.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangleStrip, offset, 4);
|
cmd.drawPrimitives_vertexStart_vertexCount(MTLPrimitiveTypeTriangleStrip, offset, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,41 @@
|
||||||
|
use crate::buffer::MetalBuffer;
|
||||||
use crate::draw_quad::DrawQuad;
|
use crate::draw_quad::DrawQuad;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::error::FilterChainError;
|
use crate::error::FilterChainError;
|
||||||
use crate::filter_pass::FilterPass;
|
use crate::filter_pass::FilterPass;
|
||||||
|
use crate::graphics_pipeline::MetalGraphicsPipeline;
|
||||||
use crate::luts::LutTexture;
|
use crate::luts::LutTexture;
|
||||||
use crate::options::FilterChainOptionsMetal;
|
use crate::options::{FilterChainOptionsMetal, FrameOptionsMetal};
|
||||||
use crate::samplers::SamplerSet;
|
use crate::samplers::SamplerSet;
|
||||||
use crate::texture::{MetalTexture, OwnedImage};
|
use crate::texture::{get_texture_size, InputTexture, OwnedTexture};
|
||||||
use icrate::Metal::{MTLCommandBuffer, MTLCommandQueue, MTLDevice};
|
use icrate::Metal::{
|
||||||
|
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLCommandQueue, MTLDevice,
|
||||||
|
MTLPixelFormat, MTLPixelFormatRGBA8Unorm, MTLTexture,
|
||||||
|
};
|
||||||
|
use librashader_common::{ImageFormat, Size, Viewport};
|
||||||
use librashader_presets::context::VideoDriver;
|
use librashader_presets::context::VideoDriver;
|
||||||
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
|
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
|
||||||
use librashader_reflect::back::targets::{MSL, WGSL};
|
use librashader_reflect::back::msl::MslVersion;
|
||||||
use librashader_reflect::back::CompileReflectShader;
|
use librashader_reflect::back::targets::MSL;
|
||||||
|
use librashader_reflect::back::{CompileReflectShader, CompileShader};
|
||||||
use librashader_reflect::front::{Glslang, SpirvCompilation};
|
use librashader_reflect::front::{Glslang, SpirvCompilation};
|
||||||
use librashader_reflect::reflect::cross::SpirvCross;
|
use librashader_reflect::reflect::cross::SpirvCross;
|
||||||
use librashader_reflect::reflect::naga::Naga;
|
|
||||||
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
|
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
|
||||||
use librashader_reflect::reflect::semantics::ShaderSemantics;
|
use librashader_reflect::reflect::semantics::ShaderSemantics;
|
||||||
|
use librashader_reflect::reflect::ReflectShader;
|
||||||
|
use librashader_runtime::binding::BindingUtil;
|
||||||
|
use librashader_runtime::framebuffer::FramebufferInit;
|
||||||
|
use librashader_runtime::image::{Image, ImageError, UVDirection, BGRA8};
|
||||||
|
use librashader_runtime::quad::QuadType;
|
||||||
|
use librashader_runtime::render_target::RenderTarget;
|
||||||
|
use librashader_runtime::scaling::ScaleFramebuffer;
|
||||||
|
use librashader_runtime::uniforms::UniformStorage;
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::ProtocolObject;
|
use objc2::runtime::ProtocolObject;
|
||||||
|
use rayon::prelude::*;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
type ShaderPassMeta =
|
type ShaderPassMeta =
|
||||||
ShaderPassArtifact<impl CompileReflectShader<MSL, SpirvCompilation, SpirvCross> + Send>;
|
ShaderPassArtifact<impl CompileReflectShader<MSL, SpirvCompilation, SpirvCross> + Send>;
|
||||||
|
@ -36,13 +50,13 @@ fn compile_passes(
|
||||||
Ok((passes, semantics))
|
Ok((passes, semantics))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wgpu filter chain.
|
/// A Metal filter chain.
|
||||||
pub struct FilterChainMetal {
|
pub struct FilterChainMetal {
|
||||||
pub(crate) common: FilterCommon,
|
pub(crate) common: FilterCommon,
|
||||||
passes: Box<[FilterPass]>,
|
passes: Box<[FilterPass]>,
|
||||||
output_framebuffers: Box<[OwnedImage]>,
|
output_framebuffers: Box<[OwnedTexture]>,
|
||||||
feedback_framebuffers: Box<[OwnedImage]>,
|
feedback_framebuffers: Box<[OwnedTexture]>,
|
||||||
history_framebuffers: VecDeque<OwnedImage>,
|
history_framebuffers: VecDeque<OwnedTexture>,
|
||||||
disable_mipmaps: bool,
|
disable_mipmaps: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,23 +66,22 @@ pub struct FilterMutable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FilterCommon {
|
pub(crate) struct FilterCommon {
|
||||||
pub output_textures: Box<[Option<MetalTexture>]>,
|
pub output_textures: Box<[Option<InputTexture>]>,
|
||||||
pub feedback_textures: Box<[Option<MetalTexture>]>,
|
pub feedback_textures: Box<[Option<InputTexture>]>,
|
||||||
pub history_textures: Box<[Option<MetalTexture>]>,
|
pub history_textures: Box<[Option<InputTexture>]>,
|
||||||
pub luts: FxHashMap<usize, LutTexture>,
|
pub luts: FxHashMap<usize, LutTexture>,
|
||||||
pub samplers: SamplerSet,
|
pub samplers: SamplerSet,
|
||||||
pub config: FilterMutable,
|
pub config: FilterMutable,
|
||||||
pub internal_frame_count: i32,
|
pub internal_frame_count: i32,
|
||||||
pub(crate) draw_quad: DrawQuad,
|
pub(crate) draw_quad: DrawQuad,
|
||||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
device: Id<ProtocolObject<dyn MTLDevice>>,
|
||||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterChainMetal {
|
impl FilterChainMetal {
|
||||||
/// Load the shader preset at the given path into a filter chain.
|
/// Load the shader preset at the given path into a filter chain.
|
||||||
pub fn load_from_path(
|
pub fn load_from_path(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||||
options: Option<&FilterChainOptionsMetal>,
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
) -> error::Result<FilterChainMetal> {
|
) -> error::Result<FilterChainMetal> {
|
||||||
// load passes from preset
|
// load passes from preset
|
||||||
|
@ -79,14 +92,15 @@ impl FilterChainMetal {
|
||||||
/// Load a filter chain from a pre-parsed `ShaderPreset`.
|
/// Load a filter chain from a pre-parsed `ShaderPreset`.
|
||||||
pub fn load_from_preset(
|
pub fn load_from_preset(
|
||||||
preset: ShaderPreset,
|
preset: ShaderPreset,
|
||||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||||
options: Option<&FilterChainOptionsMetal>,
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
) -> error::Result<FilterChainMetal> {
|
) -> error::Result<FilterChainMetal> {
|
||||||
let cmd = queue
|
let cmd = queue
|
||||||
.commandBuffer()
|
.commandBuffer()
|
||||||
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||||
|
|
||||||
let filter_chain = Self::load_from_preset_deferred(preset, queue, cmd, options)?;
|
let filter_chain =
|
||||||
|
Self::load_from_preset_deferred_internal(preset, queue.device(), &cmd, options)?;
|
||||||
|
|
||||||
cmd.commit();
|
cmd.commit();
|
||||||
unsafe { cmd.waitUntilCompleted() };
|
unsafe { cmd.waitUntilCompleted() };
|
||||||
|
@ -94,6 +108,117 @@ impl FilterChainMetal {
|
||||||
Ok(filter_chain)
|
Ok(filter_chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_luts(
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
|
textures: &[TextureConfig],
|
||||||
|
) -> error::Result<FxHashMap<usize, LutTexture>> {
|
||||||
|
let mut luts = FxHashMap::default();
|
||||||
|
|
||||||
|
let mipmapper = cmd
|
||||||
|
.blitCommandEncoder()
|
||||||
|
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||||
|
|
||||||
|
let images = textures
|
||||||
|
.par_iter()
|
||||||
|
.map(|texture| Image::<BGRA8>::load(&texture.path, UVDirection::TopLeft))
|
||||||
|
.collect::<Result<Vec<Image<BGRA8>>, ImageError>>()?;
|
||||||
|
for (index, (texture, image)) in textures.iter().zip(images).enumerate() {
|
||||||
|
let texture = LutTexture::new(device, &mipmapper, image, texture)?;
|
||||||
|
luts.insert(index, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
mipmapper.endEncoding();
|
||||||
|
Ok(luts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_passes(
|
||||||
|
device: &Id<ProtocolObject<dyn MTLDevice>>,
|
||||||
|
passes: Vec<ShaderPassMeta>,
|
||||||
|
semantics: &ShaderSemantics,
|
||||||
|
) -> error::Result<Box<[FilterPass]>> {
|
||||||
|
// todo: fix this to allow send
|
||||||
|
let filters: Vec<error::Result<FilterPass>> = passes
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, (config, source, mut reflect))| {
|
||||||
|
let reflection = reflect.reflect(index, semantics)?;
|
||||||
|
let msl = reflect.compile(Some(MslVersion::V2_0))?;
|
||||||
|
|
||||||
|
let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize);
|
||||||
|
let push_size = reflection
|
||||||
|
.push_constant
|
||||||
|
.as_ref()
|
||||||
|
.map_or(0, |push| push.size);
|
||||||
|
|
||||||
|
let uniform_storage = UniformStorage::new_with_storage(
|
||||||
|
MetalBuffer::new(&device, ubo_size)?,
|
||||||
|
MetalBuffer::new(&device, push_size as usize)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
|
||||||
|
|
||||||
|
let render_pass_format: MTLPixelFormat =
|
||||||
|
if let Some(format) = config.get_format_override() {
|
||||||
|
format.into()
|
||||||
|
} else {
|
||||||
|
source.format.into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let graphics_pipeline = MetalGraphicsPipeline::new(
|
||||||
|
&device,
|
||||||
|
&msl,
|
||||||
|
if render_pass_format == 0 {
|
||||||
|
MTLPixelFormatRGBA8Unorm
|
||||||
|
} else {
|
||||||
|
render_pass_format
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(FilterPass {
|
||||||
|
reflection,
|
||||||
|
uniform_storage,
|
||||||
|
uniform_bindings,
|
||||||
|
source,
|
||||||
|
config,
|
||||||
|
graphics_pipeline,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
//
|
||||||
|
let filters: error::Result<Vec<FilterPass>> = filters.into_iter().collect();
|
||||||
|
let filters = filters?;
|
||||||
|
Ok(filters.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_history(
|
||||||
|
&mut self,
|
||||||
|
input: &ProtocolObject<dyn MTLTexture>,
|
||||||
|
cmd: &ProtocolObject<dyn MTLBlitCommandEncoder>,
|
||||||
|
) -> error::Result<()> {
|
||||||
|
if let Some(mut back) = self.history_framebuffers.pop_back() {
|
||||||
|
if back.texture.height() != input.height()
|
||||||
|
|| back.texture.width() != input.width()
|
||||||
|
|| input.pixelFormat() != back.texture.pixelFormat()
|
||||||
|
{
|
||||||
|
let size = Size {
|
||||||
|
width: input.width() as u32,
|
||||||
|
height: input.height() as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _old_back = std::mem::replace(
|
||||||
|
&mut back,
|
||||||
|
OwnedTexture::new(&self.common.device, size, 1, input.pixelFormat())?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
back.copy_from(cmd, input)?;
|
||||||
|
|
||||||
|
self.history_framebuffers.push_front(back);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Load a filter chain from a pre-parsed `ShaderPreset`, deferring and GPU-side initialization
|
/// 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.
|
/// to the caller. This function therefore requires no external synchronization of the device queue.
|
||||||
///
|
///
|
||||||
|
@ -103,13 +228,227 @@ impl FilterChainMetal {
|
||||||
/// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
|
/// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
|
||||||
pub fn load_from_preset_deferred(
|
pub fn load_from_preset_deferred(
|
||||||
preset: ShaderPreset,
|
preset: ShaderPreset,
|
||||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||||
cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
|
) -> error::Result<FilterChainMetal> {
|
||||||
|
Self::load_from_preset_deferred_internal(preset, queue.device(), &cmd, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
/// 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).
|
||||||
|
fn load_from_preset_deferred_internal(
|
||||||
|
preset: ShaderPreset,
|
||||||
|
device: Id<ProtocolObject<dyn MTLDevice>>,
|
||||||
|
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
options: Option<&FilterChainOptionsMetal>,
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
) -> error::Result<FilterChainMetal> {
|
) -> error::Result<FilterChainMetal> {
|
||||||
let device = queue.device();
|
|
||||||
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
|
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
|
||||||
|
|
||||||
|
let filters = Self::init_passes(&device, passes, &semantics)?;
|
||||||
|
|
||||||
let samplers = SamplerSet::new(&device)?;
|
let samplers = SamplerSet::new(&device)?;
|
||||||
|
let luts = FilterChainMetal::load_luts(&device, &cmd, &preset.textures)?;
|
||||||
|
let framebuffer_gen = || {
|
||||||
|
Ok::<_, error::FilterChainError>(OwnedTexture::new(
|
||||||
|
&device,
|
||||||
|
Size::new(1, 1),
|
||||||
|
1,
|
||||||
|
ImageFormat::R8G8B8A8Unorm.into(),
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
let input_gen = || None;
|
||||||
|
let framebuffer_init = FramebufferInit::new(
|
||||||
|
filters.iter().map(|f| &f.reflection.meta),
|
||||||
|
&framebuffer_gen,
|
||||||
|
&input_gen,
|
||||||
|
);
|
||||||
|
let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
|
||||||
|
//
|
||||||
|
// initialize feedback framebuffers
|
||||||
|
let (feedback_framebuffers, feedback_textures) =
|
||||||
|
framebuffer_init.init_output_framebuffers()?;
|
||||||
|
//
|
||||||
|
// initialize history
|
||||||
|
let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
|
||||||
|
|
||||||
|
let draw_quad = DrawQuad::new(&device)?;
|
||||||
|
Ok(FilterChainMetal {
|
||||||
|
common: FilterCommon {
|
||||||
|
luts,
|
||||||
|
samplers,
|
||||||
|
config: FilterMutable {
|
||||||
|
passes_enabled: preset.shader_count as usize,
|
||||||
|
parameters: preset
|
||||||
|
.parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(|param| (param.name, param.value))
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
draw_quad,
|
||||||
|
device,
|
||||||
|
output_textures,
|
||||||
|
feedback_textures,
|
||||||
|
history_textures,
|
||||||
|
internal_frame_count: 0,
|
||||||
|
},
|
||||||
|
passes: filters,
|
||||||
|
output_framebuffers,
|
||||||
|
feedback_framebuffers,
|
||||||
|
history_framebuffers,
|
||||||
|
disable_mipmaps: options.map(|f| f.force_no_mipmaps).unwrap_or(false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records shader rendering commands to the provided command encoder.
|
||||||
|
pub fn frame(
|
||||||
|
&mut self,
|
||||||
|
input: Id<ProtocolObject<dyn MTLTexture>>,
|
||||||
|
viewport: &Viewport<&ProtocolObject<dyn MTLTexture>>,
|
||||||
|
cmd_buffer: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
|
frame_count: usize,
|
||||||
|
options: Option<&FrameOptionsMetal>,
|
||||||
|
) -> error::Result<()> {
|
||||||
|
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
|
||||||
|
let passes = &mut self.passes[0..max];
|
||||||
|
if let Some(options) = &options {
|
||||||
|
if options.clear_history {
|
||||||
|
for history in &mut self.history_framebuffers {
|
||||||
|
history.clear(cmd_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if passes.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// let original_image_view = input.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let filter = passes[0].config.filter;
|
||||||
|
let wrap_mode = passes[0].config.wrap_mode;
|
||||||
|
|
||||||
|
// update history
|
||||||
|
for (texture, image) in self
|
||||||
|
.common
|
||||||
|
.history_textures
|
||||||
|
.iter_mut()
|
||||||
|
.zip(self.history_framebuffers.iter())
|
||||||
|
{
|
||||||
|
*texture = Some(image.as_input(filter, wrap_mode)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let original = InputTexture {
|
||||||
|
texture: input
|
||||||
|
.newTextureViewWithPixelFormat(input.pixelFormat())
|
||||||
|
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||||
|
wrap_mode,
|
||||||
|
filter_mode: filter,
|
||||||
|
mip_filter: filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut source = original.try_clone()?;
|
||||||
|
|
||||||
|
// swap output and feedback **before** recording command buffers
|
||||||
|
std::mem::swap(
|
||||||
|
&mut self.output_framebuffers,
|
||||||
|
&mut self.feedback_framebuffers,
|
||||||
|
);
|
||||||
|
|
||||||
|
// rescale render buffers to ensure all bindings are valid.
|
||||||
|
OwnedTexture::scale_framebuffers_with_context(
|
||||||
|
get_texture_size(&source.texture).into(),
|
||||||
|
get_texture_size(viewport.output),
|
||||||
|
&mut self.output_framebuffers,
|
||||||
|
&mut self.feedback_framebuffers,
|
||||||
|
passes,
|
||||||
|
&self.common.device,
|
||||||
|
Some(&mut |index: usize,
|
||||||
|
pass: &FilterPass,
|
||||||
|
output: &OwnedTexture,
|
||||||
|
feedback: &OwnedTexture| {
|
||||||
|
// refresh inputs
|
||||||
|
self.common.feedback_textures[index] =
|
||||||
|
Some(feedback.as_input(pass.config.filter, pass.config.wrap_mode)?);
|
||||||
|
self.common.output_textures[index] =
|
||||||
|
Some(output.as_input(pass.config.filter, pass.config.wrap_mode)?);
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let passes_len = passes.len();
|
||||||
|
let (pass, last) = passes.split_at_mut(passes_len - 1);
|
||||||
|
let frame_direction = options.map_or(1, |f| f.frame_direction);
|
||||||
|
|
||||||
|
let mipmapper = cmd_buffer
|
||||||
|
.blitCommandEncoder()
|
||||||
|
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||||
|
|
||||||
|
for (index, pass) in pass.iter_mut().enumerate() {
|
||||||
|
let target = &self.output_framebuffers[index];
|
||||||
|
source.filter_mode = pass.config.filter;
|
||||||
|
source.wrap_mode = pass.config.wrap_mode;
|
||||||
|
source.mip_filter = pass.config.filter;
|
||||||
|
|
||||||
|
let out = RenderTarget::identity(target.texture.as_ref());
|
||||||
|
pass.draw(
|
||||||
|
&cmd_buffer,
|
||||||
|
index,
|
||||||
|
&self.common,
|
||||||
|
pass.config.get_frame_count(frame_count),
|
||||||
|
frame_direction,
|
||||||
|
viewport,
|
||||||
|
&original,
|
||||||
|
&source,
|
||||||
|
&out,
|
||||||
|
QuadType::Offscreen,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if target.max_miplevels > 1 && !self.disable_mipmaps {
|
||||||
|
target.generate_mipmaps(&mipmapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
source = self.common.output_textures[index]
|
||||||
|
.as_ref()
|
||||||
|
.map(InputTexture::try_clone)
|
||||||
|
.unwrap()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to hint the optimizer
|
||||||
|
assert_eq!(last.len(), 1);
|
||||||
|
|
||||||
|
if let Some(pass) = last.iter_mut().next() {
|
||||||
|
if pass.graphics_pipeline.render_pass_format != viewport.output.pixelFormat() {
|
||||||
|
// need to recompile
|
||||||
|
pass.graphics_pipeline
|
||||||
|
.recompile(&self.common.device, viewport.output.pixelFormat())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
source.filter_mode = pass.config.filter;
|
||||||
|
source.wrap_mode = pass.config.wrap_mode;
|
||||||
|
source.mip_filter = pass.config.filter;
|
||||||
|
let output_image = viewport.output;
|
||||||
|
let out = RenderTarget::viewport_with_output(output_image, viewport);
|
||||||
|
pass.draw(
|
||||||
|
&cmd_buffer,
|
||||||
|
passes_len - 1,
|
||||||
|
&self.common,
|
||||||
|
pass.config.get_frame_count(frame_count),
|
||||||
|
frame_direction,
|
||||||
|
viewport,
|
||||||
|
&original,
|
||||||
|
&source,
|
||||||
|
&out,
|
||||||
|
QuadType::Final,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.push_history(&input, &mipmapper)?;
|
||||||
|
self.common.internal_frame_count = self.common.internal_frame_count.wrapping_add(1);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,168 @@
|
||||||
pub struct FilterPass {}
|
use crate::buffer::MetalBuffer;
|
||||||
|
use crate::error;
|
||||||
|
use crate::filter_chain::FilterCommon;
|
||||||
|
use crate::graphics_pipeline::MetalGraphicsPipeline;
|
||||||
|
use crate::samplers::SamplerSet;
|
||||||
|
use crate::texture::{get_texture_size, InputTexture};
|
||||||
|
use icrate::Metal::{MTLCommandBuffer, MTLCommandEncoder, MTLRenderCommandEncoder, MTLTexture};
|
||||||
|
use librashader_common::{ImageFormat, Size, Viewport};
|
||||||
|
use librashader_preprocess::ShaderSource;
|
||||||
|
use librashader_presets::ShaderPassConfig;
|
||||||
|
use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
|
||||||
|
use librashader_reflect::reflect::ShaderReflection;
|
||||||
|
use librashader_runtime::binding::{BindSemantics, TextureInput};
|
||||||
|
use librashader_runtime::filter_pass::FilterPassMeta;
|
||||||
|
use librashader_runtime::quad::QuadType;
|
||||||
|
use librashader_runtime::render_target::RenderTarget;
|
||||||
|
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage};
|
||||||
|
use objc2::runtime::ProtocolObject;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
impl TextureInput for InputTexture {
|
||||||
|
fn size(&self) -> Size<u32> {
|
||||||
|
get_texture_size(&self.texture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BindSemantics<NoUniformBinder, Option<()>, MetalBuffer, MetalBuffer> for FilterPass {
|
||||||
|
type InputTexture = InputTexture;
|
||||||
|
type SamplerSet = SamplerSet;
|
||||||
|
type DescriptorSet<'a> = &'a ProtocolObject<dyn MTLRenderCommandEncoder>;
|
||||||
|
type DeviceContext = ();
|
||||||
|
type UniformOffset = MemberOffset;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn bind_texture<'a>(
|
||||||
|
renderpass: &mut Self::DescriptorSet<'a>,
|
||||||
|
samplers: &Self::SamplerSet,
|
||||||
|
binding: &TextureBinding,
|
||||||
|
texture: &Self::InputTexture,
|
||||||
|
_device: &Self::DeviceContext,
|
||||||
|
) {
|
||||||
|
let sampler = samplers.get(texture.wrap_mode, texture.filter_mode, texture.mip_filter);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
renderpass.setFragmentTexture_atIndex(Some(&texture.texture), binding.binding as usize);
|
||||||
|
renderpass.setFragmentTexture_atIndex(Some(&texture.texture), binding.binding as usize);
|
||||||
|
renderpass.setFragmentSamplerState_atIndex(Some(sampler), binding.binding as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FilterPass {
|
||||||
|
pub reflection: ShaderReflection,
|
||||||
|
pub(crate) uniform_storage:
|
||||||
|
UniformStorage<NoUniformBinder, Option<()>, MetalBuffer, MetalBuffer>,
|
||||||
|
pub uniform_bindings: FxHashMap<UniformBinding, MemberOffset>,
|
||||||
|
pub source: ShaderSource,
|
||||||
|
pub config: ShaderPassConfig,
|
||||||
|
pub graphics_pipeline: MetalGraphicsPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterPass {
|
||||||
|
pub(crate) fn draw(
|
||||||
|
&mut self,
|
||||||
|
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
|
pass_index: usize,
|
||||||
|
parent: &FilterCommon,
|
||||||
|
frame_count: u32,
|
||||||
|
frame_direction: i32,
|
||||||
|
viewport: &Viewport<&ProtocolObject<dyn MTLTexture>>,
|
||||||
|
original: &InputTexture,
|
||||||
|
source: &InputTexture,
|
||||||
|
output: &RenderTarget<ProtocolObject<dyn MTLTexture>>,
|
||||||
|
vbo_type: QuadType,
|
||||||
|
) -> error::Result<()> {
|
||||||
|
let cmd = self.graphics_pipeline.begin_rendering(output, &cmd)?;
|
||||||
|
|
||||||
|
self.build_semantics(
|
||||||
|
pass_index,
|
||||||
|
parent,
|
||||||
|
output.mvp,
|
||||||
|
frame_count,
|
||||||
|
frame_direction,
|
||||||
|
get_texture_size(output.output),
|
||||||
|
get_texture_size(viewport.output),
|
||||||
|
original,
|
||||||
|
source,
|
||||||
|
&cmd,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ubo) = &self.reflection.ubo {
|
||||||
|
unsafe {
|
||||||
|
cmd.setVertexBuffer_offset_atIndex(
|
||||||
|
Some(self.uniform_storage.inner_ubo().as_ref()),
|
||||||
|
0,
|
||||||
|
ubo.binding as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(pcb) = &self.reflection.push_constant {
|
||||||
|
unsafe {
|
||||||
|
// SPIRV-Cross always has PCB bound to 1. Naga is arbitrary but their compilation provides the next free binding for drawquad.
|
||||||
|
cmd.setVertexBuffer_offset_atIndex(
|
||||||
|
Some(self.uniform_storage.inner_ubo().as_ref()),
|
||||||
|
0,
|
||||||
|
pcb.binding.unwrap_or(1) as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.draw_quad.draw_quad(&cmd, vbo_type);
|
||||||
|
cmd.endEncoding();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_semantics<'a>(
|
||||||
|
&mut self,
|
||||||
|
pass_index: usize,
|
||||||
|
parent: &FilterCommon,
|
||||||
|
mvp: &[f32; 16],
|
||||||
|
frame_count: u32,
|
||||||
|
frame_direction: i32,
|
||||||
|
fb_size: Size<u32>,
|
||||||
|
viewport_size: Size<u32>,
|
||||||
|
original: &InputTexture,
|
||||||
|
source: &InputTexture,
|
||||||
|
mut renderpass: &ProtocolObject<dyn MTLRenderCommandEncoder>,
|
||||||
|
) {
|
||||||
|
Self::bind_semantics(
|
||||||
|
&(),
|
||||||
|
&parent.samplers,
|
||||||
|
&mut self.uniform_storage,
|
||||||
|
&mut renderpass,
|
||||||
|
mvp,
|
||||||
|
frame_count,
|
||||||
|
frame_direction,
|
||||||
|
fb_size,
|
||||||
|
viewport_size,
|
||||||
|
original,
|
||||||
|
source,
|
||||||
|
&self.uniform_bindings,
|
||||||
|
&self.reflection.meta.texture_meta,
|
||||||
|
parent.output_textures[0..pass_index]
|
||||||
|
.iter()
|
||||||
|
.map(|o| o.as_ref()),
|
||||||
|
parent.feedback_textures.iter().map(|o| o.as_ref()),
|
||||||
|
parent.history_textures.iter().map(|o| o.as_ref()),
|
||||||
|
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
|
||||||
|
&self.source.parameters,
|
||||||
|
&parent.config.parameters,
|
||||||
|
);
|
||||||
|
|
||||||
|
// flush to buffers
|
||||||
|
self.uniform_storage.inner_ubo().flush();
|
||||||
|
self.uniform_storage.inner_push().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterPassMeta for FilterPass {
|
||||||
|
fn framebuffer_format(&self) -> ImageFormat {
|
||||||
|
self.source.format
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &ShaderPassConfig {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::error::{FilterChainError, Result};
|
use crate::error::{FilterChainError, Result};
|
||||||
use icrate::Foundation::NSString;
|
use icrate::Foundation::NSString;
|
||||||
use icrate::Metal::{
|
use icrate::Metal::{
|
||||||
MTLBlendFactorOneMinusSourceAlpha, MTLBlendFactorSourceAlpha, MTLCommandBuffer,
|
MTLBlendFactorOneMinusSourceAlpha, MTLBlendFactorSourceAlpha, MTLCommandBuffer, MTLDevice,
|
||||||
MTLCommandEncoder, MTLDevice, MTLFunction, MTLLibrary, MTLLoadActionDontCare, MTLPixelFormat,
|
MTLFunction, MTLLibrary, MTLLoadActionDontCare, MTLPixelFormat,
|
||||||
MTLPrimitiveTopologyClassTriangle, MTLRenderCommandEncoder, MTLRenderPassDescriptor,
|
MTLPrimitiveTopologyClassTriangle, MTLRenderCommandEncoder, MTLRenderPassDescriptor,
|
||||||
MTLRenderPipelineColorAttachmentDescriptor, MTLRenderPipelineDescriptor,
|
MTLRenderPipelineColorAttachmentDescriptor, MTLRenderPipelineDescriptor,
|
||||||
MTLRenderPipelineState, MTLScissorRect, MTLStoreActionStore, MTLTexture,
|
MTLRenderPipelineState, MTLScissorRect, MTLStoreActionStore, MTLTexture,
|
||||||
|
@ -11,25 +11,27 @@ use icrate::Metal::{
|
||||||
};
|
};
|
||||||
use librashader_reflect::back::msl::{CrossMslContext, NagaMslContext};
|
use librashader_reflect::back::msl::{CrossMslContext, NagaMslContext};
|
||||||
use librashader_reflect::back::ShaderCompilerOutput;
|
use librashader_reflect::back::ShaderCompilerOutput;
|
||||||
use librashader_reflect::reflect::ShaderReflection;
|
|
||||||
use librashader_runtime::render_target::RenderTarget;
|
use librashader_runtime::render_target::RenderTarget;
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::ProtocolObject;
|
use objc2::runtime::ProtocolObject;
|
||||||
|
|
||||||
|
/// This is only really plausible for SPIRV-Cross, for Naga we need to supply the next plausible binding.
|
||||||
|
pub const VERTEX_BUFFER_INDEX: usize = 4;
|
||||||
|
|
||||||
pub struct MetalGraphicsPipeline {
|
pub struct MetalGraphicsPipeline {
|
||||||
pub layout: PipelineLayoutObjects,
|
pub layout: PipelineLayoutObjects,
|
||||||
render_pipeline: Id<ProtocolObject<dyn MTLRenderPipelineState>>,
|
render_pipeline: Id<ProtocolObject<dyn MTLRenderPipelineState>>,
|
||||||
|
pub render_pass_format: MTLPixelFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PipelineLayoutObjects {
|
pub struct PipelineLayoutObjects {
|
||||||
vertex_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
_vertex_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||||
fragment_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
_fragment_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||||
vertex_entry: Id<ProtocolObject<dyn MTLFunction>>,
|
vertex_entry: Id<ProtocolObject<dyn MTLFunction>>,
|
||||||
fragment_entry: Id<ProtocolObject<dyn MTLFunction>>,
|
fragment_entry: Id<ProtocolObject<dyn MTLFunction>>,
|
||||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait MslEntryPoint {
|
pub(crate) trait MslEntryPoint {
|
||||||
fn entry_point() -> Id<NSString>;
|
fn entry_point() -> Id<NSString>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ impl MslEntryPoint for NagaMslContext {
|
||||||
impl PipelineLayoutObjects {
|
impl PipelineLayoutObjects {
|
||||||
pub fn new<T: MslEntryPoint>(
|
pub fn new<T: MslEntryPoint>(
|
||||||
shader_assembly: &ShaderCompilerOutput<String, T>,
|
shader_assembly: &ShaderCompilerOutput<String, T>,
|
||||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let entry = T::entry_point();
|
let entry = T::entry_point();
|
||||||
|
|
||||||
|
@ -65,11 +67,10 @@ impl PipelineLayoutObjects {
|
||||||
.ok_or(FilterChainError::ShaderWrongEntryName)?;
|
.ok_or(FilterChainError::ShaderWrongEntryName)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
vertex_lib: vertex,
|
_vertex_lib: vertex,
|
||||||
fragment_lib: fragment,
|
_fragment_lib: fragment,
|
||||||
vertex_entry,
|
vertex_entry,
|
||||||
fragment_entry,
|
fragment_entry,
|
||||||
device,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +86,11 @@ impl PipelineLayoutObjects {
|
||||||
|
|
||||||
// hopefully metal fills in vertices otherwise we'll need to use the vec4 stuff.
|
// hopefully metal fills in vertices otherwise we'll need to use the vec4 stuff.
|
||||||
vertex_0.setFormat(MTLVertexFormatFloat2);
|
vertex_0.setFormat(MTLVertexFormatFloat2);
|
||||||
vertex_0.setBufferIndex(4);
|
vertex_0.setBufferIndex(VERTEX_BUFFER_INDEX);
|
||||||
vertex_0.setOffset(0);
|
vertex_0.setOffset(0);
|
||||||
|
|
||||||
vertex_1.setFormat(MTLVertexFormatFloat2);
|
vertex_1.setFormat(MTLVertexFormatFloat2);
|
||||||
vertex_1.setBufferIndex(4);
|
vertex_1.setBufferIndex(VERTEX_BUFFER_INDEX);
|
||||||
vertex_1.setOffset(2 * std::mem::size_of::<f32>());
|
vertex_1.setOffset(2 * std::mem::size_of::<f32>());
|
||||||
|
|
||||||
attributes.setObject_atIndexedSubscript(Some(&vertex_0), 0);
|
attributes.setObject_atIndexedSubscript(Some(&vertex_0), 0);
|
||||||
|
@ -119,6 +120,7 @@ impl PipelineLayoutObjects {
|
||||||
|
|
||||||
pub fn create_pipeline(
|
pub fn create_pipeline(
|
||||||
&self,
|
&self,
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
format: MTLPixelFormat,
|
format: MTLPixelFormat,
|
||||||
) -> Result<Id<ProtocolObject<dyn MTLRenderPipelineState>>> {
|
) -> Result<Id<ProtocolObject<dyn MTLRenderPipelineState>>> {
|
||||||
let descriptor = MTLRenderPipelineDescriptor::new();
|
let descriptor = MTLRenderPipelineDescriptor::new();
|
||||||
|
@ -140,36 +142,39 @@ impl PipelineLayoutObjects {
|
||||||
descriptor.setFragmentFunction(Some(&self.fragment_entry));
|
descriptor.setFragmentFunction(Some(&self.fragment_entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self
|
Ok(device.newRenderPipelineStateWithDescriptor_error(descriptor.as_ref())?)
|
||||||
.device
|
|
||||||
.newRenderPipelineStateWithDescriptor_error(descriptor.as_ref())?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetalGraphicsPipeline {
|
impl MetalGraphicsPipeline {
|
||||||
pub fn new<T: MslEntryPoint>(
|
pub fn new<T: MslEntryPoint>(
|
||||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
shader_assembly: &ShaderCompilerOutput<String, T>,
|
shader_assembly: &ShaderCompilerOutput<String, T>,
|
||||||
render_pass_format: MTLPixelFormat,
|
render_pass_format: MTLPixelFormat,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let layout = PipelineLayoutObjects::new(shader_assembly, device)?;
|
let layout = PipelineLayoutObjects::new(shader_assembly, device)?;
|
||||||
let pipeline = layout.create_pipeline(render_pass_format)?;
|
let pipeline = layout.create_pipeline(device, render_pass_format)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
layout,
|
layout,
|
||||||
render_pipeline: pipeline,
|
render_pipeline: pipeline,
|
||||||
|
render_pass_format,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recompile(&mut self, format: MTLPixelFormat) -> Result<()> {
|
pub fn recompile(
|
||||||
let render_pipeline = self.layout.create_pipeline(format)?;
|
&mut self,
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
format: MTLPixelFormat,
|
||||||
|
) -> Result<()> {
|
||||||
|
let render_pipeline = self.layout.create_pipeline(device, format)?;
|
||||||
self.render_pipeline = render_pipeline;
|
self.render_pipeline = render_pipeline;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_rendering<'pass>(
|
pub fn begin_rendering<'pass>(
|
||||||
&self,
|
&self,
|
||||||
output: RenderTarget<&'pass ProtocolObject<dyn MTLTexture>>,
|
output: &RenderTarget<ProtocolObject<dyn MTLTexture>>,
|
||||||
buffer: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
buffer: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
) -> Result<Id<ProtocolObject<dyn MTLRenderCommandEncoder>>> {
|
) -> Result<Id<ProtocolObject<dyn MTLRenderCommandEncoder>>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let descriptor = MTLRenderPassDescriptor::new();
|
let descriptor = MTLRenderPassDescriptor::new();
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
#![cfg(target_vendor = "apple")]
|
#![cfg(target_vendor = "apple")]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
mod draw_quad;
|
mod draw_quad;
|
||||||
mod error;
|
|
||||||
mod filter_chain;
|
mod filter_chain;
|
||||||
mod filter_pass;
|
mod filter_pass;
|
||||||
mod graphics_pipeline;
|
mod graphics_pipeline;
|
||||||
mod luts;
|
mod luts;
|
||||||
mod options;
|
|
||||||
mod samplers;
|
mod samplers;
|
||||||
mod texture;
|
mod texture;
|
||||||
|
|
||||||
|
pub use filter_chain::FilterChainMetal;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod options;
|
||||||
|
use librashader_runtime::impl_filter_chain_parameters;
|
||||||
|
impl_filter_chain_parameters!(FilterChainMetal);
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
use crate::error::{FilterChainError, Result};
|
use crate::error::{FilterChainError, Result};
|
||||||
use crate::samplers::SamplerSet;
|
use crate::texture::InputTexture;
|
||||||
use crate::texture::MetalTexture;
|
|
||||||
use icrate::Metal::{
|
use icrate::Metal::{
|
||||||
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLOrigin,
|
MTLBlitCommandEncoder, MTLDevice, MTLOrigin, MTLPixelFormatBGRA8Unorm, MTLRegion, MTLSize,
|
||||||
MTLPixelFormatBGRA8Unorm, MTLRegion, MTLSize, MTLTexture, MTLTextureDescriptor,
|
MTLTexture, MTLTextureDescriptor, MTLTextureUsageShaderRead,
|
||||||
MTLTextureUsageShaderRead,
|
|
||||||
};
|
};
|
||||||
use librashader_presets::TextureConfig;
|
use librashader_presets::TextureConfig;
|
||||||
use librashader_runtime::image::{Image, BGRA8};
|
use librashader_runtime::image::{Image, BGRA8};
|
||||||
use librashader_runtime::scaling::MipmapSize;
|
use librashader_runtime::scaling::MipmapSize;
|
||||||
use objc2::rc::Id;
|
|
||||||
use objc2::runtime::ProtocolObject;
|
use objc2::runtime::ProtocolObject;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
pub(crate) struct LutTexture(MetalTexture);
|
pub(crate) struct LutTexture(InputTexture);
|
||||||
|
|
||||||
|
impl AsRef<InputTexture> for LutTexture {
|
||||||
|
fn as_ref(&self) -> &InputTexture {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LutTexture {
|
impl LutTexture {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &ProtocolObject<dyn MTLDevice>,
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
mipmapper: &ProtocolObject<dyn MTLBlitCommandEncoder>,
|
||||||
image: Image<BGRA8>,
|
image: Image<BGRA8>,
|
||||||
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
|
||||||
config: &TextureConfig,
|
config: &TextureConfig,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let descriptor = unsafe {
|
let descriptor = unsafe {
|
||||||
|
@ -68,12 +71,14 @@ impl LutTexture {
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.mipmap {
|
if config.mipmap {
|
||||||
if let Some(encoder) = cmd.blitCommandEncoder() {
|
mipmapper.generateMipmapsForTexture(&texture);
|
||||||
encoder.generateMipmapsForTexture(&texture);
|
|
||||||
encoder.endEncoding();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(LutTexture(texture))
|
Ok(LutTexture(InputTexture {
|
||||||
|
texture,
|
||||||
|
wrap_mode: config.wrap_mode,
|
||||||
|
filter_mode: config.filter_mode,
|
||||||
|
mip_filter: config.filter_mode,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,56 @@
|
||||||
use crate::error::{FilterChainError, Result};
|
use crate::error::{FilterChainError, Result};
|
||||||
use icrate::Metal::{
|
use icrate::Metal::{
|
||||||
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLPixelFormat,
|
MTLBlitCommandEncoder, MTLCommandBuffer, MTLDevice, MTLPixelFormat, MTLTexture,
|
||||||
MTLPixelFormatBGRA8Unorm, MTLTexture, MTLTextureDescriptor, MTLTextureUsageRenderTarget,
|
MTLTextureDescriptor, MTLTextureUsageRenderTarget, MTLTextureUsageShaderRead,
|
||||||
MTLTextureUsageShaderRead, MTLTextureUsageShaderWrite,
|
MTLTextureUsageShaderWrite,
|
||||||
};
|
};
|
||||||
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
|
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
|
||||||
use librashader_presets::Scale2D;
|
use librashader_presets::Scale2D;
|
||||||
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
|
||||||
use objc2::rc::Id;
|
use objc2::rc::Id;
|
||||||
use objc2::runtime::ProtocolObject;
|
use objc2::runtime::ProtocolObject;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub type MetalTexture = Id<ProtocolObject<dyn MTLTexture>>;
|
pub type MetalTexture = Id<ProtocolObject<dyn MTLTexture>>;
|
||||||
|
|
||||||
pub struct OwnedImage {
|
pub struct OwnedTexture {
|
||||||
image: MetalTexture,
|
pub(crate) texture: MetalTexture,
|
||||||
max_miplevels: u32,
|
pub(crate) max_miplevels: u32,
|
||||||
size: Size<u32>,
|
size: Size<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnedImage {
|
pub struct InputTexture {
|
||||||
|
pub texture: MetalTexture,
|
||||||
|
pub wrap_mode: WrapMode,
|
||||||
|
pub filter_mode: FilterMode,
|
||||||
|
pub mip_filter: FilterMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputTexture {
|
||||||
|
pub fn try_clone(&self) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
texture: self
|
||||||
|
.texture
|
||||||
|
.newTextureViewWithPixelFormat(self.texture.pixelFormat())
|
||||||
|
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||||
|
wrap_mode: self.wrap_mode,
|
||||||
|
filter_mode: self.filter_mode,
|
||||||
|
mip_filter: self.mip_filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<InputTexture> for InputTexture {
|
||||||
|
fn as_ref(&self) -> &InputTexture {
|
||||||
|
&self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedTexture {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &ProtocolObject<dyn MTLDevice>,
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
size: Size<u32>,
|
size: Size<u32>,
|
||||||
max_miplevels: u32,
|
max_miplevels: u32,
|
||||||
format: ImageFormat,
|
format: MTLPixelFormat,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let format: MTLPixelFormat = format.into();
|
|
||||||
|
|
||||||
let descriptor = unsafe {
|
let descriptor = unsafe {
|
||||||
let descriptor =
|
let descriptor =
|
||||||
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
||||||
|
@ -54,7 +77,7 @@ impl OwnedImage {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
image: device
|
texture: device
|
||||||
.newTextureWithDescriptor(&descriptor)
|
.newTextureWithDescriptor(&descriptor)
|
||||||
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||||
max_miplevels,
|
max_miplevels,
|
||||||
|
@ -66,52 +89,50 @@ impl OwnedImage {
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &ProtocolObject<dyn MTLDevice>,
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
scaling: Scale2D,
|
scaling: Scale2D,
|
||||||
format: ImageFormat,
|
format: MTLPixelFormat,
|
||||||
viewport_size: &Size<u32>,
|
viewport_size: &Size<u32>,
|
||||||
source_size: &Size<u32>,
|
source_size: &Size<u32>,
|
||||||
mipmap: bool,
|
mipmap: bool,
|
||||||
) -> Size<u32> {
|
) -> Result<Size<u32>> {
|
||||||
let size = source_size.scale_viewport(scaling, *viewport_size);
|
let size = source_size.scale_viewport(scaling, *viewport_size);
|
||||||
let format: MTLPixelFormat = format.into();
|
|
||||||
|
|
||||||
if self.size != size
|
if self.size != size
|
||||||
|| (mipmap && self.max_miplevels == 1)
|
|| (mipmap && self.max_miplevels == 1)
|
||||||
|| (!mipmap && self.max_miplevels != 1)
|
|| (!mipmap && self.max_miplevels != 1)
|
||||||
|| format != self.image.pixelFormat()
|
|| format != self.texture.pixelFormat()
|
||||||
{
|
{
|
||||||
let mut new = OwnedImage::new(device, size, self.max_miplevels, format.into())?;
|
let mut new = OwnedTexture::new(device, size, self.max_miplevels, format)?;
|
||||||
std::mem::swap(self, &mut new);
|
std::mem::swap(self, &mut new);
|
||||||
}
|
}
|
||||||
size
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> InputImage {
|
pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> Result<InputTexture> {
|
||||||
// InputImage {
|
Ok(InputTexture {
|
||||||
// image: Arc::clone(&self.image),
|
texture: self
|
||||||
// view: Arc::clone(&self.view),
|
.texture
|
||||||
// wrap_mode,
|
.newTextureViewWithPixelFormat(self.texture.pixelFormat())
|
||||||
// filter_mode: filter,
|
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||||
// mip_filter: filter,
|
wrap_mode,
|
||||||
// }
|
filter_mode: filter,
|
||||||
// }
|
mip_filter: filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn copy_from(
|
pub fn copy_from(
|
||||||
&self,
|
&self,
|
||||||
|
encoder: &ProtocolObject<dyn MTLBlitCommandEncoder>,
|
||||||
other: &ProtocolObject<dyn MTLTexture>,
|
other: &ProtocolObject<dyn MTLTexture>,
|
||||||
cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let encoder = cmd
|
|
||||||
.blitCommandEncoder()
|
|
||||||
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
encoder.copyFromTexture_toTexture(other, &self.image);
|
encoder.copyFromTexture_toTexture(other, &self.texture);
|
||||||
}
|
}
|
||||||
encoder.generateMipmapsForTexture(&self.image);
|
encoder.generateMipmapsForTexture(&self.texture);
|
||||||
encoder.endEncoding();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self, cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>) {
|
pub fn clear(&self, cmd: &ProtocolObject<dyn MTLCommandBuffer>) {
|
||||||
// let render = cmd.renderCommandEncoder()
|
// let render = cmd.renderCommandEncoder()
|
||||||
// .ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
// .ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||||
// render.
|
// render.
|
||||||
|
@ -120,6 +141,39 @@ impl OwnedImage {
|
||||||
|
|
||||||
/// caller must end the blit encoder after.
|
/// caller must end the blit encoder after.
|
||||||
pub fn generate_mipmaps(&self, mipmapper: &ProtocolObject<dyn MTLBlitCommandEncoder>) {
|
pub fn generate_mipmaps(&self, mipmapper: &ProtocolObject<dyn MTLBlitCommandEncoder>) {
|
||||||
mipmapper.generateMipmapsForTexture(&self.image);
|
mipmapper.generateMipmapsForTexture(&self.texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScaleFramebuffer for OwnedTexture {
|
||||||
|
type Error = FilterChainError;
|
||||||
|
type Context = ProtocolObject<dyn MTLDevice>;
|
||||||
|
|
||||||
|
fn scale(
|
||||||
|
&mut self,
|
||||||
|
scaling: Scale2D,
|
||||||
|
format: ImageFormat,
|
||||||
|
viewport_size: &Size<u32>,
|
||||||
|
source_size: &Size<u32>,
|
||||||
|
should_mipmap: bool,
|
||||||
|
context: &Self::Context,
|
||||||
|
) -> std::result::Result<Size<u32>, Self::Error> {
|
||||||
|
Ok(self.scale(
|
||||||
|
&context,
|
||||||
|
scaling,
|
||||||
|
format.into(),
|
||||||
|
viewport_size,
|
||||||
|
source_size,
|
||||||
|
should_mipmap,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_texture_size(texture: &ProtocolObject<dyn MTLTexture>) -> Size<u32> {
|
||||||
|
let height = texture.height();
|
||||||
|
let width = texture.width();
|
||||||
|
Size {
|
||||||
|
height: height as u32,
|
||||||
|
width: width as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue