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-runtime",
|
||||
"objc2 0.5.0",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"thiserror",
|
||||
]
|
||||
|
|
|
@ -50,6 +50,7 @@ pub struct NagaMslModule {
|
|||
pub struct NagaMslContext {
|
||||
pub vertex: NagaMslModule,
|
||||
pub fragment: NagaMslModule,
|
||||
pub next_free_binding: u32,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, Naga> for MSL {
|
||||
|
|
|
@ -136,6 +136,8 @@ impl CompileShader<MSL> for NagaReflect {
|
|||
|
||||
let fragment = write_msl(&self.fragment, frag_options)?;
|
||||
let vertex = write_msl(&self.vertex, vert_options)?;
|
||||
|
||||
let vertex_binding = self.get_next_binding(0);
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: vertex.0,
|
||||
fragment: fragment.0,
|
||||
|
@ -148,6 +150,7 @@ impl CompileShader<MSL> for NagaReflect {
|
|||
translation_info: vertex.1,
|
||||
module: self.vertex,
|
||||
},
|
||||
next_free_binding: vertex_binding
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ rustc-hash = "1.1.0"
|
|||
thiserror = "1.0"
|
||||
array-concat = "0.5.2"
|
||||
bytemuck = { version = "1.12.3", features = ["derive"] }
|
||||
rayon = "1.8.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error;
|
||||
use crate::error::FilterChainError;
|
||||
use icrate::Foundation::NSRange;
|
||||
use icrate::Metal::{
|
||||
|
@ -12,8 +13,14 @@ pub struct MetalBuffer {
|
|||
size: usize,
|
||||
}
|
||||
|
||||
impl AsRef<ProtocolObject<dyn MTLBuffer>> for MetalBuffer {
|
||||
fn as_ref(&self) -> &ProtocolObject<dyn MTLBuffer> {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
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") {
|
||||
MTLResourceStorageModeShared
|
||||
} else {
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::ffi::c_void;
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use crate::error::{FilterChainError, Result};
|
||||
use crate::graphics_pipeline::VERTEX_BUFFER_INDEX;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Zeroable, Pod)]
|
||||
|
@ -92,7 +93,7 @@ impl DrawQuad {
|
|||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,41 @@
|
|||
use crate::buffer::MetalBuffer;
|
||||
use crate::draw_quad::DrawQuad;
|
||||
use crate::error;
|
||||
use crate::error::FilterChainError;
|
||||
use crate::filter_pass::FilterPass;
|
||||
use crate::graphics_pipeline::MetalGraphicsPipeline;
|
||||
use crate::luts::LutTexture;
|
||||
use crate::options::FilterChainOptionsMetal;
|
||||
use crate::options::{FilterChainOptionsMetal, FrameOptionsMetal};
|
||||
use crate::samplers::SamplerSet;
|
||||
use crate::texture::{MetalTexture, OwnedImage};
|
||||
use icrate::Metal::{MTLCommandBuffer, MTLCommandQueue, MTLDevice};
|
||||
use crate::texture::{get_texture_size, InputTexture, OwnedTexture};
|
||||
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::{ShaderPassConfig, ShaderPreset, TextureConfig};
|
||||
use librashader_reflect::back::targets::{MSL, WGSL};
|
||||
use librashader_reflect::back::CompileReflectShader;
|
||||
use librashader_reflect::back::msl::MslVersion;
|
||||
use librashader_reflect::back::targets::MSL;
|
||||
use librashader_reflect::back::{CompileReflectShader, CompileShader};
|
||||
use librashader_reflect::front::{Glslang, SpirvCompilation};
|
||||
use librashader_reflect::reflect::cross::SpirvCross;
|
||||
use librashader_reflect::reflect::naga::Naga;
|
||||
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
|
||||
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::runtime::ProtocolObject;
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
type ShaderPassMeta =
|
||||
ShaderPassArtifact<impl CompileReflectShader<MSL, SpirvCompilation, SpirvCross> + Send>;
|
||||
|
@ -36,13 +50,13 @@ fn compile_passes(
|
|||
Ok((passes, semantics))
|
||||
}
|
||||
|
||||
/// A wgpu filter chain.
|
||||
/// A Metal filter chain.
|
||||
pub struct FilterChainMetal {
|
||||
pub(crate) common: FilterCommon,
|
||||
passes: Box<[FilterPass]>,
|
||||
output_framebuffers: Box<[OwnedImage]>,
|
||||
feedback_framebuffers: Box<[OwnedImage]>,
|
||||
history_framebuffers: VecDeque<OwnedImage>,
|
||||
output_framebuffers: Box<[OwnedTexture]>,
|
||||
feedback_framebuffers: Box<[OwnedTexture]>,
|
||||
history_framebuffers: VecDeque<OwnedTexture>,
|
||||
disable_mipmaps: bool,
|
||||
}
|
||||
|
||||
|
@ -52,23 +66,22 @@ pub struct FilterMutable {
|
|||
}
|
||||
|
||||
pub(crate) struct FilterCommon {
|
||||
pub output_textures: Box<[Option<MetalTexture>]>,
|
||||
pub feedback_textures: Box<[Option<MetalTexture>]>,
|
||||
pub history_textures: Box<[Option<MetalTexture>]>,
|
||||
pub output_textures: Box<[Option<InputTexture>]>,
|
||||
pub feedback_textures: Box<[Option<InputTexture>]>,
|
||||
pub history_textures: Box<[Option<InputTexture>]>,
|
||||
pub luts: FxHashMap<usize, LutTexture>,
|
||||
pub samplers: SamplerSet,
|
||||
pub config: FilterMutable,
|
||||
pub internal_frame_count: i32,
|
||||
pub(crate) draw_quad: DrawQuad,
|
||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
||||
}
|
||||
|
||||
impl FilterChainMetal {
|
||||
/// Load the shader preset at the given path into a filter chain.
|
||||
pub fn load_from_path(
|
||||
path: impl AsRef<Path>,
|
||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
||||
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||
options: Option<&FilterChainOptionsMetal>,
|
||||
) -> error::Result<FilterChainMetal> {
|
||||
// load passes from preset
|
||||
|
@ -79,14 +92,15 @@ impl FilterChainMetal {
|
|||
/// Load a filter chain from a pre-parsed `ShaderPreset`.
|
||||
pub fn load_from_preset(
|
||||
preset: ShaderPreset,
|
||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
||||
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||
options: Option<&FilterChainOptionsMetal>,
|
||||
) -> error::Result<FilterChainMetal> {
|
||||
let cmd = queue
|
||||
.commandBuffer()
|
||||
.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();
|
||||
unsafe { cmd.waitUntilCompleted() };
|
||||
|
@ -94,6 +108,117 @@ impl FilterChainMetal {
|
|||
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
|
||||
/// 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).
|
||||
pub fn load_from_preset_deferred(
|
||||
preset: ShaderPreset,
|
||||
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
||||
cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
||||
queue: &ProtocolObject<dyn MTLCommandQueue>,
|
||||
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>,
|
||||
) -> error::Result<FilterChainMetal> {
|
||||
let device = queue.device();
|
||||
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
|
||||
|
||||
let filters = Self::init_passes(&device, passes, &semantics)?;
|
||||
|
||||
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 icrate::Foundation::NSString;
|
||||
use icrate::Metal::{
|
||||
MTLBlendFactorOneMinusSourceAlpha, MTLBlendFactorSourceAlpha, MTLCommandBuffer,
|
||||
MTLCommandEncoder, MTLDevice, MTLFunction, MTLLibrary, MTLLoadActionDontCare, MTLPixelFormat,
|
||||
MTLBlendFactorOneMinusSourceAlpha, MTLBlendFactorSourceAlpha, MTLCommandBuffer, MTLDevice,
|
||||
MTLFunction, MTLLibrary, MTLLoadActionDontCare, MTLPixelFormat,
|
||||
MTLPrimitiveTopologyClassTriangle, MTLRenderCommandEncoder, MTLRenderPassDescriptor,
|
||||
MTLRenderPipelineColorAttachmentDescriptor, MTLRenderPipelineDescriptor,
|
||||
MTLRenderPipelineState, MTLScissorRect, MTLStoreActionStore, MTLTexture,
|
||||
|
@ -11,25 +11,27 @@ use icrate::Metal::{
|
|||
};
|
||||
use librashader_reflect::back::msl::{CrossMslContext, NagaMslContext};
|
||||
use librashader_reflect::back::ShaderCompilerOutput;
|
||||
use librashader_reflect::reflect::ShaderReflection;
|
||||
use librashader_runtime::render_target::RenderTarget;
|
||||
use objc2::rc::Id;
|
||||
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 layout: PipelineLayoutObjects,
|
||||
render_pipeline: Id<ProtocolObject<dyn MTLRenderPipelineState>>,
|
||||
pub render_pass_format: MTLPixelFormat,
|
||||
}
|
||||
|
||||
pub struct PipelineLayoutObjects {
|
||||
vertex_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||
fragment_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||
_vertex_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||
_fragment_lib: Id<ProtocolObject<dyn MTLLibrary>>,
|
||||
vertex_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>;
|
||||
}
|
||||
|
||||
|
@ -48,7 +50,7 @@ impl MslEntryPoint for NagaMslContext {
|
|||
impl PipelineLayoutObjects {
|
||||
pub fn new<T: MslEntryPoint>(
|
||||
shader_assembly: &ShaderCompilerOutput<String, T>,
|
||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
) -> Result<Self> {
|
||||
let entry = T::entry_point();
|
||||
|
||||
|
@ -65,11 +67,10 @@ impl PipelineLayoutObjects {
|
|||
.ok_or(FilterChainError::ShaderWrongEntryName)?;
|
||||
|
||||
Ok(Self {
|
||||
vertex_lib: vertex,
|
||||
fragment_lib: fragment,
|
||||
_vertex_lib: vertex,
|
||||
_fragment_lib: fragment,
|
||||
vertex_entry,
|
||||
fragment_entry,
|
||||
device,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -85,11 +86,11 @@ impl PipelineLayoutObjects {
|
|||
|
||||
// hopefully metal fills in vertices otherwise we'll need to use the vec4 stuff.
|
||||
vertex_0.setFormat(MTLVertexFormatFloat2);
|
||||
vertex_0.setBufferIndex(4);
|
||||
vertex_0.setBufferIndex(VERTEX_BUFFER_INDEX);
|
||||
vertex_0.setOffset(0);
|
||||
|
||||
vertex_1.setFormat(MTLVertexFormatFloat2);
|
||||
vertex_1.setBufferIndex(4);
|
||||
vertex_1.setBufferIndex(VERTEX_BUFFER_INDEX);
|
||||
vertex_1.setOffset(2 * std::mem::size_of::<f32>());
|
||||
|
||||
attributes.setObject_atIndexedSubscript(Some(&vertex_0), 0);
|
||||
|
@ -119,6 +120,7 @@ impl PipelineLayoutObjects {
|
|||
|
||||
pub fn create_pipeline(
|
||||
&self,
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
format: MTLPixelFormat,
|
||||
) -> Result<Id<ProtocolObject<dyn MTLRenderPipelineState>>> {
|
||||
let descriptor = MTLRenderPipelineDescriptor::new();
|
||||
|
@ -140,36 +142,39 @@ impl PipelineLayoutObjects {
|
|||
descriptor.setFragmentFunction(Some(&self.fragment_entry));
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.device
|
||||
.newRenderPipelineStateWithDescriptor_error(descriptor.as_ref())?)
|
||||
Ok(device.newRenderPipelineStateWithDescriptor_error(descriptor.as_ref())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl MetalGraphicsPipeline {
|
||||
pub fn new<T: MslEntryPoint>(
|
||||
device: Id<ProtocolObject<dyn MTLDevice>>,
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
shader_assembly: &ShaderCompilerOutput<String, T>,
|
||||
render_pass_format: MTLPixelFormat,
|
||||
) -> Result<Self> {
|
||||
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 {
|
||||
layout,
|
||||
render_pipeline: pipeline,
|
||||
render_pass_format,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recompile(&mut self, format: MTLPixelFormat) -> Result<()> {
|
||||
let render_pipeline = self.layout.create_pipeline(format)?;
|
||||
pub fn recompile(
|
||||
&mut self,
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
format: MTLPixelFormat,
|
||||
) -> Result<()> {
|
||||
let render_pipeline = self.layout.create_pipeline(device, format)?;
|
||||
self.render_pipeline = render_pipeline;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn begin_rendering<'pass>(
|
||||
&self,
|
||||
output: RenderTarget<&'pass ProtocolObject<dyn MTLTexture>>,
|
||||
buffer: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
||||
output: &RenderTarget<ProtocolObject<dyn MTLTexture>>,
|
||||
buffer: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||
) -> Result<Id<ProtocolObject<dyn MTLRenderCommandEncoder>>> {
|
||||
unsafe {
|
||||
let descriptor = MTLRenderPassDescriptor::new();
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
#![cfg(target_vendor = "apple")]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
mod buffer;
|
||||
mod draw_quad;
|
||||
mod error;
|
||||
mod filter_chain;
|
||||
mod filter_pass;
|
||||
mod graphics_pipeline;
|
||||
mod luts;
|
||||
mod options;
|
||||
mod samplers;
|
||||
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::samplers::SamplerSet;
|
||||
use crate::texture::MetalTexture;
|
||||
use crate::texture::InputTexture;
|
||||
use icrate::Metal::{
|
||||
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLOrigin,
|
||||
MTLPixelFormatBGRA8Unorm, MTLRegion, MTLSize, MTLTexture, MTLTextureDescriptor,
|
||||
MTLTextureUsageShaderRead,
|
||||
MTLBlitCommandEncoder, MTLDevice, MTLOrigin, MTLPixelFormatBGRA8Unorm, MTLRegion, MTLSize,
|
||||
MTLTexture, MTLTextureDescriptor, MTLTextureUsageShaderRead,
|
||||
};
|
||||
use librashader_presets::TextureConfig;
|
||||
use librashader_runtime::image::{Image, BGRA8};
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use std::ffi::c_void;
|
||||
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 {
|
||||
pub fn new(
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
mipmapper: &ProtocolObject<dyn MTLBlitCommandEncoder>,
|
||||
image: Image<BGRA8>,
|
||||
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||
config: &TextureConfig,
|
||||
) -> Result<Self> {
|
||||
let descriptor = unsafe {
|
||||
|
@ -68,12 +71,14 @@ impl LutTexture {
|
|||
}
|
||||
|
||||
if config.mipmap {
|
||||
if let Some(encoder) = cmd.blitCommandEncoder() {
|
||||
encoder.generateMipmapsForTexture(&texture);
|
||||
encoder.endEncoding();
|
||||
}
|
||||
mipmapper.generateMipmapsForTexture(&texture);
|
||||
}
|
||||
|
||||
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 icrate::Metal::{
|
||||
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLPixelFormat,
|
||||
MTLPixelFormatBGRA8Unorm, MTLTexture, MTLTextureDescriptor, MTLTextureUsageRenderTarget,
|
||||
MTLTextureUsageShaderRead, MTLTextureUsageShaderWrite,
|
||||
MTLBlitCommandEncoder, MTLCommandBuffer, MTLDevice, MTLPixelFormat, MTLTexture,
|
||||
MTLTextureDescriptor, MTLTextureUsageRenderTarget, MTLTextureUsageShaderRead,
|
||||
MTLTextureUsageShaderWrite,
|
||||
};
|
||||
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
|
||||
use librashader_presets::Scale2D;
|
||||
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
|
||||
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type MetalTexture = Id<ProtocolObject<dyn MTLTexture>>;
|
||||
|
||||
pub struct OwnedImage {
|
||||
image: MetalTexture,
|
||||
max_miplevels: u32,
|
||||
pub struct OwnedTexture {
|
||||
pub(crate) texture: MetalTexture,
|
||||
pub(crate) max_miplevels: 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(
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
size: Size<u32>,
|
||||
max_miplevels: u32,
|
||||
format: ImageFormat,
|
||||
format: MTLPixelFormat,
|
||||
) -> Result<Self> {
|
||||
let format: MTLPixelFormat = format.into();
|
||||
|
||||
let descriptor = unsafe {
|
||||
let descriptor =
|
||||
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
||||
|
@ -54,7 +77,7 @@ impl OwnedImage {
|
|||
};
|
||||
|
||||
Ok(Self {
|
||||
image: device
|
||||
texture: device
|
||||
.newTextureWithDescriptor(&descriptor)
|
||||
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||
max_miplevels,
|
||||
|
@ -66,52 +89,50 @@ impl OwnedImage {
|
|||
&mut self,
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
scaling: Scale2D,
|
||||
format: ImageFormat,
|
||||
format: MTLPixelFormat,
|
||||
viewport_size: &Size<u32>,
|
||||
source_size: &Size<u32>,
|
||||
mipmap: bool,
|
||||
) -> Size<u32> {
|
||||
) -> Result<Size<u32>> {
|
||||
let size = source_size.scale_viewport(scaling, *viewport_size);
|
||||
let format: MTLPixelFormat = format.into();
|
||||
|
||||
if self.size != size
|
||||
|| (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);
|
||||
}
|
||||
size
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
// pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> InputImage {
|
||||
// InputImage {
|
||||
// image: Arc::clone(&self.image),
|
||||
// view: Arc::clone(&self.view),
|
||||
// wrap_mode,
|
||||
// filter_mode: filter,
|
||||
// mip_filter: filter,
|
||||
// }
|
||||
// }
|
||||
pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> Result<InputTexture> {
|
||||
Ok(InputTexture {
|
||||
texture: self
|
||||
.texture
|
||||
.newTextureViewWithPixelFormat(self.texture.pixelFormat())
|
||||
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||
wrap_mode,
|
||||
filter_mode: filter,
|
||||
mip_filter: filter,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn copy_from(
|
||||
&self,
|
||||
encoder: &ProtocolObject<dyn MTLBlitCommandEncoder>,
|
||||
other: &ProtocolObject<dyn MTLTexture>,
|
||||
cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
||||
) -> Result<()> {
|
||||
let encoder = cmd
|
||||
.blitCommandEncoder()
|
||||
.ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||
unsafe {
|
||||
encoder.copyFromTexture_toTexture(other, &self.image);
|
||||
encoder.copyFromTexture_toTexture(other, &self.texture);
|
||||
}
|
||||
encoder.generateMipmapsForTexture(&self.image);
|
||||
encoder.endEncoding();
|
||||
encoder.generateMipmapsForTexture(&self.texture);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear(&self, cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>) {
|
||||
pub fn clear(&self, cmd: &ProtocolObject<dyn MTLCommandBuffer>) {
|
||||
// let render = cmd.renderCommandEncoder()
|
||||
// .ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||
// render.
|
||||
|
@ -120,6 +141,39 @@ impl OwnedImage {
|
|||
|
||||
/// caller must end the blit encoder after.
|
||||
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