rt(mtl): implement texture and buffer abstractions
This commit is contained in:
parent
6780397d49
commit
ba3154b92d
|
@ -29,7 +29,6 @@ pub mod metal;
|
||||||
|
|
||||||
mod viewport;
|
mod viewport;
|
||||||
|
|
||||||
|
|
||||||
pub use viewport::Viewport;
|
pub use viewport::Viewport;
|
||||||
|
|
||||||
use num_traits::AsPrimitive;
|
use num_traits::AsPrimitive;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use crate::{FilterMode, ImageFormat, Size, WrapMode};
|
||||||
use icrate::Metal;
|
use icrate::Metal;
|
||||||
use crate::{Size, ImageFormat, FilterMode, WrapMode};
|
|
||||||
|
|
||||||
impl From<ImageFormat> for Metal::MTLPixelFormat {
|
impl From<ImageFormat> for Metal::MTLPixelFormat {
|
||||||
fn from(format: ImageFormat) -> Self {
|
fn from(format: ImageFormat) -> Self {
|
||||||
|
@ -61,7 +61,6 @@ impl From<Size<u32>> for Metal::MTLViewport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl From<WrapMode> for Metal::MTLSamplerAddressMode {
|
impl From<WrapMode> for Metal::MTLSamplerAddressMode {
|
||||||
fn from(value: WrapMode) -> Self {
|
fn from(value: WrapMode) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
@ -91,5 +90,3 @@ impl FilterMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ librashader-runtime = { path = "../librashader-runtime" , version = "0.2.0-beta.
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
array-concat = "0.5.2"
|
array-concat = "0.5.2"
|
||||||
bytemuck = "1.14.3"
|
bytemuck = { version = "1.12.3", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
||||||
|
|
54
librashader-runtime-metal/src/buffer.rs
Normal file
54
librashader-runtime-metal/src/buffer.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::error::FilterChainError;
|
||||||
|
use icrate::Foundation::NSRange;
|
||||||
|
use icrate::Metal::{
|
||||||
|
MTLBuffer, MTLDevice, MTLResourceStorageModeManaged, MTLResourceStorageModeShared,
|
||||||
|
};
|
||||||
|
use objc2::rc::Id;
|
||||||
|
use objc2::runtime::ProtocolObject;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
pub struct MetalBuffer {
|
||||||
|
buffer: Id<ProtocolObject<dyn MTLBuffer>>,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetalBuffer {
|
||||||
|
pub fn new(device: &ProtocolObject<dyn MTLDevice>, size: usize) -> Result<Self> {
|
||||||
|
let resource_mode = if cfg!(target_os = "ios") {
|
||||||
|
MTLResourceStorageModeShared
|
||||||
|
} else {
|
||||||
|
MTLResourceStorageModeManaged
|
||||||
|
};
|
||||||
|
|
||||||
|
let buffer = device
|
||||||
|
.newBufferWithLength_options(size, resource_mode)
|
||||||
|
.ok_or(FilterChainError::BufferError)?;
|
||||||
|
Ok(Self { buffer, size })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&self) {
|
||||||
|
// We don't know what was actually written to so...
|
||||||
|
self.buffer.didModifyRange(NSRange {
|
||||||
|
location: 0,
|
||||||
|
length: self.size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for MetalBuffer {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
// SAFETY: the lifetime of this reference must be longer than of the MetalBuffer.
|
||||||
|
// Additionally, `MetalBuffer.buffer` is never lent out directly
|
||||||
|
unsafe { std::slice::from_raw_parts(self.buffer.contents().as_ptr().cast(), self.size) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for MetalBuffer {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
// SAFETY: the lifetime of this reference must be longer than of the MetalBuffer.
|
||||||
|
// Additionally, `MetalBuffer.buffer` is never lent out directly
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(self.buffer.contents().as_ptr().cast(), self.size) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,20 +65,20 @@ pub struct DrawQuad {
|
||||||
impl DrawQuad {
|
impl DrawQuad {
|
||||||
pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<DrawQuad> {
|
pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<DrawQuad> {
|
||||||
let vbo_data: &'static [u8] = bytemuck::cast_slice(&VBO_DATA);
|
let vbo_data: &'static [u8] = bytemuck::cast_slice(&VBO_DATA);
|
||||||
let Some(buffer) = (unsafe {
|
let buffer = unsafe {
|
||||||
device.newBufferWithBytes_length_options(
|
device
|
||||||
// SAFETY: this pointer is const.
|
.newBufferWithBytes_length_options(
|
||||||
// https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes
|
// SAFETY: this pointer is const.
|
||||||
NonNull::new_unchecked(vbo_data.as_ptr() as *mut c_void),
|
// https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes
|
||||||
vbo_data.len(),
|
NonNull::new_unchecked(vbo_data.as_ptr() as *mut c_void),
|
||||||
if cfg!(target_os = "ios") {
|
vbo_data.len(),
|
||||||
MTLResourceStorageModeShared
|
if cfg!(target_os = "ios") {
|
||||||
} else {
|
MTLResourceStorageModeShared
|
||||||
MTLResourceStorageModeManaged
|
} else {
|
||||||
},
|
MTLResourceStorageModeManaged
|
||||||
)
|
},
|
||||||
}) else {
|
)
|
||||||
return Err(FilterChainError::BufferError);
|
.ok_or(FilterChainError::BufferError)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(DrawQuad { buffer })
|
Ok(DrawQuad { buffer })
|
||||||
|
|
|
@ -26,11 +26,15 @@ pub enum FilterChainError {
|
||||||
#[error("buffer creation error")]
|
#[error("buffer creation error")]
|
||||||
BufferError,
|
BufferError,
|
||||||
#[error("metal error")]
|
#[error("metal error")]
|
||||||
MetalError(Id<NSError>),
|
MetalError(#[from] Id<NSError>),
|
||||||
#[error("couldn't find entry for shader")]
|
#[error("couldn't find entry for shader")]
|
||||||
ShaderWrongEntryName,
|
ShaderWrongEntryName,
|
||||||
#[error("couldn't create render pass")]
|
#[error("couldn't create render pass")]
|
||||||
FailedToCreateRenderPass,
|
FailedToCreateRenderPass,
|
||||||
|
#[error("couldn't create texture")]
|
||||||
|
FailedToCreateTexture,
|
||||||
|
#[error("couldn't create command buffer")]
|
||||||
|
FailedToCreateCommandBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type for Metal filter chains.
|
/// Result type for Metal filter chains.
|
||||||
|
|
115
librashader-runtime-metal/src/filter_chain.rs
Normal file
115
librashader-runtime-metal/src/filter_chain.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use crate::draw_quad::DrawQuad;
|
||||||
|
use crate::error;
|
||||||
|
use crate::error::FilterChainError;
|
||||||
|
use crate::filter_pass::FilterPass;
|
||||||
|
use crate::luts::LutTexture;
|
||||||
|
use crate::options::FilterChainOptionsMetal;
|
||||||
|
use crate::samplers::SamplerSet;
|
||||||
|
use crate::texture::{MetalTexture, OwnedImage};
|
||||||
|
use icrate::Metal::{MTLCommandBuffer, MTLCommandQueue, MTLDevice};
|
||||||
|
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::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 objc2::rc::Id;
|
||||||
|
use objc2::runtime::ProtocolObject;
|
||||||
|
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>;
|
||||||
|
fn compile_passes(
|
||||||
|
shaders: Vec<ShaderPassConfig>,
|
||||||
|
textures: &[TextureConfig],
|
||||||
|
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
|
||||||
|
let (passes, semantics) =
|
||||||
|
MSL::compile_preset_passes::<Glslang, SpirvCompilation, SpirvCross, FilterChainError>(
|
||||||
|
shaders, &textures,
|
||||||
|
)?;
|
||||||
|
Ok((passes, semantics))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wgpu filter chain.
|
||||||
|
pub struct FilterChainMetal {
|
||||||
|
pub(crate) common: FilterCommon,
|
||||||
|
passes: Box<[FilterPass]>,
|
||||||
|
output_framebuffers: Box<[OwnedImage]>,
|
||||||
|
feedback_framebuffers: Box<[OwnedImage]>,
|
||||||
|
history_framebuffers: VecDeque<OwnedImage>,
|
||||||
|
disable_mipmaps: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FilterMutable {
|
||||||
|
pub passes_enabled: usize,
|
||||||
|
pub(crate) parameters: FxHashMap<String, f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FilterCommon {
|
||||||
|
pub output_textures: Box<[Option<MetalTexture>]>,
|
||||||
|
pub feedback_textures: Box<[Option<MetalTexture>]>,
|
||||||
|
pub history_textures: Box<[Option<MetalTexture>]>,
|
||||||
|
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>>,
|
||||||
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
|
) -> error::Result<FilterChainMetal> {
|
||||||
|
// load passes from preset
|
||||||
|
let preset = ShaderPreset::try_parse_with_driver_context(path, VideoDriver::Metal)?;
|
||||||
|
Self::load_from_preset(preset, queue, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a filter chain from a pre-parsed `ShaderPreset`.
|
||||||
|
pub fn load_from_preset(
|
||||||
|
preset: ShaderPreset,
|
||||||
|
queue: Id<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)?;
|
||||||
|
|
||||||
|
cmd.commit();
|
||||||
|
unsafe { cmd.waitUntilCompleted() };
|
||||||
|
|
||||||
|
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.
|
||||||
|
/// 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 fn load_from_preset_deferred(
|
||||||
|
preset: ShaderPreset,
|
||||||
|
queue: Id<ProtocolObject<dyn MTLCommandQueue>>,
|
||||||
|
cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>,
|
||||||
|
options: Option<&FilterChainOptionsMetal>,
|
||||||
|
) -> error::Result<FilterChainMetal> {
|
||||||
|
let device = queue.device();
|
||||||
|
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
|
||||||
|
|
||||||
|
let samplers = SamplerSet::new(&device)?;
|
||||||
|
}
|
||||||
|
}
|
1
librashader-runtime-metal/src/filter_pass.rs
Normal file
1
librashader-runtime-metal/src/filter_pass.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub struct FilterPass {}
|
|
@ -30,7 +30,7 @@ pub struct PipelineLayoutObjects {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait MslEntryPoint {
|
trait MslEntryPoint {
|
||||||
fn entry_point() -> NSString;
|
fn entry_point() -> Id<NSString>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MslEntryPoint for CrossMslContext {
|
impl MslEntryPoint for CrossMslContext {
|
||||||
|
@ -112,7 +112,7 @@ impl PipelineLayoutObjects {
|
||||||
ca.setSourceAlphaBlendFactor(MTLBlendFactorSourceAlpha);
|
ca.setSourceAlphaBlendFactor(MTLBlendFactorSourceAlpha);
|
||||||
ca.setSourceRGBBlendFactor(MTLBlendFactorSourceAlpha);
|
ca.setSourceRGBBlendFactor(MTLBlendFactorSourceAlpha);
|
||||||
ca.setDestinationAlphaBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
ca.setDestinationAlphaBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
||||||
ca.setDetinationRGBBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
ca.setDestinationRGBBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
||||||
|
|
||||||
ca
|
ca
|
||||||
}
|
}
|
||||||
|
@ -153,10 +153,10 @@ impl MetalGraphicsPipeline {
|
||||||
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)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
layout,
|
layout,
|
||||||
render_pipeline: layout.create_pipeline(render_pass_format)?,
|
render_pipeline: pipeline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
#![cfg(target_vendor = "apple")]
|
#![cfg(target_vendor = "apple")]
|
||||||
|
mod buffer;
|
||||||
mod draw_quad;
|
mod draw_quad;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod filter_chain;
|
||||||
|
mod filter_pass;
|
||||||
mod graphics_pipeline;
|
mod graphics_pipeline;
|
||||||
|
mod luts;
|
||||||
|
mod options;
|
||||||
mod samplers;
|
mod samplers;
|
||||||
|
mod texture;
|
||||||
|
|
79
librashader-runtime-metal/src/luts.rs
Normal file
79
librashader-runtime-metal/src/luts.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use crate::error::{FilterChainError, Result};
|
||||||
|
use crate::samplers::SamplerSet;
|
||||||
|
use crate::texture::MetalTexture;
|
||||||
|
use icrate::Metal::{
|
||||||
|
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, 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);
|
||||||
|
|
||||||
|
impl LutTexture {
|
||||||
|
pub fn new(
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
image: Image<BGRA8>,
|
||||||
|
cmd: &ProtocolObject<dyn MTLCommandBuffer>,
|
||||||
|
config: &TextureConfig,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let descriptor = unsafe {
|
||||||
|
let descriptor =
|
||||||
|
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
||||||
|
MTLPixelFormatBGRA8Unorm,
|
||||||
|
image.size.width as usize,
|
||||||
|
image.size.height as usize,
|
||||||
|
config.mipmap,
|
||||||
|
);
|
||||||
|
|
||||||
|
descriptor.setSampleCount(1);
|
||||||
|
descriptor.setMipmapLevelCount(if config.mipmap {
|
||||||
|
image.size.calculate_miplevels() as usize
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
});
|
||||||
|
|
||||||
|
descriptor.setUsage(MTLTextureUsageShaderRead);
|
||||||
|
|
||||||
|
descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture = device
|
||||||
|
.newTextureWithDescriptor(&descriptor)
|
||||||
|
.ok_or(FilterChainError::FailedToCreateTexture)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let region = MTLRegion {
|
||||||
|
origin: MTLOrigin { x: 0, y: 0, z: 0 },
|
||||||
|
size: MTLSize {
|
||||||
|
width: image.size.width as usize,
|
||||||
|
height: image.size.height as usize,
|
||||||
|
depth: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
texture.replaceRegion_mipmapLevel_withBytes_bytesPerRow(
|
||||||
|
region,
|
||||||
|
0,
|
||||||
|
// SAFETY: replaceRegion withBytes is const.
|
||||||
|
NonNull::new_unchecked(image.bytes.as_slice().as_ptr() as *mut c_void),
|
||||||
|
4 * image.size.width as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.mipmap {
|
||||||
|
if let Some(encoder) = cmd.blitCommandEncoder() {
|
||||||
|
encoder.generateMipmapsForTexture(&texture);
|
||||||
|
encoder.endEncoding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LutTexture(texture))
|
||||||
|
}
|
||||||
|
}
|
20
librashader-runtime-metal/src/options.rs
Normal file
20
librashader-runtime-metal/src/options.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//! Metal shader runtime options.
|
||||||
|
|
||||||
|
/// Options for each Vulkan shader frame.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct FrameOptionsMetal {
|
||||||
|
/// Whether or not to clear the history buffers.
|
||||||
|
pub clear_history: bool,
|
||||||
|
/// The direction of rendering.
|
||||||
|
/// -1 indicates that the frames are played in reverse order.
|
||||||
|
pub frame_direction: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for filter chain creation.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct FilterChainOptionsMetal {
|
||||||
|
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
|
||||||
|
pub force_no_mipmaps: bool,
|
||||||
|
}
|
125
librashader-runtime-metal/src/texture.rs
Normal file
125
librashader-runtime-metal/src/texture.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use crate::error::{FilterChainError, Result};
|
||||||
|
use icrate::Metal::{
|
||||||
|
MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLPixelFormat,
|
||||||
|
MTLPixelFormatBGRA8Unorm, MTLTexture, MTLTextureDescriptor, MTLTextureUsageRenderTarget,
|
||||||
|
MTLTextureUsageShaderRead, MTLTextureUsageShaderWrite,
|
||||||
|
};
|
||||||
|
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
|
||||||
|
use librashader_presets::Scale2D;
|
||||||
|
use librashader_runtime::scaling::{MipmapSize, 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,
|
||||||
|
size: Size<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedImage {
|
||||||
|
pub fn new(
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
size: Size<u32>,
|
||||||
|
max_miplevels: u32,
|
||||||
|
format: ImageFormat,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let format: MTLPixelFormat = format.into();
|
||||||
|
|
||||||
|
let descriptor = unsafe {
|
||||||
|
let descriptor =
|
||||||
|
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
||||||
|
format,
|
||||||
|
size.width as usize,
|
||||||
|
size.height as usize,
|
||||||
|
max_miplevels <= 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
descriptor.setSampleCount(1);
|
||||||
|
descriptor.setMipmapLevelCount(if max_miplevels <= 1 {
|
||||||
|
size.calculate_miplevels() as usize
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
});
|
||||||
|
|
||||||
|
descriptor.setUsage(
|
||||||
|
MTLTextureUsageShaderRead
|
||||||
|
| MTLTextureUsageShaderWrite
|
||||||
|
| MTLTextureUsageRenderTarget,
|
||||||
|
);
|
||||||
|
|
||||||
|
descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
image: device
|
||||||
|
.newTextureWithDescriptor(&descriptor)
|
||||||
|
.ok_or(FilterChainError::FailedToCreateTexture)?,
|
||||||
|
max_miplevels,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(
|
||||||
|
&mut self,
|
||||||
|
device: &ProtocolObject<dyn MTLDevice>,
|
||||||
|
scaling: Scale2D,
|
||||||
|
format: ImageFormat,
|
||||||
|
viewport_size: &Size<u32>,
|
||||||
|
source_size: &Size<u32>,
|
||||||
|
mipmap: bool,
|
||||||
|
) -> 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()
|
||||||
|
{
|
||||||
|
let mut new = OwnedImage::new(device, size, self.max_miplevels, format.into())?;
|
||||||
|
std::mem::swap(self, &mut new);
|
||||||
|
}
|
||||||
|
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 fn copy_from(
|
||||||
|
&self,
|
||||||
|
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.generateMipmapsForTexture(&self.image);
|
||||||
|
encoder.endEncoding();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self, cmd: Id<ProtocolObject<dyn MTLCommandBuffer>>) {
|
||||||
|
// let render = cmd.renderCommandEncoder()
|
||||||
|
// .ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
|
||||||
|
// render.
|
||||||
|
// cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// caller must end the blit encoder after.
|
||||||
|
pub fn generate_mipmaps(&self, mipmapper: &ProtocolObject<dyn MTLBlitCommandEncoder>) {
|
||||||
|
mipmapper.generateMipmapsForTexture(&self.image);
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,4 +59,3 @@ macro_rules! impl_filter_chain_parameters {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue