use super::buffer::*;
use super::bindings::*;
use super::matrix_buffer::*;
use super::render_target::*;
use super::pipeline_configuration::*;
use crate::action::*;
use crate::buffer::*;
use flo_canvas;
use metal;
use std::sync::*;
use std::ops::{Range};
use std::collections::{HashMap};
pub struct MetalRenderer {
device: metal::Device,
flip_y: bool,
shader_library: metal::Library,
command_queue: metal::CommandQueue,
vertex_buffers: Vec<Option<Buffer>>,
index_buffers: Vec<Option<Buffer>>,
render_targets: Vec<Option<RenderTarget>>,
textures: Vec<Option<metal::Texture>>,
pipeline_states: HashMap<PipelineConfiguration, metal::RenderPipelineState>
}
struct RenderState<'a> {
main_texture: metal::Texture,
target_texture: metal::Texture,
fill_texture: Option<metal::Texture>,
erase_texture: Option<metal::Texture>,
clip_texture: Option<metal::Texture>,
matrix: MatrixBuffer,
texture_transform: Option<MatrixBuffer>,
texture_alpha: Option<f64>,
pipeline_config: PipelineConfiguration,
pipeline_state: metal::RenderPipelineState,
command_buffer: &'a metal::CommandBufferRef,
command_encoder: &'a metal::RenderCommandEncoderRef
}
impl MetalRenderer {
pub fn with_default_device() -> MetalRenderer {
let device = metal::Device::system_default().expect("No Metal device available");
let command_queue = device.new_command_queue();
let shader_library = device.new_library_with_data(include_bytes![concat!(env!("OUT_DIR"), "/flo.metallib")]).unwrap();
MetalRenderer {
device: device,
flip_y: false,
command_queue: command_queue,
vertex_buffers: vec![],
index_buffers: vec![],
render_targets: vec![],
textures: vec![],
shader_library: shader_library,
pipeline_states: HashMap::new()
}
}
pub fn with_device(device: &metal::Device, flip_y: bool) -> MetalRenderer {
let device = device.clone();
let command_queue = device.new_command_queue();
let shader_library = device.new_library_with_data(include_bytes![concat!(env!("OUT_DIR"), "/flo.metallib")]).unwrap();
MetalRenderer {
device: device,
flip_y: flip_y,
command_queue: command_queue,
vertex_buffers: vec![],
index_buffers: vec![],
render_targets: vec![],
textures: vec![],
shader_library: shader_library,
pipeline_states: HashMap::new()
}
}
fn get_pipeline_state(&mut self, config: &PipelineConfiguration) -> metal::RenderPipelineState {
let pipeline_states = &mut self.pipeline_states;
let device = &self.device;
let shader_library = &self.shader_library;
if let Some(pipeline) = pipeline_states.get(config) {
pipeline.clone()
} else {
let pipeline = config.to_pipeline_state(&device, &shader_library);
pipeline_states.insert(config.clone(), pipeline.clone());
pipeline
}
}
fn get_command_encoder<'a>(&mut self, command_buffer: &'a metal::CommandBufferRef, render_target: &metal::Texture) -> &'a metal::RenderCommandEncoderRef {
let render_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_descriptor.color_attachments().object_at(0).unwrap();
color_attachment.set_texture(Some(render_target));
color_attachment.set_load_action(metal::MTLLoadAction::Load);
color_attachment.set_store_action(metal::MTLStoreAction::Store);
command_buffer.new_render_command_encoder(&render_descriptor)
}
fn get_blit_command_encoder<'a>(&self, command_buffer: &'a metal::CommandBufferRef) -> &'a metal::BlitCommandEncoderRef {
command_buffer.new_blit_command_encoder()
}
fn get_command_encoder_with_clear<'a>(&mut self, command_buffer: &'a metal::CommandBufferRef, render_target: &metal::Texture, clear_color: Rgba8) -> &'a metal::RenderCommandEncoderRef {
let render_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_descriptor.color_attachments().object_at(0).unwrap();
let Rgba8([r, g, b, a]) = clear_color;
let clear_color = metal::MTLClearColor::new((r as f64) / 255.0, (g as f64) / 255.0, (b as f64) / 255.0, (a as f64) / 255.0);
color_attachment.set_texture(Some(render_target));
color_attachment.set_clear_color(clear_color);
color_attachment.set_load_action(metal::MTLLoadAction::Clear);
color_attachment.set_store_action(metal::MTLStoreAction::Store);
command_buffer.new_render_command_encoder(&render_descriptor)
}
fn setup_command_encoder(&mut self, state: &RenderState) {
state.command_encoder.set_render_pipeline_state(&state.pipeline_state);
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexMatrix as u64, Some(&state.matrix), 0);
state.command_encoder.set_fragment_texture(FragmentInputIndex_FragmentIndexEraseTexture as u64, state.erase_texture.as_ref().map::<&metal::TextureRef, _>(|t| t));
state.command_encoder.set_fragment_texture(FragmentInputIndex_FragmentIndexClipMaskTexture as u64, state.clip_texture.as_ref().map::<&metal::TextureRef, _>(|t| t));
state.command_encoder.set_fragment_texture(FragmentInputIndex_FragmentIndexTexture as u64, state.fill_texture.as_ref().map::<&metal::TextureRef, _>(|t| t));
if let Some(texture_matrix) = &state.texture_transform {
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexTextureMatrix as u64, Some(texture_matrix), 0);
}
if let Some(texture_alpha) = &state.texture_alpha {
let alpha = *texture_alpha as f32;
let alpha = alpha.to_ne_bytes();
state.command_encoder.set_fragment_bytes(FragmentInputIndex_FragmentAlpha as u64, 4, alpha.as_ptr() as _);
}
}
pub fn render_to_buffer<Actions: IntoIterator<Item=RenderAction>>(&mut self, actions: Actions, target_texture: &metal::Texture) -> metal::CommandBuffer {
let command_queue = self.command_queue.clone();
let matrix = if self.flip_y { Matrix::identity().flip_y() } else { Matrix::identity() };
let matrix = MatrixBuffer::from_matrix(&self.device, matrix);
let pipeline_config = PipelineConfiguration::for_texture(target_texture);
let pipeline_state = self.get_pipeline_state(&pipeline_config);
let command_buffer = command_queue.new_command_buffer();
let command_encoder = self.get_command_encoder_with_clear(command_buffer, target_texture, Rgba8([0, 0, 0, 0]));
let mut render_state = RenderState {
main_texture: target_texture.clone(),
target_texture: target_texture.clone(),
fill_texture: None,
erase_texture: None,
clip_texture: None,
matrix: matrix,
texture_transform: None,
texture_alpha: None,
pipeline_config: pipeline_config,
pipeline_state: pipeline_state,
command_buffer: command_buffer,
command_encoder: command_encoder
};
self.setup_command_encoder(&render_state);
for action in actions {
use self::RenderAction::*;
match action {
SetTransform(matrix) => { self.set_transform(matrix, &mut render_state); }
CreateVertex2DBuffer(id, vertices) => { self.create_vertex_buffer_2d(id, vertices); }
CreateIndexBuffer(id, indices) => { self.create_index_buffer(id, indices); }
FreeVertexBuffer(id) => { self.free_vertex_buffer(id); }
FreeIndexBuffer(id) => { self.free_index_buffer(id); }
BlendMode(blend_mode) => { self.blend_mode(blend_mode, &mut render_state); }
CreateRenderTarget(render_id, texture_id, width, height, render_type) => { self.create_render_target(render_id, texture_id, width, height, render_type); }
FreeRenderTarget(render_id) => { self.free_render_target(render_id); }
SelectRenderTarget(render_id) => { self.select_render_target(render_id, &mut render_state); }
RenderToFrameBuffer => { self.select_main_frame_buffer(&mut render_state); }
DrawFrameBuffer(render_id, x, y) => { self.draw_frame_buffer(render_id, x, y, &mut render_state); }
ShowFrameBuffer => { }
CreateTextureBgra(texture_id, width, height) => { self.create_bgra_texture(texture_id, width, height); }
CreateTextureMono(texture_id, width, height) => { self.create_mono_texture(texture_id, width, height); }
Create1DTextureBgra(texture_id, width) => { self.create_bgra_1d_texture(texture_id, width); }
Create1DTextureMono(texture_id, width) => { self.create_mono_1d_texture(texture_id, width); }
WriteTextureData(texture_id, (x1, y1), (x2, y2), data) => { self.write_texture_data_2d(texture_id, x1, y1, x2, y2, data); }
WriteTexture1D(texture_id, x1, x2, data) => { self.write_texture_data_1d(texture_id, x1, x2, data); }
CreateMipMaps(texture_id) => { self.create_mipmaps(texture_id, &mut render_state); }
CopyTexture(src_texture, tgt_texture) => { self.copy_texture(src_texture, tgt_texture, &mut render_state); }
FreeTexture(texture_id) => { self.free_texture(texture_id); }
Clear(color) => { self.clear(color, &mut render_state); }
UseShader(shader_type) => { self.use_shader(shader_type, &mut render_state); }
DrawTriangles(buffer_id, buffer_range) => { self.draw_triangles(buffer_id, buffer_range, &mut render_state); }
DrawIndexedTriangles(vertex_buffer, index_buffer, num_vertices) => { self.draw_indexed_triangles(vertex_buffer, index_buffer, num_vertices, &mut render_state); }
}
}
render_state.command_encoder.end_encoding();
command_buffer.to_owned()
}
pub fn render<Actions: IntoIterator<Item=RenderAction>>(&mut self, actions: Actions, target_drawable: &metal::Drawable, target_texture: &metal::Texture) {
let command_buffer = self.render_to_buffer(actions, target_texture);
command_buffer.present_drawable(target_drawable);
command_buffer.commit();
}
fn set_transform(&mut self, matrix: Matrix, state: &mut RenderState) {
let matrix = if self.flip_y { matrix.flip_y() } else { matrix };
state.matrix = MatrixBuffer::from_matrix(&self.device, matrix);
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexMatrix as u64, Some(&state.matrix), 0);
}
fn create_vertex_buffer_2d(&mut self, VertexBufferId(vertex_id): VertexBufferId, vertices: Vec<Vertex2D>) {
if vertex_id >= self.vertex_buffers.len() {
self.vertex_buffers.extend((self.vertex_buffers.len()..(vertex_id+1))
.into_iter()
.map(|_| None));
}
self.vertex_buffers[vertex_id] = None;
if vertices.len() == 0 {
return;
}
self.vertex_buffers[vertex_id] = Some(Buffer::from_vertices(&self.device, vertices));
}
fn create_index_buffer(&mut self, IndexBufferId(index_id): IndexBufferId, indices: Vec<u16>) {
if index_id >= self.index_buffers.len() {
self.index_buffers.extend((self.index_buffers.len()..(index_id+1))
.into_iter()
.map(|_| None));
}
self.index_buffers[index_id] = None;
if indices.len() == 0 {
return;
}
self.index_buffers[index_id] = Some(Buffer::from_indices(&self.device, indices));
}
fn free_vertex_buffer(&mut self, VertexBufferId(vertex_id): VertexBufferId) {
self.vertex_buffers[vertex_id] = None;
}
fn free_index_buffer(&mut self, IndexBufferId(id): IndexBufferId) {
self.index_buffers[id] = None;
}
fn blend_mode(&mut self, blend_mode: BlendMode, state: &mut RenderState) {
state.pipeline_config.blend_mode = blend_mode;
state.pipeline_state = self.get_pipeline_state(&state.pipeline_config);
state.command_encoder.set_render_pipeline_state(&state.pipeline_state);
}
fn create_render_target(&mut self, RenderTargetId(render_id): RenderTargetId, TextureId(texture_id): TextureId, width: usize, height: usize, render_target_type: RenderTargetType) {
if render_id >= self.render_targets.len() {
self.render_targets.extend((self.render_targets.len()..(render_id+1))
.into_iter()
.map(|_| None));
}
if texture_id >= self.textures.len() {
self.textures.extend((self.textures.len()..(texture_id+1))
.into_iter()
.map(|_| None));
}
self.render_targets[render_id] = None;
self.textures[texture_id] = None;
let new_render_target = RenderTarget::new(&self.device, width, height, render_target_type);
self.textures[texture_id] = Some(new_render_target.render_texture().clone());
self.render_targets[render_id] = Some(new_render_target);
}
fn free_render_target(&mut self, RenderTargetId(render_id): RenderTargetId) {
self.render_targets[render_id] = None;
}
fn select_render_target(&mut self, RenderTargetId(render_id): RenderTargetId, state: &mut RenderState) {
let render_target = match &self.render_targets[render_id] { Some(texture) => texture, None => { return } };
state.target_texture = render_target.render_texture().clone();
state.command_encoder.end_encoding();
state.command_encoder = self.get_command_encoder(state.command_buffer, &state.target_texture);
state.pipeline_config.update_for_texture(&state.target_texture);
state.pipeline_state = self.get_pipeline_state(&state.pipeline_config);
state.command_encoder.set_render_pipeline_state(&state.pipeline_state);
self.setup_command_encoder(state);
}
fn select_main_frame_buffer(&mut self, state: &mut RenderState) {
state.target_texture = state.main_texture.clone();
state.command_encoder.end_encoding();
state.command_encoder = self.get_command_encoder(state.command_buffer, &state.target_texture);
state.pipeline_config.update_for_texture(&state.target_texture);
state.pipeline_state = self.get_pipeline_state(&state.pipeline_config);
state.command_encoder.set_render_pipeline_state(&state.pipeline_state);
self.setup_command_encoder(state);
}
fn draw_frame_buffer(&mut self, RenderTargetId(source_buffer): RenderTargetId, x: i32, y: i32, state: &mut RenderState) {
let render_targets = &self.render_targets;
if let Some(source_buffer) = &render_targets[source_buffer] {
let source_texture = source_buffer.render_texture().clone();
let source_width = source_texture.width() as f32;
let source_height = source_texture.height() as f32;
let mut config = PipelineConfiguration::for_texture(&state.target_texture);
config.vertex_shader = String::from("simple_vertex");
config.blend_mode = BlendMode::SourceOver;
config.fragment_shader = if source_buffer.is_multisampled() { String::from("texture_multisample_fragment") } else { String::from("texture_fragment") };
let pipeline_state = self.get_pipeline_state(&config);
state.command_encoder.set_render_pipeline_state(&pipeline_state);
let target_width = state.target_texture.width() as f32;
let target_height = state.target_texture.height() as f32;
let scale_transform = flo_canvas::Transform2D::scale(2.0/target_width, 2.0/target_height);
let viewport_transform = scale_transform * flo_canvas::Transform2D::translate(-(target_width/2.0), -(target_height/2.0));
let viewport_matrix = transform_to_matrix(&viewport_transform);
let viewport_matrix = MatrixBuffer::from_matrix(&self.device, viewport_matrix);
let triangle_strip = vec![
Vertex2D { pos: [ x as f32, y as f32 ], tex_coord: [ 0.0, source_height ], color: [0,0,0,0] },
Vertex2D { pos: [ x as f32, y as f32 + source_height ], tex_coord: [ 0.0, 0.0], color: [0,0,0,0] },
Vertex2D { pos: [ x as f32 + source_width, y as f32 ], tex_coord: [ source_width, source_height ], color: [0,0,0,0] },
Vertex2D { pos: [ x as f32 + source_width, y as f32 + source_height ], tex_coord: [ source_width, 0.0 ], color: [0,0,0,0] },
];
let triangle_strip = Buffer::from_vertices(&self.device, triangle_strip);
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexMatrix as u64, Some(&viewport_matrix), 0);
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexVertices as u64, Some(&triangle_strip), 0);
state.command_encoder.set_fragment_texture(FragmentInputIndex_FragmentIndexTexture as u64, Some(&source_texture));
state.command_encoder.draw_primitives(metal::MTLPrimitiveType::TriangleStrip, 0, 4);
state.command_encoder.set_fragment_texture(FragmentInputIndex_FragmentIndexTexture as u64, None);
state.command_encoder.set_render_pipeline_state(&state.pipeline_state);
self.setup_command_encoder(state);
}
}
#[inline] fn store_texture(&mut self, texture_id: usize, texture: metal::Texture) {
while self.textures.len() <= texture_id {
self.textures.push(None);
}
self.textures[texture_id] = Some(texture);
}
fn create_bgra_texture(&mut self, TextureId(texture_id): TextureId, width: usize, height: usize) {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_texture_type(metal::MTLTextureType::D2);
texture_descriptor.set_width(width as u64);
texture_descriptor.set_height(height as u64);
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
texture_descriptor.set_usage(metal::MTLTextureUsage::ShaderRead);
texture_descriptor.set_mipmap_level_count_for_size(metal::MTLSize { width: width as _, height: height as _, depth: 1 });
let texture = self.device.new_texture(&texture_descriptor);
self.store_texture(texture_id, texture);
}
fn create_mono_texture(&mut self, TextureId(texture_id): TextureId, width: usize, height: usize) {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_texture_type(metal::MTLTextureType::D2);
texture_descriptor.set_width(width as u64);
texture_descriptor.set_height(height as u64);
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::R8Unorm);
texture_descriptor.set_usage(metal::MTLTextureUsage::ShaderRead);
texture_descriptor.set_mipmap_level_count_for_size(metal::MTLSize { width: width as _, height: height as _, depth: 1 });
let texture = self.device.new_texture(&texture_descriptor);
self.store_texture(texture_id, texture);
}
fn create_bgra_1d_texture(&mut self, TextureId(texture_id): TextureId, width: usize) {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_texture_type(metal::MTLTextureType::D1);
texture_descriptor.set_width(width as u64);
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
texture_descriptor.set_usage(metal::MTLTextureUsage::ShaderRead);
let texture = self.device.new_texture(&texture_descriptor);
self.store_texture(texture_id, texture);
}
fn create_mono_1d_texture(&mut self, TextureId(texture_id): TextureId, width: usize) {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_texture_type(metal::MTLTextureType::D1);
texture_descriptor.set_width(width as u64);
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::R8Unorm);
texture_descriptor.set_usage(metal::MTLTextureUsage::ShaderRead);
let texture = self.device.new_texture(&texture_descriptor);
self.store_texture(texture_id, texture);
}
fn write_texture_data_2d(&mut self, TextureId(texture_id): TextureId, x1: usize, y1: usize, x2: usize, y2: usize, data: Arc<Vec<u8>>) {
if x2<x1 { return; }
if y2<y1 { return; }
let texture = if texture_id < self.textures.len() { self.textures[texture_id].as_ref() } else { None };
let texture = if let Some(texture) = texture { texture } else { return; };
let region = metal::MTLRegion {
origin: metal::MTLOrigin { x: x1 as _, y: y1 as _, z: 0 },
size: metal::MTLSize { width: (x2-x1) as _, height: (y2-y1) as _, depth: 1 }
};
let bytes_per_pixel = match texture.pixel_format() {
metal::MTLPixelFormat::R8Unorm => 1,
metal::MTLPixelFormat::BGRA8Unorm => 4,
_ => todo!("Unsupported texture pixel format")
};
let expected_size = (x2-x1)*(y2-y1)*bytes_per_pixel;
if data.len() < expected_size {
return;
}
texture.replace_region(region, 0, data.as_ptr() as _, (bytes_per_pixel * (x2-x1)) as _ );
}
fn write_texture_data_1d(&mut self, TextureId(texture_id): TextureId, x1: usize, x2: usize, data: Arc<Vec<u8>>) {
if x2<x1 { return; }
let texture = if texture_id < self.textures.len() { self.textures[texture_id].as_ref() } else { None };
let texture = if let Some(texture) = texture { texture } else { return; };
let region = metal::MTLRegion {
origin: metal::MTLOrigin { x: x1 as _, y: 0, z: 0 },
size: metal::MTLSize { width: (x2-x1) as _, height: 1, depth: 1 }
};
let bytes_per_pixel = match texture.pixel_format() {
metal::MTLPixelFormat::R8Unorm => 1,
metal::MTLPixelFormat::BGRA8Unorm => 4,
_ => todo!("Unsupported texture pixel format")
};
let expected_size = (x2-x1)*bytes_per_pixel;
if data.len() < expected_size {
return;
}
texture.replace_region(region, 0, data.as_ptr() as _, (bytes_per_pixel * (x2-x1)) as _ );
}
fn create_mipmaps(&mut self, TextureId(texture_id): TextureId, state: &mut RenderState) {
let texture = if texture_id < self.textures.len() { self.textures[texture_id].as_ref() } else { None };
let texture = if let Some(texture) = texture { texture } else { return; };
if texture.mipmap_level_count() <= 1 { return; }
state.command_encoder.end_encoding();
let blit_encoder = self.get_blit_command_encoder(state.command_buffer);
blit_encoder.generate_mipmaps(texture);
blit_encoder.end_encoding();
state.command_encoder = self.get_command_encoder(state.command_buffer, &state.target_texture);
self.setup_command_encoder(state);
}
fn copy_texture(&mut self, TextureId(src_texture_id): TextureId, TextureId(tgt_texture_id): TextureId, state: &mut RenderState) {
if src_texture_id == tgt_texture_id { return; }
while self.textures.len() <= tgt_texture_id {
self.textures.push(None);
}
self.textures[tgt_texture_id] = None;
let src_texture = if src_texture_id < self.textures.len() { self.textures[src_texture_id].as_ref() } else { None };
let src_texture = if let Some(src_texture) = src_texture { src_texture } else { return; };
let texture_descriptor = metal::TextureDescriptor::new();
let texture_type = src_texture.texture_type();
let width = src_texture.width();
let height = src_texture.height();
texture_descriptor.set_texture_type(texture_type);
texture_descriptor.set_width(width);
texture_descriptor.set_pixel_format(src_texture.pixel_format());
texture_descriptor.set_usage(metal::MTLTextureUsage::ShaderRead);
if texture_type == metal::MTLTextureType::D2 {
texture_descriptor.set_height(height);
texture_descriptor.set_mipmap_level_count_for_size(metal::MTLSize { width: width as _, height: height as _, depth: 1 });
}
let tgt_texture = self.device.new_texture(&texture_descriptor);
state.command_encoder.end_encoding();
let blit_encoder = self.get_blit_command_encoder(state.command_buffer);
blit_encoder.copy_from_texture(&src_texture, 0, 0, metal::MTLOrigin { x: 0, y: 0, z: 0 }, metal::MTLSize { width, height, depth: 1 },
&tgt_texture, 0, 0, metal::MTLOrigin { x: 0, y: 0, z: 0 });
blit_encoder.end_encoding();
state.command_encoder = self.get_command_encoder(state.command_buffer, &state.target_texture);
self.setup_command_encoder(state);
self.store_texture(tgt_texture_id, tgt_texture);
}
fn free_texture(&mut self, TextureId(texture_id): TextureId) {
if texture_id < self.textures.len() {
self.textures[texture_id] = None;
}
}
fn clear(&mut self, color: Rgba8, state: &mut RenderState) {
state.command_encoder.end_encoding();
state.command_encoder = self.get_command_encoder_with_clear(state.command_buffer, &state.target_texture, color);
self.setup_command_encoder(state);
}
fn use_shader(&mut self, shader_type: ShaderType, state: &mut RenderState) {
state.pipeline_config.vertex_shader = String::from("simple_vertex");
state.fill_texture = None;
state.erase_texture = None;
state.clip_texture = None;
state.texture_transform = None;
match shader_type {
ShaderType::DashedLine { dash_texture: _, erase_texture: _, clip_texture: _ } => {
todo!()
}
ShaderType::Simple { erase_texture: None, clip_texture: None } => {
state.pipeline_config.fragment_shader = String::from("simple_fragment")
}
ShaderType::Simple { erase_texture: Some(TextureId(erase_texture)), clip_texture: None } => {
state.pipeline_config.fragment_shader = String::from("simple_eraser_multisample_fragment");
state.erase_texture = self.textures[erase_texture].clone();
}
ShaderType::Simple { erase_texture: None, clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.fragment_shader = String::from("simple_clip_mask_multisample_fragment");
state.clip_texture = self.textures[clip_texture].clone();
}
ShaderType::Simple { erase_texture: Some(TextureId(erase_texture)), clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.fragment_shader = String::from("simple_eraser_clip_mask_multisample_fragment");
state.erase_texture = self.textures[erase_texture].clone();
state.clip_texture = self.textures[clip_texture].clone();
}
ShaderType::Texture { texture: TextureId(fill_texture), texture_transform, repeat, alpha, erase_texture: None, clip_texture: None } => {
state.pipeline_config.vertex_shader = String::from("texture_vertex");
state.pipeline_config.fragment_shader = String::from("texture_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[fill_texture].clone();
}
ShaderType::Texture { texture: TextureId(fill_texture), texture_transform, repeat, alpha, erase_texture: Some(TextureId(erase_texture)), clip_texture: None } => {
state.pipeline_config.vertex_shader = String::from("texture_vertex");
state.pipeline_config.fragment_shader = String::from("texture_eraser_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[fill_texture].clone();
state.erase_texture = self.textures[erase_texture].clone();
}
ShaderType::Texture { texture: TextureId(fill_texture), texture_transform, repeat, alpha, erase_texture: None, clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.vertex_shader = String::from("texture_vertex");
state.pipeline_config.fragment_shader = String::from("texture_clip_mask_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[fill_texture].clone();
state.clip_texture = self.textures[clip_texture].clone();
}
ShaderType::Texture { texture: TextureId(fill_texture), texture_transform, repeat, alpha, erase_texture: Some(TextureId(erase_texture)), clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.vertex_shader = String::from("texture_vertex");
state.pipeline_config.fragment_shader = String::from("texture_eraser_clip_mask_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[fill_texture].clone();
state.erase_texture = self.textures[erase_texture].clone();
state.clip_texture = self.textures[clip_texture].clone();
}
ShaderType::LinearGradient { texture: TextureId(gradient_texture), texture_transform, repeat, alpha, erase_texture: None, clip_texture: None } => {
state.pipeline_config.vertex_shader = String::from("gradient_vertex");
state.pipeline_config.fragment_shader = String::from("gradient_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[gradient_texture].clone();
}
ShaderType::LinearGradient { texture: TextureId(gradient_texture), texture_transform, repeat, alpha, erase_texture: Some(TextureId(erase_texture)), clip_texture: None } => {
state.pipeline_config.vertex_shader = String::from("gradient_vertex");
state.pipeline_config.fragment_shader = String::from("gradient_eraser_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[gradient_texture].clone();
state.erase_texture = self.textures[erase_texture].clone();
}
ShaderType::LinearGradient { texture: TextureId(gradient_texture), texture_transform, repeat, alpha, erase_texture: None, clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.vertex_shader = String::from("gradient_vertex");
state.pipeline_config.fragment_shader = String::from("gradient_clip_mask_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[gradient_texture].clone();
state.clip_texture = self.textures[clip_texture].clone();
}
ShaderType::LinearGradient { texture: TextureId(gradient_texture), texture_transform, repeat, alpha, erase_texture: Some(TextureId(erase_texture)), clip_texture: Some(TextureId(clip_texture)) } => {
state.pipeline_config.vertex_shader = String::from("gradient_vertex");
state.pipeline_config.fragment_shader = String::from("gradient_eraser_clip_mask_multisample_fragment");
state.texture_transform = Some(MatrixBuffer::from_matrix(&self.device, texture_transform));
state.texture_alpha = Some(alpha as _);
state.fill_texture = self.textures[gradient_texture].clone();
state.erase_texture = self.textures[erase_texture].clone();
state.clip_texture = self.textures[clip_texture].clone();
}
}
state.pipeline_state = self.get_pipeline_state(&state.pipeline_config);
self.setup_command_encoder(state);
}
fn draw_triangles(&mut self, VertexBufferId(vertex_buffer_id): VertexBufferId, range: Range<usize>, state: &mut RenderState) {
let buffer = match &self.vertex_buffers[vertex_buffer_id] { Some(buffer) => buffer, None => { return } };
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexVertices as u64, Some(buffer), 0);
state.command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, range.start as u64, range.len() as u64);
}
fn draw_indexed_triangles(&mut self, VertexBufferId(vertex_buffer_id): VertexBufferId, IndexBufferId(index_buffer_id): IndexBufferId, num_vertices: usize, state: &mut RenderState) {
let vertex_buffer = match &self.vertex_buffers[vertex_buffer_id] { Some(buffer) => buffer, None => { return } };
let index_buffer = match &self.index_buffers[index_buffer_id] { Some(buffer) => buffer, None => { return } };
state.command_encoder.set_vertex_buffer(VertexInputIndex_VertexInputIndexVertices as u64, Some(vertex_buffer), 0);
state.command_encoder.draw_indexed_primitives(metal::MTLPrimitiveType::Triangle, num_vertices as u64, metal::MTLIndexType::UInt16, index_buffer, 0);
}
}
fn transform_to_matrix(transform: &flo_canvas::Transform2D) -> Matrix {
let flo_canvas::Transform2D(t) = transform;
Matrix([
[t[0][0], t[0][1], 0.0, t[0][2]],
[t[1][0], t[1][1], 0.0, t[1][2]],
[t[2][0], t[2][1], 1.0, t[2][2]],
[0.0, 0.0, 0.0, 1.0]
])
}