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;
|
||||
|
||||
|
||||
pub use viewport::Viewport;
|
||||
|
||||
use num_traits::AsPrimitive;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{FilterMode, ImageFormat, Size, WrapMode};
|
||||
use icrate::Metal;
|
||||
use crate::{Size, ImageFormat, FilterMode, WrapMode};
|
||||
|
||||
impl From<ImageFormat> for Metal::MTLPixelFormat {
|
||||
fn from(format: ImageFormat) -> Self {
|
||||
|
@ -61,7 +61,6 @@ impl From<Size<u32>> for Metal::MTLViewport {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<WrapMode> for Metal::MTLSamplerAddressMode {
|
||||
fn from(value: WrapMode) -> Self {
|
||||
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"
|
||||
thiserror = "1.0"
|
||||
array-concat = "0.5.2"
|
||||
bytemuck = "1.14.3"
|
||||
bytemuck = { version = "1.12.3", features = ["derive"] }
|
||||
|
||||
[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 {
|
||||
pub fn new(device: &ProtocolObject<dyn MTLDevice>) -> Result<DrawQuad> {
|
||||
let vbo_data: &'static [u8] = bytemuck::cast_slice(&VBO_DATA);
|
||||
let Some(buffer) = (unsafe {
|
||||
device.newBufferWithBytes_length_options(
|
||||
// SAFETY: this pointer is const.
|
||||
// https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes
|
||||
NonNull::new_unchecked(vbo_data.as_ptr() as *mut c_void),
|
||||
vbo_data.len(),
|
||||
if cfg!(target_os = "ios") {
|
||||
MTLResourceStorageModeShared
|
||||
} else {
|
||||
MTLResourceStorageModeManaged
|
||||
},
|
||||
)
|
||||
}) else {
|
||||
return Err(FilterChainError::BufferError);
|
||||
let buffer = unsafe {
|
||||
device
|
||||
.newBufferWithBytes_length_options(
|
||||
// SAFETY: this pointer is const.
|
||||
// https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes
|
||||
NonNull::new_unchecked(vbo_data.as_ptr() as *mut c_void),
|
||||
vbo_data.len(),
|
||||
if cfg!(target_os = "ios") {
|
||||
MTLResourceStorageModeShared
|
||||
} else {
|
||||
MTLResourceStorageModeManaged
|
||||
},
|
||||
)
|
||||
.ok_or(FilterChainError::BufferError)?
|
||||
};
|
||||
|
||||
Ok(DrawQuad { buffer })
|
||||
|
|
|
@ -26,11 +26,15 @@ pub enum FilterChainError {
|
|||
#[error("buffer creation error")]
|
||||
BufferError,
|
||||
#[error("metal error")]
|
||||
MetalError(Id<NSError>),
|
||||
MetalError(#[from] Id<NSError>),
|
||||
#[error("couldn't find entry for shader")]
|
||||
ShaderWrongEntryName,
|
||||
#[error("couldn't create render pass")]
|
||||
FailedToCreateRenderPass,
|
||||
#[error("couldn't create texture")]
|
||||
FailedToCreateTexture,
|
||||
#[error("couldn't create command buffer")]
|
||||
FailedToCreateCommandBuffer,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
fn entry_point() -> NSString;
|
||||
fn entry_point() -> Id<NSString>;
|
||||
}
|
||||
|
||||
impl MslEntryPoint for CrossMslContext {
|
||||
|
@ -112,7 +112,7 @@ impl PipelineLayoutObjects {
|
|||
ca.setSourceAlphaBlendFactor(MTLBlendFactorSourceAlpha);
|
||||
ca.setSourceRGBBlendFactor(MTLBlendFactorSourceAlpha);
|
||||
ca.setDestinationAlphaBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
||||
ca.setDetinationRGBBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
||||
ca.setDestinationRGBBlendFactor(MTLBlendFactorOneMinusSourceAlpha);
|
||||
|
||||
ca
|
||||
}
|
||||
|
@ -153,10 +153,10 @@ impl MetalGraphicsPipeline {
|
|||
render_pass_format: MTLPixelFormat,
|
||||
) -> Result<Self> {
|
||||
let layout = PipelineLayoutObjects::new(shader_assembly, device)?;
|
||||
|
||||
let pipeline = layout.create_pipeline(render_pass_format)?;
|
||||
Ok(Self {
|
||||
layout,
|
||||
render_pipeline: layout.create_pipeline(render_pass_format)?,
|
||||
render_pipeline: pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
#![cfg(target_vendor = "apple")]
|
||||
mod buffer;
|
||||
mod draw_quad;
|
||||
mod error;
|
||||
mod filter_chain;
|
||||
mod filter_pass;
|
||||
mod graphics_pipeline;
|
||||
mod luts;
|
||||
mod options;
|
||||
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