Merge branch '2-show-triangle-geometry' into 'dev'

Resolve "Show triangle geometry"

See merge request Steins7/iv!2
This commit is contained in:
Steins7 2021-01-30 12:05:48 +01:00
commit 13ababefc8
10 changed files with 611 additions and 24 deletions

30
Cargo.lock generated
View File

@ -122,6 +122,15 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "cmake"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "cocoa" name = "cocoa"
version = "0.20.2" version = "0.20.2"
@ -362,6 +371,7 @@ dependencies = [
"log", "log",
"num-traits", "num-traits",
"raw-window-handle", "raw-window-handle",
"shaderc",
"winit", "winit",
] ]
@ -755,6 +765,26 @@ version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
[[package]]
name = "shaderc"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03f0cb8d1f8667fc9c50d5054be830a117af5f9a15f87c66b72bbca0c2fca484"
dependencies = [
"libc",
"shaderc-sys",
]
[[package]]
name = "shaderc-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89175f80244b82f882033a81bd188f87307c4c39b2fe8d0f194314f270bdea9"
dependencies = [
"cmake",
"libc",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"

View File

@ -14,6 +14,7 @@ gfx-hal = "^0.6.0"
winit = "^0.22.0" winit = "^0.22.0"
raw-window-handle = "^0.3.3" raw-window-handle = "^0.3.3"
num-traits = "^0.2.12" num-traits = "^0.2.12"
shaderc = "^0.7"
[dependencies.gfx-backend-vulkan] [dependencies.gfx-backend-vulkan]
version = "^0.6.1" version = "^0.6.1"

File diff suppressed because one or more lines are too long

View File

@ -14,7 +14,7 @@ where
O: Output<W>, O: Output<W>,
{ {
pipelines: Vec<SubenginePipeline<'a, I, W, O>>, pipelines: Vec<SubenginePipeline<'a, I, W, O>>,
color: [f32; 4], mouse_pos: [f32; 2],
} }
impl<I, W, O> Controller<'_, I, W, O> impl<I, W, O> Controller<'_, I, W, O>
@ -38,7 +38,7 @@ where
Controller { Controller {
pipelines: pipelines_vec, pipelines: pipelines_vec,
color: [0.0, 1.0, 1.0, 0.0], mouse_pos: [0.5, 0.5],
} }
} }
@ -48,6 +48,7 @@ where
use crate::{ use crate::{
subengine::subengine_controller::SubengineCommand, subengine::subengine_controller::SubengineCommand,
io::Key, io::Key,
utils::Triangle,
}; };
let mut input_keys: Vec<Key> = Vec::new(); let mut input_keys: Vec<Key> = Vec::new();
@ -61,11 +62,10 @@ where
} }
for input_key in &input_keys { for input_key in &input_keys {
match input_key { match input_key {
Key::MouseMove{x,y} => self.color = [ Key::MouseMove{x,y} => self.mouse_pos = [
(x/1280.0) as f32, (x/1280.0 * 2.0 - 1.0) as f32,
(y/720.0) as f32, (y/720.0 * 2.0 - 1.0) as f32,
((x/1280.0 + y/720.0)/2.0) as f32, ],
1.0],
Key::Close => return, Key::Close => return,
_ => (), _ => (),
}; };
@ -78,13 +78,24 @@ where
subengine.wait_for_exec(Duration::from_millis(1)).unwrap(); subengine.wait_for_exec(Duration::from_millis(1)).unwrap();
} }
} }
let triangle = Triangle {
points: [self.mouse_pos, [-0.5, 0.5], [-0.5, -0.5]],
};
debug!("Triangle : {:#?}", triangle);
for (renderer, output) in &mut pipeline.renderers { for (renderer, output) in &mut pipeline.renderers {
match renderer.draw_clear_frame(output, self.color) { // match renderer.draw_clear_frame(output, self.color) {
// Err(err) => warn!("{}", err),
// _ => (),
// }
match renderer.draw_triangle_frame(output, triangle) {
Err(err) => warn!("{}", err), Err(err) => warn!("{}", err),
_ => (), _ => (),
} };
} };
} };
} }
} }
//These tests are disabled because of some stange issue with cargo not waiting for the drop //These tests are disabled because of some stange issue with cargo not waiting for the drop

View File

@ -9,13 +9,18 @@ use std::{
use crate::{ use crate::{
io::Output, io::Output,
utils::Triangle,
}; };
mod gpu; mod gpu;
use gpu::Gpu; use self::gpu::Gpu;
mod swap_system; mod swap_system;
use swap_system::SwapSystem; use self::swap_system::SwapSystem;
mod pipeline;
use self::pipeline::Pipeline;
//--Renderer implementation------------------------------------------------------------------------- //--Renderer implementation-------------------------------------------------------------------------
#[derive(Debug)] #[derive(Debug)]
@ -23,6 +28,7 @@ pub struct Renderer<B: gfx_hal::Backend> {
instance: ManuallyDrop<B::Instance>, instance: ManuallyDrop<B::Instance>,
gpu: ManuallyDrop<Gpu<B>>, gpu: ManuallyDrop<Gpu<B>>,
swap_systems: Vec<SwapSystem<B>>, swap_systems: Vec<SwapSystem<B>>,
pipelines: Vec<Pipeline<B>>,
} }
impl<B> Drop for Renderer<B> impl<B> Drop for Renderer<B>
@ -35,16 +41,17 @@ where
device::Device, device::Device,
}; };
debug!("Dropping Pipelines...");
for pipeline in self.pipelines.drain(..) {
pipeline.drop(&mut self.gpu);
}
debug!("Waiting for device to idle..."); debug!("Waiting for device to idle...");
let _ = self.gpu let _ = self.gpu
.device() .device()
.wait_idle(); .wait_idle();
info!("Dropping Renderer..."); info!("Dropping Renderer...");
// for mut pipeline in self.pipelines.drain(..) {
// }
//
unsafe { unsafe {
for mut swap_system in self.swap_systems.drain(..) { for mut swap_system in self.swap_systems.drain(..) {
self.instance.destroy_surface(swap_system.drop(&mut self.gpu)); self.instance.destroy_surface(swap_system.drop(&mut self.gpu));
@ -79,12 +86,15 @@ where
.map(|surface| SwapSystem::new(&mut gpu, surface)) .map(|surface| SwapSystem::new(&mut gpu, surface))
.collect::<Result<Vec<_>, &str>>()? .collect::<Result<Vec<_>, &str>>()?
}; };
let pipelines = vec!(Pipeline::new(&mut gpu, &swap_systems[0]) //TODO improve that
.map_err(|err| err)?);
debug!("Renderer created !"); debug!("Renderer created !");
Ok( Renderer { Ok( Renderer {
instance: ManuallyDrop::new(instance), instance: ManuallyDrop::new(instance),
gpu: ManuallyDrop::new(gpu), gpu: ManuallyDrop::new(gpu),
swap_systems, swap_systems,
pipelines,
}) })
} }
@ -171,5 +181,103 @@ where
Ok(()) Ok(())
} }
pub fn draw_triangle_frame<W, O>(&mut self, output: &RefCell<O>, triangle: Triangle)
-> Result<(), &'static str>
where
W: raw_window_handle::HasRawWindowHandle,
O: Output<W>,
{
use gfx_hal::{
window::AcquireError,
device::Device,
queue::Submission,
};
let swap_system = &mut self.swap_systems[output.borrow_mut().get_id()];
let mut frame = match swap_system.acquire_frame(&self.gpu) {
Ok(frame) => frame,
Err(err) => match err {
AcquireError::NotReady => {
return Err("Frame acquisition failed because all Frames are in use");
},
AcquireError::OutOfDate => {
swap_system.recreate(&mut self.gpu)?;
debug!("SwapSystem : {:#?}", swap_system);
return Ok(());
},
_ => Err("Could not acquire Frame from SwapSystem")?,
}};
trace!("Waiting for Frame...");
unsafe {
let _ = self.gpu.device()
.wait_for_fence(&frame.fences[0], !0)
.map_err(|_| "Failed to wait for Fence")?;
let _ = self.gpu.device()
.reset_fence(&frame.fences[0])
.map_err(|_| "Failed to reset fence")?;
}
trace!("Uploading triangle data...");
let points = triangle.points_flat();
self.pipelines[0].write_vertex_buffer(&self.gpu, 0, (&points).to_vec())?; //TODO meh
trace!("Recording CommandBuffer...");
unsafe {
use gfx_hal::command::{
CommandBufferFlags,
SubpassContents,
ClearValue,
ClearColor,
CommandBuffer,
};
frame.command_buffer.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
const TRIANGLE_CLEAR: ClearValue =
ClearValue {color : ClearColor{float32 : [0.1, 0.2, 0.3, 1.0]}};
frame.command_buffer.begin_render_pass(
&swap_system.render_pass,
&frame.framebuffer.as_ref().unwrap(),
swap_system.render_area,
iter::once(TRIANGLE_CLEAR),
SubpassContents::Inline,
);
frame.command_buffer.bind_graphics_pipeline(self.pipelines[0].raw_pipeline());
// storing const data via the CommandBuffer
//let buffer_ref: &B::Buffer = &self.buffer;
//let buffers: Vec<[_; 1]> = vec![(buffer_ref, 0)].into();
frame.command_buffer.bind_vertex_buffers(0, self.pipelines[0].raw_vertex_buffers());
frame.command_buffer.draw(0..3, 0..1);
frame.command_buffer.end_render_pass();
frame.command_buffer.finish();
}
trace!("Submiting to queue...");
let submission = Submission {
command_buffers: iter::once(&*frame.command_buffer),
wait_semaphores: None,
signal_semaphores: iter::once(&frame.signal_semaphores[0]),
};
unsafe {
use gfx_hal::queue::CommandQueue;
self.gpu.queue_mut().submit(submission, Some(&frame.fences[0]));
}
let result = swap_system.present_frame(frame, &mut self.gpu);
if result.is_err() {
swap_system.recreate(&mut self.gpu).unwrap();
}
Ok(())
}
} }

View File

@ -1,20 +1,309 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::{ use std::mem::{ManuallyDrop, size_of};
mem::ManuallyDrop,
use gfx_hal::{
buffer::SubRange,
}; };
use super::{
gpu::Gpu,
swap_system::SwapSystem,
};
mod attachement;
use self::attachement::Attachement;
const VERTEX_SOURCE: &str =
"#version 440 core
layout (location = 0) in vec2 position;
out gl_PerVertex {
vec4 gl_Position;
};
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
}";
const FRAGMENT_SOURCE: &str =
"#version 440 core
layout (location = 0) out vec4 frag_color;
void main()
{
frag_color = vec4(0.5, 0.5, 0.5, 0.5);
}";
//--Pipeline implementation------------------------------------------------------------------------- //--Pipeline implementation-------------------------------------------------------------------------
#[derive(Debug)] #[derive(Debug)]
pub struct Pipeline<B: gfx_hal::Backend> { pub struct Pipeline<B: gfx_hal::Backend> {
render_pass: ManuallyDrop<B::RenderPass>, set_layout: Vec<B::DescriptorSetLayout>,
layout: ManuallyDrop<B::PipelineLayout>,
gfx_pipeline: ManuallyDrop<B::GraphicsPipeline>,
vertex_buffers: Vec<Attachement<B>>,
} }
impl<B> Pipeline<B> impl<B> Pipeline<B>
where where
B: gfx_hal::Backend, B: gfx_hal::Backend,
{ {
pub fn drop(mut self, gpu: &mut Gpu<B>) {
use std::ptr::read;
use gfx_hal::device::Device;
debug!("Dropping Pipeline...");
for buffer in self.vertex_buffers.drain(..) {
buffer.drop(gpu);
}
unsafe {
gpu.device()
.destroy_graphics_pipeline(
ManuallyDrop::into_inner(read(&mut self.gfx_pipeline)));
gpu.device()
.destroy_pipeline_layout(
ManuallyDrop::into_inner(read(&mut self.layout)));
for layout in self.set_layout.drain(..) {
gpu.device().destroy_descriptor_set_layout(layout);
}}
}
pub fn new(gpu: &mut Gpu<B>, swap_system: &SwapSystem<B>) -> Result<Pipeline<B>, &'static str> {
use gfx_hal::{
device::Device,
pso::{EntryPoint, Specialization, VertexBufferDesc, VertexInputRate, AttributeDesc,
Element, InputAssemblerDesc, Primitive, PrimitiveAssemblerDesc, Rasterizer,
PolygonMode, Face, FrontFace, State, DepthStencilDesc, BakedStates, Viewport,
DescriptorSetLayoutBinding, ShaderStageFlags
},
format::Format,
};
use std::borrow::Cow;
debug!("Compiling shaders...");
let mut compiler = shaderc::Compiler::new().ok_or("shaderc not found")?;
let vertex_compile_artifact = compiler
.compile_into_spirv(VERTEX_SOURCE, shaderc::ShaderKind::Vertex, "vertex.vert",
"main",
None)
.map_err(|err| {error!("{}", err);
"Could not compile vertex shader"})?;
let fragment_compile_artifact = compiler
.compile_into_spirv(FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragement.frag",
"main",
None)
.map_err(|err| {error!("{}", err);
"Could not compile fragment shader"})?;
trace!("Creating ShaderModules...");
let vertex_shader_module = unsafe {
gpu.device()
.create_shader_module(vertex_compile_artifact.as_binary())
.map_err(|err| {error!("{}", err);
"Could not create vertex shader module"})?
};
let fragment_shader_module = unsafe {
gpu.device()
.create_shader_module(fragment_compile_artifact.as_binary())
.map_err(|err| {error!("{}", err);
"Could not create fragment shader module"})?
};
trace!("Creating shader set...");
let (vs_entry, fs_entry) = (
EntryPoint {
entry: "main",
module: &vertex_shader_module,
specialization: Specialization {
constants: Cow::Borrowed{0: &[]},
data: Cow::Borrowed{0: &[]},
},
},
EntryPoint {
entry: "main",
module: &fragment_shader_module,
specialization: Specialization {
constants: Cow::Borrowed{0: &[]},
data: Cow::Borrowed{0: &[]},
},
},
);
trace!("Creating PrimitiveAssembler...");
let buffers: Vec<VertexBufferDesc> =
vec![VertexBufferDesc {
binding: 0,
stride: (size_of::<f32>()*2) as u32,
rate: VertexInputRate::Vertex,
}];
let attributes: Vec<AttributeDesc> =
vec![AttributeDesc {
location: 0,
binding: 0,
element: Element {
format: Format::Rgb32Sfloat,
offset: 0,
},
}];
let input_assembler = InputAssemblerDesc {
primitive: Primitive::TriangleList, //TODO switch to strips
with_adjacency: false,
restart_index: None,
};
let primitive_assembler = PrimitiveAssemblerDesc::Vertex {
buffers: &buffers,
attributes: &attributes,
input_assembler,
vertex: vs_entry,
tessellation: None,
geometry: None,
};
trace!("Creating Rasterizer...");
let rasterizer = Rasterizer {
polygon_mode: PolygonMode::Fill,
cull_face: Face::NONE, //TODO adjut that
front_face: FrontFace::CounterClockwise,
depth_clamping: false,
depth_bias: None,
conservative: false,
line_width: State::Static{0: 1.0}, //TODO may need to be changed
};
trace!("Configuring color blending...");
let blender = {
use gfx_hal::pso::{BlendState, BlendOp, Factor, BlendDesc, LogicOp, ColorBlendDesc,
ColorMask};
let blend_state = BlendState {
color: BlendOp::Add {
src: Factor::One,
dst: Factor::Zero,
},
alpha: BlendOp::Add {
src: Factor::One,
dst: Factor::Zero,
}};
BlendDesc {
logic_op: Some(LogicOp::Copy),
targets: vec![
ColorBlendDesc {
mask: ColorMask::ALL,
blend: Some(blend_state),
}]}
};
trace!("Configuring depth options...");
let depth_stencil = DepthStencilDesc {
depth: None,
depth_bounds: false,
stencil: None,
};
trace!("Configuring baked-in pipeline states...");
let baked_states = BakedStates {
viewport: Some(Viewport {
rect: swap_system.render_area.clone(),
depth: (0.0..1.0),
}),
scissor: Some(swap_system.render_area.clone()),
blend_color: None,
depth_bounds: None,
};
trace!("Creating PipelineLayout...");
let set_layout = {
let bindings = Vec::<DescriptorSetLayoutBinding>::new();
let immutable_samplers = Vec::<B::Sampler>::new();
unsafe {
vec![gpu.device()
.create_descriptor_set_layout(bindings, immutable_samplers)
.map_err(|_| "Could not create DescriptorSetLayout")?
]}};
let layout = {
let push_constants = Vec::<(ShaderStageFlags, std::ops::Range<u32>)>::new();
unsafe {
gpu.device()
.create_pipeline_layout(&set_layout, push_constants)
.map_err(|_| "Could not create PipelineLayout")?
}};
debug!("Creating GraphicsPipeline...");
let gfx_pipeline = {
use gfx_hal::{
pso::{GraphicsPipelineDesc, PipelineCreationFlags, BasePipeline},
pass::Subpass,
};
//manual deref for ManuallyDrop to not cause troubles
let render_pass_ref: &B::RenderPass = &swap_system.render_pass;
let pipeline_desc = GraphicsPipelineDesc {
primitive_assembler,
rasterizer,
fragment: Some(fs_entry),
blender,
depth_stencil,
multisampling: None,
baked_states,
layout: &layout,
subpass: Subpass {
index: 0,
main_pass: render_pass_ref,
},
flags: PipelineCreationFlags::empty(),
parent: BasePipeline::None,
};
unsafe {
gpu.device()
.create_graphics_pipeline(&pipeline_desc, None)
.map_err(|_| "Could not create GraphicsPipeline")?
}};
trace!("Destroying no-longer-needed shader modules...");
unsafe {
gpu.device()
.destroy_shader_module(vertex_shader_module);
gpu.device()
.destroy_shader_module(fragment_shader_module);
};
let vertex_buffers = vec![Attachement::new(gpu)?];
Ok( Pipeline {
set_layout,
layout: ManuallyDrop::new(layout),
gfx_pipeline: ManuallyDrop::new(gfx_pipeline),
vertex_buffers,
})
}
pub fn raw_pipeline(&self) -> &B::GraphicsPipeline {
&self.gfx_pipeline
}
pub fn raw_vertex_buffers(&self) -> Vec<(&B::Buffer, SubRange)> {
self.vertex_buffers
.iter()
//TODO move SubRange to Attachement ?
.map(|buffer| (buffer.get_buffer(), SubRange {offset: 0, size: None}))
.collect()
}
pub fn write_vertex_buffer(&mut self, gpu: &Gpu<B>, index: usize, data: Vec<f32>)
-> Result<(), &'static str>
{
self.vertex_buffers[index].write_buffer(gpu, data)
}
} }

View File

@ -0,0 +1,135 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use std::mem::ManuallyDrop;
use crate::renderer::gpu::Gpu;
//--Attachement implementation----------------------------------------------------------------------
#[derive(Debug)]
pub struct Attachement<B: gfx_hal::Backend> {
buffer: ManuallyDrop<B::Buffer>,
memory: ManuallyDrop<B::Memory>,
size: u64,
}
impl<B> Attachement<B>
where
B: gfx_hal::Backend,
{
pub fn drop(mut self, gpu: &mut Gpu<B>) {
use std::ptr::read;
use gfx_hal::device::Device;
debug!("Dropping Attachement...");
unsafe {
gpu.device()
.free_memory(ManuallyDrop::into_inner(read(&mut self.memory)));
gpu.device()
.destroy_buffer(ManuallyDrop::into_inner(read(&mut self.buffer)));
}
}
pub fn new(gpu: &mut Gpu<B>) -> Result<Attachement<B>, &'static str> {
use std::mem::size_of;
use gfx_hal::{
device::Device,
adapter::PhysicalDevice,
buffer::Usage,
};
debug!("Creating attachement...");
let mut buffer = unsafe {
gpu.device()
.create_buffer((size_of::<f32>()*2*3) as u64, Usage::VERTEX)
.map_err(|_| "Could not create buffer")?
};
trace!("Creating underlying attachement memory...");
let (memory, size) = {
use gfx_hal::{
memory::Properties,
MemoryTypeId,
};
let requirements = unsafe {
gpu.device().get_buffer_requirements(&buffer)
};
let memory_type = gpu.adapter()
.physical_device
.memory_properties()
.memory_types
.iter()
.enumerate()
.find(|&(id, memory_type)| {
requirements.type_mask & (1 << id) != 0 &&
memory_type.properties.contains(Properties::CPU_VISIBLE)})
.map(|(id, _)| MemoryTypeId(id))
.ok_or("Could not find a suitable memory type to allocate attachement memory")?;
(
unsafe {
gpu.device()
.allocate_memory(memory_type, requirements.size)
.map_err(|_| "Could not allocate buffer memory...")?
},
requirements.size,
)};
trace!("Binding memory to buffer...");
unsafe {
gpu.device()
.bind_buffer_memory(&memory, 0, &mut buffer)
.map_err(|__| "Could not bind memory to buffer")?;
}
Ok( Attachement {
buffer: ManuallyDrop::new(buffer),
memory: ManuallyDrop::new(memory),
size,
})
}
pub fn get_buffer(&self) -> &B::Buffer {
//manual deref for ManuallyDrop to not cause troubles
&self.buffer
}
pub fn write_buffer(&self, gpu: &Gpu<B>, data: Vec<f32>) -> Result<(), &'static str>
{
use gfx_hal::{
device::Device,
memory::Segment,
};
trace!("writing data to buffer...");
unsafe {
let mapped_memory = gpu.device()
.map_memory(&self.memory, Segment::ALL)
.map_err(|_| "Could not map buffer memory")?;
//debug!("before : {}", std::ptr::read(mapped_memory as *mut f32));
std::ptr::copy_nonoverlapping(data.as_ptr() as *const u8,
mapped_memory, self.size as usize);
//debug!("after : {}", std::ptr::read(mapped_memory as *mut f32));
//manual deref for ManuallyDrop to not cause troubles
let memory_ref: &B::Memory = &self.memory;
gpu.device()
.flush_mapped_memory_ranges(std::iter::once((memory_ref, Segment::ALL)))
.map_err(|_| "Could not flush mapped buffer memory")?;
gpu.device().unmap_memory(&self.memory);
}
Ok(())
}
}

View File

@ -25,7 +25,7 @@ pub struct SwapSystem<B: gfx_hal::Backend> {
format: Format, format: Format,
extent: Extent2D, extent: Extent2D,
pub render_area: GfxRect, //TODO may not be needed (duplicate of extent) pub render_area: GfxRect, //TODO may not be needed (duplicate of extent)
pub render_pass: ManuallyDrop<B::RenderPass>, pub render_pass: ManuallyDrop<B::RenderPass>, //TODO move to Pipeline ?
frames: VecDeque<Frame<B>>, frames: VecDeque<Frame<B>>,
frame_nb: usize, frame_nb: usize,
} }
@ -51,7 +51,7 @@ where
} }
} }
pub fn new(gpu: &mut Gpu<B>, mut surface: B::Surface) pub fn new(gpu: &mut Gpu<B>, mut surface: B::Surface)
-> Result<SwapSystem<B>, &'static str> -> Result<SwapSystem<B>, &'static str>
{ {

View File

@ -25,7 +25,7 @@ where
pub inputs: Vec<&'a RefCell<I>>, pub inputs: Vec<&'a RefCell<I>>,
pub subengines: Vec<Vec<SubengineController>>, pub subengines: Vec<Vec<SubengineController>>,
pub renderers: Vec<(Renderer<vk_back::Backend>, &'a RefCell<O>)>, pub renderers: Vec<(Renderer<vk_back::Backend>, &'a RefCell<O>)>,
pub phantom: PhantomData<W>, //needed because of compiler limitations phantom: PhantomData<W>, //needed because of compiler limitations
} }
impl<'a, I, W, O> SubenginePipeline<'a, I, W, O> impl<'a, I, W, O> SubenginePipeline<'a, I, W, O>

View File

@ -9,3 +9,16 @@ pub struct Rect<I: Num> {
pub h: I, pub h: I,
} }
#[derive(Debug, Copy, Clone)]
pub struct Triangle {
pub points: [[f32; 2]; 3],
}
impl Triangle {
pub fn points_flat(&self) -> [f32; 6] {
let [[a, b], [c, d], [e, f]] = self.points;
[a, b, c, d, e, f]
}
}