Move to workspace

This commit is contained in:
2025-12-20 22:04:47 +00:00
parent 8fcd512c36
commit b6b0c2114f
41 changed files with 4540 additions and 3402 deletions
Generated
+17
View File
@@ -921,6 +921,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "humantime"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
[[package]]
name = "icu_collections"
version = "2.1.1"
@@ -2543,6 +2549,7 @@ dependencies = [
"rand",
"rspirv",
"simplelog",
"tape-load",
"utf-8",
"vulkano",
"vulkano-shaders",
@@ -2550,6 +2557,16 @@ dependencies = [
"winit",
]
[[package]]
name = "tape-load"
version = "0.1.0"
dependencies = [
"foldhash",
"glam",
"humantime",
"rspirv",
]
[[package]]
name = "termcolor"
version = "1.4.1"
+8 -47
View File
@@ -1,45 +1,12 @@
[package]
name = "tape-drive"
version = "0.3.0"
edition = "2024"
[workspace]
resolver = "3"
members = ["tape-load", "tape-drive"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
vulkano = "0.35.1"
vulkano-shaders = { version = "0.35.0", features = ["shaderc-debug"] }
winit = "0.30"
vulkano-util = "0.35.0"
obj = "0.10"
bytemuck = { version = "1.13", features = [
"derive",
"extern_crate_std",
"min_const_generics",
] }
glam = "0.30.3"
egui = "0.31.1"
egui_winit_vulkano = "0.28.0"
egui_plot = "0.32.1"
#serde = { version = "1", features = ["derive"] }
#serde_json = "1"
utf-8 = "0.7"
rand = "0.9.1"
foldhash = "0.1.5"
log = "0.4"
simplelog = "0.12"
rspirv = "0.12"
num_cpus = "1"
notify = "8.2"
[profile.final]
inherits = "release"
strip = true
lto = true
codegen-units = 1
# using latest gits
[patch.crates-io]
@@ -49,9 +16,3 @@ notify = "8.2"
#vulkano-taskgraph = { git = "https://github.com/vulkano-rs/vulkano" }
#vulkano-util = { git = "https://github.com/vulkano-rs/vulkano" }
#egui_winit_vulkano = { git = "https://github.com/Molive-0/egui_winit_vulkano" }
[profile.final]
inherits = "release"
strip = true
lto = true
codegen-units = 1
-2829
View File
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
[package]
name = "tape-drive"
version = "0.3.0"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
vulkano = "0.35.1"
vulkano-shaders = { version = "0.35.0", features = ["shaderc-debug"] }
winit = "0.30"
vulkano-util = "0.35.0"
obj = "0.10"
bytemuck = { version = "1.13", features = [
"derive",
"extern_crate_std",
"min_const_generics",
] }
glam = "0.30.3"
egui = "0.31.1"
egui_winit_vulkano = "0.28.0"
egui_plot = "0.32.1"
#serde = { version = "1", features = ["derive"] }
#serde_json = "1"
utf-8 = "0.7"
rand = "0.9.1"
foldhash = "0.1.5"
log = "0.4"
simplelog = "0.12"
rspirv = "0.12"
num_cpus = "1"
notify = "8.2"
tape-load = { version = "*", path = "../tape-load" }
@@ -6,12 +6,13 @@ import sys
import subprocess
import re
SHADERS_DIR = "shaders_out"
SHADERS_OUT = "shaders_out"
SHADERS_IN = "src/shaders"
TARGET_ENV = "vulkan1.3"
if os.path.isdir(SHADERS_DIR):
shutil.rmtree(SHADERS_DIR)
os.mkdir(SHADERS_DIR)
if os.path.isdir(SHADERS_OUT):
shutil.rmtree(SHADERS_OUT)
os.mkdir(SHADERS_OUT)
normal_shaders = [
("fullscreen.vert", "vert"),
@@ -33,8 +34,8 @@ for shader, ty in normal_shaders:
"--target-env",
TARGET_ENV,
"-o",
f"{SHADERS_DIR}/{shader}.spv",
f"src/shaders/{shader}.glsl",
f"{SHADERS_OUT}/{shader}.spv",
f"{SHADERS_IN}/{shader}.glsl",
]
)
if result.returncode != 0:
@@ -43,7 +44,7 @@ for shader, ty in normal_shaders:
replacement_shaders = [("trace.frag", "frag"), ("normals.frag", "frag")]
for shader, ty in replacement_shaders:
print(f"src/shaders/{shader}.glsl")
print(f"{SHADERS_IN}/{shader}.glsl")
result = subprocess.run(
[
"glslangValidator",
@@ -55,7 +56,7 @@ for shader, ty in replacement_shaders:
"--target-env",
TARGET_ENV,
"--quiet",
"src/shaders/trace.frag.glsl",
f"{SHADERS_IN}/{shader}.glsl",
],
capture_output=True,
text=True,
@@ -63,9 +64,11 @@ for shader, ty in replacement_shaders:
if result.returncode != 0:
sys.exit(result.returncode)
asm = result.stdout
asm = re.sub(r"(?ms)%11 =.*OpFunctionEnd", "", asm)
with open(f"{SHADERS_OUT}/{shader}.old.spv-dis", "w") as f:
f.write(asm)
asm = re.sub(r"(?ms)%11 =.*?OpFunctionEnd", "", asm)
asm = re.sub(r"%11 ", "%1000 ", asm)
with open(f"{SHADERS_DIR}/{shader}.spv-dis", "w") as f:
with open(f"{SHADERS_OUT}/{shader}.spv-dis", "w") as f:
f.write(asm)
result = subprocess.run(
[
@@ -74,7 +77,7 @@ for shader, ty in replacement_shaders:
"--target-env",
"vulkan1.3",
"-o",
f"{SHADERS_DIR}/{shader}.spv",
f"{SHADERS_OUT}/{shader}.spv",
],
input=asm,
encoding="ascii",
View File
+34 -8
View File
@@ -38,7 +38,8 @@ pub(crate) struct GState {
pub(crate) new_csgs_needed: u8,
pub(crate) fps: [f64; 128],
pub(crate) cpu_mpf: [f64; 1024],
pub(crate) gpu_mpf: [f64; 1024],
pub(crate) debug: PreviousDebug,
@@ -65,7 +66,8 @@ impl Default for GState {
new_csgs_needed: 0,
fps: [0.0; 128],
cpu_mpf: [0.0; 1024],
gpu_mpf: [0.0; 1024],
debug: Default::default(),
@@ -271,17 +273,41 @@ pub(crate) fn gui_up(gui: &mut Gui, state: &mut GState) {
state.lights.push(Light::default());
}
let fps: PlotPoints = state
.fps
let cpu_mpf: PlotPoints = state
.cpu_mpf
.iter()
.enumerate()
.map(|(x, y)| [x as f64, *y])
.collect::<Vec<_>>()
.into();
let average_fps = state.fps.iter().sum::<f64>() / state.fps.len() as f64;
let line = Line::new("fps_line", fps);
ui.heading(format!("FPS ({:.2})", average_fps));
Plot::new("fps")
let average_cpu_mpf =
state.cpu_mpf.iter().sum::<f64>() / state.cpu_mpf.len() as f64;
let line = Line::new("cpu_mpf_line", cpu_mpf);
ui.heading(format!("CPU ms per frame ({:.2})", average_cpu_mpf));
ui.heading(format!(
"CPU frames per second ({:.2})",
1000.0 / average_cpu_mpf
));
Plot::new("cpu_mpf")
.view_aspect(2.0)
.show(ui, |plot_ui| plot_ui.line(line));
let gpu_mpf: PlotPoints = state
.gpu_mpf
.iter()
.enumerate()
.map(|(x, y)| [x as f64, *y])
.collect::<Vec<_>>()
.into();
let average_gpu_mpf =
state.gpu_mpf.iter().sum::<f64>() / state.gpu_mpf.len() as f64;
let line = Line::new("gpu_mpf_line", gpu_mpf);
ui.heading(format!("GPU ms per frame ({:.2})", average_gpu_mpf));
ui.heading(format!(
"GPU frames per second ({:.2})",
1000.0 / average_gpu_mpf
));
Plot::new("gpu_mpf")
.view_aspect(2.0)
.show(ui, |plot_ui| plot_ui.line(line));
+115 -83
View File
@@ -5,14 +5,12 @@ const DUMP_SPV_TO_FILE: bool = true;
const PIPELINE_CACHING: bool = false;
const IGNORE_STENCIL: bool = true;
const BYTECODE_IMITATES_GLSL: bool = false;
const DEFAULT_SUBDIVISION: u32 = 16;
const MAXIMUM_SUBDIVISION: u32 = 64;
const MINUMUM_SUBDIVISION: u32 = 8;
const RUN_STANDARD_PIPELINE: bool = false;
const RUN_RT_PIPELINE: bool = true;
const RUN_STANDARD_PIPELINE: bool = true;
const RUN_RT_PIPELINE: bool = false;
use std::{
error::Error,
@@ -77,6 +75,7 @@ use vulkano::{
ShaderBindingTable,
},
},
query::{QueryPool, QueryPoolCreateInfo, QueryResultFlags, QueryType},
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
shader::{ShaderModule, ShaderModuleCreateInfo, SpecializationConstant},
swapchain::{
@@ -84,7 +83,7 @@ use vulkano::{
acquire_next_image,
},
sync::{
self, GpuFuture,
self, GpuFuture, PipelineStage,
future::{FenceSignalFuture, NowFuture},
},
};
@@ -106,17 +105,10 @@ use crate::{
trace_vs::{Camera, Lights, PushConstantData},
};
mod objects;
use tape_load::{instruction_set, interpreters, ssa, types, vm};
use crate::objects::*;
mod ssa;
mod instruction_set;
mod interpreter;
mod types;
mod vm;
mod threads;
mod scene;
@@ -155,18 +147,18 @@ struct Keys {
struct CState {
position: Vec3,
forward: Vec3,
looking: bool,
keys: Keys,
forward: Vec3,
looking: bool,
keys: Keys,
}
impl Default for CState {
fn default() -> Self {
CState {
position: vec3(0., 0., 3.),
forward: vec3(0., 0., 0.),
looking: false,
keys: Keys {
forward: vec3(0., 0., 0.),
looking: false,
keys: Keys {
w: false,
s: false,
a: false,
@@ -229,16 +221,17 @@ struct App {
rcx: Arc<Mutex<Option<RenderContext>>>,
_watcher: Option<RecommendedWatcher>,
glsl_changed: Arc<AtomicBool>,
application_start: Instant,
}
#[derive(Debug, Clone)]
struct ShaderModules {
trace_vs: Arc<ShaderModule>,
trace_vs: Arc<ShaderModule>,
fullscreen_vs: Arc<ShaderModule>,
lighting_fs: Arc<ShaderModule>,
raygen: Arc<ShaderModule>,
closest_hit: Arc<ShaderModule>,
miss: Arc<ShaderModule>,
lighting_fs: Arc<ShaderModule>,
raygen: Arc<ShaderModule>,
closest_hit: Arc<ShaderModule>,
miss: Arc<ShaderModule>,
}
struct RenderContext {
@@ -264,6 +257,7 @@ struct RenderContext {
recreate_swapchain: bool,
recreate_pipelines: bool,
previous_frame_end: Vec<Option<FenceSignalFuture<Box<dyn GpuFuture>>>>,
time_query_pool: Arc<QueryPool>,
gui: Gui,
render_start: Instant,
}
@@ -559,10 +553,10 @@ impl App {
notify::EventKind::Modify(_) => {
info!("event: {:?}", event);
watcher_glsl_changed.store(true, std::sync::atomic::Ordering::Relaxed);
}
_ => {}
},
_ => {},
}
}
},
Err(e) => error!("watch error: {:?}", e),
},
)
@@ -614,6 +608,7 @@ impl App {
rcx: Arc::new(Mutex::new(None)),
_watcher: watcher,
glsl_changed,
application_start: Instant::now(),
}
}
}
@@ -823,12 +818,12 @@ impl ApplicationHandler for App {
.map(|load| load(self.device.clone()).unwrap());
let shader_modules = ShaderModules {
trace_vs: pariter.next().unwrap(),
trace_vs: pariter.next().unwrap(),
fullscreen_vs: pariter.next().unwrap(),
lighting_fs: pariter.next().unwrap(),
raygen: pariter.next().unwrap(),
closest_hit: pariter.next().unwrap(),
miss: pariter.next().unwrap(),
lighting_fs: pariter.next().unwrap(),
raygen: pariter.next().unwrap(),
closest_hit: pariter.next().unwrap(),
miss: pariter.next().unwrap(),
};
drop(pariter);
@@ -892,8 +887,8 @@ impl ApplicationHandler for App {
.unwrap();
let viewport = Viewport {
offset: [0.0, window_size.height as f32],
extent: [window_size.width as f32, window_size.height as f32 * -1.],
offset: [0.0, window_size.height as f32],
extent: [window_size.width as f32, window_size.height as f32 * -1.],
depth_range: 0.0..=1.0,
};
@@ -959,11 +954,15 @@ impl ApplicationHandler for App {
images[0].format(),
GuiConfig {
allow_srgb_render_target: true,
is_overlay: true,
samples: SampleCount::Sample1,
is_overlay: true,
samples: SampleCount::Sample1,
},
);
let mut query_create_info = QueryPoolCreateInfo::query_type(QueryType::Timestamp);
query_create_info.query_count = 2;
let time_query_pool = QueryPool::new(self.device.clone(), query_create_info).unwrap();
*self.rcx.lock().unwrap() = Some(RenderContext {
window,
swapchain,
@@ -987,6 +986,7 @@ impl ApplicationHandler for App {
recreate_swapchain: false,
recreate_pipelines: false,
previous_frame_end,
time_query_pool,
gui,
render_start: Instant::now(),
});
@@ -1025,16 +1025,16 @@ impl ApplicationHandler for App {
match &event {
WindowEvent::CloseRequested => {
event_loop.exit();
}
},
WindowEvent::Resized(_) => {
rcx.recreate_swapchain = true;
}
},
WindowEvent::ScaleFactorChanged { .. } => {
rcx.recreate_swapchain = true;
}
},
WindowEvent::DroppedFile(_file) => {
todo!()
}
},
WindowEvent::MouseInput {
device_id: d,
state: s,
@@ -1045,7 +1045,7 @@ impl ApplicationHandler for App {
if b == &MouseButton::Right {
self.cstate.looking = s == &ElementState::Pressed;
}
}
},
WindowEvent::KeyboardInput {
device_id: _,
event: input,
@@ -1053,19 +1053,19 @@ impl ApplicationHandler for App {
} => match input.physical_key {
PhysicalKey::Code(KeyCode::KeyW) => {
self.cstate.keys.w = input.state == ElementState::Pressed;
}
},
PhysicalKey::Code(KeyCode::KeyS) => {
self.cstate.keys.s = input.state == ElementState::Pressed;
}
},
PhysicalKey::Code(KeyCode::KeyA) => {
self.cstate.keys.a = input.state == ElementState::Pressed;
}
},
PhysicalKey::Code(KeyCode::KeyD) => {
self.cstate.keys.d = input.state == ElementState::Pressed;
}
},
PhysicalKey::Code(KeyCode::ShiftLeft) => {
self.cstate.looking = input.state == ElementState::Pressed;
}
},
PhysicalKey::Code(KeyCode::Space) => {
if input.state == ElementState::Pressed {
self.thread_work_creation
@@ -1087,18 +1087,18 @@ impl ApplicationHandler for App {
))
.unwrap();
}
}
},
PhysicalKey::Code(KeyCode::KeyG) => {
if input.state == ElementState::Pressed {
self.draw_gui = !self.draw_gui;
}
}
_ => {}
},
_ => {},
},
WindowEvent::RedrawRequested => {
self.redraw(rcx);
}
_ => {}
},
_ => {},
}
}
@@ -1169,23 +1169,23 @@ impl App {
* Mat4::from_translation(Vec3::ZERO - self.cstate.position);
let uniform_data = trace_vs::Camera {
proj_view: (proj * view).to_cols_array_2d(),
inv_proj_view: (proj * view).inverse().to_cols_array_2d(),
inv_proj: (proj).inverse().to_cols_array_2d(),
inv_view: (view).inverse().to_cols_array_2d(),
campos_and_time: [
proj_view: (proj * view).to_cols_array_2d(),
inv_proj_view: (proj * view).inverse().to_cols_array_2d(),
inv_proj: (proj).inverse().to_cols_array_2d(),
inv_view: (view).inverse().to_cols_array_2d(),
campos_and_time: [
self.cstate.position.x,
self.cstate.position.y,
self.cstate.position.z,
self.time,
],
specular: self.gstate.specular.into(),
specular_tint: self.gstate.specular_tint.into(),
sheen_tint: self.gstate.sheen_tint.into(),
sheen: self.gstate.sheen.into(),
specular: self.gstate.specular.into(),
specular_tint: self.gstate.specular_tint.into(),
sheen_tint: self.gstate.sheen_tint.into(),
sheen: self.gstate.sheen.into(),
clear_coat_gloss: self.gstate.clear_coat_gloss.into(),
subsurface: self.gstate.subsurface.into(),
clear_coat: self.gstate.clear_coat.into(),
subsurface: self.gstate.subsurface.into(),
clear_coat: self.gstate.clear_coat.into(),
};
if self.cstate.looking {
@@ -1537,9 +1537,9 @@ impl App {
} else {
self.gstate.csg[index as usize] = csg;
}
}
WorkComplete::GetPushConstants(..) => {}
WorkComplete::RecompilePipelines(..) => {}
},
WorkComplete::GetPushConstants(..) => {},
WorkComplete::RecompilePipelines(..) => {},
}
}
@@ -1706,7 +1706,7 @@ impl App {
match work {
WorkComplete::RecompilePipelines(_index) => {
csg_left -= 1;
}
},
other => work_for_later.push(other),
}
}
@@ -1720,14 +1720,14 @@ impl App {
return;
}
for i in 1..self.gstate.fps.len() {
self.gstate.fps[i - 1] = self.gstate.fps[i];
for i in 1..self.gstate.cpu_mpf.len() {
self.gstate.cpu_mpf[i - 1] = self.gstate.cpu_mpf[i];
}
self.time = (Instant::now() - rcx.render_start).as_secs_f32();
self.time = (Instant::now() - self.application_start).as_secs_f32();
self.gstate.fps[self.gstate.fps.len() - 1] =
1.0 / (Instant::now() - rcx.render_start).as_secs_f64();
self.gstate.cpu_mpf[self.gstate.cpu_mpf.len() - 1] =
(Instant::now() - rcx.render_start).as_secs_f64() * 1000.0;
for future in rcx.previous_frame_end.iter_mut().flatten() {
future.cleanup_finished();
@@ -1780,7 +1780,7 @@ impl App {
let mut push_constants = vec![
PushConstantData {
world: Default::default(),
world: Default::default(),
inv_world: Default::default(),
};
self.gstate.csg.len()
@@ -1857,7 +1857,7 @@ impl App {
match work {
WorkComplete::RecompilePipelines(_index) => {
csg_left -= 1;
}
},
other => work_for_later.push(other),
}
}
@@ -1872,7 +1872,7 @@ impl App {
Err(VulkanError::OutOfDate) => {
rcx.recreate_swapchain = true;
return;
}
},
Err(e) => panic!("Failed to acquire next image: {e}"),
};
@@ -1927,6 +1927,16 @@ impl App {
)
.unwrap();
unsafe {
builder
.reset_query_pool(rcx.time_query_pool.clone(), 0..2)
.unwrap();
builder
.write_timestamp(rcx.time_query_pool.clone(), 0, PipelineStage::TopOfPipe)
.unwrap();
}
let mut futures = camera_and_lights.boxed();
if RUN_RT_PIPELINE {
@@ -1975,7 +1985,7 @@ impl App {
Some(future.boxed())
};
push_constants_left -= 1;
}
},
other => work_for_later.push(other),
}
}
@@ -2013,6 +2023,12 @@ impl App {
builder.end_render_pass(Default::default()).unwrap();
}
unsafe {
builder
.write_timestamp(rcx.time_query_pool.clone(), 1, PipelineStage::BottomOfPipe)
.unwrap();
}
let command_buffer = builder.build().unwrap();
for future in rcx.previous_frame_end.iter_mut().flatten() {
@@ -2020,6 +2036,22 @@ impl App {
future.cleanup_finished();
}
let mut time_stamps = [0u64; 2];
rcx.time_query_pool
.get_results(0..2, &mut time_stamps, QueryResultFlags::WAIT)
.unwrap();
let delta_in_ms = (time_stamps[1] - time_stamps[0]) as f32
* self.device.physical_device().properties().timestamp_period
/ 1000000.0;
for i in 1..self.gstate.gpu_mpf.len() {
self.gstate.gpu_mpf[i - 1] = self.gstate.gpu_mpf[i];
}
self.gstate.gpu_mpf[self.gstate.gpu_mpf.len() - 1] = delta_in_ms as f64;
let future = rcx.previous_frame_end[image_index]
.take()
.map(|f| f.boxed())
@@ -2050,20 +2082,20 @@ impl App {
match future.map_err(Validated::unwrap) {
Ok(future) => {
rcx.previous_frame_end[image_index] = Some(future);
}
},
Err(VulkanError::OutOfDate) => {
rcx.recreate_swapchain = true;
rcx.previous_frame_end[image_index] = None;
}
},
Err(e) => {
error!("Failed to flush future: {e:?}");
rcx.previous_frame_end[image_index] = None;
}
},
}
for work in self.thread_work_completion.try_iter() {
match work {
WorkComplete::GetPushConstants(..) => {}
WorkComplete::GetPushConstants(..) => {},
other => work_for_later.push(other),
}
}
@@ -2109,8 +2141,8 @@ fn framebuffer_generation(
depth_buffer.clone(),
ImageViewCreateInfo {
subresource_range: ImageSubresourceRange {
aspects: ImageAspects::DEPTH,
mip_levels: 0..1,
aspects: ImageAspects::DEPTH,
mip_levels: 0..1,
array_layers: 0..1,
},
..ImageViewCreateInfo::from_image(&depth_buffer.clone())
@@ -2406,7 +2438,7 @@ fn pipeline_recompile(
RayTracingShaderGroupCreateInfo::General { general_shader: 1 },
RayTracingShaderGroupCreateInfo::TrianglesHit {
closest_hit_shader: Some(2),
any_hit_shader: None,
any_hit_shader: None,
},
];
+16 -15
View File
@@ -11,6 +11,7 @@ use glam::{EulerRot, Mat4, Vec3};
use log::info;
use rand::{Rng, SeedableRng, rngs::SmallRng};
use rspirv::{binary::Assemble, dr::Module};
use tape_load::interpreters;
use vulkano::{
buffer::{Subbuffer, allocator::SubbufferAllocator},
command_buffer::{CommandBufferExecFuture, allocator::StandardCommandBufferAllocator},
@@ -42,7 +43,7 @@ use crate::{
DUMP_SPV_TO_FILE, IVertex, MAXIMUM_SUBDIVISION, MINUMUM_SUBDIVISION, ShaderModules,
get_spec_constants, gpu_upload,
gui::PreviousDebug,
interpreter::{self, IntervalInterpreter, PointInterpreter, VALUE_0},
interpreters::{VALUE_0, interval::IntervalInterpreter, point::PointInterpreter},
objects::CSG,
ssa::{SSAInput, SSAOpcode, SSAOpcodeSized, SSATape},
trace_vs::{Object, PushConstantData},
@@ -520,25 +521,25 @@ fn interval_check(
command_allocator: Arc<StandardCommandBufferAllocator>,
transfer_queue: Arc<Queue>,
) -> CommandBufferExecFuture<NowFuture> {
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([
const INTERPRET_INPUT_X: interpreters::Value =
interpreters::Value::from_array([10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0, 0.0, 0.0]);
const INTERPRET_INPUT_Y: interpreters::Value =
interpreters::Value::from_array([0.0, 10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0, 0.0]);
const INTERPRET_INPUT_Z: interpreters::Value =
interpreters::Value::from_array([0.0, 0.0, 10000.0, 0.0, 0.0, -10000.0, 0.0, 0.0]);
const INTERPRET_MUL: interpreters::Value =
interpreters::Value::from_array([-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0]);
const INTERPRET_ADD: interpreters::Value = interpreters::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 mut interpreter = PointInterpreter::new(&csg.parts);
let interpreter_out = interpreter
.scene(
INTERPRET_INPUT_X,
INTERPRET_INPUT_Y,
INTERPRET_INPUT_Z,
interpreter::Value::splat(time),
interpreters::Value::splat(time),
)
.mul_add(INTERPRET_MUL, INTERPRET_ADD)
.to_array();
@@ -576,7 +577,7 @@ fn interval_check(
for y in 0..ydim {
for z in 0..(zdim / 8) {
let interval_input_z: Interval = Interval::new_unchecked(
interpreter::Value::from_array([
interpreters::Value::from_array([
((((8.0 / (zdim as f32)) * z as f32) + (0.0 / (zdim as f32)))
* obj.dimensions[2])
+ obj.lowest_corner[2],
@@ -602,7 +603,7 @@ fn interval_check(
* obj.dimensions[2])
+ obj.lowest_corner[2],
]),
interpreter::Value::from_array([
interpreters::Value::from_array([
((((8.0 / (zdim as f32)) * z as f32) + (1.0 / (zdim as f32)))
* obj.dimensions[2])
+ obj.lowest_corner[2],
@@ -630,7 +631,7 @@ fn interval_check(
]),
);
let mut interpreter = IntervalInterpreter::new(csg);
let mut interpreter = IntervalInterpreter::new(&csg.parts);
let output = interpreter.scene(
Interval::const_splat2(
(x as f32 / (xdim as f32)) * obj.dimensions[0] + obj.lowest_corner[0],
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "tape-load"
version = "0.1.0"
edition = "2024"
[dependencies]
glam = "0.30.3"
rspirv = "0.12"
foldhash = "0.1.5"
humantime = "2.3"
@@ -1,7 +1,7 @@
#[repr(u8)]
#[allow(non_snake_case)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum InstructionSet {
pub enum InstructionSet {
#[allow(non_snake_case)]
#[allow(dead_code)]
/// Returns the input. Useful for copying registers.
@@ -1,411 +1,15 @@
use std::simd::{
StdFloat,
cmp::{SimdPartialEq, SimdPartialOrd},
num::SimdFloat,
};
use std::simd::{StdFloat, cmp::SimdPartialOrd, num::SimdFloat};
use crate::{
BYTECODE_IMITATES_GLSL, CSG,
ssa::{SSAInput, SSAInstruction, SSAOpcode},
interpreters::VALUE_0,
ssa::{SSAInput, SSAInstruction, SSAOpcode, SSATape},
types::Interval,
};
pub type Value = std::simd::f32x8;
pub type Mask = std::simd::mask32x8;
pub const VALUE_NAN: Value = Value::splat(core::f32::NAN);
pub const VALUE_1: Value = Value::splat(1.0);
pub const VALUE_0: Value = Value::splat(0.0);
pub const VALUE_05: Value = Value::splat(0.5);
pub const VALUE_M1: Value = Value::splat(-1.0);
pub const VALUE_2: Value = Value::splat(2.0);
pub const VALUE_PI: Value = Value::splat(core::f32::consts::PI);
pub const VALUE_PI_2: Value = Value::splat(core::f32::consts::FRAC_PI_2);
pub const VALUE_TAU: Value = Value::splat(core::f32::consts::TAU);
pub fn glsign(f: Value) -> Value {
if BYTECODE_IMITATES_GLSL {
f.simd_eq(VALUE_0).select(f, f.signum())
} else {
f.signum()
}
}
pub fn glfract(f: Value) -> Value {
if BYTECODE_IMITATES_GLSL {
f - f.floor()
} else {
f.fract()
}
}
#[derive(Clone, Debug)]
pub(crate) struct PointInterpreter<'csg> {
value_map: Vec<Value>,
csg: &'csg CSG,
}
impl PointInterpreter<'_> {
fn load(&self, consider: SSAInput) -> Value {
match consider {
SSAInput::Register(r) => self.value_map[r as usize],
SSAInput::Constant(c) => Value::splat(c),
}
}
fn store(&mut self, location: u32, value: Value) {
self.value_map[location as usize] = value;
}
}
impl<'csg> PointInterpreter<'csg> {
pub(crate) fn new(csg: &'csg CSG) -> Self {
PointInterpreter {
value_map: vec![VALUE_0; csg.parts.last_output as usize],
csg,
}
}
fn clear_stacks(&mut self) {
self.value_map = vec![VALUE_0; self.csg.parts.last_output as usize];
}
fn param_one(&mut self, instruction: &SSAInstruction, func: impl Fn(Value) -> Value) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
self.store(instruction.outputs[i], func(val_a));
}
}
fn param_two(&mut self, instruction: &SSAInstruction, func: impl Fn(Value, Value) -> Value) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
self.store(instruction.outputs[i], func(val_a, val_b));
}
}
fn param_three(
&mut self,
instruction: &SSAInstruction,
func: impl Fn(Value, Value, Value) -> Value,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
self.store(instruction.outputs[i], func(val_a, val_b, val_c));
}
}
fn param_four(
&mut self,
instruction: &SSAInstruction,
func: impl Fn(Value, Value, Value, Value) -> Value,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
let val_d = self.load(instruction.inputs[i + (instruction.opcode.size * 3) as usize]);
self.store(instruction.outputs[i], func(val_a, val_b, val_c, val_d));
}
}
// cargo asm "tape-drive::interpreter::PointInterpreter::scene" --no-color
// --rust > scene.asm
pub(crate) fn scene(&mut self, px: Value, py: Value, pz: Value, time: Value) -> Value {
self.clear_stacks();
for instruction in &self.csg.parts.tape {
use SSAOpcode::*;
match instruction.opcode.opcode {
SSAReturn => {
return self.load(instruction.inputs[0]);
},
SSAPosition => {
self.store(instruction.outputs[0], px);
self.store(instruction.outputs[1], py);
self.store(instruction.outputs[2], pz);
self.store(instruction.outputs[3], time);
},
SSAAdd => {
self.param_two(instruction, |val_a, val_b| val_a + val_b);
},
SSASub => {
self.param_two(instruction, |val_a, val_b| val_a - val_b);
},
SSAMul => {
self.param_two(instruction, |val_a, val_b| val_a * val_b);
},
SSADiv => {
self.param_two(instruction, |val_a, val_b| val_a / val_b);
},
SSAMod => {
self.param_two(instruction, |val_a, val_b| val_a % val_b);
},
SSAAtan2 => self.param_two(instruction, |val_a, val_b| {
let mut val_a = val_a.to_array();
let val_b = val_b.to_array();
for i in 0..Value::LEN {
val_a[i] = val_a[i].atan2(val_b[i]);
}
Value::from_array(val_a)
}),
SSAMin => {
self.param_two(instruction, |val_a, val_b| val_a.simd_min(val_b));
},
SSAMinMaterial => todo!(),
SSAMax => {
self.param_two(instruction, |val_a, val_b| val_a.simd_max(val_b));
},
SSAMaxMaterial => todo!(),
SSADot => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_a[0] * val_b[0],
2 => (val_a[0] * val_b[0]) + (val_a[1] * val_b[1]),
3 => {
(val_a[0] * val_b[0])
+ (val_a[1] * val_b[1])
+ (val_a[2] * val_b[2])
},
4 => {
(val_a[0] * val_b[0])
+ (val_a[1] * val_b[1])
+ (val_a[2] * val_b[2])
+ (val_a[3] * val_b[3])
},
_ => unreachable!(),
},
);
},
SSALength => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_a[0],
2 => ((val_a[0] * val_a[0]) + (val_a[1] * val_a[1])).sqrt(),
3 => ((val_a[0] * val_a[0])
+ (val_a[1] * val_a[1])
+ (val_a[2] * val_a[2]))
.sqrt(),
4 => ((val_a[0] * val_a[0])
+ (val_a[1] * val_a[1])
+ (val_a[2] * val_a[2])
+ (val_a[3] * val_a[3]))
.sqrt(),
_ => unreachable!(),
},
);
},
SSADistance => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_b[0] - val_a[0],
2 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1])))
.sqrt(),
3 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1]))
+ ((val_a[2] - val_b[2]) * (val_a[2] - val_b[2]))
+ ((val_a[3] - val_b[3]) * (val_a[3] - val_b[3])))
.sqrt(),
4 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1]))
+ ((val_a[2] - val_b[2]) * (val_a[2] - val_b[2]))
+ ((val_a[3] - val_b[3]) * (val_a[3] - val_b[3]))
+ ((val_a[4] - val_b[4]) * (val_a[4] - val_b[4])))
.sqrt(),
_ => unreachable!(),
},
);
},
SSANegate => {
self.param_one(instruction, |val_a| -val_a);
},
SSARound => {
self.param_one(instruction, |val_a| val_a.round());
},
SSAAbs => {
self.param_one(instruction, |val_a| val_a.abs());
},
SSAFloor => {
self.param_one(instruction, |val_a| val_a.floor());
},
SSACeil => {
self.param_one(instruction, |val_a| val_a.ceil());
},
SSAFract => {
self.param_one(instruction, |val_a| glfract(val_a));
},
SSASin => {
self.param_one(instruction, |val_a| val_a.sin());
},
SSACos => {
self.param_one(instruction, |val_a| val_a.cos());
},
SSATan => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.tan()))
});
},
SSAAsin => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.asin()))
});
},
SSAAcos => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.acos()))
});
},
SSAAtan => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.atan()))
});
},
SSAExp => {
self.param_one(instruction, |val_a| val_a.exp());
},
SSALog => {
self.param_one(instruction, |val_a| val_a.ln());
},
SSASqrt => {
self.param_one(instruction, |val_a| val_a.sqrt());
},
SSASquare => {
self.param_one(instruction, |val_a| val_a * val_a);
},
SSACube => {
self.param_one(instruction, |val_a| val_a * val_a * val_a);
},
SSASmoothMin => {
self.param_three(instruction, |d1, d2, k| {
let h =
(VALUE_05 + (VALUE_05 * (d2 - d1) / k)).simd_clamp(VALUE_0, VALUE_1);
return ((d2 * (VALUE_1 - h)) + (d1 * h)) - k * h * (VALUE_1 - h);
});
},
SSASmoothMax => {
self.param_three(instruction, |d1, d2, k| {
let h =
(VALUE_05 - (VALUE_05 * (d2 + d1) / k)).simd_clamp(VALUE_0, VALUE_1);
return ((d2 * (VALUE_1 - h)) + (-d1 * h)) + k * h * (VALUE_1 - h);
});
},
SSASmoothMinMaterial => todo!(),
SSASmoothMaxMaterial => todo!(),
SSAClamp => {
self.param_three(instruction, |val_a, val_b, val_c| {
val_a.simd_clamp(val_b, val_c)
});
},
SSAMix => {
self.param_three(instruction, |val_a, val_b, val_c| {
(val_a * (VALUE_1 - val_c)) + (val_b * val_c)
});
},
SSAFMA => {
self.param_three(instruction, |val_a, val_b, val_c| {
val_a.mul_add(val_b, val_c)
});
},
SSASDFSphere => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let radius = self.load(instruction.inputs[3]);
self.store(
instruction.outputs[0],
((pos_x * pos_x) + (pos_y * pos_y) + (pos_z * pos_z)).sqrt() - radius,
);
},
SSASDFBox => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let rad_x = self.load(instruction.inputs[3]);
let rad_y = self.load(instruction.inputs[4]);
let rad_z = self.load(instruction.inputs[5]);
let qx = pos_x.abs() - rad_x;
let qy = pos_y.abs() - rad_y;
let qz = pos_z.abs() - rad_z;
let qxmax = qx.simd_max(VALUE_0);
let qymax = qy.simd_max(VALUE_0);
let qzmax = qz.simd_max(VALUE_0);
self.store(
instruction.outputs[0],
((qxmax * qxmax) + (qymax * qymax) + (qzmax * qzmax)).sqrt()
+ qx.simd_max(qy.simd_max(qz)).simd_min(VALUE_0),
);
},
SSASDFTorus => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let radius1 = self.load(instruction.inputs[3]);
let radius2 = self.load(instruction.inputs[4]);
let q = ((pos_x * pos_x) + (pos_z * pos_z)).sqrt() - radius1;
self.store(
instruction.outputs[0],
((q * q) + (pos_y * pos_y)).sqrt() - radius2,
);
},
SSACompare => {
self.param_two(instruction, |val_a, val_b| {
val_a
.simd_gt(val_b)
.select(VALUE_1, val_a.simd_lt(val_b).select(VALUE_M1, VALUE_0))
});
},
SSAAnd => {
self.param_two(instruction, |val_a, val_b| {
val_a.simd_eq(VALUE_0).select(val_a, val_b)
});
},
SSAOr => {
self.param_two(instruction, |val_a, val_b| {
val_a.simd_eq(VALUE_0).select(val_b, val_a)
});
},
SSARecip => {
self.param_one(instruction, |val_a| val_a.recip());
},
SSANot => {
self.param_one(instruction, |val_a| {
val_a.simd_eq(VALUE_0).select(VALUE_1, VALUE_0)
});
},
SSAStop => return VALUE_0,
}
}
return VALUE_NAN;
}
}
#[derive(Clone, Debug)]
pub(crate) struct IntervalInterpreter<'csg> {
pub struct IntervalInterpreter<'csg> {
value_map: Vec<Interval>,
csg: &'csg CSG,
csg: &'csg SSATape,
}
impl IntervalInterpreter<'_> {
@@ -422,15 +26,15 @@ impl IntervalInterpreter<'_> {
}
impl<'csg> IntervalInterpreter<'csg> {
pub(crate) fn new(csg: &'csg CSG) -> Self {
pub fn new(csg: &'csg SSATape) -> Self {
IntervalInterpreter {
value_map: vec![Interval::ZERO; csg.parts.last_output as usize],
value_map: vec![Interval::ZERO; csg.last_output as usize],
csg,
}
}
fn clear_stacks(&mut self) {
self.value_map = vec![Interval::ZERO; self.csg.parts.last_output as usize];
self.value_map = vec![Interval::ZERO; self.csg.last_output as usize];
}
fn param_one(&mut self, instruction: &SSAInstruction, func: impl Fn(Interval) -> Interval) {
@@ -482,7 +86,7 @@ impl<'csg> IntervalInterpreter<'csg> {
// cargo asm "tape-drive::interpreter::IntervalInterpreter::scene" --no-color
// --rust > scene.asm
pub(crate) fn scene(
pub fn scene(
&mut self,
px: Interval,
py: Interval,
@@ -491,7 +95,7 @@ impl<'csg> IntervalInterpreter<'csg> {
) -> Interval {
self.clear_stacks();
for instruction in &self.csg.parts.tape {
for instruction in &self.csg.tape {
use SSAOpcode::*;
match instruction.opcode.opcode {
SSAReturn => {
+35
View File
@@ -0,0 +1,35 @@
use std::simd::{StdFloat, cmp::SimdPartialEq, num::SimdFloat};
use crate::BYTECODE_IMITATES_GLSL;
pub mod interval;
pub mod point;
pub type Value = std::simd::f32x8;
pub type Mask = std::simd::mask32x8;
pub const VALUE_NAN: Value = Value::splat(core::f32::NAN);
pub const VALUE_1: Value = Value::splat(1.0);
pub const VALUE_0: Value = Value::splat(0.0);
pub const VALUE_05: Value = Value::splat(0.5);
pub const VALUE_M1: Value = Value::splat(-1.0);
pub const VALUE_2: Value = Value::splat(2.0);
pub const VALUE_PI: Value = Value::splat(core::f32::consts::PI);
pub const VALUE_PI_2: Value = Value::splat(core::f32::consts::FRAC_PI_2);
pub const VALUE_TAU: Value = Value::splat(core::f32::consts::TAU);
pub fn glsign(f: Value) -> Value {
if BYTECODE_IMITATES_GLSL {
f.simd_eq(VALUE_0).select(f, f.signum())
} else {
f.signum()
}
}
pub fn glfract(f: Value) -> Value {
if BYTECODE_IMITATES_GLSL {
f - f.floor()
} else {
f.fract()
}
}
+373
View File
@@ -0,0 +1,373 @@
use std::simd::{
StdFloat,
cmp::{SimdPartialEq, SimdPartialOrd},
num::SimdFloat,
};
use crate::{
interpreters::{VALUE_0, VALUE_1, VALUE_05, VALUE_M1, VALUE_NAN, Value, glfract},
ssa::{SSAInput, SSAInstruction, SSAOpcode, SSATape},
};
#[derive(Clone, Debug)]
pub struct PointInterpreter<'csg> {
value_map: Vec<Value>,
csg: &'csg SSATape,
}
impl PointInterpreter<'_> {
fn load(&self, consider: SSAInput) -> Value {
match consider {
SSAInput::Register(r) => self.value_map[r as usize],
SSAInput::Constant(c) => Value::splat(c),
}
}
fn store(&mut self, location: u32, value: Value) {
self.value_map[location as usize] = value;
}
}
impl<'csg> PointInterpreter<'csg> {
pub fn new(csg: &'csg SSATape) -> Self {
PointInterpreter {
value_map: vec![VALUE_0; csg.last_output as usize],
csg,
}
}
fn clear_stacks(&mut self) {
self.value_map = vec![VALUE_0; self.csg.last_output as usize];
}
fn param_one(&mut self, instruction: &SSAInstruction, func: impl Fn(Value) -> Value) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
self.store(instruction.outputs[i], func(val_a));
}
}
fn param_two(&mut self, instruction: &SSAInstruction, func: impl Fn(Value, Value) -> Value) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
self.store(instruction.outputs[i], func(val_a, val_b));
}
}
fn param_three(
&mut self,
instruction: &SSAInstruction,
func: impl Fn(Value, Value, Value) -> Value,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
self.store(instruction.outputs[i], func(val_a, val_b, val_c));
}
}
fn param_four(
&mut self,
instruction: &SSAInstruction,
func: impl Fn(Value, Value, Value, Value) -> Value,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = self.load(instruction.inputs[i]);
let val_b = self.load(instruction.inputs[i + instruction.opcode.size as usize]);
let val_c = self.load(instruction.inputs[i + (instruction.opcode.size * 2) as usize]);
let val_d = self.load(instruction.inputs[i + (instruction.opcode.size * 3) as usize]);
self.store(instruction.outputs[i], func(val_a, val_b, val_c, val_d));
}
}
// cargo asm "tape-drive::interpreter::PointInterpreter::scene" --no-color
// --rust > scene.asm
pub fn scene(&mut self, px: Value, py: Value, pz: Value, time: Value) -> Value {
self.clear_stacks();
for instruction in &self.csg.tape {
use SSAOpcode::*;
match instruction.opcode.opcode {
SSAReturn => {
return self.load(instruction.inputs[0]);
},
SSAPosition => {
self.store(instruction.outputs[0], px);
self.store(instruction.outputs[1], py);
self.store(instruction.outputs[2], pz);
self.store(instruction.outputs[3], time);
},
SSAAdd => {
self.param_two(instruction, |val_a, val_b| val_a + val_b);
},
SSASub => {
self.param_two(instruction, |val_a, val_b| val_a - val_b);
},
SSAMul => {
self.param_two(instruction, |val_a, val_b| val_a * val_b);
},
SSADiv => {
self.param_two(instruction, |val_a, val_b| val_a / val_b);
},
SSAMod => {
self.param_two(instruction, |val_a, val_b| val_a % val_b);
},
SSAAtan2 => self.param_two(instruction, |val_a, val_b| {
let mut val_a = val_a.to_array();
let val_b = val_b.to_array();
for i in 0..Value::LEN {
val_a[i] = val_a[i].atan2(val_b[i]);
}
Value::from_array(val_a)
}),
SSAMin => {
self.param_two(instruction, |val_a, val_b| val_a.simd_min(val_b));
},
SSAMinMaterial => todo!(),
SSAMax => {
self.param_two(instruction, |val_a, val_b| val_a.simd_max(val_b));
},
SSAMaxMaterial => todo!(),
SSADot => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_a[0] * val_b[0],
2 => (val_a[0] * val_b[0]) + (val_a[1] * val_b[1]),
3 => {
(val_a[0] * val_b[0])
+ (val_a[1] * val_b[1])
+ (val_a[2] * val_b[2])
},
4 => {
(val_a[0] * val_b[0])
+ (val_a[1] * val_b[1])
+ (val_a[2] * val_b[2])
+ (val_a[3] * val_b[3])
},
_ => unreachable!(),
},
);
},
SSALength => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_a[0],
2 => ((val_a[0] * val_a[0]) + (val_a[1] * val_a[1])).sqrt(),
3 => ((val_a[0] * val_a[0])
+ (val_a[1] * val_a[1])
+ (val_a[2] * val_a[2]))
.sqrt(),
4 => ((val_a[0] * val_a[0])
+ (val_a[1] * val_a[1])
+ (val_a[2] * val_a[2])
+ (val_a[3] * val_a[3]))
.sqrt(),
_ => unreachable!(),
},
);
},
SSADistance => {
let val_a = (0..instruction.opcode.size)
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| self.load(instruction.inputs[i as usize]))
.collect::<Vec<_>>();
self.store(
instruction.outputs[0],
match instruction.opcode.size {
1 => val_b[0] - val_a[0],
2 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1])))
.sqrt(),
3 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1]))
+ ((val_a[2] - val_b[2]) * (val_a[2] - val_b[2]))
+ ((val_a[3] - val_b[3]) * (val_a[3] - val_b[3])))
.sqrt(),
4 => (((val_a[0] - val_b[0]) * (val_a[0] - val_b[0]))
+ ((val_a[1] - val_b[1]) * (val_a[1] - val_b[1]))
+ ((val_a[2] - val_b[2]) * (val_a[2] - val_b[2]))
+ ((val_a[3] - val_b[3]) * (val_a[3] - val_b[3]))
+ ((val_a[4] - val_b[4]) * (val_a[4] - val_b[4])))
.sqrt(),
_ => unreachable!(),
},
);
},
SSANegate => {
self.param_one(instruction, |val_a| -val_a);
},
SSARound => {
self.param_one(instruction, |val_a| val_a.round());
},
SSAAbs => {
self.param_one(instruction, |val_a| val_a.abs());
},
SSAFloor => {
self.param_one(instruction, |val_a| val_a.floor());
},
SSACeil => {
self.param_one(instruction, |val_a| val_a.ceil());
},
SSAFract => {
self.param_one(instruction, |val_a| glfract(val_a));
},
SSASin => {
self.param_one(instruction, |val_a| val_a.sin());
},
SSACos => {
self.param_one(instruction, |val_a| val_a.cos());
},
SSATan => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.tan()))
});
},
SSAAsin => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.asin()))
});
},
SSAAcos => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.acos()))
});
},
SSAAtan => {
self.param_one(instruction, |val_a| {
Value::from_array(val_a.to_array().map(|f| f.atan()))
});
},
SSAExp => {
self.param_one(instruction, |val_a| val_a.exp());
},
SSALog => {
self.param_one(instruction, |val_a| val_a.ln());
},
SSASqrt => {
self.param_one(instruction, |val_a| val_a.sqrt());
},
SSASquare => {
self.param_one(instruction, |val_a| val_a * val_a);
},
SSACube => {
self.param_one(instruction, |val_a| val_a * val_a * val_a);
},
SSASmoothMin => {
self.param_three(instruction, |d1, d2, k| {
let h =
(VALUE_05 + (VALUE_05 * (d2 - d1) / k)).simd_clamp(VALUE_0, VALUE_1);
return ((d2 * (VALUE_1 - h)) + (d1 * h)) - k * h * (VALUE_1 - h);
});
},
SSASmoothMax => {
self.param_three(instruction, |d1, d2, k| {
let h =
(VALUE_05 - (VALUE_05 * (d2 + d1) / k)).simd_clamp(VALUE_0, VALUE_1);
return ((d2 * (VALUE_1 - h)) + (-d1 * h)) + k * h * (VALUE_1 - h);
});
},
SSASmoothMinMaterial => todo!(),
SSASmoothMaxMaterial => todo!(),
SSAClamp => {
self.param_three(instruction, |val_a, val_b, val_c| {
val_a.simd_clamp(val_b, val_c)
});
},
SSAMix => {
self.param_three(instruction, |val_a, val_b, val_c| {
(val_a * (VALUE_1 - val_c)) + (val_b * val_c)
});
},
SSAFMA => {
self.param_three(instruction, |val_a, val_b, val_c| {
val_a.mul_add(val_b, val_c)
});
},
SSASDFSphere => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let radius = self.load(instruction.inputs[3]);
self.store(
instruction.outputs[0],
((pos_x * pos_x) + (pos_y * pos_y) + (pos_z * pos_z)).sqrt() - radius,
);
},
SSASDFBox => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let rad_x = self.load(instruction.inputs[3]);
let rad_y = self.load(instruction.inputs[4]);
let rad_z = self.load(instruction.inputs[5]);
let qx = pos_x.abs() - rad_x;
let qy = pos_y.abs() - rad_y;
let qz = pos_z.abs() - rad_z;
let qxmax = qx.simd_max(VALUE_0);
let qymax = qy.simd_max(VALUE_0);
let qzmax = qz.simd_max(VALUE_0);
self.store(
instruction.outputs[0],
((qxmax * qxmax) + (qymax * qymax) + (qzmax * qzmax)).sqrt()
+ qx.simd_max(qy.simd_max(qz)).simd_min(VALUE_0),
);
},
SSASDFTorus => {
let pos_x = self.load(instruction.inputs[0]);
let pos_y = self.load(instruction.inputs[1]);
let pos_z = self.load(instruction.inputs[2]);
let radius1 = self.load(instruction.inputs[3]);
let radius2 = self.load(instruction.inputs[4]);
let q = ((pos_x * pos_x) + (pos_z * pos_z)).sqrt() - radius1;
self.store(
instruction.outputs[0],
((q * q) + (pos_y * pos_y)).sqrt() - radius2,
);
},
SSACompare => {
self.param_two(instruction, |val_a, val_b| {
val_a
.simd_gt(val_b)
.select(VALUE_1, val_a.simd_lt(val_b).select(VALUE_M1, VALUE_0))
});
},
SSAAnd => {
self.param_two(instruction, |val_a, val_b| {
val_a.simd_eq(VALUE_0).select(val_a, val_b)
});
},
SSAOr => {
self.param_two(instruction, |val_a, val_b| {
val_a.simd_eq(VALUE_0).select(val_b, val_a)
});
},
SSARecip => {
self.param_one(instruction, |val_a| val_a.recip());
},
SSANot => {
self.param_one(instruction, |val_a| {
val_a.simd_eq(VALUE_0).select(VALUE_1, VALUE_0)
});
},
SSAStop => return VALUE_0,
}
}
return VALUE_NAN;
}
}
+9
View File
@@ -0,0 +1,9 @@
#![feature(portable_simd)]
pub mod instruction_set;
pub mod interpreters;
mod spirv_compilers;
pub mod ssa;
pub mod types;
pub mod vm;
const BYTECODE_IMITATES_GLSL: bool = false;
+876
View File
@@ -0,0 +1,876 @@
use foldhash::{HashMap, HashMapExt};
use rspirv::{dr::Builder, spirv};
use crate::{
spirv_compilers::SpirVTypes,
ssa::{SSAInput, SSAInstruction, SSAOpcode, SSATape},
};
pub(crate) fn compile_gradient_function(
b: &mut Builder,
tape: &SSATape,
types: SpirVTypes,
function_id: Option<spirv::Word>,
) {
let _scene = b
.begin_function(
types.float,
function_id,
//spirv::FunctionControl::DONT_INLINE
spirv::FunctionControl::INLINE
| spirv::FunctionControl::PURE
| spirv::FunctionControl::CONST,
types.point_fn_type,
)
.unwrap();
let pos_p = b.function_parameter(types.vec4p).unwrap();
b.begin_block(None).unwrap();
let pos = b.load(types.vec4, None, pos_p, None, []).unwrap();
let mut mapping = HashMap::<u32, u32>::new();
for (line, instruction) in tape.tape.iter().enumerate() {
use SSAOpcode::*;
use rspirv::dr::Operand::IdRef;
b.line(types.jit_string, line as u32, 0);
fn input_resolve(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &HashMap<u32, u32>,
value: SSAInput,
) -> u32 {
match value {
SSAInput::Register(r) => mapping[&r],
SSAInput::Constant(c) => b.constant_bit32(float, c.to_bits()),
}
}
fn param_one(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
mapping.insert(instruction.outputs[i], func(b, val_a));
}
}
fn param_two(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b));
}
}
fn param_three(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
let val_c = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 2)],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b, val_c));
}
}
fn param_four(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
let val_c = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 2)],
);
let val_d = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 3)],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b, val_c, val_d));
}
}
match instruction.opcode.opcode {
SSAStop => {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
b.ret_value(zero).unwrap();
},
SSAReturn => {
let value = input_resolve(types.float, b, &mapping, instruction.inputs[0]);
b.ret_value(value).unwrap();
},
SSAPosition => {
mapping.insert(
instruction.outputs[0],
b.composite_extract(types.float, None, pos, [0]).unwrap(),
);
mapping.insert(
instruction.outputs[1],
b.composite_extract(types.float, None, pos, [1]).unwrap(),
);
mapping.insert(
instruction.outputs[2],
b.composite_extract(types.float, None, pos, [2]).unwrap(),
);
mapping.insert(
instruction.outputs[3],
b.composite_extract(types.float, None, pos, [3]).unwrap(),
);
},
SSAAdd => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_add(types.float, None, val_a, val_b).unwrap(),
);
},
SSASub => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_sub(types.float, None, val_a, val_b).unwrap(),
);
},
SSAMul => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_mul(types.float, None, val_a, val_b).unwrap(),
);
},
SSADiv => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_div(types.float, None, val_a, val_b).unwrap(),
);
},
SSAMod => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_mod(types.float, None, val_a, val_b).unwrap(),
);
},
SSAAtan2 => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Atan2 as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMin => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMin as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMinMaterial => todo!(),
SSAMax => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMaxMaterial => todo!(),
SSADot => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let dot = if instruction.opcode.size == 1 {
b.f_mul(types.float, None, val_a[0], val_b[0]).unwrap()
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
let val_b = b.composite_construct(vector, None, val_b).unwrap();
b.dot(types.float, None, val_a, val_b).unwrap()
};
mapping.insert(instruction.outputs[0], dot);
},
SSALength => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let length = if instruction.opcode.size == 1 {
val_a[0]
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(val_a)],
)
.unwrap()
};
mapping.insert(instruction.outputs[0], length);
},
SSADistance => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let distance = if instruction.opcode.size == 1 {
b.f_sub(types.float, None, val_b[0], val_a[0]).unwrap()
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
let val_b = b.composite_construct(vector, None, val_b).unwrap();
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Distance as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
};
mapping.insert(instruction.outputs[0], distance);
},
SSARecip => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
b.f_div(types.float, None, one, val_a).unwrap()
});
},
SSANegate => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.f_negate(types.float, None, val_a).unwrap()
});
},
SSARound => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Round as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAbs => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FAbs as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAFloor => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Floor as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSACeil => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Ceil as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAFract => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Fract as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASin => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sin as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSACos => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Cos as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSATan => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Tan as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAsin => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Asin as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAcos => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Acos as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAtan => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Atan as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAExp => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Exp as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSALog => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Log as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASqrt => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sqrt as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASquare => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.f_mul(types.float, None, val_a, val_a).unwrap()
});
},
SSACube => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let square = b.f_mul(types.float, None, val_a, val_a).unwrap();
b.f_mul(types.float, None, val_a, square).unwrap()
});
},
SSASmoothMin => {
param_three(types.float, b, &mut mapping, instruction, |b, d1, d2, k| {
let half_const = b.constant_bit32(types.float, (0.5f32).to_bits());
let zero_const = b.constant_bit32(types.float, (0.0f32).to_bits());
let one_const = b.constant_bit32(types.float, (1.0f32).to_bits());
let sub = b.f_sub(types.float, None, d2, d1).unwrap();
let mul_half = b.f_mul(types.float, None, sub, half_const).unwrap();
let div_k = b.f_div(types.float, None, mul_half, k).unwrap();
let add_half = b.f_add(types.float, None, div_k, half_const).unwrap();
let h = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(add_half), IdRef(zero_const), IdRef(one_const)],
)
.unwrap();
let negh = b.f_sub(types.float, None, one_const, h).unwrap();
let h_negh = b.f_mul(types.float, None, h, negh).unwrap();
let kh_negh = b.f_mul(types.float, None, k, h_negh).unwrap();
let mix = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(d2), IdRef(d1), IdRef(h)],
)
.unwrap();
b.f_sub(types.float, None, mix, kh_negh).unwrap()
});
},
SSASmoothMax => {
param_three(types.float, b, &mut mapping, instruction, |b, d1, d2, k| {
let half_const = b.constant_bit32(types.float, (0.5f32).to_bits());
let zero_const = b.constant_bit32(types.float, (0.0f32).to_bits());
let one_const = b.constant_bit32(types.float, (1.0f32).to_bits());
let sub = b.f_add(types.float, None, d2, d1).unwrap();
let mul_half = b.f_mul(types.float, None, sub, half_const).unwrap();
let div_k = b.f_div(types.float, None, mul_half, k).unwrap();
let add_half = b.f_sub(types.float, None, half_const, div_k).unwrap();
let h = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(add_half), IdRef(zero_const), IdRef(one_const)],
)
.unwrap();
let negh = b.f_sub(types.float, None, one_const, h).unwrap();
let h_negh = b.f_mul(types.float, None, h, negh).unwrap();
let kh_negh = b.f_mul(types.float, None, k, h_negh).unwrap();
let negate = b.f_negate(types.float, None, d1).unwrap();
let mix = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(d2), IdRef(negate), IdRef(h)],
)
.unwrap();
b.f_add(types.float, None, mix, kh_negh).unwrap()
});
},
SSASmoothMinMaterial => todo!(),
SSASmoothMaxMaterial => todo!(),
SSAClamp => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSAMix => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSAFMA => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Fma as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSASDFSphere => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos = b.composite_construct(types.vec3, None, pos_part).unwrap();
let radius =
input_resolve(types.float, b, &mapping, instruction.inputs[3 as usize]);
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(pos)],
)
.unwrap();
let sphere = b.f_sub(types.float, None, length, radius).unwrap();
mapping.insert(instruction.outputs[0], sphere);
},
SSASDFBox => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos = b.composite_construct(types.vec3, None, pos_part).unwrap();
let dim_part = (3..6)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let dim = b.composite_construct(types.vec3, None, dim_part).unwrap();
let abs = b
.ext_inst(
types.vec3,
None,
types.glsl,
spirv::GLOp::FAbs as u32,
[IdRef(pos)],
)
.unwrap();
let q = b.f_sub(types.vec3, None, abs, dim).unwrap();
let zero = b.constant_bit32(types.float, 0);
let zero_vec3 = b
.composite_construct(types.vec3, None, [zero, zero, zero])
.unwrap();
let q_limit = b
.ext_inst(
types.vec3,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(q), IdRef(zero_vec3)],
)
.unwrap();
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(q_limit)],
)
.unwrap();
let q_x = b.composite_extract(types.float, None, q, [0]).unwrap();
let q_y = b.composite_extract(types.float, None, q, [1]).unwrap();
let q_z = b.composite_extract(types.float, None, q, [2]).unwrap();
let max1 = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(q_x), IdRef(q_y)],
)
.unwrap();
let max2 = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(max1), IdRef(q_z)],
)
.unwrap();
let min = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(max2), IdRef(zero)],
)
.unwrap();
mapping.insert(
instruction.outputs[0],
b.f_add(types.float, None, length, min).unwrap(),
);
},
SSASDFTorus => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos_vec2 = b
.composite_construct(types.vec2, None, [pos_part[0], pos_part[2]])
.unwrap();
let rad1 = input_resolve(types.float, b, &mapping, instruction.inputs[3 as usize]);
let rad2 = input_resolve(types.float, b, &mapping, instruction.inputs[4 as usize]);
let dot = b.dot(types.float, None, pos_vec2, pos_vec2).unwrap();
let sqrt = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sqrt as u32,
[IdRef(dot)],
)
.unwrap();
let subtx = b.f_sub(types.float, None, sqrt, rad1).unwrap();
let q = b
.composite_construct(types.vec2, None, [subtx, pos_part[1]])
.unwrap();
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(q)],
)
.unwrap();
mapping.insert(
instruction.outputs[0],
b.f_sub(types.float, None, length, rad2).unwrap(),
);
},
SSACompare => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let nan = b.constant_bit32(types.float, f32::NAN.to_bits());
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
let onen = b.constant_bit32(types.float, (-1.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, val_b).unwrap();
let less = b.f_ord_less_than(types.bool, None, val_a, val_b).unwrap();
let more = b
.f_ord_greater_than(types.bool, None, val_a, val_b)
.unwrap();
let select_less = b.select(types.float, None, less, onen, nan).unwrap();
let select_more =
b.select(types.float, None, more, one, select_less).unwrap();
let select_eq = b
.select(types.float, None, equal, zero, select_more)
.unwrap();
select_eq
},
);
},
SSAAnd => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, val_a, val_b).unwrap()
},
);
},
SSAOr => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, val_b, val_a).unwrap()
},
);
},
SSANot => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, one, zero).unwrap()
});
},
}
}
b.end_function().unwrap();
}
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
pub(crate) mod gradient;
pub(crate) mod point;
//pub(crate) mod interval;
#[derive(Debug, Clone, Copy)]
pub(crate) struct SpirVTypes {
pub glsl: u32,
pub void: u32,
pub float: u32,
pub bool: u32,
pub vec2: u32,
pub vec3: u32,
pub vec4: u32,
pub vec4p: u32,
pub point_fn_type: u32,
pub interval_fn_type: u32,
pub jit_string: u32,
}
+876
View File
@@ -0,0 +1,876 @@
use foldhash::{HashMap, HashMapExt};
use rspirv::{dr::Builder, spirv};
use crate::{
spirv_compilers::SpirVTypes,
ssa::{SSAInput, SSAInstruction, SSAOpcode, SSATape},
};
pub(crate) fn compile_point_function(
b: &mut Builder,
tape: &SSATape,
types: SpirVTypes,
function_id: Option<spirv::Word>,
) {
let _scene = b
.begin_function(
types.float,
function_id,
//spirv::FunctionControl::DONT_INLINE
spirv::FunctionControl::INLINE
| spirv::FunctionControl::PURE
| spirv::FunctionControl::CONST,
types.point_fn_type,
)
.unwrap();
let pos_p = b.function_parameter(types.vec4p).unwrap();
b.begin_block(None).unwrap();
let pos = b.load(types.vec4, None, pos_p, None, []).unwrap();
let mut mapping = HashMap::<u32, u32>::new();
for (line, instruction) in tape.tape.iter().enumerate() {
use SSAOpcode::*;
use rspirv::dr::Operand::IdRef;
b.line(types.jit_string, line as u32, 0);
fn input_resolve(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &HashMap<u32, u32>,
value: SSAInput,
) -> u32 {
match value {
SSAInput::Register(r) => mapping[&r],
SSAInput::Constant(c) => b.constant_bit32(float, c.to_bits()),
}
}
fn param_one(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
mapping.insert(instruction.outputs[i], func(b, val_a));
}
}
fn param_two(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b));
}
}
fn param_three(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
let val_c = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 2)],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b, val_c));
}
}
fn param_four(
float: u32,
b: &mut rspirv::dr::Builder,
mapping: &mut HashMap<u32, u32>,
instruction: &SSAInstruction,
func: impl Fn(&mut rspirv::dr::Builder, u32, u32, u32, u32) -> u32,
) {
for i in 0..instruction.opcode.size as usize {
let val_a = input_resolve(float, b, &mapping, instruction.inputs[i]);
let val_b = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + instruction.opcode.size as usize],
);
let val_c = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 2)],
);
let val_d = input_resolve(
float,
b,
&mapping,
instruction.inputs[i + (instruction.opcode.size as usize * 3)],
);
mapping.insert(instruction.outputs[i], func(b, val_a, val_b, val_c, val_d));
}
}
match instruction.opcode.opcode {
SSAStop => {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
b.ret_value(zero).unwrap();
},
SSAReturn => {
let value = input_resolve(types.float, b, &mapping, instruction.inputs[0]);
b.ret_value(value).unwrap();
},
SSAPosition => {
mapping.insert(
instruction.outputs[0],
b.composite_extract(types.float, None, pos, [0]).unwrap(),
);
mapping.insert(
instruction.outputs[1],
b.composite_extract(types.float, None, pos, [1]).unwrap(),
);
mapping.insert(
instruction.outputs[2],
b.composite_extract(types.float, None, pos, [2]).unwrap(),
);
mapping.insert(
instruction.outputs[3],
b.composite_extract(types.float, None, pos, [3]).unwrap(),
);
},
SSAAdd => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_add(types.float, None, val_a, val_b).unwrap(),
);
},
SSASub => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_sub(types.float, None, val_a, val_b).unwrap(),
);
},
SSAMul => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_mul(types.float, None, val_a, val_b).unwrap(),
);
},
SSADiv => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_div(types.float, None, val_a, val_b).unwrap(),
);
},
SSAMod => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| b.f_mod(types.float, None, val_a, val_b).unwrap(),
);
},
SSAAtan2 => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Atan2 as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMin => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMin as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMinMaterial => todo!(),
SSAMax => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
},
);
},
SSAMaxMaterial => todo!(),
SSADot => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let dot = if instruction.opcode.size == 1 {
b.f_mul(types.float, None, val_a[0], val_b[0]).unwrap()
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
let val_b = b.composite_construct(vector, None, val_b).unwrap();
b.dot(types.float, None, val_a, val_b).unwrap()
};
mapping.insert(instruction.outputs[0], dot);
},
SSALength => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let length = if instruction.opcode.size == 1 {
val_a[0]
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(val_a)],
)
.unwrap()
};
mapping.insert(instruction.outputs[0], length);
},
SSADistance => {
let val_a = (0..instruction.opcode.size)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let val_b = (instruction.opcode.size..(instruction.opcode.size * 2))
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let distance = if instruction.opcode.size == 1 {
b.f_sub(types.float, None, val_b[0], val_a[0]).unwrap()
} else {
let vector = [types.void, types.float, types.vec2, types.vec3, types.vec4]
[instruction.opcode.size as usize];
let val_a = b.composite_construct(vector, None, val_a).unwrap();
let val_b = b.composite_construct(vector, None, val_b).unwrap();
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Distance as u32,
[IdRef(val_a), IdRef(val_b)],
)
.unwrap()
};
mapping.insert(instruction.outputs[0], distance);
},
SSARecip => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
b.f_div(types.float, None, one, val_a).unwrap()
});
},
SSANegate => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.f_negate(types.float, None, val_a).unwrap()
});
},
SSARound => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Round as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAbs => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FAbs as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAFloor => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Floor as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSACeil => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Ceil as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAFract => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Fract as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASin => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sin as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSACos => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Cos as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSATan => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Tan as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAsin => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Asin as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAcos => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Acos as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAAtan => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Atan as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSAExp => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Exp as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSALog => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Log as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASqrt => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sqrt as u32,
[IdRef(val_a)],
)
.unwrap()
});
},
SSASquare => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
b.f_mul(types.float, None, val_a, val_a).unwrap()
});
},
SSACube => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let square = b.f_mul(types.float, None, val_a, val_a).unwrap();
b.f_mul(types.float, None, val_a, square).unwrap()
});
},
SSASmoothMin => {
param_three(types.float, b, &mut mapping, instruction, |b, d1, d2, k| {
let half_const = b.constant_bit32(types.float, (0.5f32).to_bits());
let zero_const = b.constant_bit32(types.float, (0.0f32).to_bits());
let one_const = b.constant_bit32(types.float, (1.0f32).to_bits());
let sub = b.f_sub(types.float, None, d2, d1).unwrap();
let mul_half = b.f_mul(types.float, None, sub, half_const).unwrap();
let div_k = b.f_div(types.float, None, mul_half, k).unwrap();
let add_half = b.f_add(types.float, None, div_k, half_const).unwrap();
let h = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(add_half), IdRef(zero_const), IdRef(one_const)],
)
.unwrap();
let negh = b.f_sub(types.float, None, one_const, h).unwrap();
let h_negh = b.f_mul(types.float, None, h, negh).unwrap();
let kh_negh = b.f_mul(types.float, None, k, h_negh).unwrap();
let mix = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(d2), IdRef(d1), IdRef(h)],
)
.unwrap();
b.f_sub(types.float, None, mix, kh_negh).unwrap()
});
},
SSASmoothMax => {
param_three(types.float, b, &mut mapping, instruction, |b, d1, d2, k| {
let half_const = b.constant_bit32(types.float, (0.5f32).to_bits());
let zero_const = b.constant_bit32(types.float, (0.0f32).to_bits());
let one_const = b.constant_bit32(types.float, (1.0f32).to_bits());
let sub = b.f_add(types.float, None, d2, d1).unwrap();
let mul_half = b.f_mul(types.float, None, sub, half_const).unwrap();
let div_k = b.f_div(types.float, None, mul_half, k).unwrap();
let add_half = b.f_sub(types.float, None, half_const, div_k).unwrap();
let h = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(add_half), IdRef(zero_const), IdRef(one_const)],
)
.unwrap();
let negh = b.f_sub(types.float, None, one_const, h).unwrap();
let h_negh = b.f_mul(types.float, None, h, negh).unwrap();
let kh_negh = b.f_mul(types.float, None, k, h_negh).unwrap();
let negate = b.f_negate(types.float, None, d1).unwrap();
let mix = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(d2), IdRef(negate), IdRef(h)],
)
.unwrap();
b.f_add(types.float, None, mix, kh_negh).unwrap()
});
},
SSASmoothMinMaterial => todo!(),
SSASmoothMaxMaterial => todo!(),
SSAClamp => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FClamp as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSAMix => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMix as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSAFMA => {
param_three(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b, val_c| {
b.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Fma as u32,
[IdRef(val_a), IdRef(val_b), IdRef(val_c)],
)
.unwrap()
},
);
},
SSASDFSphere => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos = b.composite_construct(types.vec3, None, pos_part).unwrap();
let radius =
input_resolve(types.float, b, &mapping, instruction.inputs[3 as usize]);
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(pos)],
)
.unwrap();
let sphere = b.f_sub(types.float, None, length, radius).unwrap();
mapping.insert(instruction.outputs[0], sphere);
},
SSASDFBox => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos = b.composite_construct(types.vec3, None, pos_part).unwrap();
let dim_part = (3..6)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let dim = b.composite_construct(types.vec3, None, dim_part).unwrap();
let abs = b
.ext_inst(
types.vec3,
None,
types.glsl,
spirv::GLOp::FAbs as u32,
[IdRef(pos)],
)
.unwrap();
let q = b.f_sub(types.vec3, None, abs, dim).unwrap();
let zero = b.constant_bit32(types.float, 0);
let zero_vec3 = b
.composite_construct(types.vec3, None, [zero, zero, zero])
.unwrap();
let q_limit = b
.ext_inst(
types.vec3,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(q), IdRef(zero_vec3)],
)
.unwrap();
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(q_limit)],
)
.unwrap();
let q_x = b.composite_extract(types.float, None, q, [0]).unwrap();
let q_y = b.composite_extract(types.float, None, q, [1]).unwrap();
let q_z = b.composite_extract(types.float, None, q, [2]).unwrap();
let max1 = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(q_x), IdRef(q_y)],
)
.unwrap();
let max2 = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(max1), IdRef(q_z)],
)
.unwrap();
let min = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::FMax as u32,
[IdRef(max2), IdRef(zero)],
)
.unwrap();
mapping.insert(
instruction.outputs[0],
b.f_add(types.float, None, length, min).unwrap(),
);
},
SSASDFTorus => {
let pos_part = (0..3)
.map(|i| {
input_resolve(types.float, b, &mapping, instruction.inputs[i as usize])
})
.collect::<Vec<_>>();
let pos_vec2 = b
.composite_construct(types.vec2, None, [pos_part[0], pos_part[2]])
.unwrap();
let rad1 = input_resolve(types.float, b, &mapping, instruction.inputs[3 as usize]);
let rad2 = input_resolve(types.float, b, &mapping, instruction.inputs[4 as usize]);
let dot = b.dot(types.float, None, pos_vec2, pos_vec2).unwrap();
let sqrt = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Sqrt as u32,
[IdRef(dot)],
)
.unwrap();
let subtx = b.f_sub(types.float, None, sqrt, rad1).unwrap();
let q = b
.composite_construct(types.vec2, None, [subtx, pos_part[1]])
.unwrap();
let length = b
.ext_inst(
types.float,
None,
types.glsl,
spirv::GLOp::Length as u32,
[IdRef(q)],
)
.unwrap();
mapping.insert(
instruction.outputs[0],
b.f_sub(types.float, None, length, rad2).unwrap(),
);
},
SSACompare => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let nan = b.constant_bit32(types.float, f32::NAN.to_bits());
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
let onen = b.constant_bit32(types.float, (-1.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, val_b).unwrap();
let less = b.f_ord_less_than(types.bool, None, val_a, val_b).unwrap();
let more = b
.f_ord_greater_than(types.bool, None, val_a, val_b)
.unwrap();
let select_less = b.select(types.float, None, less, onen, nan).unwrap();
let select_more =
b.select(types.float, None, more, one, select_less).unwrap();
let select_eq = b
.select(types.float, None, equal, zero, select_more)
.unwrap();
select_eq
},
);
},
SSAAnd => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, val_a, val_b).unwrap()
},
);
},
SSAOr => {
param_two(
types.float,
b,
&mut mapping,
instruction,
|b, val_a, val_b| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, val_b, val_a).unwrap()
},
);
},
SSANot => {
param_one(types.float, b, &mut mapping, instruction, |b, val_a| {
let zero = b.constant_bit32(types.float, (0.0f32).to_bits());
let one = b.constant_bit32(types.float, (1.0f32).to_bits());
let equal = b.f_ord_equal(types.bool, None, val_a, zero).unwrap();
b.select(types.float, None, equal, one, zero).unwrap()
});
},
}
}
b.end_function().unwrap();
}
+536
View File
@@ -0,0 +1,536 @@
use core::f32;
use rspirv::{binary::Disassemble, dr::Module, spirv};
use crate::{
instruction_set::InstructionSet,
spirv_compilers::{
SpirVTypes,
//gradient::compile_gradient_function,
//interval::compile_interval_function,
point::compile_point_function,
},
};
const JIT_VERSION: u32 = 1;
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum SSAOpcode {
SSAAdd,
SSASub,
SSAMul,
SSADiv,
SSAAtan2,
SSAMin,
SSAMax,
SSACompare,
SSAMod,
SSAAnd,
SSAOr,
SSANegate,
SSAAbs,
SSARecip,
SSASqrt,
SSASquare,
SSAFloor,
SSACeil,
SSARound,
SSASin,
SSACos,
SSATan,
SSAAsin,
SSAAcos,
SSAAtan,
SSAExp,
SSALog,
SSANot,
SSAFract,
SSACube,
SSASmoothMin,
SSASmoothMax,
SSAClamp,
SSAMix,
SSAFMA,
SSADot,
SSALength,
SSADistance,
#[default]
SSAStop,
SSAReturn,
SSAPosition,
SSAMinMaterial,
SSAMaxMaterial,
SSASmoothMinMaterial,
SSASmoothMaxMaterial,
SSASDFSphere,
SSASDFBox,
SSASDFTorus,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct SSAOpcodeSized {
pub opcode: SSAOpcode,
pub size: u8,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SSAInput {
Register(u32),
Constant(f32),
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct SSAInstruction {
pub opcode: SSAOpcodeSized,
pub inputs: Vec<SSAInput>,
pub outputs: Vec<u32>,
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct SSATape {
pub last_output: u32,
pub tape: Vec<SSAInstruction>,
constants: Vec<f32>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
struct GPUOpcode(u8);
pub struct GPUTape {
pub instructions: Vec<u8>,
pub io: Vec<u8>,
pub constants: Vec<f32>,
}
impl SSAOpcodeSized {
const fn output(&self) -> u8 {
use SSAOpcode::*;
match self.opcode {
SSAStop => 0,
SSAReturn => 0,
SSAPosition => 4,
SSAMinMaterial => 1,
SSAMaxMaterial => 1,
SSASmoothMinMaterial => 1,
SSASmoothMaxMaterial => 1,
SSADistance => 1,
SSALength => 1,
SSADot => 1,
SSASDFSphere => 1,
SSASDFBox => 1,
SSASDFTorus => 1,
_ => self.size,
}
}
const fn input(&self) -> u8 {
use SSAOpcode::*;
match self.opcode {
SSAStop => 0,
SSAReturn => 1,
SSAPosition => 0,
SSAMinMaterial => 2,
SSAMaxMaterial => 2,
SSASmoothMinMaterial => 3,
SSASmoothMaxMaterial => 3,
SSASDFSphere => 3 + 1,
SSASDFBox => 3 + 3,
SSASDFTorus => 3 + 2,
SSAAdd | SSASub | SSAMul | SSADiv | SSAAtan2 | SSAMin | SSAMax | SSACompare
| SSAMod | SSAAnd | SSAOr | SSADot | SSADistance => self.size * 2,
SSASmoothMin | SSASmoothMax | SSAClamp | SSAMix | SSAFMA => self.size * 3,
_ => self.size,
}
}
const fn lifetime_elementwise(&self) -> (u8, u8) {
use SSAOpcode::*;
match self.opcode {
SSAStop | SSAReturn | SSAPosition | SSAMinMaterial | SSAMaxMaterial
| SSASmoothMinMaterial | SSASmoothMaxMaterial | SSASDFSphere | SSASDFBox
| SSASDFTorus | SSADot | SSADistance | SSALength => (0, 0),
SSAAdd | SSASub | SSAMul | SSADiv | SSAAtan2 | SSAMin | SSACompare | SSAMod
| SSAAnd | SSAOr => (2, 1),
SSASmoothMin | SSASmoothMax | SSAClamp | SSAMix | SSAFMA => (3, 1),
_ => (1, 1),
}
}
const fn to_raw_opcode(&self) -> GPUOpcode {
use InstructionSet::*;
use SSAOpcode::*;
const fn opcode_drop(inst: InstructionSet, width: u8) -> GPUOpcode {
assert!(width <= 4);
assert!(width > 0);
GPUOpcode(inst as u8 + ((width - 1) << 6))
}
match self.opcode {
SSAStop => opcode_drop(OPReturn, 1),
SSAReturn => opcode_drop(OPReturn, 1),
SSAPosition => opcode_drop(OPPosition, 1),
SSAMinMaterial => opcode_drop(OPMinMaterial, 1),
SSAMaxMaterial => opcode_drop(OPMaxMaterial, 1),
SSASmoothMinMaterial => opcode_drop(OPSmoothMinMaterial, 1),
SSASmoothMaxMaterial => opcode_drop(OPSmoothMaxMaterial, 1),
SSASDFSphere => opcode_drop(OPSDFSphere, 1),
SSASDFBox => opcode_drop(OPSDFBox, 1),
SSASDFTorus => opcode_drop(OPSDFTorus, 1),
SSAAdd => opcode_drop(OPAdd, self.size),
SSASub => opcode_drop(OPSub, self.size),
SSAMul => opcode_drop(OPMul, self.size),
SSADiv => opcode_drop(OPDiv, self.size),
SSAMod => opcode_drop(OPMod, self.size),
SSAAtan2 => opcode_drop(OPAtan2, self.size),
SSAMin => opcode_drop(OPMin, self.size),
SSAMax => opcode_drop(OPMax, self.size),
SSADot => opcode_drop(OPDot, self.size),
SSALength => opcode_drop(OPLength, self.size),
SSADistance => opcode_drop(OPDistance, self.size),
SSANegate => opcode_drop(OPNegate, self.size),
SSARound => opcode_drop(OPRound, self.size),
SSAAbs => opcode_drop(OPAbs, self.size),
SSAFloor => opcode_drop(OPFloor, self.size),
SSACeil => opcode_drop(OPCeil, self.size),
SSAFract => opcode_drop(OPFract, self.size),
SSASin => opcode_drop(OPSin, self.size),
SSACos => opcode_drop(OPCos, self.size),
SSATan => opcode_drop(OPTan, self.size),
SSAAsin => opcode_drop(OPAsin, self.size),
SSAAcos => opcode_drop(OPAcos, self.size),
SSAAtan => opcode_drop(OPAtan, self.size),
SSAExp => opcode_drop(OPExp, self.size),
SSALog => opcode_drop(OPLog, self.size),
SSASqrt => opcode_drop(OPSqrt, self.size),
SSASquare => opcode_drop(OPSquare, self.size),
SSACube => opcode_drop(OPCube, self.size),
SSASmoothMin => opcode_drop(OPSmoothMin, self.size),
SSASmoothMax => opcode_drop(OPSmoothMax, self.size),
SSAClamp => opcode_drop(OPClamp, self.size),
SSAMix => opcode_drop(OPMix, self.size),
SSAFMA => opcode_drop(OPFMA, self.size),
SSACompare => opcode_drop(OPCompare, self.size),
SSAAnd => opcode_drop(OPAnd, self.size),
SSAOr => opcode_drop(OPOr, self.size),
SSARecip => opcode_drop(OPRecip, self.size),
SSANot => opcode_drop(OPNot, self.size),
}
}
}
impl SSATape {
pub fn push_instruction(
&mut self,
opcode: SSAOpcodeSized,
inputs: Vec<SSAInput>,
) -> Vec<SSAInput> {
assert!(
inputs
.iter()
.filter_map(|i| match i {
SSAInput::Constant(_) => None,
SSAInput::Register(r) => Some(r),
})
.all(|&i| i < self.last_output)
);
assert!(opcode.size <= 4);
assert!(opcode.size >= 1);
assert_eq!(inputs.len(), opcode.input() as usize);
let outputs = (self.last_output..)
.take(opcode.output() as usize)
.collect::<Vec<u32>>();
let outputs_register = outputs
.iter()
.map(|r| SSAInput::Register(*r))
.collect::<Vec<SSAInput>>();
self.last_output += opcode.output() as u32;
self.constants.extend(inputs.iter().filter_map(|i| match i {
SSAInput::Constant(0.0) => None,
SSAInput::Constant(c) => Some(c),
SSAInput::Register(_) => None,
}));
self.tape.push(SSAInstruction {
opcode,
inputs,
outputs,
});
return outputs_register;
}
pub fn compile_to_gpu(&self) -> GPUTape {
let mut lifetimes = Vec::<(u32, u32)>::with_capacity(self.last_output as usize);
let mut time_unit = 0;
for SSAInstruction {
opcode,
inputs,
outputs,
} in self.tape.iter()
{
let per_element = opcode.lifetime_elementwise();
if per_element == (0, 0) {
for &value in inputs {
if let SSAInput::Register(r) = value {
assert!((r as usize) < lifetimes.len());
lifetimes[r as usize].1 = time_unit;
}
}
for &value in outputs {
assert_eq!(value as usize, lifetimes.len());
lifetimes.push((time_unit, time_unit));
}
time_unit += 1;
} else {
let mut input_iterators = (0..per_element.0)
.map(|i| inputs.iter().skip(i.into()).step_by(per_element.0.into()))
.collect::<Vec<_>>();
let mut output_iterators = (0..per_element.1)
.map(|i| outputs.iter().skip(i.into()).step_by(per_element.1.into()))
.collect::<Vec<_>>();
assert_eq!(
opcode.input() / per_element.0,
opcode.output() / per_element.1
);
for _ in 0..(opcode.input() / per_element.0) {
for iterator in input_iterators.iter_mut() {
let &value = iterator.next().unwrap();
if let SSAInput::Register(r) = value {
assert!((r as usize) < lifetimes.len());
lifetimes[r as usize].1 = time_unit;
}
}
for iterator in output_iterators.iter_mut() {
let &value = iterator.next().unwrap();
assert_eq!(value as usize, lifetimes.len());
lifetimes.push((time_unit, time_unit));
}
time_unit += 1;
}
}
}
// Registers are held UNTIL, non inclusive.
let mut register_hold = [0u32; 14];
let mut register_allocation = vec![0u8; lifetimes.len()];
for ((life_start, life_end), allocation) in
lifetimes.iter().zip(register_allocation.iter_mut())
{
if life_start == life_end {
*allocation = 0;
} else if let Some(register) = register_hold.iter().position(|reg| reg <= life_start) {
register_hold[register] = *life_end;
*allocation = (register + 1) as u8;
} else {
panic!("Failed to allocate registers");
}
}
let mut gpu_tape = GPUTape {
instructions: vec![],
io: vec![],
constants: self.constants.clone(),
};
let mut low_nibble = true;
let mut staging_byte = 0u8;
for SSAInstruction {
opcode,
inputs,
outputs,
} in self.tape.iter()
{
let code = opcode.to_raw_opcode();
gpu_tape.instructions.push(code.0);
let per_element = opcode.lifetime_elementwise();
if per_element == (0, 0) {
for input in inputs {
let register = match input {
SSAInput::Constant(0.0) => 0,
SSAInput::Constant(_) => 15,
SSAInput::Register(u) => register_allocation[*u as usize],
};
if low_nibble {
staging_byte |= register;
} else {
staging_byte |= register << 4;
gpu_tape.io.push(staging_byte);
}
low_nibble = !low_nibble;
}
for output in outputs {
let register = register_allocation[*output as usize];
if low_nibble {
staging_byte |= register;
} else {
staging_byte |= register << 4;
gpu_tape.io.push(staging_byte);
}
low_nibble = !low_nibble;
}
// Stop is implemented as SSAReturn(0);
if opcode.opcode == SSAOpcode::SSAStop {
let register = 0;
if low_nibble {
staging_byte |= register;
} else {
staging_byte |= register << 4;
gpu_tape.io.push(staging_byte);
}
low_nibble = !low_nibble;
}
} else {
let mut input_iterators = (0..per_element.0)
.map(|i| inputs.iter().skip(i.into()).step_by(per_element.0.into()))
.collect::<Vec<_>>();
let mut output_iterators = (0..per_element.1)
.map(|i| outputs.iter().skip(i.into()).step_by(per_element.1.into()))
.collect::<Vec<_>>();
assert_eq!(
opcode.input() / per_element.0,
opcode.output() / per_element.1
);
for _ in 0..(opcode.input() / per_element.0) {
for iterator in input_iterators.iter_mut() {
let &value = iterator.next().unwrap();
let register = match value {
SSAInput::Constant(0.0) => 0,
SSAInput::Constant(_) => 15,
SSAInput::Register(u) => register_allocation[u as usize],
};
if low_nibble {
staging_byte |= register;
} else {
staging_byte |= register << 4;
gpu_tape.io.push(staging_byte);
}
low_nibble = !low_nibble;
}
for iterator in output_iterators.iter_mut() {
let &value = iterator.next().unwrap();
let register = register_allocation[value as usize];
if low_nibble {
staging_byte |= register;
} else {
staging_byte |= register << 4;
gpu_tape.io.push(staging_byte);
}
low_nibble = !low_nibble;
}
}
}
}
if !low_nibble {
gpu_tape.io.push(staging_byte);
}
gpu_tape
}
pub fn compile_to_spirv(&self, module: Option<Module>) -> rspirv::dr::Module {
let with_module = module.is_some();
let mut b = if let Some(module) = module {
rspirv::dr::Builder::new_from_module(module)
} else {
let mut b = rspirv::dr::Builder::new();
b.set_version(1, 6);
b.module_processed(format!("Tape Drive JIT {JIT_VERSION}"));
b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::GLSL450);
b
};
let glsl = if with_module {
1
} else {
b.ext_inst_import("GLSL.std.450")
};
let void = b.type_void();
let float = b.type_float(32);
let bool = b.type_bool();
let vec2 = b.type_vector(float, 2);
let vec3 = b.type_vector(float, 3);
let vec4 = b.type_vector(float, 4);
let vec4p = b.type_pointer(None, spirv::StorageClass::Function, vec4);
let point_fn_type = b.type_function(float, vec![vec4p]);
let interval_fn_type = b.type_function(vec2, vec![vec4p, vec4p]);
let jit_string = b.string("JIT");
let types = SpirVTypes {
glsl,
void,
float,
bool,
vec2,
vec3,
vec4,
vec4p,
point_fn_type,
interval_fn_type,
jit_string,
};
compile_point_function(
&mut b,
&self,
types,
if with_module { Some(1000) } else { None },
);
//compile_interval_function(
// &mut b,
// &self,
// types,
// if with_module { Some(2000) } else { None },
//);
//compile_gradient_function(
// &mut b,
// &self,
// types,
// if with_module { Some(3000) } else { None },
//);
let module = b.module();
std::fs::write(
format!(
"{}.spv-dis",
humantime::format_rfc3339(std::time::SystemTime::now())
)
.to_string(),
module.disassemble(),
)
.unwrap();
module
}
}
@@ -2,11 +2,10 @@ use std::simd::{
StdFloat,
cmp::{SimdPartialEq, SimdPartialOrd},
num::SimdFloat,
u32x8,
};
use crate::{
interpreter::{
interpreters::{
Mask, VALUE_0, VALUE_1, VALUE_2, VALUE_05, VALUE_M1, VALUE_NAN, VALUE_PI, VALUE_PI_2,
VALUE_TAU, Value, glfract, glsign,
},