Multithreaded render
This commit is contained in:
+7
-14
@@ -1,12 +1,10 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::{Color32, Frame, Id};
|
||||
use egui_plot::{Line, Plot, PlotPoints};
|
||||
use egui_winit_vulkano::Gui;
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::{
|
||||
create_csg,
|
||||
objects::{CSG, Light, Mesh},
|
||||
};
|
||||
use crate::objects::{CSG, Light, Mesh};
|
||||
|
||||
fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
|
||||
ui.label(egui::RichText::new(text).size(size));
|
||||
@@ -29,7 +27,7 @@ pub(crate) struct GState {
|
||||
|
||||
pub(crate) meshes: Vec<Mesh>,
|
||||
pub(crate) lights: Vec<Light>,
|
||||
pub(crate) csg: Vec<CSG>,
|
||||
pub(crate) csg: Vec<Arc<RwLock<CSG>>>,
|
||||
|
||||
pub(crate) fps: [f64; 128],
|
||||
|
||||
@@ -53,7 +51,7 @@ impl Default for GState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState) {
|
||||
pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState, new_csg_needed: &mut bool) {
|
||||
gui.immediate_ui(|gui| {
|
||||
let ctx = gui.context();
|
||||
egui::SidePanel::left(Id::new("main_left"))
|
||||
@@ -118,6 +116,7 @@ pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState) {
|
||||
ui.heading("Implicit Surfaces");
|
||||
let mut csgdel = vec![];
|
||||
for csg in &mut state.csg {
|
||||
let mut csg = csg.write().unwrap();
|
||||
ui.label(csg.name.clone());
|
||||
csgdel.push(ui.small_button("remove csg").clicked());
|
||||
ui.add(
|
||||
@@ -153,13 +152,7 @@ pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState) {
|
||||
state.csg.remove(i);
|
||||
}
|
||||
if state.csg.len() < 32 && ui.small_button("add csg").clicked() {
|
||||
state.csg.push(CSG {
|
||||
name: "generated".to_string(),
|
||||
parts: create_csg(),
|
||||
pos: Vec3::ZERO,
|
||||
rot: Vec3::ZERO,
|
||||
scale: Vec3::ONE,
|
||||
});
|
||||
*new_csg_needed = true;
|
||||
}
|
||||
ui.heading("Lights");
|
||||
let mut lightdel = vec![];
|
||||
|
||||
+190
-615
File diff suppressed because it is too large
Load Diff
+8
-6
@@ -8,7 +8,7 @@ use vulkano::{
|
||||
memory::allocator::{
|
||||
AllocationCreateInfo, MemoryAllocatePreference, MemoryTypeFilter, StandardMemoryAllocator,
|
||||
},
|
||||
pipeline::graphics::vertex_input::Vertex,
|
||||
pipeline::{GraphicsPipeline, graphics::vertex_input::Vertex},
|
||||
};
|
||||
|
||||
use crate::ssa::SSATape;
|
||||
@@ -36,11 +36,13 @@ pub(crate) struct Mesh {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CSG {
|
||||
pub(crate) name: String,
|
||||
pub(crate) parts: SSATape,
|
||||
pub(crate) pos: Vec3,
|
||||
pub(crate) rot: Vec3,
|
||||
pub(crate) scale: Vec3,
|
||||
pub(crate) name: String,
|
||||
pub(crate) parts: SSATape,
|
||||
pub(crate) pos: Vec3,
|
||||
pub(crate) rot: Vec3,
|
||||
pub(crate) scale: Vec3,
|
||||
pub(crate) deferred_pipeline: Arc<GraphicsPipeline>,
|
||||
pub(crate) normals_pipeline: Arc<GraphicsPipeline>,
|
||||
}
|
||||
|
||||
pub(crate) fn load_obj(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::ahash::HashMapExt;
|
||||
use foldhash::HashMap;
|
||||
use rspirv::{dr::Module, spirv};
|
||||
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Write,
|
||||
simd::{StdFloat, cmp::SimdPartialOrd},
|
||||
sync::{Arc, RwLock, mpmc, mpsc},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use foldhash::HashSet;
|
||||
use glam::{EulerRot, Mat4, Vec3};
|
||||
use log::info;
|
||||
use rspirv::{binary::Assemble, dr::Module};
|
||||
use vulkano::{
|
||||
device::Device,
|
||||
pipeline::{
|
||||
DynamicState, GraphicsPipeline, PipelineCreateFlags, PipelineLayout,
|
||||
PipelineShaderStageCreateInfo,
|
||||
cache::PipelineCache,
|
||||
graphics::{
|
||||
GraphicsPipelineCreateInfo,
|
||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
||||
depth_stencil::{DepthState, DepthStencilState},
|
||||
input_assembly::InputAssemblyState,
|
||||
multisample::MultisampleState,
|
||||
rasterization::{CullMode, FrontFace, PolygonMode, RasterizationState},
|
||||
vertex_input::{Vertex, VertexDefinition, VertexInputState},
|
||||
},
|
||||
layout::PipelineDescriptorSetLayoutCreateInfo,
|
||||
},
|
||||
render_pass::{RenderPass, Subpass},
|
||||
shader::{ShaderModule, ShaderModuleCreateInfo},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
DUMP_SPV_TO_FILE, IVertex, ShaderModules, get_spec_constants,
|
||||
gui::PreviousDebug,
|
||||
implicit_vs::PushConstantData,
|
||||
interpreter::{self, IntervalInterpreter, PointInterpreter, VALUE_0},
|
||||
objects::CSG,
|
||||
ssa::{SSAInput, SSAOpcode, SSAOpcodeSized, SSATape},
|
||||
types::Interval,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WorkItem {
|
||||
CreateCSG(
|
||||
Arc<Device>,
|
||||
Module,
|
||||
Module,
|
||||
Arc<RenderPass>,
|
||||
Arc<PipelineCache>,
|
||||
ShaderModules,
|
||||
PreviousDebug,
|
||||
isize,
|
||||
),
|
||||
GetPushConstants(Arc<RwLock<CSG>>, f32, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WorkComplete {
|
||||
CreateCSG(Arc<RwLock<CSG>>, isize),
|
||||
GetPushConstants(PushConstantData, usize),
|
||||
}
|
||||
|
||||
pub fn thread_loop(recv: mpmc::Receiver<WorkItem>, send: mpsc::SyncSender<WorkComplete>) {
|
||||
for work in recv.into_iter() {
|
||||
match work {
|
||||
WorkItem::CreateCSG(
|
||||
device,
|
||||
trace_module,
|
||||
normals_module,
|
||||
render_pass,
|
||||
cache,
|
||||
modules,
|
||||
debug,
|
||||
index,
|
||||
) => {
|
||||
let csg_start = Instant::now();
|
||||
let parts = create_csg();
|
||||
|
||||
let trace_shader_module =
|
||||
sdf_specialize_module(device.clone(), &parts, trace_module, &format!("trace"));
|
||||
let normals_shader_module = sdf_specialize_module(
|
||||
device.clone(),
|
||||
&parts,
|
||||
normals_module,
|
||||
&format!("normals"),
|
||||
);
|
||||
|
||||
let (deferred_pipeline, normals_pipeline) = deferred_pipelines_recompile(
|
||||
device,
|
||||
render_pass,
|
||||
cache,
|
||||
modules,
|
||||
trace_shader_module,
|
||||
normals_shader_module,
|
||||
debug,
|
||||
);
|
||||
|
||||
let csg = Arc::new(RwLock::new(CSG {
|
||||
name: "example".to_string(),
|
||||
parts,
|
||||
pos: Vec3::ZERO,
|
||||
rot: Vec3::ZERO,
|
||||
scale: Vec3::ONE,
|
||||
deferred_pipeline,
|
||||
normals_pipeline,
|
||||
}));
|
||||
|
||||
send.send(WorkComplete::CreateCSG(csg, index)).unwrap();
|
||||
|
||||
let csg_end = Instant::now();
|
||||
info!(
|
||||
"CSG compile took {} milliseconds",
|
||||
(csg_end - csg_start).as_secs_f64() * 1000.0
|
||||
);
|
||||
},
|
||||
WorkItem::GetPushConstants(csg, time, index) => {
|
||||
let mut push_constants = PushConstantData {
|
||||
world: Mat4::IDENTITY.to_cols_array_2d(),
|
||||
dimensions: Vec3::ONE.to_array().into(),
|
||||
lowest_corner: Vec3::ZERO.into(),
|
||||
enable: [0; 16],
|
||||
material: 0,
|
||||
};
|
||||
|
||||
let csg = csg.read().unwrap();
|
||||
|
||||
push_constants.world = (Mat4::from_translation(csg.pos * 0.01)
|
||||
* Mat4::from_euler(
|
||||
EulerRot::XYZ,
|
||||
csg.rot.x.to_radians(),
|
||||
csg.rot.y.to_radians(),
|
||||
csg.rot.z.to_radians(),
|
||||
)
|
||||
* Mat4::from_scale(csg.scale * 2.0))
|
||||
.to_cols_array_2d();
|
||||
|
||||
interval_check(&csg, &mut push_constants, time);
|
||||
|
||||
push_constants.material = index as _;
|
||||
|
||||
send.send(WorkComplete::GetPushConstants(push_constants, index))
|
||||
.unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deferred_pipelines_recompile(
|
||||
device: Arc<Device>,
|
||||
render_pass: Arc<RenderPass>,
|
||||
cache: Arc<PipelineCache>,
|
||||
shader_modules: ShaderModules,
|
||||
trace_shader_module: Arc<ShaderModule>,
|
||||
normals_shader_module: Arc<ShaderModule>,
|
||||
debug: PreviousDebug,
|
||||
) -> (Arc<GraphicsPipeline>, Arc<GraphicsPipeline>) {
|
||||
let specs = get_spec_constants(&debug);
|
||||
|
||||
let dynamic_state = [DynamicState::Viewport].into_iter().collect::<HashSet<_>>();
|
||||
|
||||
let implicit_vs_entry = shader_modules
|
||||
.implicit_vs
|
||||
.specialize(specs.clone())
|
||||
.unwrap()
|
||||
.single_entry_point()
|
||||
.unwrap();
|
||||
|
||||
let vertex_input_state = [IVertex::per_vertex()]
|
||||
.definition(&implicit_vs_entry)
|
||||
.unwrap();
|
||||
|
||||
let implicit_vs_info = PipelineShaderStageCreateInfo::new(implicit_vs_entry);
|
||||
|
||||
let implicit_fs_info = PipelineShaderStageCreateInfo::new(
|
||||
trace_shader_module
|
||||
.specialize(specs.clone())
|
||||
.unwrap()
|
||||
.single_entry_point()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let layout = PipelineLayout::new(
|
||||
device.clone(),
|
||||
PipelineDescriptorSetLayoutCreateInfo::from_stages([&implicit_vs_info, &implicit_fs_info])
|
||||
.into_pipeline_layout_create_info(device.clone())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let stages = [implicit_vs_info.clone(), implicit_fs_info]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let deferred_subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
let implicit_pipeline = GraphicsPipeline::new(
|
||||
device.clone(),
|
||||
Some(cache.clone()),
|
||||
GraphicsPipelineCreateInfo {
|
||||
flags: PipelineCreateFlags::DISABLE_OPTIMIZATION,
|
||||
stages,
|
||||
vertex_input_state: Some(vertex_input_state.clone()),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
dynamic_state: dynamic_state.clone(),
|
||||
viewport_state: Some(Default::default()),
|
||||
rasterization_state: Some(RasterizationState {
|
||||
front_face: FrontFace::Clockwise,
|
||||
cull_mode: CullMode::Back,
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
..RasterizationState::default()
|
||||
}),
|
||||
depth_stencil_state: Some(DepthStencilState {
|
||||
depth: Some(DepthState::simple()),
|
||||
..Default::default()
|
||||
}),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
deferred_subpass.num_color_attachments(),
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
subpass: Some(deferred_subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(layout.clone())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let fullscreen_vs_entry = shader_modules
|
||||
.fullscreen_vs
|
||||
.specialize(specs.clone())
|
||||
.unwrap()
|
||||
.single_entry_point()
|
||||
.unwrap();
|
||||
|
||||
let vertex_input_state = VertexInputState::new();
|
||||
|
||||
let fullscreen_vs_info = PipelineShaderStageCreateInfo::new(fullscreen_vs_entry);
|
||||
|
||||
let normals_fs_info = PipelineShaderStageCreateInfo::new(
|
||||
normals_shader_module
|
||||
.specialize(specs.clone())
|
||||
.unwrap()
|
||||
.single_entry_point()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let layout = PipelineLayout::new(
|
||||
device.clone(),
|
||||
PipelineDescriptorSetLayoutCreateInfo::from_stages([&fullscreen_vs_info, &normals_fs_info])
|
||||
.into_pipeline_layout_create_info(device.clone())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let stages = [fullscreen_vs_info.clone(), normals_fs_info]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let normals_subpass = Subpass::from(render_pass.clone(), 1).unwrap();
|
||||
|
||||
let normals_pipeline = GraphicsPipeline::new(
|
||||
device.clone(),
|
||||
Some(cache.clone()),
|
||||
GraphicsPipelineCreateInfo {
|
||||
flags: PipelineCreateFlags::DISABLE_OPTIMIZATION,
|
||||
stages,
|
||||
vertex_input_state: Some(vertex_input_state.clone()),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
dynamic_state: dynamic_state.clone(),
|
||||
viewport_state: Some(Default::default()),
|
||||
rasterization_state: Some(RasterizationState {
|
||||
front_face: FrontFace::Clockwise,
|
||||
cull_mode: CullMode::Back,
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
..RasterizationState::default()
|
||||
}),
|
||||
depth_stencil_state: None,
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
normals_subpass.num_color_attachments(),
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
subpass: Some(normals_subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(layout.clone())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(implicit_pipeline, normals_pipeline)
|
||||
}
|
||||
|
||||
fn create_csg() -> SSATape {
|
||||
let mut tape = SSATape::default();
|
||||
let pos = tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSAPosition,
|
||||
size: 1,
|
||||
},
|
||||
vec![],
|
||||
);
|
||||
let offset = tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSAAdd,
|
||||
size: 3,
|
||||
},
|
||||
vec![
|
||||
pos[0],
|
||||
pos[1],
|
||||
pos[2],
|
||||
SSAInput::Constant(rand::random_range(0.0..3.0)),
|
||||
SSAInput::Constant(rand::random_range(0.0..3.0)),
|
||||
SSAInput::Constant(rand::random_range(0.0..3.0)),
|
||||
],
|
||||
);
|
||||
let sphere = tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSASDFSphere,
|
||||
size: 1,
|
||||
},
|
||||
vec![
|
||||
offset[0],
|
||||
offset[1],
|
||||
offset[2],
|
||||
SSAInput::Constant(rand::random_range(0.0..1.0)),
|
||||
],
|
||||
);
|
||||
let torus = tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSASDFTorus,
|
||||
size: 1,
|
||||
},
|
||||
vec![
|
||||
pos[0],
|
||||
pos[1],
|
||||
pos[2],
|
||||
SSAInput::Constant(rand::random_range(0.0..1.0)),
|
||||
SSAInput::Constant(rand::random_range(0.0..1.0)),
|
||||
],
|
||||
);
|
||||
let min = tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSAMin,
|
||||
size: 1,
|
||||
},
|
||||
vec![sphere[0], torus[0]],
|
||||
);
|
||||
tape.push_instruction(
|
||||
SSAOpcodeSized {
|
||||
opcode: SSAOpcode::SSAReturn,
|
||||
size: 1,
|
||||
},
|
||||
min,
|
||||
);
|
||||
tape
|
||||
}
|
||||
|
||||
fn interval_check(csg: &CSG, push_constants: &mut PushConstantData, time: f32) {
|
||||
const INTERPRET_INPUT_X: interpreter::Value =
|
||||
interpreter::Value::from_array([10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0, 0.0, 0.0]);
|
||||
const INTERPRET_INPUT_Y: interpreter::Value =
|
||||
interpreter::Value::from_array([0.0, 10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0, 0.0]);
|
||||
const INTERPRET_INPUT_Z: interpreter::Value =
|
||||
interpreter::Value::from_array([0.0, 0.0, 10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0]);
|
||||
const INTERPRET_MUL: interpreter::Value =
|
||||
interpreter::Value::from_array([-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0]);
|
||||
const INTERPRET_ADD: interpreter::Value = interpreter::Value::from_array([
|
||||
10000.0, 10000.0, 10000.0, -10000.0, -10000.0, -10000.0, 0.0, 0.0,
|
||||
]);
|
||||
|
||||
let mut interpreter = PointInterpreter::new(csg);
|
||||
let interpreter_out = interpreter
|
||||
.scene(
|
||||
INTERPRET_INPUT_X,
|
||||
INTERPRET_INPUT_Y,
|
||||
INTERPRET_INPUT_Z,
|
||||
interpreter::Value::splat(time),
|
||||
)
|
||||
.mul_add(INTERPRET_MUL, INTERPRET_ADD)
|
||||
.to_array();
|
||||
|
||||
let highest_corner = Vec3::new(interpreter_out[0], interpreter_out[1], interpreter_out[2]);
|
||||
let lowest_corner = Vec3::new(interpreter_out[3], interpreter_out[4], interpreter_out[5]);
|
||||
|
||||
push_constants.dimensions = (highest_corner - lowest_corner).to_array().into();
|
||||
push_constants.lowest_corner = lowest_corner.into();
|
||||
|
||||
push_constants.enable = [0; 16];
|
||||
|
||||
for x in 0..8 {
|
||||
for y in 0..8 {
|
||||
let interval_input_z: Interval = Interval::new_unchecked(
|
||||
interpreter::Value::from_array([
|
||||
(0.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(1.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(2.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(3.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(4.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(5.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(6.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(7.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
]),
|
||||
interpreter::Value::from_array([
|
||||
(1.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(2.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(3.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(4.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(5.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(6.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(7.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
(8.0 / 8.0) * push_constants.dimensions[2] + push_constants.lowest_corner[2],
|
||||
]),
|
||||
);
|
||||
|
||||
let mut interpreter = IntervalInterpreter::new(csg);
|
||||
let output = interpreter.scene(
|
||||
Interval::const_splat2(
|
||||
(x as f32 / 8.0) * push_constants.dimensions[0]
|
||||
+ push_constants.lowest_corner[0],
|
||||
((x + 1) as f32 / 8.0) * push_constants.dimensions[0]
|
||||
+ push_constants.lowest_corner[0],
|
||||
),
|
||||
Interval::const_splat2(
|
||||
(y as f32 / 8.0) * push_constants.dimensions[1]
|
||||
+ push_constants.lowest_corner[1],
|
||||
((y + 1) as f32 / 8.0) * push_constants.dimensions[1]
|
||||
+ push_constants.lowest_corner[1],
|
||||
),
|
||||
interval_input_z,
|
||||
Interval::const_splat(time),
|
||||
);
|
||||
|
||||
let output = ((output.lower().simd_le(VALUE_0) & output.upper().simd_ge(VALUE_0))
|
||||
.to_bitmask() as u32)
|
||||
<< ((y & 0b11) * 8);
|
||||
push_constants.enable[(x << 1) | (y >> 2)] |= output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sdf_specialize_module(
|
||||
device: Arc<Device>,
|
||||
parts: &SSATape,
|
||||
module: Module,
|
||||
name: &str,
|
||||
) -> Arc<ShaderModule> {
|
||||
let new_module = parts.compile_to_spirv(Some(module));
|
||||
|
||||
let assembled_module = new_module.assemble();
|
||||
let module_as_bytes = assembled_module
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(u32::to_le_bytes)
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if DUMP_SPV_TO_FILE {
|
||||
File::create(format!("{name}.out.spv"))
|
||||
.unwrap()
|
||||
.write(&module_as_bytes)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
::vulkano::shader::ShaderModule::new(device, ShaderModuleCreateInfo::new(&assembled_module))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user