diff --git a/src-frontend/src/components/NodeGraph.jsx b/src-frontend/src/components/NodeGraph.jsx
new file mode 100644
index 00000000..5e5f8651
--- /dev/null
+++ b/src-frontend/src/components/NodeGraph.jsx
@@ -0,0 +1,441 @@
+import { useRef, useState, useCallback, useEffect } from 'react'
+import Slider from './Slider.jsx'
+import { KERNELS, BLEND_MODES, defaultKernelProps, newNodeId } from '../store.js'
+
+// ── Layout constants ───────────────────────────────────────────────────────────
+const NODE_W = 220
+const PORT_R = 6
+const PORT_TOP = 18 // y offset of first port from node top (center of header)
+const PORT_STRIDE = 26 // vertical gap between combine input ports
+
+const KERNEL_PARAMS = {
+ Luminance: ['blur_radius'],
+ Sobel: ['blur_radius'],
+ ColorGradient: ['blur_radius'],
+ Laplacian: ['blur_radius'],
+ Saturation: ['blur_radius', 'sat_min_value'],
+ Canny: ['canny_low', 'canny_high'],
+ XDoG: ['blur_radius', 'xdog_sigma2', 'xdog_tau', 'xdog_phi'],
+}
+const PARAM_META = {
+ blur_radius: { label: 'Blur σ', min: 0, max: 10, step: 0.1 },
+ sat_min_value: { label: 'Min val', min: 0, max: 1, step: 0.01 },
+ canny_low: { label: 'Low thr', min: 1, max: 254, step: 1 },
+ canny_high: { label: 'High', min: 1, max: 254, step: 1 },
+ xdog_sigma2: { label: 'σ2', min: 0.2, max: 10, step: 0.1 },
+ xdog_tau: { label: 'τ', min: 0, max: 1, step: 0.01 },
+ xdog_phi: { label: 'φ', min: 1, max: 100, step: 1 },
+}
+
+// ── Port position helpers (world coords) ───────────────────────────────────────
+function outPort(node) {
+ return { x: node.x + NODE_W, y: node.y + PORT_TOP }
+}
+function inPort(node, portIdx) {
+ return { x: node.x, y: node.y + PORT_TOP + portIdx * PORT_STRIDE }
+}
+function edgePath(from, to) {
+ const dx = Math.max(Math.abs(to.x - from.x) * 0.5, 60)
+ return `M ${from.x} ${from.y} C ${from.x + dx} ${from.y}, ${to.x - dx} ${to.y}, ${to.x} ${to.y}`
+}
+
+// ── Main component ─────────────────────────────────────────────────────────────
+export default function NodeGraph({ graph, onChange, nodePreviews }) {
+ const canvasRef = useRef(null)
+ const [pan, setPan] = useState({ x: 40, y: 40 })
+ const [zoom, setZoom] = useState(1)
+
+ // pan drag
+ const panRef = useRef(null)
+
+ // node drag
+ const nodeDragRef = useRef(null)
+
+ // wire in progress
+ const [wire, setWire] = useState(null) // { fromId, fromX, fromY, mouseX, mouseY }
+ const wireRef = useRef(null)
+
+ // ── Coordinate conversion ──────────────────────────────────────────────────
+ const toWorld = useCallback((clientX, clientY) => {
+ const r = canvasRef.current.getBoundingClientRect()
+ return {
+ x: (clientX - r.left - pan.x) / zoom,
+ y: (clientY - r.top - pan.y) / zoom,
+ }
+ }, [pan, zoom])
+
+ // ── Wheel zoom ─────────────────────────────────────────────────────────────
+ const onWheel = useCallback(e => {
+ e.preventDefault()
+ const r = canvasRef.current.getBoundingClientRect()
+ const cx = e.clientX - r.left
+ const cy = e.clientY - r.top
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1
+ setZoom(z => {
+ const nz = Math.min(Math.max(z * factor, 0.2), 4)
+ setPan(p => ({
+ x: cx - (cx - p.x) * (nz / z),
+ y: cy - (cy - p.y) * (nz / z),
+ }))
+ return nz
+ })
+ }, [])
+
+ useEffect(() => {
+ const el = canvasRef.current
+ el.addEventListener('wheel', onWheel, { passive: false })
+ return () => el.removeEventListener('wheel', onWheel)
+ }, [onWheel])
+
+ // ── Canvas mouse handlers (pan + wire cancel) ──────────────────────────────
+ function onCanvasMouseDown(e) {
+ if (e.target !== canvasRef.current && !e.target.dataset.canvas) return
+ if (e.button !== 0) return
+ panRef.current = { startX: e.clientX - pan.x, startY: e.clientY - pan.y }
+ }
+
+ // ── Global mouse move / up ─────────────────────────────────────────────────
+ useEffect(() => {
+ function onMove(e) {
+ if (panRef.current) {
+ setPan({ x: e.clientX - panRef.current.startX, y: e.clientY - panRef.current.startY })
+ }
+ if (nodeDragRef.current) {
+ const { nodeId, startNodeX, startNodeY, startX, startY } = nodeDragRef.current
+ const wx = startNodeX + (e.clientX - startX) / zoom
+ const wy = startNodeY + (e.clientY - startY) / zoom
+ onChange({ ...graph, nodes: graph.nodes.map(n => n.id === nodeId ? { ...n, x: wx, y: wy } : n) })
+ }
+ if (wireRef.current) {
+ const r = canvasRef.current.getBoundingClientRect()
+ const mx = (e.clientX - r.left - pan.x) / zoom
+ const my = (e.clientY - r.top - pan.y) / zoom
+ setWire(w => w ? { ...w, mouseX: mx, mouseY: my } : w)
+ wireRef.current = { ...wireRef.current, mouseX: mx, mouseY: my }
+ }
+ }
+ function onUp(e) {
+ panRef.current = null
+ nodeDragRef.current = null
+ if (e.key === undefined && wireRef.current) {
+ setWire(null)
+ wireRef.current = null
+ }
+ }
+ function onKey(e) {
+ if (e.key === 'Escape') {
+ setWire(null)
+ wireRef.current = null
+ }
+ }
+ window.addEventListener('mousemove', onMove)
+ window.addEventListener('mouseup', onUp)
+ window.addEventListener('keydown', onKey)
+ return () => {
+ window.removeEventListener('mousemove', onMove)
+ window.removeEventListener('mouseup', onUp)
+ window.removeEventListener('keydown', onKey)
+ }
+ }, [graph, onChange, pan, zoom])
+
+ // ── Wire interactions ──────────────────────────────────────────────────────
+ function startWire(e, fromId) {
+ e.stopPropagation()
+ const node = graph.nodes.find(n => n.id === fromId)
+ const p = outPort(node)
+ const state = { fromId, fromX: p.x, fromY: p.y, mouseX: p.x, mouseY: p.y }
+ wireRef.current = state
+ setWire(state)
+ }
+
+ function endWire(e, toId, port) {
+ e.stopPropagation()
+ if (!wireRef.current) return
+ const { fromId } = wireRef.current
+
+ // Prevent self-loop and duplicate edges
+ if (fromId === toId) { setWire(null); wireRef.current = null; return }
+ const exists = graph.edges.some(ed => ed.from === fromId && ed.to === toId && ed.port === port)
+ if (!exists) {
+ // Remove any existing edge to this specific port
+ const filtered = graph.edges.filter(ed => !(ed.to === toId && ed.port === port))
+ onChange({ ...graph, edges: [...filtered, { from: fromId, to: toId, port }] })
+ }
+ setWire(null)
+ wireRef.current = null
+ }
+
+ function removeEdge(idx) {
+ onChange({ ...graph, edges: graph.edges.filter((_, i) => i !== idx) })
+ }
+
+ // ── Node mutations ─────────────────────────────────────────────────────────
+ function updateNode(id, patch) {
+ onChange({ ...graph, nodes: graph.nodes.map(n => n.id === id ? { ...n, ...patch } : n) })
+ }
+ function deleteNode(id) {
+ onChange({
+ nodes: graph.nodes.filter(n => n.id !== id),
+ edges: graph.edges.filter(e => e.from !== id && e.to !== id),
+ })
+ }
+ function addNode(kind) {
+ const r = canvasRef.current.getBoundingClientRect()
+ const x = (r.width / 2 - pan.x) / zoom - NODE_W / 2
+ const y = (r.height / 2 - pan.y) / zoom - 60
+ const id = newNodeId(kind)
+ const base = { id, kind, x, y }
+ const node = kind === 'Kernel'
+ ? { ...base, ...defaultKernelProps() }
+ : { ...base, blendMode: 'Average', inputCount: 2 }
+ onChange({ ...graph, nodes: [...graph.nodes, node] })
+ }
+ function addCombinePort(id) {
+ const n = graph.nodes.find(n => n.id === id)
+ updateNode(id, { inputCount: (n.inputCount ?? 2) + 1 })
+ }
+ function removeCombinePort(id) {
+ const n = graph.nodes.find(n => n.id === id)
+ const cnt = n.inputCount ?? 2
+ if (cnt <= 2) return
+ const lastPort = cnt - 1
+ onChange({
+ ...graph,
+ nodes: graph.nodes.map(nd => nd.id === id ? { ...nd, inputCount: cnt - 1 } : nd),
+ edges: graph.edges.filter(e => !(e.to === id && e.port === lastPort)),
+ })
+ }
+
+ // ── Node header drag start ─────────────────────────────────────────────────
+ function startNodeDrag(e, nodeId) {
+ e.stopPropagation()
+ if (e.button !== 0) return
+ const node = graph.nodes.find(n => n.id === nodeId)
+ nodeDragRef.current = {
+ nodeId, startNodeX: node.x, startNodeY: node.y,
+ startX: e.clientX, startY: e.clientY,
+ }
+ }
+
+ // ── Node rendering ─────────────────────────────────────────────────────────
+ function renderNode(node) {
+ const isFixed = node.kind === 'Source' || node.kind === 'Output'
+ const preview = nodePreviews?.[node.id]
+ const inputCnt = node.kind === 'Combine' ? (node.inputCount ?? 2) : node.kind === 'Kernel' || node.kind === 'Output' ? 1 : 0
+ const hasOut = node.kind !== 'Output'
+
+ return (
+
+ {/* Input ports */}
+ {Array.from({ length: inputCnt }, (_, i) => (
+
endWire(e, node.id, i)}
+ style={{
+ position: 'absolute',
+ left: -PORT_R, top: PORT_TOP + i * PORT_STRIDE - PORT_R,
+ width: PORT_R * 2, height: PORT_R * 2, borderRadius: '50%',
+ background: wire ? '#14b8a6' : '#374151',
+ border: '2px solid #14b8a6', cursor: 'crosshair', zIndex: 10,
+ boxShadow: wire ? '0 0 6px #14b8a6' : 'none',
+ transition: 'background 0.15s, box-shadow 0.15s',
+ }}
+ />
+ ))}
+
+ {/* Output port */}
+ {hasOut && (
+
startWire(e, node.id)}
+ style={{
+ position: 'absolute',
+ right: -PORT_R, top: PORT_TOP - PORT_R,
+ width: PORT_R * 2, height: PORT_R * 2, borderRadius: '50%',
+ background: '#6366f1', border: '2px solid #818cf8',
+ cursor: 'crosshair', zIndex: 10,
+ }}
+ />
+ )}
+
+ {/* Node body */}
+
+ {/* Header */}
+
startNodeDrag(e, node.id)}
+ style={{
+ padding: '4px 8px', cursor: 'move',
+ background: node.kind === 'Source' ? '#2e1065' : node.kind === 'Output' ? '#1c1003' : '#1e293b',
+ display: 'flex', alignItems: 'center', gap: 6, minHeight: 32,
+ }}
+ >
+
+ {node.kind === 'Source' ? 'Source'
+ : node.kind === 'Output' ? 'Output'
+ : node.kind === 'Kernel' ? node.kernel ?? 'Kernel'
+ : 'Combine'}
+
+ {!isFixed && (
+
+ )}
+
+
+ {/* Body */}
+
+
+ {/* Kernel controls */}
+ {node.kind === 'Kernel' && (<>
+ {/* Kernel selector */}
+
+ {KERNELS.map(k => (
+
+ ))}
+
+
updateNode(node.id, { weight: v })} />
+ {(KERNEL_PARAMS[node.kernel] ?? []).map(p => {
+ const m = PARAM_META[p]
+ return updateNode(node.id, { [p]: v })} />
+ })}
+
+ >)}
+
+ {/* Combine controls */}
+ {node.kind === 'Combine' && (<>
+
+ {BLEND_MODES.map(m => (
+
+ ))}
+
+
+ Inputs: {node.inputCount ?? 2}
+
+
+
+ >)}
+
+ {/* Preview thumbnail */}
+ {preview && (
+
+ )}
+
+
+
+ )
+ }
+
+ // ── SVG edges ──────────────────────────────────────────────────────────────
+ const nodeById = Object.fromEntries(graph.nodes.map(n => [n.id, n]))
+
+ const svgEdges = graph.edges.map((edge, idx) => {
+ const fn_ = nodeById[edge.from]
+ const tn = nodeById[edge.to]
+ if (!fn_ || !tn) return null
+ const from = outPort(fn_)
+ const to = inPort(tn, edge.port)
+ return (
+
removeEdge(idx)}
+ />
+ )
+ })
+
+ const wireEdge = wire && (
+
+ )
+
+ // ── Canvas size for SVG ────────────────────────────────────────────────────
+ const WORLD_SIZE = 4000
+
+ return (
+
+ {/* Toolbar */}
+
+
+
+
+ scroll to zoom · drag to pan · click wire to delete
+
+
+
+ {/* World transform */}
+
+ {/* SVG for edges */}
+
+
+ {/* Nodes */}
+ {graph.nodes.map(renderNode)}
+
+
+ )
+}
diff --git a/src-frontend/src/components/PassPanel.jsx b/src-frontend/src/components/PassPanel.jsx
index 1f1a8235..4176ba0c 100644
--- a/src-frontend/src/components/PassPanel.jsx
+++ b/src-frontend/src/components/PassPanel.jsx
@@ -1,6 +1,5 @@
import Section from './Section.jsx'
import Slider from './Slider.jsx'
-import DetectionLayers from './DetectionLayers.jsx'
import ColorFilter from './ColorFilter.jsx'
import { FILL_STRATEGIES, FILL_STRATEGY_PARAMS, FILL_USES_ANGLE } from '../store.js'
@@ -46,14 +45,6 @@ export default function PassPanel({
)}
- {/* ── Detection ── kernels that produce the response map */}
-
- setDetection({ layers })}
- />
-
-
{/* ── Hulls & Contours ── how the response map becomes components */}
Vec;
-}
-
-/// Trait which may be inherited by objects that wish to receive events
-pub trait Eventable {
- fn notify(&mut self, event: &Event) -> ();
-}
-
-/// Trait which may be inherited by objects that wish to be updated
-pub trait Updatable {
- fn update(&mut self, delta_time: f32) -> ();
-}
-
-/// Accumulator for Vectors of VertexTypes
-#[derive(Default)]
-pub struct CanvasFrame {
- pub map: Vec,
- window_size: (u32, u32),
-}
-
-impl CanvasFrame {
-
- pub fn new(window_size: (u32, u32)) -> CanvasFrame {
- CanvasFrame {
- map: vec![],
- window_size: window_size,
- }
- }
- /// Push this drawable onto the back of the accumulator
- pub fn draw(&mut self, drawable: &dyn Drawable) {
- for i in drawable.get(self.window_size) {
- self.map.push(i);
- }
- }
-}
-
-
-
diff --git a/src/canvas/canvas_state.rs b/src/canvas/canvas_state.rs
deleted file mode 100644
index 3c28c5a9..00000000
--- a/src/canvas/canvas_state.rs
+++ /dev/null
@@ -1,640 +0,0 @@
-use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
-use std::collections::{HashMap, HashSet};
-use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer};
-use std::sync::Arc;
-use vulkano::format::{ClearValue, Format, R8Unorm, ClearValuesTuple};
-use vulkano::framebuffer::{FramebufferAbstract, Framebuffer, RenderPass, RenderPassAbstract};
-use vulkano::device::{Device, Queue};
-use vulkano::instance::PhysicalDevice;
-use vulkano::image::immutable::ImmutableImage;
-use vulkano::image::{Dimensions, ImageAccess, ImageDimensions, SwapchainImage, ImageUsage, AttachmentImage, ImageLayout};
-use vulkano::sampler::{Sampler, SamplerAddressMode, MipmapMode, Filter};
-use vulkano::descriptor::DescriptorSet;
-use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
-use std::path::PathBuf;
-use image::GenericImageView;
-use std::iter::FromIterator;
-use vulkano::swapchain::Capabilities;
-use vulkano::pipeline::viewport::Viewport;
-use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer;
-use crate::canvas::canvas_frame::CanvasFrame;
-use std::hash::Hash;
-use vulkano::pipeline::depth_stencil::{StencilFaceFlags, DynamicStencilValue};
-use vulkano::memory::pool::PotentialDedicatedAllocation::Generic;
-use std::borrow::Borrow;
-use std::fs::File;
-use std::io::Read;
-use rusttype::{Font, PositionedGlyph, Scale, Rect, point, GlyphId, Line, Curve, Segment};
-use vulkano::pipeline::vertex::{VertexDefinition, Vertex};
-use crate::canvas::managed::shader::dynamic_vertex::RuntimeVertexDef;
-use crate::canvas::managed::handles::{CanvasTextureHandle, CanvasImageHandle, CanvasFontHandle, CompiledShaderHandle, Handle, DrawableHandle};
-use crate::canvas::managed::gpu_buffers::{CanvasImage, CanvasTexture, CanvasFont};
-use crate::canvas::managed::shader::shader_common::CompiledShader;
-use crate::canvas::managed::shader::generic_shader::GenericShader;
-use crate::VertexType;
-use crate::util::vertex::{TextVertex3D, TextureVertex3D, ImageVertex3D, ColorVertex3D, CanvasFrameAllocation};
-use shade_runner::Input;
-use winit::window::Window;
-
-
-/// Canvas state is used for storage of texture and image buffers in addition to vertex buffers
-/// Canvas state also contains logic for writing the stored buffers to the command_buffer
-#[derive(Clone)]
-pub struct CanvasState {
- /// Generated during new()
- dynamic_state: DynamicState,
- /// Generated during new()
- sampler: Arc,
-
- /// hold the image, texture, and Fonts the same was as we do CompuState
- image_buffers: Vec>,
- texture_buffers: Vec>,
- font_buffers: Vec>,
-
- /// Compiled Graphics pipelines have a handle which self describe their position in this vector
- shader_buffers: Vec>>,
-
- /// Looks like we gotta hold onto the queue for managing textures
- queue: Arc,
- device: Arc,
- render_pass: Arc,
-}
-
-
-impl CanvasState {
- /// This method is called once during initialization, then again whenever the window is resized
- pub fn window_size_dependent_setup(&mut self, images: &[Arc>])
- -> Vec> {
- let dimensions = images[0].dimensions();
-
- self.dynamic_state.viewports =
- Some(vec![Viewport {
- origin: [0.0, 0.0],
- dimensions: [dimensions.width() as f32, dimensions.height() as f32],
- depth_range: 0.0..1.0,
- }]);
-
- let dimensions = [dimensions.width(), dimensions.height()];
- let depth_buffer = AttachmentImage::transient(self.device.clone(), dimensions, Format::D32Sfloat_S8Uint).unwrap();
-
- images.iter().map(|image| {
- Arc::new(
- Framebuffer::start(self.render_pass.clone())
- .add(image.clone()).unwrap()
- .add(depth_buffer.clone()).unwrap()
- .build().unwrap()
- ) as Arc
- }).collect::>()
- }
-
- /// Creates a Canvas State. Which at this point is pretty empty
- pub fn new(queue: Arc,
- device: Arc,
- physical: PhysicalDevice,
- capabilities: Capabilities) -> CanvasState {
- let format = capabilities.supported_formats[0].0;
-
- let render_pass = Arc::new(vulkano::single_pass_renderpass!(
- device.clone(),
-
- // Attachments are outgoing like f_color
- attachments: {
- // `color` is a custom name we give to the first and only attachment.
- color: {
- // `load: Clear` means that we ask the GPU to clear the content of this
- // attachment at the start of the drawing.
- load: Clear,
- // `store: Store` means that we ask the GPU to store the output of the draw
- // in the actual image. We could also ask it to discard the result.
- store: Store,
- // `format: ` indicates the type of the format of the image. This has to
- // be one of the types of the `vulkano::format` module (or alternatively one
- // of your structs that implements the `FormatDesc` trait). Here we use the
- // same format as the swapchain.
- format: format,
- samples: 1,
- },
-
- depth: {
- load: Clear,
- store: DontCare,
- format: Format::D32Sfloat_S8Uint,
- samples: 1,
- }
- },
- pass: {
- // We use the attachment named `color` as the one and only color attachment.
- color: [color],
- // No depth-stencil attachment is indicated with empty brackets.
- depth_stencil: {depth}
- }
- ).unwrap());
-
-
- CanvasState {
-
- // TODO: Might need to move this
- dynamic_state: DynamicState {
- line_width: None,
- viewports: None,
- scissors: None,
- compare_mask: Some(DynamicStencilValue {
- face: StencilFaceFlags::StencilFrontAndBack,
- value: 0xFF,
- }),
- write_mask: Some(DynamicStencilValue {
- face: StencilFaceFlags::StencilFrontAndBack,
- value: 0xFF,
- }),
- reference: Some(DynamicStencilValue {
- face: StencilFaceFlags::StencilFrontAndBack,
- value: 0xFF,
- }),
- },
- sampler: Sampler::new(device.clone(),
- Filter::Linear, Filter::Linear,
- MipmapMode::Nearest,
- SamplerAddressMode::Repeat, SamplerAddressMode::Repeat,
- SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(),
- image_buffers: vec![],
- texture_buffers: vec![],
- shader_buffers: vec![],
- font_buffers: vec![],
-
- queue: queue.clone(),
- device: device.clone(),
- render_pass: render_pass.clone(),
- }
- }
-
- /// Using the dimensions and suggested usage, load a CanvasImage and return it's handle
- pub fn create_image(&mut self, dimensions: (u32, u32), usage: ImageUsage) -> Arc {
- let handle = Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32 });
-
- let image = CanvasImage {
- handle: handle.clone(),
- buffer: AttachmentImage::with_usage(
- self.device.clone(),
- [dimensions.0, dimensions.1],
- Format::R8G8B8A8Uint,
- usage).unwrap(),
- size: dimensions,
- };
-
- self.image_buffers.push(Arc::new(image));
-
- handle
- }
-
- /// Return the image buffer from an input image handle
- pub fn get_image(&self, image_handle: Arc) -> Arc {
- self.image_buffers.get((*image_handle).clone().get_handle() as usize).unwrap()
- .clone().buffer.clone()
- }
-
- /// Load a texture buffer from an input filename
- fn get_texture_from_file(&self, image_filename: String) -> Arc> {
- let project_root =
- std::env::current_dir()
- .expect("failed to get root directory");
-
- let mut compute_path = project_root.clone();
- compute_path.push(PathBuf::from("resources/images/"));
- compute_path.push(PathBuf::from(image_filename.clone()));
-
- let img = image::open(compute_path).expect("Couldn't find image");
-
- let xy = img.dimensions();
-
- let data_length = xy.0 * xy.1 * 4;
- let pixel_count = img.raw_pixels().len();
-
- let mut image_buffer = Vec::new();
-
- if pixel_count != data_length as usize {
- println!("Creating alpha channel for {}", image_filename.clone());
- for i in img.raw_pixels().iter() {
- if (image_buffer.len() + 1) % 4 == 0 {
- image_buffer.push(255);
- }
- image_buffer.push(*i);
- }
- image_buffer.push(255);
- } else {
- image_buffer = img.raw_pixels();
- }
-
- let (texture, tex_future) = ImmutableImage::from_iter(
- image_buffer.iter().cloned(),
- Dimensions::Dim2d { width: xy.0, height: xy.1 },
- Format::R8G8B8A8Srgb,
- self.queue.clone(),
- ).unwrap();
-
- texture
- }
-
- /// Load a texture using it's filename from a file. Returns the handle of the loaded texture
- pub fn load_texture(&mut self, filename: String) -> Option> {
- let handle = Arc::new(CanvasTextureHandle {
- handle: self.texture_buffers.len() as u32
- });
-
- let texture = Arc::new(CanvasTexture {
- handle: handle.clone(),
- buffer: self.get_texture_from_file(filename.clone()),
- name: filename.clone(),
- size: (0, 0),
- });
-
- self.texture_buffers.push(texture);
-
- Some(handle)
- }
-
- /// Load and Compile a shader with the filename at resources/shaders
- /// Takes physical and capabilities as we don't store that in Canvas
- pub fn load_shader(&mut self,
- filename: String,
- capabilities: Capabilities) -> Option>
- where T: CompiledShader, V: Vertex {
- let handle = Arc::new(CompiledShaderHandle {
- handle: self.shader_buffers.len() as u32
- });
-
- let shader: Box = Box::new(T::new::(
- filename.clone(),
- self.device.clone(),
- handle.clone(),
- self.render_pass.clone(),
- ));
-
- self.shader_buffers.push(Arc::new(shader));
-
- Some(handle)
- }
-
- ///
- pub fn load_font(&mut self, name: String) -> Arc {
- let handle = Arc::new(CanvasFontHandle { handle: self.font_buffers.len() as u32 });
-
- self.font_buffers.push(Arc::new({
- let font = Font::from_bytes({
- let mut f = File::open("resources/fonts/sansation.ttf")
- .expect("Font file not found");
- let mut font_data = Vec::new();
- f.read_to_end(&mut font_data).expect("Dont know");
- font_data
- }).unwrap();
-
- let mut current_x = 0;
- let mut current_y = 0;
-
- let mut accumulator = Vec::new();
-
-
- for i in (0..255) {
- let glyph = font.glyph('d');
-
- let s = glyph.scaled(Scale { x: 1.0, y: 1.0 });
-
- let shape = s.shape().unwrap();
-
- for contour in shape {
- for segment in contour.segments {
- match segment {
- Segment::Line(l) => {
- accumulator.push(TextVertex3D {
- position: [l.p[0].x as f32, l.p[0].y as f32, 0.0],
- });
- }
- Segment::Curve(c) => {
- accumulator.push(TextVertex3D {
- position: [c.p[0].x as f32, c.p[0].y as f32, 0.0],
- });
- }
- }
- }
- }
- }
-
- CanvasFont {
- handle: handle.clone(),
- font: font.clone(),
- name: name,
- buffer: ImmutableBuffer::from_iter(
- accumulator.iter().cloned(),
- BufferUsage::vertex_buffer(), self.queue.clone()).unwrap().0,
- }
- }));
-
- handle
- }
-
- /// Using the texture name, iterates through the stored textures and matches by the name
- pub fn get_texture_handle(&self, texture_name: String)
- -> Option> {
- for i in self.texture_buffers.clone() {
- if i.name == texture_name {
- return Some(i.handle.clone());
- }
- }
- None
- }
-
- /// Using the shader name, iterates through the stored shaders and matches by the name
- pub fn get_shader_handle(&self, shader_name: String)
- -> Option> {
- for shader in self.shader_buffers.clone() {
- if shader.get_name() == shader_name {
- return Some(shader.get_handle().clone());
- }
- }
- None
- }
-
- /// Using the font name, iterates through the stored fonts and matches by the name
- pub fn get_font_handle(&self, font_name: String) -> Option> {
- for font in self.font_buffers.clone() {
- if font.name == font_name {
- return Some(font.handle.clone());
- }
- }
- None
- }
-
- /// Using the texture handle, grab the stored texture and return the buffer
- pub fn get_texture(&self, texture_handle: Arc)
- -> Arc> {
- let handle = texture_handle.get_handle() as usize;
-
- if let Some(i) = self.texture_buffers.get(handle) {
- return i.clone().buffer.clone();
- } else {
- panic!("{} : Texture not loaded", handle);
- }
- }
-
- /// Builds the descriptor set for solid colors using the input kernel (needs to support solid colors)
- fn get_solid_color_descriptor_set(&self, kernel: Arc) -> Box {
- let o: Box = Box::new(
- PersistentDescriptorSet::start(
- kernel.clone().get_pipeline().clone().descriptor_set_layout(0).unwrap().clone(),
- ).build().unwrap());
- o
- }
-
- /// Consume and allocate the canvas frame data to the GPU
- pub fn allocate(&mut self, canvas_frame: &CanvasFrame) -> CanvasFrameAllocation {
-
- let mut colored_vertex_buffer: Vec = Vec::default();
- let mut textured_vertex_buffer: HashMap, Vec> = HashMap::new();
- let mut image_vertex_buffer: HashMap, Vec> = HashMap::new();
- let mut text_instances: HashMap, Vec> = HashMap::new();
- let mut text_vertex_buffer: Vec = Vec::new();
-
- // separate the mux of vertex containers back out
- for value in &canvas_frame.map {
- match value {
- VertexType::TextureType(vertices, handle) => {
- textured_vertex_buffer.entry(handle.clone()).or_insert(vertices.clone()).extend(vertices);
- }
- VertexType::ImageType(vertices, handle) => {
- image_vertex_buffer.entry(handle.clone()).or_insert(vertices.clone()).extend(vertices);
- }
- VertexType::ColorType(vertices) => {
- colored_vertex_buffer.extend(vertices);
- }
- VertexType::ThreeDType(vertices) => {
-
- }
- VertexType::TextType(vertices) => {
- text_vertex_buffer.extend(vertices);
- }
- };
- };
-
- let mut allocated_colored_buffer: Vec> = Vec::new();
- if !colored_vertex_buffer.is_empty() {
- allocated_colored_buffer.push(ImmutableBuffer::from_iter(
- colored_vertex_buffer.iter().cloned(),
- BufferUsage::vertex_buffer(),
- self.queue.clone(),
- ).unwrap().0);
- }
-
- let mut allocated_text_buffer: Vec> = Vec::new();
- if !text_vertex_buffer.is_empty() {
- allocated_text_buffer.push(ImmutableBuffer::from_iter(
- text_vertex_buffer.iter().cloned(),
- BufferUsage::vertex_buffer(),
- self.queue.clone(),
- ).unwrap().0);
- }
-
- CanvasFrameAllocation {
- colored_vertex_buffer: allocated_colored_buffer,
- textured_vertex_buffer: textured_vertex_buffer.into_iter().map(|(k, v)| {
- (k,
- ImmutableBuffer::from_iter(
- v.iter().cloned(),
- BufferUsage::vertex_buffer(),
- self.queue.clone(),
- ).unwrap().0 as Arc<(dyn BufferAccess + Send + Sync)>)
- }).collect(),
- image_vertex_buffer: image_vertex_buffer.into_iter().map(|(k, v)| {
- (k,
- ImmutableBuffer::from_iter(
- v.iter().cloned(),
- BufferUsage::vertex_buffer(),
- self.queue.clone(),
- ).unwrap().0 as Arc<(dyn BufferAccess + Send + Sync)>)
- }).collect(),
- text_instances: Default::default(),
- text_vertex_buffer: allocated_text_buffer,
- }
- }
-
- /// Pushes the draw commands to the command buffer. Requires the framebuffers and
- /// image number to be passed in as they are taken care of by the vkprocessor
- pub fn draw_commands(&mut self,
- mut command_buffer: &mut AutoCommandBufferBuilder,
- framebuffers: Vec>,
- image_num: usize,
- allocated_buffers: CanvasFrameAllocation) {
-
- // Specify the color to clear the framebuffer with i.e. blue
- let clear_values = vec!(
- ClearValue::Float([0.0, 0.0, 0.0, 0.0]),
- ClearValue::DepthStencil((1.0, 0x00)),
- );
-
- self.dynamic_state = DynamicState {
- line_width: None,
- viewports: self.dynamic_state.viewports.clone(),
- scissors: None,
- compare_mask: None,
- write_mask: None,
- reference: None,
- };
-
- command_buffer = command_buffer.begin_render_pass(
- framebuffers[image_num].clone(), false, clear_values.clone(),
- ).unwrap();
-
- // Solid colors
- let mut shader = self.shader_buffers.get(
- self.get_shader_handle(String::from("color-passthrough"))
- .unwrap().clone().get_handle() as usize
- ).unwrap();
-
- // This looks a little weird as colored_vertex_buffer is a vec of GPU allocated vecs.
- // But we can pass in multiple vertex buffers
-
- if !allocated_buffers.colored_vertex_buffer.is_empty() {
- command_buffer = command_buffer.draw(
- shader.get_pipeline().clone(),
- &self.dynamic_state.clone(),
- allocated_buffers.colored_vertex_buffer.clone(),
- (), (),
- ).unwrap();
- }
-
- // Images
- let mut shader = self.shader_buffers.get(
- self.get_shader_handle(String::from("simple_image"))
- .unwrap().clone().get_handle() as usize
- ).unwrap();
-
-
- if !allocated_buffers.image_vertex_buffer.is_empty() {
- for (image_handle, vertex_buffer) in allocated_buffers.image_vertex_buffer.clone() {
- let handle = image_handle.clone().get_handle() as usize;
- let descriptor_set = self.image_buffers.get(handle).clone().unwrap().clone()
- .get_descriptor_set(shader.get_pipeline().clone());
-
- command_buffer = command_buffer.draw(
- shader.get_pipeline().clone(),
- // Multiple vertex buffers must have their definition in the pipeline!
- &self.dynamic_state.clone(), vec![vertex_buffer],
- vec![descriptor_set], (),
- ).unwrap();
- }
- }
-
- // Textures
- let mut shader = self.shader_buffers.get(
- self.get_shader_handle(String::from("simple_texture"))
- .unwrap().clone().get_handle() as usize
- ).unwrap();
-
- if !allocated_buffers.textured_vertex_buffer.is_empty() {
- for (texture_handle, vertex_buffer) in allocated_buffers.textured_vertex_buffer.clone() {
- let handle = texture_handle.clone().get_handle() as usize;
- let descriptor_set = self.texture_buffers.get(handle).clone().unwrap().clone()
- .get_descriptor_set(shader.get_pipeline(), self.sampler.clone());
-
- command_buffer = command_buffer.draw(
- shader.get_pipeline().clone(),
- // Multiple vertex buffers must have their definition in the pipeline!
- &self.dynamic_state.clone(), vec![vertex_buffer],
- vec![descriptor_set], (),
- ).unwrap();
- }
- }
-
- // Text
- // let mut shader = self.shader_buffers.get(
- // self.get_shader_handle(String::from("simple_text"))
- // .unwrap().clone().get_handle() as usize
- // ).unwrap();
-//
-// self.dynamic_state = DynamicState {
-// line_width: None,
-// viewports: self.dynamic_state.viewports.clone(),
-// scissors: None,
-// compare_mask: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0x00,
-// }),
-// write_mask: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0xFF,
-// }),
-// reference: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0x00,
-// }),
-// };
-//
-// if !allocated_buffers.text_vertex_buffer.is_empty() {
-// command_buffer = command_buffer.draw(
-// shader.get_pipeline().clone(),
-// &self.dynamic_state.clone(),
-// allocated_buffers.text_vertex_buffer.clone(),
-// (), (),
-// ).unwrap();
-// }
-//
-// self.dynamic_state = DynamicState {
-// line_width: None,
-// viewports: self.dynamic_state.viewports.clone(),
-// scissors: None,
-// compare_mask: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0xFF,
-// }),
-// write_mask: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0x00,
-// }),
-// reference: Some(DynamicStencilValue {
-// face: StencilFaceFlags::StencilFrontAndBack,
-// value: 0x00,
-// }),
-// };
-
- // if !allocated_buffers.text_vertex_buffer.is_empty() {
- // command_buffer = command_buffer.draw(
- // shader.get_pipeline().clone(),
- // &self.dynamic_state.clone(),
- // allocated_buffers.text_vertex_buffer.clone(),
- // (), (),
- // ).unwrap();
- // }
-
- command_buffer
- .end_render_pass()
- .unwrap();
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/canvas/managed/canvas_text.rs b/src/canvas/managed/canvas_text.rs
deleted file mode 100644
index e583e8b7..00000000
--- a/src/canvas/managed/canvas_text.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
-use rusttype::{Font, PositionedGlyph, Scale, Rect, point, GlyphId};
-use rusttype::gpu_cache::Cache;
-use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer};
-use vulkano::device::{Device, Queue};
-use std::sync::Arc;
-use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
-use vulkano::image::{ImmutableImage, ImageUsage, ImageLayout, Dimensions};
-use vulkano::format::ClearValue;
-use vulkano::format::Format::R8Unorm;
-use std::fs::File;
-use std::io::Read;
-
-
-//pub struct Glyph {}
-//
-///// So currently, I'm using these as container classes which vkprocessor owns
-///// I then use a CanvasFrame which accumulates lists of handles and vertices.
-//pub struct CanvasFonto {
-// font: Font<'static>,
-// font_name: String,
-// allocated_font_atlas: Arc<(dyn BufferAccess + Send + Sync)>,
-//}
-//
-//impl CanvasFonto {
-//
-// pub fn parse_to_vertex_buffer(font: Font) -> Vec {
-//
-// let mut current_x = 0;
-// let mut current_y = 0;
-//
-// let mut accumulator = Vec::new();
-//
-// for i in (0..255) {
-//
-// let glyph = font.glyph(GlyphId{ 0: 40 });
-//
-// let glyph_data = glyph.get_data().unwrap();
-//
-// for vertex in glyph_data.clone().shape.clone().unwrap() {
-// accumulator.push(TextVertex3D {
-// position: [vertex.x as f32, vertex.y as f32, 0.0],
-// });
-// }
-// }
-//
-// accumulator
-// }
-// /// Load the font
-// pub fn new(device: Arc, queue: Arc, font_name: String) -> CanvasFonto {
-//
-// let font = Font::from_bytes({
-// let mut f = File::open("resources/fonts/sansation.ttf").expect("Font file not found");
-// let mut font_data = Vec::new();
-// f.read_to_end(&mut font_data).expect("Dont know");
-// font_data
-// }).unwrap();
-//
-// CanvasFont {
-// font: font.clone(),
-// font_name: font_name,
-// allocated_font_atlas: ImmutableBuffer::from_iter(
-// CanvasFont::parse_to_vertex_buffer(font.clone()).iter().cloned(),
-// BufferUsage::vertex_buffer(), queue).unwrap().0,
-// }
-// }
-//
-// /// Generate a vertex buffer from the font
-// pub fn get_vertex_buffer(&self) -> Arc<(dyn BufferAccess + Send + Sync)> {
-// return self.allocated_font_atlas.clone();
-// }
-//
-//}
\ No newline at end of file
diff --git a/src/canvas/managed/gpu_buffers.rs b/src/canvas/managed/gpu_buffers.rs
deleted file mode 100644
index 6a2c2a38..00000000
--- a/src/canvas/managed/gpu_buffers.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use vulkano::image::{ImmutableImage, AttachmentImage};
-use std::sync::Arc;
-use vulkano::format::{Format, R8Unorm};
-use vulkano::sampler::Sampler;
-use vulkano::descriptor::{DescriptorSet, PipelineLayoutAbstract};
-use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
-use vulkano::buffer::{CpuAccessibleBuffer, BufferAccess};
-use vulkano::pipeline::GraphicsPipelineAbstract;
-use rusttype::Font;
-use crate::canvas::managed::handles::{CanvasTextureHandle, CanvasImageHandle, CanvasFontHandle};
-
-
-/// Canvas buffer which represents an allocated Texture with a key and dimensions
-#[derive(Clone)]
-pub struct CanvasTexture {
- pub(crate) handle: Arc,
- pub(crate) buffer: Arc>,
- pub(crate) name: String,
- pub(crate) size: (u32, u32),
-}
-
-impl CanvasTexture {
- pub fn get_descriptor_set(&self,
- pipeline: Arc,
- sampler: Arc) -> Box {
- let o: Box = Box::new(
- PersistentDescriptorSet::start(
- pipeline.clone().descriptor_set_layout(0).unwrap().clone(),
- )
- .add_sampled_image(self.buffer.clone(), sampler.clone()).unwrap()
- .build().unwrap());
- o
- }
-}
-
-/// Canvas buffer which represents an allocated image and dimension
-#[derive(Clone)]
-pub struct CanvasImage {
- pub(crate) handle: Arc,
- pub(crate) buffer: Arc,
- pub(crate) size: (u32, u32),
-}
-
-impl CanvasImage {
- pub fn get_descriptor_set(&self, pipeline: Arc)
- -> Box {
- let o: Box = Box::new(
- PersistentDescriptorSet::start(
- pipeline.clone().descriptor_set_layout(0).unwrap().clone()
- )
- .add_image(self.buffer.clone()).unwrap()
- .build().unwrap());
- o
- }
-}
-
-/// Canvas Font which represents an allocated image and dimension
-#[derive(Clone)]
-pub struct CanvasFont {
- pub(crate) handle: Arc,
- pub(crate) buffer: Arc<(dyn BufferAccess + Send + Sync)>, // Font atlas
- pub(crate) font: Font<'static>,
- pub(crate) name: String,
-
-}
-
-impl CanvasFont {
- pub fn get_descriptor_set(&self, pipeline: Arc)
- -> Box {
- let o: Box = Box::new(
- PersistentDescriptorSet::start(
- pipeline.clone().descriptor_set_layout(0).unwrap().clone()
- )
- .add_buffer(self.buffer.clone()).unwrap()
- .build().unwrap());
- o
- }
-}
diff --git a/src/canvas/managed/handles.rs b/src/canvas/managed/handles.rs
deleted file mode 100644
index a1463830..00000000
--- a/src/canvas/managed/handles.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-
-pub trait Handle {
- fn get_handle(&self) -> u32;
-}
-
-pub enum DrawableHandle {
- Texture(CanvasTextureHandle),
- Image(CanvasImageHandle),
- Font(CanvasFontHandle),
-}
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CanvasFontHandle {
- pub(in crate::canvas) handle: u32
-}
-
-impl Handle for CanvasFontHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CanvasTextureHandle {
- pub/*(in crate::canvas)*/ handle: u32
-}
-
-impl Handle for CanvasTextureHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CanvasImageHandle {
- pub(in crate::canvas) handle: u32
-}
-
-impl Handle for CanvasImageHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CompiledShaderHandle {
- pub(in crate::canvas) handle: u32
-}
-
-impl Handle for CompiledShaderHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-
diff --git a/src/canvas/managed/mod.rs b/src/canvas/managed/mod.rs
deleted file mode 100644
index 682e852c..00000000
--- a/src/canvas/managed/mod.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-pub mod shader;
-pub mod handles;
-pub mod canvas_text;
-pub mod gpu_buffers;
-
-use vulkano::pipeline::shader::{SpecializationConstants, SpecializationMapEntry};
-
-#[repr(C)]
-#[derive(Default, Debug, Clone)]
-/// Specialization constants which can be passed to the shader. Pretty much placeholder ATM
-struct ShaderSpecializationConstants {
- first_constant: i32,
- second_constant: u32,
- third_constant: f32,
-}
-
-unsafe impl SpecializationConstants for ShaderSpecializationConstants {
- fn descriptors() -> &'static [SpecializationMapEntry] {
- static DESCRIPTORS: [SpecializationMapEntry; 3] = [
- SpecializationMapEntry {
- constant_id: 0,
- offset: 0,
- size: 4,
- },
- SpecializationMapEntry {
- constant_id: 1,
- offset: 4,
- size: 4,
- },
- SpecializationMapEntry {
- constant_id: 2,
- offset: 8,
- size: 4,
- },
- ];
-
- &DESCRIPTORS
- }
-}
\ No newline at end of file
diff --git a/src/canvas/managed/shader/dynamic_vertex.rs b/src/canvas/managed/shader/dynamic_vertex.rs
deleted file mode 100644
index c3ba5f60..00000000
--- a/src/canvas/managed/shader/dynamic_vertex.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-use vulkano::pipeline::vertex::{VertexDefinition, InputRate, AttributeInfo, IncompatibleVertexDefinitionError, VertexSource, VertexMemberInfo, VertexMemberTy};
-use vulkano::pipeline::shader::ShaderInterfaceDef;
-use vulkano::buffer::BufferAccess;
-use std::sync::Arc;
-use cgmath::num_traits::real::Real;
-use std::vec::IntoIter as VecIntoIter;
-use std::mem;
-
-/// Runtime Vertex def is just a generic holder of "dynamic vertex definitions"
-// This baby needs to be able to be copied....
-#[derive(Default, Debug, Clone)]
-pub struct RuntimeVertexDef {
- buffers: Vec<(u32, usize, InputRate)>, // (attribute id, stride, Vertex or Instance data)
- vertex_buffer_ids: Vec<(usize, usize)>,//
- attributes: Vec<(String, u32, AttributeInfo)>,
- num_vertices: u32,
-}
-
-impl RuntimeVertexDef {
-
- /// primitive is an input value or struct which can then describe
- /// these damn values that are required for inputting them into vulkan
- pub fn from_primitive(primitive: u32) -> RuntimeVertexDef {
-
- // Literally every value in this class
- let mut buffers = Vec::new();
- let mut vertex_buffer_ids = Vec::new();
- let mut attributes = Vec::new();
- let mut num_vertices = u32::max_value();
-
- // https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Box/glTF/Box.gltf
- // https://github.com/tomaka/vulkano-examples/blob/gltf/gltf/gltf_system.rs
-
-
- num_vertices = 3;
-
-
-// for (attribute_id, attribute) in primitive.attributes().enumerate() {
-// let (name, accessor) = match attribute.clone() {
-// Attribute::Positions(accessor) => ("i_position".to_owned(), accessor),
-// Attribute::Normals(accessor) => ("i_normal".to_owned(), accessor),
-// Attribute::Tangents(accessor) => ("i_tangent".to_owned(), accessor),
-// Attribute::Colors(0, accessor) => ("i_color_0".to_owned(), accessor),
-// Attribute::TexCoords(0, accessor) => ("i_texcoord_0".to_owned(), accessor),
-// Attribute::TexCoords(1, accessor) => ("i_texcoord_1".to_owned(), accessor),
-// Attribute::Joints(0, accessor) => ("i_joints_0".to_owned(), accessor),
-// Attribute::Weights(0, accessor) => ("i_weights_0".to_owned(), accessor),
-// _ => unimplemented!(),
-// };
-//
-// if (accessor.count() as u32) < num_vertices {
-// num_vertices = accessor.count() as u32;
-// }
-//
-// let infos = AttributeInfo {
-// offset: 0,
-// format: match (accessor.data_type(), accessor.dimensions()) {
-// (DataType::I8, Dimensions::Scalar) => Format::R8Snorm,
-// (DataType::U8, Dimensions::Scalar) => Format::R8Unorm,
-// (DataType::F32, Dimensions::Vec2) => Format::R32G32Sfloat,
-// (DataType::F32, Dimensions::Vec3) => Format::R32G32B32Sfloat,
-// (DataType::F32, Dimensions::Vec4) => Format::R32G32B32A32Sfloat,
-// _ => unimplemented!()
-// },
-// };
-//
-// let view = accessor.view();
-// buffers.push((attribute_id as u32,
-// view.stride().unwrap_or(accessor.size()),
-// InputRate::Vertex
-// ));
-// attributes.push((name, attribute_id as u32, infos));
-// vertex_buffer_ids.push((view.buffer().index(), view.offset() + accessor.offset()));
-// }
-
- RuntimeVertexDef {
- buffers: buffers,
- vertex_buffer_ids: vertex_buffer_ids,
- num_vertices: num_vertices,
- attributes: attributes,
- }
- }
-
- /// Returns the indices of the buffers to bind as vertex buffers and the byte offset, when
- /// drawing the primitive.
- pub fn vertex_buffer_ids(&self) -> &[(usize, usize)] {
- &self.vertex_buffer_ids
- }
-}
-/// Implementing VertexDefinition
-unsafe impl VertexDefinition for RuntimeVertexDef
- where I: ShaderInterfaceDef
-{
- /// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer.
- type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
- /// Iterator that returns the attribute location, buffer id, and infos.
- type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;
-
- /// Builds the vertex definition to use to link this definition to a vertex shader's input
- /// interface.
- ///
- /// At this point I need to have enough information from the implementing type to
- /// describe its elements
- ///
- /// Needs:
- /// buffers
- /// attributes
- ///
- fn definition(&self, interface: &I)
- -> Result<(Self::BuffersIter, Self::AttribsIter), IncompatibleVertexDefinitionError>
- {
-
- let buffers_iter = self.buffers.clone().into_iter();
-
- let mut attributes = Vec::default();
-
- for input in interface.elements() {
-
- attributes.push((
- input.location.start as u32,
- input.location.start as u32,
- AttributeInfo { offset: 0, format: input.format }
- ));
-
- println!("{:?}", input.location);
- println!("{:?}", input.format);
- println!("{:?}", input.name);
- }
-
-// let mut attribs_iter = self.attributes.iter().map(|&(ref name, buffer_id, ref infos)| {
-// let attrib_loc = interface
-// .elements()
-// .find(|e| e.name.as_ref().map(|n| &n[..]) == Some(&name[..]))
-// .unwrap()
-// .location.start;
-//
-// (
-// attrib_loc as u32,
-// buffer_id,
-// AttributeInfo { offset: infos.offset, format: infos.format }
-// )
-// }).collect::>();
-
-
- // This does nothing?
- for binding in interface.elements() {
- if attributes.iter().any(|a| a.0 == binding.location.start) {
- continue;
- }
-
- attributes.push((binding.location.start, 0,
- AttributeInfo { offset: 0, format: binding.format }));
- }
-
- // The number of actually bound inputs
- let buffers = vec![
- (0, mem::size_of::(), InputRate::Vertex),
- (1, mem::size_of::(), InputRate::Vertex),
- (2, mem::size_of::(), InputRate::Vertex),
- (3, mem::size_of::(), InputRate::Vertex),
- (4, mem::size_of::(), InputRate::Vertex),
- (5, mem::size_of::(), InputRate::Vertex),
- (6, mem::size_of::(), InputRate::Vertex),
- ].into_iter();
-
- Ok((buffers, attributes.into_iter()))
- }
-}
-
-/// I don't know what the fuck is going on here... It just repackages the buffs
-/// Needs the num vertices
-unsafe impl VertexSource>> for RuntimeVertexDef {
- fn decode(&self, bufs: Vec>)
- -> (Vec>, usize, usize)
- {
- (
- bufs.into_iter().map(|b| Box::new(b) as Box<_>).collect(), // Box up the buffers
- self.num_vertices as usize, // Number of vertices
- 1 // Number of instances
- )
- }
-}
diff --git a/src/canvas/managed/shader/generic_shader.rs b/src/canvas/managed/shader/generic_shader.rs
deleted file mode 100644
index 9e156daf..00000000
--- a/src/canvas/managed/shader/generic_shader.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-
-use vulkano::pipeline::GraphicsPipelineAbstract;
-use std::sync::Arc;
-use std::collections::{HashSet, HashMap};
-use vulkano::device::Device;
-use vulkano::framebuffer::{RenderPassAbstract, Subpass};
-use vulkano::pipeline::GraphicsPipeline;
-use vulkano::pipeline::shader::{GraphicsEntryPoint, ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode, ShaderInterfaceDef};
-use shade_runner::{Input, Output, Layout, Entry};
-use std::ffi::CStr;
-use std::marker::PhantomData;
-use vulkano::pipeline::depth_stencil::{DepthStencil, Compare, DepthBounds, Stencil, StencilOp};
-use vulkano::pipeline::vertex::{SingleBufferDefinition, VertexDefinition, Vertex};
-use shade_runner as sr;
-use vulkano::memory::pool::PotentialDedicatedAllocation::Generic;
-use vulkano::SafeDeref;
-use crate::canvas::managed::shader::shader_common::{ShaderType, CompiledShaderResources, CompiledShader};
-use crate::canvas::managed::handles::CompiledShaderHandle;
-use crate::canvas::managed::shader::dynamic_vertex::RuntimeVertexDef;
-use crate::canvas::managed::ShaderSpecializationConstants;
-use crate::util::vertex::{VertexType, ColorVertex3D};
-
-/// CanvasShader holds the pipeline and render pass for the input shader source
-#[derive(Clone)]
-pub struct GenericShader {
- graphics_pipeline: Option>,
-
- handle: Arc,
- name: String,
-
- device: Arc,
- renderpass: Arc,
-}
-
-
-impl GenericShader {
-}
-
-/// Gives CanvasShader the resource functions
-impl CompiledShaderResources for GenericShader {}
-
-/// Convenience interface so we don't have to juggle shader types
-impl CompiledShader for GenericShader {
-
- /// This will explode when the shader does not want to compile
- fn new(filename: String,
- device: Arc,
- handle: Arc,
- render_pass: Arc) -> GenericShader {
-
- let compiled_vertex = GenericShader::compile(
- GenericShader::get_path(filename.clone(), ShaderType::VERTEX),
- device.clone(), ShaderType::VERTEX
- );
-
- let vertex_entry_point = unsafe {
- Some(compiled_vertex.1.graphics_entry_point(
- &CStr::from_bytes_with_nul_unchecked(b"main\0"),
- compiled_vertex.0.input.unwrap(),
- compiled_vertex.0.output.unwrap(),
- compiled_vertex.0.layout,
- GenericShader::convert_vk(ShaderType::VERTEX)
- )).unwrap()
- };
-
- let compiled_fragment = GenericShader::compile(
- GenericShader::get_path(filename.clone(), ShaderType::FRAGMENT).into(),
- device.clone(), ShaderType::FRAGMENT
- );
-
- let fragment_entry_point = unsafe {
- Some(compiled_fragment.1.graphics_entry_point(
- &CStr::from_bytes_with_nul_unchecked(b"main\0"),
- compiled_fragment.0.input.unwrap(),
- compiled_fragment.0.output.unwrap(),
- compiled_fragment.0.layout,
- GenericShader::convert_vk(ShaderType::FRAGMENT)
- )).unwrap()
- };
-
- let vertex_definition = RuntimeVertexDef::from_primitive(0);
-
- GenericShader {
- graphics_pipeline:
- Some(Arc::new(GraphicsPipeline::start()
-
- .vertex_input(SingleBufferDefinition::::new())
- //.vertex_input(vertex_definition)
-
- .vertex_shader(vertex_entry_point.clone(), ShaderSpecializationConstants {
- first_constant: 0,
- second_constant: 0,
- third_constant: 0.0,
- })
-
- .triangle_list()
- // Use a resizable viewport set to draw over the entire window
- .viewports_dynamic_scissors_irrelevant(1)
-
- .fragment_shader(fragment_entry_point.clone(), ShaderSpecializationConstants {
- first_constant: 0,
- second_constant: 0,
- third_constant: 0.0,
- })
-
- .depth_stencil_simple_depth()
-
- // We have to indicate which subpass of which render pass this pipeline is going to be used
- // in. The pipeline will only be usable from this particular subpass.
- .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
-
- .build(device.clone())
- .unwrap())),
-
- device: device,
- handle: handle.clone(),
- name: filename.clone(),
- renderpass: render_pass.clone(),
- }
- }
-
- fn get_name(&self) -> String {
- self.name.clone()
- }
-
- fn get_handle(&self) -> Arc {
- self.handle.clone()
- }
-
- fn get_pipeline(&self) -> Arc {
- self.graphics_pipeline.clone().unwrap()
- }
-
- fn get_renderpass(&self) -> Arc {
- self.renderpass.clone()
- }
-
- fn recompile(self, render_pass: Arc) -> GenericShader {
- GenericShader::new::(self.name,
- self.device,
- self.handle,
- render_pass.clone())
- }
-}
-
diff --git a/src/canvas/managed/shader/mod.rs b/src/canvas/managed/shader/mod.rs
deleted file mode 100644
index 9c56f6ec..00000000
--- a/src/canvas/managed/shader/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-pub mod text_shader;
-pub mod generic_shader;
-pub mod dynamic_vertex;
-pub mod shader_common;
\ No newline at end of file
diff --git a/src/canvas/managed/shader/shader_common.rs b/src/canvas/managed/shader/shader_common.rs
deleted file mode 100644
index 14c5a8f1..00000000
--- a/src/canvas/managed/shader/shader_common.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-
-use std::collections::HashSet;
-use std::path::PathBuf;
-use vulkano::pipeline::GraphicsPipelineAbstract;
-use vulkano::framebuffer::RenderPassAbstract;
-use std::sync::Arc;
-use vulkano::pipeline::shader::{ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode};
-use vulkano::device::Device;
-use shade_runner::Entry;
-use shaderc::ShaderKind;
-use crate::canvas::managed::handles::CompiledShaderHandle;
-use vulkano::pipeline::vertex::Vertex;
-
-/*
-
-Realistically, what should the API for this thing look like...
-
-It's going to just generate a pipeline. But that consists of loading and compiling various shaders,
- and generating a pipeline for those shaders and other customer behaviour.
-
-This best works I think if I allow users to
- A.) impl from a base trait which allows resource lookup
- B.) Generate 1 of each of the types of shaders
- C.) Modify specilization constants, whatever that might mean
- D.) impl from a base trait which defines it's interface
-
-*/
-
-
-
-/// Inheriting this gives private functions to grab resources
-pub trait CompiledShaderResources {
-
- fn get_path(filename: String, shader_type: ShaderType) -> PathBuf {
- let project_root =
- std::env::current_dir()
- .expect("failed to get root directory");
-
- let mut shader_path = project_root.clone();
- shader_path.push(PathBuf::from("resources/shaders/"));
-
-
- let mut shader_path = shader_path.clone();
-
- match shader_type {
- ShaderType::VERTEX => {
- shader_path.push(PathBuf::from(filename.clone() + ".vert"));
- }
- ShaderType::FRAGMENT => {
- shader_path.push(PathBuf::from(filename.clone() + ".frag"));
- }
- ShaderType::GEOMETRY => {
- shader_path.push(PathBuf::from(filename.clone() + ".geom"));
- }
- ShaderType::TESSELLATION_CONTROL => {
- shader_path.push(PathBuf::from(filename.clone() + ".tesscont"));
- }
- ShaderType::TESSELLATION_EVALUATION => {
- shader_path.push(PathBuf::from(filename.clone() + ".tesseval"));
- }
- }
-
- shader_path
- }
-
-
- fn compile(filepath: PathBuf, device: Arc, shader_type: ShaderType) -> (Entry, Arc) {
- let compiled_shader = shade_runner::load(filepath, None, Self::convert_sr(shader_type), None)
- .expect("Shader didn't compile");
-
- let vulkano_entry =
- shade_runner::parse(&compiled_shader)
- .expect("failed to parse");
-
- (vulkano_entry, unsafe {
- ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone())
- }.unwrap())
- }
-
- fn convert_vk(shader_type: ShaderType) -> GraphicsShaderType {
- match shader_type {
- ShaderType::VERTEX => { GraphicsShaderType::Vertex }
- ShaderType::FRAGMENT => { GraphicsShaderType::Fragment }
- ShaderType::GEOMETRY => { GraphicsShaderType::Geometry(GeometryShaderExecutionMode::Triangles) }
- ShaderType::TESSELLATION_CONTROL => { GraphicsShaderType::TessellationControl }
- ShaderType::TESSELLATION_EVALUATION => { GraphicsShaderType::TessellationEvaluation }
- }
- }
-
- fn convert_sr(shader_type: ShaderType) -> ShaderKind {
- match shader_type {
- ShaderType::VERTEX => { ShaderKind::Vertex }
- ShaderType::FRAGMENT => { ShaderKind::Fragment }
- ShaderType::GEOMETRY => { ShaderKind::Geometry }
- ShaderType::TESSELLATION_CONTROL => { ShaderKind::TessControl }
- ShaderType::TESSELLATION_EVALUATION => { ShaderKind::TessEvaluation }
- }
- }
-}
-
-
-
-pub trait CompiledShader {
- fn new(filename: String,
- device: Arc,
- handle: Arc,
- render_pass: Arc) -> Self where Self: Sized, V: Vertex,;
- fn get_name(&self) -> String;
- fn get_handle(&self) -> Arc;
- fn get_pipeline(&self) -> Arc;
- fn get_renderpass(&self) -> Arc;
- fn recompile(self, render_pass: Arc)
- -> Self where Self: Sized;
-}
-
-/// Legacy ShaderType enum for single type shaders.
-#[derive(PartialEq, Eq, Hash, Clone)]
-pub enum ShaderType {
- VERTEX = 0,
- FRAGMENT = 1,
- GEOMETRY = 2,
- TESSELLATION_CONTROL = 3,
- TESSELLATION_EVALUATION = 4,
-}
\ No newline at end of file
diff --git a/src/canvas/managed/shader/text_shader.rs b/src/canvas/managed/shader/text_shader.rs
deleted file mode 100644
index 28bfceb7..00000000
--- a/src/canvas/managed/shader/text_shader.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-use vulkano::pipeline::GraphicsPipelineAbstract;
-use std::sync::Arc;
-use std::collections::{HashSet, HashMap};
-use vulkano::device::Device;
-use vulkano::framebuffer::{RenderPassAbstract, Subpass};
-use vulkano::pipeline::GraphicsPipeline;
-use vulkano::pipeline::shader::{GraphicsEntryPoint, ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode};
-use shade_runner::{Input, Output, Layout, Entry};
-use std::ffi::CStr;
-use std::marker::PhantomData;
-use vulkano::pipeline::depth_stencil::{DepthStencil, Compare, DepthBounds, Stencil, StencilOp};
-use vulkano::pipeline::vertex::{SingleBufferDefinition, OneVertexOneInstanceDefinition, Vertex};
-use shade_runner as sr;
-use crate::canvas::managed::shader::shader_common::{ShaderType, CompiledShaderResources, CompiledShader};
-use crate::canvas::managed::handles::CompiledShaderHandle;
-use crate::canvas::managed::shader::generic_shader::GenericShader;
-use crate::canvas::managed::shader::dynamic_vertex::RuntimeVertexDef;
-use crate::canvas::managed::ShaderSpecializationConstants;
-use crate::util::vertex::ColorVertex3D;
-use vulkano::pipeline::blend::{LogicOp, AttachmentBlend, BlendOp, BlendFactor};
-
-
-/// CanvasShader holds the pipeline and render pass for the input shader source
-#[derive(Clone)]
-pub struct TextShader {
- graphics_pipeline: Option>,
-
- handle: Arc,
- name: String,
-
- device: Arc,
- renderpass: Arc,
-}
-
-impl TextShader {}
-
-/// Gives CanvasShader the resource functions
-impl CompiledShaderResources for TextShader {}
-
-/// Convenience interface so we don't have to juggle shader types
-impl CompiledShader for TextShader {
-
- /// This will explode when the shader does not want to compile
- fn new(filename: String,
- device: Arc,
- handle: Arc,
- render_pass: Arc) -> TextShader {
-
- let compiled_vertex = GenericShader::compile(
- GenericShader::get_path(filename.clone(), ShaderType::VERTEX).into(),
- device.clone(), ShaderType::VERTEX
- );
-
- let vertex_entry_point = unsafe {
- Some(compiled_vertex.1.graphics_entry_point(
- &CStr::from_bytes_with_nul_unchecked(b"main\0"),
- compiled_vertex.0.input.unwrap(),
- compiled_vertex.0.output.unwrap(),
- compiled_vertex.0.layout,
- GenericShader::convert_vk(ShaderType::VERTEX),
- )).unwrap()
- };
-
- let compiled_fragment = GenericShader::compile(
- GenericShader::get_path(filename.clone(), ShaderType::FRAGMENT).into(),
- device.clone(), ShaderType::FRAGMENT
- );
-
- let fragment_entry_point = unsafe {
- Some(compiled_fragment.1.graphics_entry_point(
- &CStr::from_bytes_with_nul_unchecked(b"main\0"),
- compiled_fragment.0.input.unwrap(),
- compiled_fragment.0.output.unwrap(),
- compiled_fragment.0.layout,
- GenericShader::convert_vk(ShaderType::FRAGMENT),
- )).unwrap()
- };
-
- let stencil = DepthStencil {
- depth_compare: Compare::Less,
- depth_write: true,
- depth_bounds_test: DepthBounds::Disabled,
- stencil_front: Stencil {
- compare: Compare::NotEqual,
- pass_op: StencilOp::Keep,
- fail_op: StencilOp::IncrementAndWrap,
- depth_fail_op: StencilOp::Keep,
- compare_mask: None,
- write_mask: None,
- reference: None,
- },
- stencil_back: Stencil {
- compare: Compare::NotEqual,
- pass_op: StencilOp::Keep,
- fail_op: StencilOp::DecrementAndWrap,
- depth_fail_op: StencilOp::Keep,
- compare_mask: None,
- write_mask: None,
- reference: None,
- },
- };
-
- let vertex_definition = RuntimeVertexDef::from_primitive(0);
-
-
- let blend = AttachmentBlend {
- enabled: true,
- color_op: BlendOp::Add,
- // color_source: BlendFactor::SrcAlpha,
- // color_destination: BlendFactor::OneMinusSrcAlpha,
- color_source: BlendFactor::One,
- color_destination: BlendFactor::One,
- alpha_op: BlendOp::Add,
- alpha_source: BlendFactor::One,
- alpha_destination: BlendFactor::One,
- mask_red: true,
- mask_green: true,
- mask_blue: true,
- mask_alpha: true,
- };
-
- TextShader {
- graphics_pipeline:
- Some(Arc::new(GraphicsPipeline::start()
-
- .vertex_input(SingleBufferDefinition::::new())
- //.vertex_input(vertex_definition)
-
- .vertex_shader(vertex_entry_point.clone(), ShaderSpecializationConstants {
- first_constant: 0,
- second_constant: 0,
- third_constant: 0.0,
- })
-
- .triangle_list()
- // Use a resizable viewport set to draw over the entire window
- .viewports_dynamic_scissors_irrelevant(1)
-
- .fragment_shader(fragment_entry_point.clone(), ShaderSpecializationConstants {
- first_constant: 0,
- second_constant: 0,
- third_constant: 0.0,
- })
-
- .depth_stencil(stencil)
-// .blend_collective(blend)
-
- //.blend_logic_op(LogicOp::Noop)
- // We have to indicate which subpass of which render pass this pipeline is going to be used
- // in. The pipeline will only be usable from this particular subpass.
- .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
-
- .build(device.clone())
- .unwrap())),
-
- device: device,
- handle: handle.clone(),
- name: filename.clone(),
- renderpass: render_pass.clone(),
- }
- }
-
- fn get_name(&self) -> String {
- self.name.clone()
- }
-
- fn get_handle(&self) -> Arc {
- self.handle.clone()
- }
-
- fn get_pipeline(&self) -> Arc {
- self.graphics_pipeline.clone().unwrap()
- }
- fn get_renderpass(&self) -> Arc {
- self.renderpass.clone()
- }
- fn recompile(self, render_pass: Arc) -> TextShader {
- TextShader::new::(self.name,
- self.device,
- self.handle,
- self.renderpass.clone())
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/canvas/mod.rs b/src/canvas/mod.rs
deleted file mode 100644
index 71f7215a..00000000
--- a/src/canvas/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-
-pub mod canvas_state;
-pub mod canvas_frame;
-pub mod managed;
-
-
diff --git a/src/compute/compu_frame.rs b/src/compute/compu_frame.rs
deleted file mode 100644
index 0c679278..00000000
--- a/src/compute/compu_frame.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use std::sync::Arc;
-use crate::canvas::managed::handles::{CanvasImageHandle};
-use crate::compute::managed::handles::{CompuKernelHandle, CompuBufferHandle};
-use crate::drawables::compu_sprite::CompuSprite;
-use crate::canvas::canvas_frame::Drawable;
-use crate::util::vertex::VertexType;
-
-pub struct CompuFrame {
- // Vec<(Buffer, Kernel)>
- pub pure_compute: Vec<(
- Arc,
- Arc)>,
-
- // Vec<(Buffer, Image, Kernel)>
- pub swapped_to_image: Vec<(
- Arc,
- Arc,
- Arc)>,
-
- // Vec<(Input Buffer, Output Buffer, Kernel)>
- pub swapped_to_buffer: Vec<(
- Arc,
- Arc,
- Arc)>,
-
- window_size: (u32, u32),
-}
-
-impl CompuFrame {
- pub fn new(window_size: (u32, u32)) -> CompuFrame {
- CompuFrame {
- pure_compute: vec![],
- swapped_to_image: vec![],
- swapped_to_buffer: vec![],
- window_size: window_size,
- }
- }
-
- pub fn add(&mut self, buffer: Arc, kernel: Arc) {
- self.pure_compute.push((buffer, kernel));
- }
-
- /*
- INPUT_BUFFER -> input -> kernel -> output
- v------------------^
- OUTPUT_BUFFER -> input X kernel X output
- */
- pub fn add_chained(&mut self,
- input_buffer: Arc,
- output_buffer: Arc,
- kernel: Arc) {
- self.swapped_to_buffer.push((input_buffer, output_buffer, kernel));
- }
-
- pub fn add_with_image_swap(&mut self,
- buffer: Arc,
- kernel: Arc,
- sprite: &CompuSprite) {
-
- let compu_sprites = sprite.get(self.window_size);
-
- if compu_sprites.len() == 1 {
- if let VertexType::ImageType(a, b) = compu_sprites.first().unwrap() {
- self.swapped_to_image.push((buffer, b.clone(), kernel))
- };
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/compute/compu_state.rs b/src/compute/compu_state.rs
deleted file mode 100644
index cbeeea9c..00000000
--- a/src/compute/compu_state.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-use std::ffi::CStr;
-use vulkano::buffer::{CpuAccessibleBuffer, BufferUsage};
-use std::sync::Arc;
-use vulkano::framebuffer::RenderPassAbstract;
-use vulkano::pipeline::{GraphicsPipelineAbstract, ComputePipeline};
-use vulkano::device::Device;
-use image::ImageBuffer;
-use image::GenericImageView;
-use vulkano::image::{ImageUsage, AttachmentImage};
-use vulkano::descriptor::descriptor_set::{PersistentDescriptorSetBuf, PersistentDescriptorSet};
-use vulkano::format::Format;
-use vulkano::descriptor::pipeline_layout::PipelineLayout;
-use std::borrow::Borrow;
-use image::Rgba;
-use vulkano::command_buffer::AutoCommandBufferBuilder;
-use std::path::PathBuf;
-use shade_runner::{CompiledShaders, Entry, CompileError};
-use vulkano::pipeline::shader::ShaderModule;
-use shaderc::CompileOptions;
-use crate::compute::compu_frame::CompuFrame;
-use crate::canvas::managed::handles::Handle;
-use crate::compute::managed::compu_buffer::CompuBuffers;
-use crate::compute::managed::handles::{CompuKernelHandle, CompuBufferHandle};
-use crate::compute::managed::compu_kernel::CompuKernel;
-use crate::canvas::canvas_state::CanvasState;
-
-
-/// State holding the compute buffers for computation and the kernels which will compute them
-pub struct CompuState {
- compute_buffers: Vec,
- kernels: Vec,
-}
-
-impl CompuState {
-
- pub fn new() -> CompuState {
- CompuState {
- compute_buffers: vec![],
- kernels: vec![],
- }
- }
-
- /// Creates a 2d compute buffer from incoming data
- pub fn new_compute_buffer(&mut self,
- data: Vec,
- dimensions: (u32, u32),
- stride: u32,
- device: Arc) -> Arc {
-
- let handle = Arc::new(CompuBufferHandle {
- handle: self.compute_buffers.len() as u32
- });
-
- self.compute_buffers.push(
- (CompuBuffers::new(device.clone(), data, dimensions, stride, handle.clone())));
-
- handle
- }
-
- pub fn read_compute_buffer(&mut self, handle: Arc) -> Vec {
- let mut buffer : &CompuBuffers = self.compute_buffers.get(handle.handle as usize).unwrap();
- let v = buffer.read_output_buffer();
- v.into_vec()
- }
-
- /// Write to the compute buffer, ostensibly overwriting what's already there
- pub fn write_compute_buffer(&self, handle: Arc, data: Vec) {
- unimplemented!("read_compute_buffer is not implemented")
- }
-
- pub fn new_kernel(&mut self,
- filename: String,
- device: Arc) -> Arc {
- let handle = Arc::new(CompuKernelHandle {
- handle: self.kernels.len() as u32
- });
-
- self.kernels.push((CompuKernel::new(filename, device.clone(), handle.clone())));
-
- handle
- }
-
- pub fn get_kernel_handle(&self, kernel_name: String) -> Option> {
- for i in self.kernels.clone() {
- if i.get_name() == kernel_name {
- return Some(i.get_handle());
- }
- }
- None
- }
-
- pub fn compute_commands(&mut self,
- compute_frame: &CompuFrame,
- mut command_buffer: &mut AutoCommandBufferBuilder,
- canvas: &CanvasState) {
-
- // i = (Buffer, Kernel)
- for i in &compute_frame.pure_compute {
- let buffer_id = (*i.0).clone().get_handle() as usize;
- let kernel_id = (*i.1).clone().get_handle() as usize;
-
- let buffer = self.compute_buffers.get(buffer_id).unwrap();
- let kernel = self.kernels.get(kernel_id).unwrap();
-
- let pipeline = kernel.clone().get_pipeline();
- let descriptorset = buffer.get_descriptor_set(kernel.clone().get_pipeline());
-
- let size = buffer.get_size();
-
- command_buffer = command_buffer
- .dispatch([size.0 / 8, size.1 / 8, 1], pipeline, descriptorset, ()).unwrap()
- }
-
- // i = (Buffer, Image, Kernel)
- for i in &compute_frame.swapped_to_image {
- let buffer_id = (*i.0).clone().get_handle() as usize;
- let image_id = i.1.clone();
- let kernel_id = (*i.2).clone().handle as usize;
-
- let buffer = self.compute_buffers.get(buffer_id).unwrap();
- let image = canvas.get_image(image_id);
- let kernel = self.kernels.get(kernel_id).unwrap();
-
- let p = kernel.clone().get_pipeline();
- let d = buffer.get_descriptor_set(kernel.clone().get_pipeline());
-
- let dimensions = image.dimensions();
- let dimensions = (dimensions[0], dimensions[1]);
- if dimensions != buffer.get_size() {
- panic!("Buffer sizes not the same");
- }
-
- let size = buffer.get_size();
-
- command_buffer = command_buffer
- .dispatch([size.0 / 8, size.1 / 8, 1], p, d, ()).unwrap()
- .copy_buffer_to_image(buffer.get_output_buffer(), image).unwrap();
- }
-
-
- // i = (Input Buffer, Output Buffer, Kernel)
- // Input buffer -> Kernel -> Output buffer
- for i in &compute_frame.swapped_to_buffer {
- let input_buffer_id = (*i.0).clone().get_handle() as usize;
- let output_buffer_id = (*i.1).clone().get_handle() as usize;
- let kernel_id = (*i.2).clone().handle as usize;
-
- let input_buffer = self.compute_buffers.get(input_buffer_id).unwrap();
- let output_buffer = self.compute_buffers.get(output_buffer_id).unwrap();
- let kernel = self.kernels.get(kernel_id).unwrap();
-
- let pipeline = kernel.clone().get_pipeline();
- let descriptor_set = input_buffer.get_descriptor_set(kernel.clone().get_pipeline());
-
- if input_buffer.get_size() != output_buffer.get_size() {
- panic!("Buffer sizes not the same");
- }
- let size = input_buffer.get_size();
-
- command_buffer = command_buffer
- // .dispatch([size.0/8, size.1/8,1], pipeline, descriptor_set, ()).unwrap()
- .copy_buffer(
- input_buffer.get_output_buffer(),
- output_buffer.get_input_buffer()).unwrap();
- }
-
- }
-}
-
diff --git a/src/compute/managed/compu_buffer.rs b/src/compute/managed/compu_buffer.rs
deleted file mode 100644
index f8020ef5..00000000
--- a/src/compute/managed/compu_buffer.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use std::sync::Arc;
-use vulkano::device::Device;
-use vulkano::buffer::{CpuAccessibleBuffer, BufferUsage};
-use vulkano::pipeline::ComputePipeline;
-use vulkano::descriptor::pipeline_layout::PipelineLayout;
-use vulkano::descriptor::descriptor_set::{PersistentDescriptorSet, PersistentDescriptorSetBuf};
-use image::ImageBuffer;
-use image::Rgba;
-use shade_runner::Layout;
-use crate::compute::managed::handles::CompuBufferHandle;
-use vulkano::descriptor::PipelineLayoutAbstract;
-
-
-#[derive(Clone)]
-pub struct CompuBuffers {
- dimensions: (u32, u32),
- device: Arc,
- handle: Arc,
-
- io_buffers: Vec>>,
- settings_buffer: Arc>,
-}
-
-impl CompuBuffers {
- pub fn new(device: Arc, data: Vec,
- dimensions: (u32, u32), stride: u32,
- handle: Arc) -> CompuBuffers {
-
- let data_length = dimensions.0 * dimensions.1 * stride;
-
- let input_buffer = {
- let mut buff = data.iter();
- let data_iter = (0..data_length).map(|n| *(buff.next().unwrap()));
- CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
- };
-
- let output_data = vec![0; data_length as usize];
- let output_buffer = {
- let mut buff = output_data.iter();
- let data_iter = (0..data_length).map(|n| *(buff.next().unwrap()));
- CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), false, data_iter).unwrap()
- };
-
- // Settings buffer which holds i32's
- // TODO: Compile macros into the kernel eventually to index them
- let settings_buffer = {
- let vec = vec![dimensions.0, dimensions.1];
- let mut buff = vec.iter();
- let data_iter =
- (0..2).map(|n| *(buff.next().unwrap()));
- CpuAccessibleBuffer::from_iter(device.clone(),
- BufferUsage::all(),
- false, data_iter).unwrap()
- };
-
- CompuBuffers {
- dimensions: dimensions,
- device: device.clone(),
-
- handle: handle,
- io_buffers: vec![output_buffer, input_buffer],
- settings_buffer: settings_buffer,
- }
- }
-
- pub fn get_size(&self) -> (u32, u32) {
- self.dimensions
- }
-
- pub fn get_descriptor_set(&self, compute_pipeline: std::sync::Arc>>)
- -> Arc>>),
- PersistentDescriptorSetBuf>>),
- PersistentDescriptorSetBuf>>)>> {
-
- Arc::new(PersistentDescriptorSet::start(compute_pipeline.clone().descriptor_set_layout(0).unwrap().clone())
- .add_buffer(self.io_buffers.get(0).unwrap().clone()).unwrap()
- .add_buffer(self.io_buffers.get(1).unwrap().clone()).unwrap()
- .add_buffer(self.settings_buffer.clone()).unwrap()
- .build().unwrap())
- }
-
- pub fn read_output_buffer(&self) -> ImageBuffer, Vec> {
- let xy = self.get_size();
-
- self.io_buffers.get(0).unwrap().write().unwrap().map(|x| x);
- let data_buffer_content = self.io_buffers.get(0).unwrap().read().unwrap();
-
- ImageBuffer::from_fn(xy.0, xy.1, |x, y| {
- let r = data_buffer_content[((xy.0 * y + x) * 4 + 0) as usize] as u8;
- let g = data_buffer_content[((xy.0 * y + x) * 4 + 1) as usize] as u8;
- let b = data_buffer_content[((xy.0 * y + x) * 4 + 2) as usize] as u8;
- let a = data_buffer_content[((xy.0 * y + x) * 4 + 3) as usize] as u8;
-
- image::Rgba([r, g, b, a])
- })
- }
-
- pub fn get_input_buffer(&self) -> Arc> {
- self.io_buffers.get(1).unwrap().clone()
- }
-
- pub fn get_output_buffer(&self) -> Arc> {
- self.io_buffers.get(0).unwrap().clone()
- }
-}
diff --git a/src/compute/managed/compu_kernel.rs b/src/compute/managed/compu_kernel.rs
deleted file mode 100644
index c3879a2a..00000000
--- a/src/compute/managed/compu_kernel.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-use vulkano::device::{Device};
-use vulkano::pipeline::{ComputePipeline};
-use std::sync::Arc;
-use std::ffi::CStr;
-use std::path::PathBuf;
-use shade_runner as sr;
-use vulkano::descriptor::pipeline_layout::PipelineLayout;
-use shade_runner::{CompileError, Layout, Input, Output, CompiledShaders, Entry, CompiledShader};
-use shaderc::CompileOptions;
-use vulkano::pipeline::shader::{ShaderModule, GraphicsEntryPoint, SpecializationConstants, SpecializationMapEntry};
-use crate::compute::managed::handles::CompuKernelHandle;
-
-
-#[derive(Clone)]
-pub struct CompuKernel {
-
- handle: Arc,
-
- compute_pipeline: Option>>>,
- compute_kernel_path: PathBuf,
-
- name: String,
-
- shader: CompiledShader,
- entry: Entry,
- shader_module: Arc,
- device: Arc,
- specialization_constants: ComputeSpecializationConstants,
-}
-
-impl CompuKernel {
-
- fn get_path(filename: String) -> PathBuf {
-
- let project_root =
- std::env::current_dir()
- .expect("failed to get root directory");
-
- let mut compute_path = project_root.clone();
- compute_path.push(PathBuf::from("resources/shaders/"));
- compute_path.push(PathBuf::from(filename));
-
- compute_path
- }
-
- pub fn new(filename: String, device: Arc,
- handle: Arc) -> CompuKernel {
-
- let compute_path = CompuKernel::get_path(filename.clone());
-
- let mut options = CompileOptions::new().ok_or(CompileError::CreateCompiler).unwrap();
-
- let shader = sr::load_compute(compute_path.clone(), Some(options))
- .expect("Failed to compile");
-
- let entry = sr::parse_compute(&shader)
- .expect("Failed to parse");
-
- let shader_module = unsafe {
- vulkano::pipeline::shader::ShaderModule::from_words(device.clone(), &shader.spriv)
- }.unwrap();
-
-
- CompuKernel {
- name: filename,
- handle: handle,
- device: device,
- shader: shader,
- compute_pipeline: Option::None,
- compute_kernel_path: compute_path,
- entry: entry,
- shader_module: shader_module,
- specialization_constants: ComputeSpecializationConstants {
- first_constant: 0,
- second_constant: 0,
- third_constant: 0.0
- }
- }
- }
-
- pub fn get_pipeline(&mut self) -> std::sync::Arc>> {
-
- match self.compute_pipeline.clone() {
- Some(t) => t,
- None => {
- self.compute_pipeline = Some(Arc::new({
- unsafe {
- ComputePipeline::new(self.device.clone(), &self.shader_module.compute_entry_point(
- CStr::from_bytes_with_nul_unchecked(b"main\0"),
- self.entry.layout.clone()), &self.specialization_constants,
- ).unwrap()
- }
- }));
- self.compute_pipeline.clone().unwrap()
- }
- }
- }
-
- pub fn recompile_kernel(&mut self) -> std::sync::Arc>> {
- self.compile_kernel(String::from(self.compute_kernel_path.clone().to_str().unwrap()))
- }
-
- pub fn compile_kernel(&mut self, filename: String) -> std::sync::Arc>> {
-
- let mut options = CompileOptions::new().ok_or(CompileError::CreateCompiler).unwrap();
- self.compute_kernel_path = CompuKernel::get_path(filename);
-
- self.shader =
- sr::load_compute(self.compute_kernel_path.clone(), Some(options))
- .expect("Failed to compile");
-
- self.entry =
- sr::parse_compute(&self.shader)
- .expect("Failed to parse");
-
- self.shader_module = unsafe {
- vulkano::pipeline::shader::ShaderModule::from_words(self.device.clone(), &self.shader.spriv)
- }.unwrap();
-
- self.get_pipeline()
- }
-
- pub fn get_handle(&self) -> Arc {
- self.handle.clone()
- }
- pub fn get_name(&self) -> String {
- self.name.clone()
- }
-}
-
-#[repr(C)]
-#[derive(Default, Debug, Clone)]
-pub struct ComputeSpecializationConstants {
- first_constant: i32,
- second_constant: u32,
- third_constant: f32,
-}
-
-unsafe impl SpecializationConstants for ComputeSpecializationConstants {
- fn descriptors() -> &'static [SpecializationMapEntry] {
- static DESCRIPTORS: [SpecializationMapEntry; 3] = [
- SpecializationMapEntry {
- constant_id: 0,
- offset: 0,
- size: 4,
- },
- SpecializationMapEntry {
- constant_id: 1,
- offset: 4,
- size: 4,
- },
- SpecializationMapEntry {
- constant_id: 2,
- offset: 8,
- size: 4,
- },
- ];
-
- &DESCRIPTORS
- }
-}
diff --git a/src/compute/managed/handles.rs b/src/compute/managed/handles.rs
deleted file mode 100644
index f5338cec..00000000
--- a/src/compute/managed/handles.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::canvas::managed::handles::Handle;
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CompuBufferHandle {
- pub(in crate::compute) handle: u32,
-}
-
-impl Handle for CompuBufferHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-/// Typed wrapper for a u32 handle
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CompuKernelHandle {
- pub(in crate::compute) handle: u32,
-}
-
-impl Handle for CompuKernelHandle {
- fn get_handle(&self) -> u32 {
- self.handle
- }
-}
-
-
-
-
diff --git a/src/compute/managed/mod.rs b/src/compute/managed/mod.rs
deleted file mode 100644
index a34df75a..00000000
--- a/src/compute/managed/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-
-pub mod compu_buffer;
-pub mod compu_kernel;
-pub mod handles;
\ No newline at end of file
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
deleted file mode 100644
index 7503ae4b..00000000
--- a/src/compute/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod compu_frame;
-pub mod compu_state;
-pub mod managed;
-
-use crate::compute::compu_state::CompuState;
-use crate::compute::compu_frame::CompuFrame;
\ No newline at end of file
diff --git a/src/detect.rs b/src/detect.rs
index ec02f51d..507154e3 100644
--- a/src/detect.rs
+++ b/src/detect.rs
@@ -383,6 +383,215 @@ fn gaussian_3x3(src: &[f32], w: usize, h: usize) -> Vec {
out
}
+// ── Graph model ───────────────────────────────────────────────────────────────
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum BlendMode {
+ Average,
+ Min,
+ Max,
+ Multiply,
+ Screen,
+ Difference,
+}
+
+impl BlendMode {
+ pub fn from_str(s: &str) -> Self {
+ match s {
+ "Min" => BlendMode::Min,
+ "Max" => BlendMode::Max,
+ "Multiply" => BlendMode::Multiply,
+ "Screen" => BlendMode::Screen,
+ "Difference" => BlendMode::Difference,
+ _ => BlendMode::Average,
+ }
+ }
+ pub fn label(self) -> &'static str {
+ match self {
+ BlendMode::Average => "Avg",
+ BlendMode::Min => "Min",
+ BlendMode::Max => "Max",
+ BlendMode::Multiply => "Mul",
+ BlendMode::Screen => "Scr",
+ BlendMode::Difference => "Dif",
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum NodeKind {
+ Source,
+ Kernel(DetectionLayer),
+ Combine(BlendMode),
+ Output,
+}
+
+#[derive(Debug, Clone)]
+pub struct GraphNode {
+ pub id: String,
+ pub kind: NodeKind,
+}
+
+#[derive(Debug, Clone)]
+pub struct GraphEdge {
+ pub from: String,
+ pub to: String,
+ pub port: usize,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct DetectionGraph {
+ pub nodes: Vec,
+ pub edges: Vec,
+}
+
+/// Evaluate the detection graph. Returns (final_response_map, per_node_maps).
+/// Per-node maps: node_id → Vec response map (0=ink, 255=bg).
+/// Source and Output nodes are excluded from per-node maps.
+pub fn evaluate_graph(
+ rgb: &RgbImage,
+ graph: &DetectionGraph,
+) -> (Vec, std::collections::HashMap>) {
+ use std::collections::{HashMap, VecDeque};
+
+ let n = (rgb.width() * rgb.height()) as usize;
+ let bg = || vec![255u8; n];
+
+ if graph.nodes.is_empty() {
+ return (bg(), HashMap::new());
+ }
+
+ // Build adjacency: incoming edges per node (sorted by port for combine)
+ let mut incoming: HashMap<&str, Vec<(&str, usize)>> = HashMap::new();
+ let mut out_edges: HashMap<&str, Vec<&str>> = HashMap::new();
+ for node in &graph.nodes {
+ incoming.entry(&node.id).or_default();
+ out_edges.entry(&node.id).or_default();
+ }
+ for edge in &graph.edges {
+ incoming.entry(&edge.to).or_default().push((&edge.from, edge.port));
+ out_edges.entry(&edge.from).or_default().push(&edge.to);
+ }
+
+ // Kahn's topological sort
+ let mut in_deg: HashMap<&str, usize> = graph.nodes.iter()
+ .map(|n| (n.id.as_str(), incoming[n.id.as_str()].len()))
+ .collect();
+
+ let mut queue: VecDeque<&str> = in_deg.iter()
+ .filter(|(_, &d)| d == 0)
+ .map(|(&id, _)| id)
+ .collect();
+ let mut order: Vec<&str> = Vec::new();
+ while let Some(id) = queue.pop_front() {
+ order.push(id);
+ for &next in out_edges.get(id).into_iter().flatten() {
+ let d = in_deg.get_mut(next).unwrap();
+ *d -= 1;
+ if *d == 0 { queue.push_back(next); }
+ }
+ }
+ if order.len() != graph.nodes.len() {
+ return (bg(), HashMap::new()); // cycle
+ }
+
+ let node_map: HashMap<&str, &GraphNode> = graph.nodes.iter()
+ .map(|n| (n.id.as_str(), n))
+ .collect();
+
+ let mut outputs: HashMap<&str, Vec> = HashMap::new();
+
+ for &id in &order {
+ let node = node_map[id];
+ let result: Option> = match &node.kind {
+ NodeKind::Source => None,
+ NodeKind::Kernel(layer) => Some(apply_layer(rgb, layer)),
+ NodeKind::Combine(mode) => {
+ let mut ins = incoming[id].clone();
+ ins.sort_by_key(|&(_, p)| p);
+ let maps: Vec<&[u8]> = ins.iter()
+ .filter_map(|(fid, _)| outputs.get(fid).map(|v| v.as_slice()))
+ .collect();
+ Some(if maps.is_empty() { bg() } else { blend_maps(&maps, *mode, n) })
+ }
+ NodeKind::Output => {
+ let upstream = incoming[id].iter()
+ .find_map(|(fid, _)| outputs.get(fid).cloned());
+ Some(upstream.unwrap_or_else(bg))
+ }
+ };
+ if let Some(map) = result {
+ outputs.insert(id, map);
+ }
+ }
+
+ let final_map = graph.nodes.iter()
+ .find(|n| matches!(n.kind, NodeKind::Output))
+ .and_then(|n| outputs.get(n.id.as_str()).cloned())
+ .unwrap_or_else(bg);
+
+ // Per-node previews — omit Source (no output) and Output (same as final_map)
+ let previews: HashMap> = outputs.into_iter()
+ .filter(|(id, _)| {
+ let key: &str = id;
+ node_map.get(key).map_or(true, |n|
+ !matches!(n.kind, NodeKind::Source | NodeKind::Output))
+ })
+ .map(|(id, map)| (id.to_string(), map))
+ .collect();
+
+ (final_map, previews)
+}
+
+fn blend_maps(maps: &[&[u8]], mode: BlendMode, n: usize) -> Vec {
+ match mode {
+ BlendMode::Average => {
+ let mut sum = vec![0u32; n];
+ for map in maps { for (s, &v) in sum.iter_mut().zip(*map) { *s += v as u32; } }
+ let c = maps.len() as u32;
+ sum.iter().map(|&s| (s / c) as u8).collect()
+ }
+ BlendMode::Min => {
+ let mut r = vec![255u8; n];
+ for map in maps { for (r, &v) in r.iter_mut().zip(*map) { if v < *r { *r = v; } } }
+ r
+ }
+ BlendMode::Max => {
+ let mut r = vec![0u8; n];
+ for map in maps { for (r, &v) in r.iter_mut().zip(*map) { if v > *r { *r = v; } } }
+ r
+ }
+ BlendMode::Multiply => {
+ let mut r = vec![255u8; n];
+ for map in maps {
+ for (r, &v) in r.iter_mut().zip(*map) {
+ *r = ((*r as u32 * v as u32) / 255) as u8;
+ }
+ }
+ r
+ }
+ BlendMode::Screen => {
+ let mut r = vec![0u8; n];
+ for map in maps {
+ for (r, &v) in r.iter_mut().zip(*map) {
+ let a = *r as u32; let b = v as u32;
+ *r = (255 - (255 - a) * (255 - b) / 255) as u8;
+ }
+ }
+ r
+ }
+ BlendMode::Difference => {
+ let mut r = maps[0].to_vec();
+ for map in &maps[1..] {
+ for (r, &v) in r.iter_mut().zip(*map) {
+ *r = (*r as i16 - v as i16).unsigned_abs() as u8;
+ }
+ }
+ r
+ }
+ }
+}
+
// ── Tests ──────────────────────────────────────────────────────────────────────
#[cfg(test)]
diff --git a/src/drawables/compu_sprite.rs b/src/drawables/compu_sprite.rs
deleted file mode 100644
index 5baeb103..00000000
--- a/src/drawables/compu_sprite.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::sync::Arc;
-use crate::canvas::managed::handles::{CanvasImageHandle, CanvasTextureHandle};
-use crate::canvas::canvas_frame::Drawable;
-use crate::util::vertex::{VertexType, ImageVertex3D};
-
-pub struct CompuSprite {
-
- pub verts: VertexType,
-
- position: (f32, f32),
- size: (f32, f32),
- color: (f32, f32, f32, f32),
-
-}
-
-impl CompuSprite {
- pub fn new(position: (f32, f32),
- size: (f32, f32),
- depth: u32,
- image_size: (f32, f32),
- image_handle: Arc) -> CompuSprite {
-
- let normalized_depth = (depth as f32 / 255.0);
-
- let verts = vec![
- ImageVertex3D {
- v_position: [position.0, position.1, normalized_depth], // top left
- ti_position: [-0.0, -0.0] },
- ImageVertex3D {
- v_position: [position.0, position.1 + size.1, normalized_depth], // bottom left
- ti_position: [-0.0, image_size.1] },
- ImageVertex3D {
- v_position: [position.0 + size.0, position.1 + size.1, normalized_depth], // bottom right
- ti_position: [image_size.0, image_size.1] },
- ImageVertex3D {
- v_position: [position.0, position.1, normalized_depth], // top left
- ti_position: [-0.0, -0.0] },
- ImageVertex3D {
- v_position: [position.0 + size.0, position.1 + size.1, normalized_depth], // bottom right
- ti_position: [image_size.0, image_size.1] },
- ImageVertex3D {
- v_position: [position.0 + size.0, position.1, normalized_depth], // top right
- ti_position: [image_size.0, -0.0] },
- ];
-
- CompuSprite {
- verts: VertexType::ImageType(verts, image_handle.clone()),
- position: position,
- size: size,
- color: (0.0, 0.0, 0.0, 0.0),
- }
- }
-}
-
-impl Drawable for CompuSprite {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- vec![self.verts.clone()]
- }
-}
\ No newline at end of file
diff --git a/src/drawables/mod.rs b/src/drawables/mod.rs
deleted file mode 100644
index 5bc05a9c..00000000
--- a/src/drawables/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod slider;
-pub mod polygon;
-pub mod sprite;
-pub mod rect;
-pub mod compu_sprite;
-pub mod text;
\ No newline at end of file
diff --git a/src/drawables/polygon.rs b/src/drawables/polygon.rs
deleted file mode 100644
index ba4d80ce..00000000
--- a/src/drawables/polygon.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use std::sync::Arc;
-use crate::canvas::*;
-use crate::canvas::managed::handles::{CanvasFontHandle, CanvasImageHandle, CanvasTextureHandle, Handle};
-use crate::canvas::canvas_frame::{Drawable};
-use crate::util::vertex::{VertexType, TextureVertex3D, Vertex3D, ColorVertex3D};
-use crate::drawables::sprite::Sprite;
-
-/// Convex multi verticy polygon
-#[derive(Debug, Clone)]
-pub struct Polygon {
-
- pub verts: VertexType,
-
- position: (f32, f32),
- size: (f32, f32),
-}
-
-/// Container class which implements drawable.
-impl Polygon {
-
- ///
- pub fn new(position: (f32, f32),
- size: (f32, f32),
- depth: u32,) -> Polygon {
-
- let normalized_depth = (depth as f32 / 255.0);
-
- let verts = vec![
- ColorVertex3D{v_position: [-0.5, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-0.25, 0.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-0.25, 0.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.0, 0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.25, 0.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.0, 0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.5, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.25, 0.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.25, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.5, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.25, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.0, -0.1, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-0.25, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [0.0, -0.1, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-0.5, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-1.0, 1.0, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ColorVertex3D{v_position: [-0.25, -0.5, normalized_depth], color: [1.0, 1.0, 0.0, 1.0] },
- ];
-
-
- Polygon {
- verts: VertexType::ColorType(verts),
- position: position,
- size: size,
- }
- }
-}
-impl Drawable for Polygon {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- vec![self.verts.clone()]
- }
-
-}
-
-
-
diff --git a/src/drawables/rect.rs b/src/drawables/rect.rs
deleted file mode 100644
index 9503ba3c..00000000
--- a/src/drawables/rect.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use crate::canvas::canvas_frame::Drawable;
-use crate::util::vertex::{VertexType, ColorVertex3D};
-
-///
-#[derive(Debug, Clone)]
-pub struct Rect {
- position: (f32, f32),
- size: (f32, f32),
- color: (f32, f32, f32, f32),
- depth: f32,
-}
-
-/// Container class which implements drawable.
-impl Rect {
- ///
- pub fn new(position: (f32, f32),
- size: (f32, f32),
- depth: u32,
- color: (f32, f32, f32, f32)) -> Rect {
- let normalized_depth = (depth as f32 / 255.0);
-
- Rect {
- position: position,
- size: size,
- color: color,
- depth: normalized_depth,
- }
- }
-
- fn generate_vertices(window_size: (u32, u32),
- position: (f32, f32),
- size: (f32, f32),
- depth: f32,
- color: (f32, f32, f32, f32)) -> Vec {
-
- let ss_position = (
- position.0 / window_size.0 as f32 - 1.0,
- position.1 / window_size.1 as f32 - 1.0
- );
-
- let ss_size = (
- size.0 / window_size.0 as f32,
- size.1 / window_size.1 as f32
- );
-
- vec![
- ColorVertex3D {
- v_position: [ss_position.0, ss_position.1, depth], // top left
- color: [color.0, color.1, color.2, color.3],
- },
- ColorVertex3D {
- v_position: [ss_position.0, ss_position.1 + ss_size.1, depth], // bottom left
- color: [color.0, color.1, color.2, color.3],
- },
- ColorVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1 + ss_size.1, depth], // bottom right
- color: [color.0, color.1, color.2, color.3],
- },
- ColorVertex3D {
- v_position: [ss_position.0, ss_position.1, depth], // top left
- color: [color.0, color.1, color.2, color.3],
- },
- ColorVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1 + ss_size.1, depth], // bottom right
- color: [color.0, color.1, color.2, color.3],
- },
- ColorVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1, depth], // top right
- color: [color.0, color.1, color.2, color.3],
- },
- ]
- }
-}
-
-impl Drawable for Rect {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- vec![
- VertexType::ColorType(
- Rect::generate_vertices(window_size, self.position, self.size, self.depth, self.color)
- )
- ]
- }
-}
diff --git a/src/drawables/slider.rs b/src/drawables/slider.rs
deleted file mode 100644
index d4416c03..00000000
--- a/src/drawables/slider.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use std::collections::HashSet;
-
-use winit::event::Event;
-
-use crate::canvas::canvas_frame::{Drawable, Eventable};
-use crate::drawables::rect::Rect;
-use crate::drawables::sprite::Sprite;
-use crate::util::vertex::VertexType;
-
-pub struct Slider {
- handle: Rect,
- guide: Vec,
-
- scaler: u32,
- position: (f32, f32),
- size: (f32, f32),
- value: u16,
-}
-
-impl Slider {
- pub fn new(size: (f32, f32), position: (f32, f32), value: u16) -> Slider {
-
- // render the guide first
- let red = (1.0, 0.0, 0.0, 0.0);
- let green = (0.0, 1.0, 0.0, 0.0);
- let blue = (0.0, 1.0, 1.0, 0.0);
- let rg = (1.0, 1.0, 0.0, 0.0);
-
- let left_guide_bar = Rect::new((position.0, position.1), (2.0, size.1), 1, red);
- let right_guide_bar = Rect::new((position.0 + size.0, position.1), (2.0, size.1), 1, blue);
- let line = Rect::new((position.0, position.1 - (size.1 / 2.0) ), (size.0, 2.0), 1, green);
-
- let scale = value as f32 / u16::max_value() as f32;
- let handle = Rect::new((position.0 + (size.0 * scale), position.1), (15.0, size.1), 1, rg);
-
- Slider {
- handle: handle,
- guide: vec![left_guide_bar, right_guide_bar, line],
- scaler: 255,
- position,
- size,
- value,
- }
- }
-}
-
-impl Drawable for Slider {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- let mut vertices = self.handle.get(window_size).clone();
-
- vertices.extend_from_slice(
- self.guide.iter()
- .map(|x| x.get(window_size))
- .flatten()
- .collect::>()
- .as_slice()
- );
-
- vertices.extend_from_slice(self.guide[0].get(window_size).as_slice());
- vertices
- }
-}
-
-impl Eventable for Slider {
- fn notify(&mut self, event: &Event) -> () {
- unimplemented!()
- }
-}
diff --git a/src/drawables/sprite.rs b/src/drawables/sprite.rs
deleted file mode 100644
index 644e8297..00000000
--- a/src/drawables/sprite.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-use std::sync::Arc;
-use crate::canvas::*;
-use crate::canvas::managed::handles::{CanvasFontHandle, CanvasImageHandle, CanvasTextureHandle, Handle};
-use crate::canvas::canvas_frame::{Drawable, Eventable, Updatable};
-use crate::util::vertex::{VertexType, TextureVertex3D, Vertex3D};
-use winit::event::{DeviceEvent, MouseButton, ElementState, Event, WindowEvent};
-
-///
-#[derive(Debug, Clone)]
-pub struct Sprite {
- pub position: (f32, f32),
- pub size: (f32, f32),
- depth: f32,
- texture_handle: Arc,
-}
-
-/// Container class which implements drawable.
-impl Sprite {
- fn generate_verts(window_size: (u32, u32), position: (f32, f32), size: (f32, f32), depth: f32) -> Vec {
-
- let ss_position = (
- position.0 / window_size.0 as f32 - 1.0,
- position.1 / window_size.1 as f32 - 1.0
- );
-
- let ss_size = (
- size.0 / window_size.0 as f32,
- size.1 / window_size.1 as f32
- );
-
- vec![
- TextureVertex3D {
- v_position: [ss_position.0, ss_position.1, depth], // top left
- ti_position: [-0.0, -0.0],
- },
- TextureVertex3D {
- v_position: [ss_position.0, ss_position.1 + ss_size.1, depth], // bottom left
- ti_position: [-0.0, 1.0],
- },
- TextureVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1 + ss_size.1, depth], // bottom right
- ti_position: [1.0, 1.0],
- },
- TextureVertex3D {
- v_position: [ss_position.0, ss_position.1, depth], // top left
- ti_position: [-0.0, -0.0],
- },
- TextureVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1 + ss_size.1, depth], // bottom right
- ti_position: [1.0, 1.0],
- },
- TextureVertex3D {
- v_position: [ss_position.0 + ss_size.0, ss_position.1, depth], // top right
- ti_position: [1.0, -0.0],
- },
- ]
- }
-
- ///
- pub fn new(position: (f32, f32),
- size: (f32, f32),
- depth: u32,
- texture_handle: Arc) -> Sprite {
- let normalized_depth = (depth as f32 / 255.0);
-
- Sprite {
- position: position,
- size: size,
- depth: normalized_depth,
- texture_handle: texture_handle.clone(),
- }
- }
-}
-
-impl Drawable for Sprite {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- vec![
- VertexType::TextureType(
- Sprite::generate_verts(window_size, self.position, self.size, self.depth),
- self.texture_handle.clone())
- ]
- }
-}
-
-impl Eventable for Sprite {
- fn notify(&mut self, event: &Event) -> () {
- match event {
- Event::WindowEvent { event: WindowEvent::MouseInput { device_id, state, button, modifiers }, .. } => {
- match button {
- MouseButton::Left => {
- if *state == ElementState::Pressed {
- self.position = (self.position.0, self.position.1 + 0.1);
- }
- }
- _ => {}
- }
- }
- _ => {}
- }
- }
-}
-
-impl Updatable for Sprite {
- fn update(&mut self, delta_time: f32) -> () {
-
- }
-}
diff --git a/src/drawables/text.rs b/src/drawables/text.rs
deleted file mode 100644
index c3215380..00000000
--- a/src/drawables/text.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use crate::canvas::canvas_frame::Drawable;
-use crate::util::vertex::{VertexType, ColorVertex3D};
-
-///
-#[derive(Debug, Clone)]
-pub struct Text {
- pub verts: VertexType,
-
- position: (f32, f32),
- size: (f32, f32),
-}
-
-/// Container class which implements drawable.
-impl Text {
-
- fn accumulator(depth: f32, accumulator: &mut f32, constant: f32) -> f32{
-
- let accum = *accumulator;
- *accumulator += constant;
- return depth + accum + constant;
- }
- ///
- pub fn new(position: (f32, f32),
- size: (f32, f32),
- depth: u32) -> Text {
- let normalized_depth = (depth as f32 / 255.0);
-
- let mut depth_accumulator = 1.0;
- let depth_accum_constant = -0.01 as f32;
-
- let verts = {
- vec![
- ColorVertex3D {
- v_position: [-0.5, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)+0.1],
- color: [1.0, 0.0, 0.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)+0.1],
- color: [1.0, 0.8, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-0.25, 0.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)+0.1],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-0.25, 0.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [0.8, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.0, 0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 0.8, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.25, 0.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 0.8, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.0, 0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.5, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.25, 0.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.25, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.5, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.25, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.0, -0.1, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-0.25, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [0.0, -0.1, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-0.5, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-1.0, 1.0, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- },
- ColorVertex3D {
- v_position: [-0.25, -0.5, Text::accumulator(normalized_depth, &mut depth_accumulator, depth_accum_constant)],
- color: [1.0, 1.0, 1.0, 1.0/255.0],
- }]
- };
-
- Text {
- verts: VertexType::TextType(verts),
- position: position,
- size: size,
- }
- }
-}
-
-impl Drawable for Text {
- fn get(&self, window_size: (u32, u32)) -> Vec {
- vec![self.verts.clone()]
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index ecd22658..3cb59989 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -49,17 +49,37 @@ pub struct ImageInfo {
}
#[derive(Deserialize, Clone, Debug)]
-pub struct DetectionLayerPayload {
- pub kernel: String,
- pub weight: f32,
- pub invert: bool,
- pub blur_radius: f32,
- pub sat_min_value: f32,
- pub canny_low: f32,
- pub canny_high: f32,
- pub xdog_sigma2: f32,
- pub xdog_tau: f32,
- pub xdog_phi: f32,
+pub struct GraphNodePayload {
+ pub id: String,
+ pub kind: String, // "Source" | "Kernel" | "Combine" | "Output"
+ pub x: f32,
+ pub y: f32,
+ // Kernel params (optional)
+ pub kernel: Option,
+ pub weight: Option,
+ pub invert: Option,
+ pub blur_radius: Option,
+ pub sat_min_value: Option,
+ pub canny_low: Option,
+ pub canny_high: Option,
+ pub xdog_sigma2: Option,
+ pub xdog_tau: Option,
+ pub xdog_phi: Option,
+ // Combine params (optional)
+ pub blend_mode: Option,
+}
+
+#[derive(Deserialize, Clone, Debug)]
+pub struct GraphEdgePayload {
+ pub from: String,
+ pub to: String,
+ pub port: usize,
+}
+
+#[derive(Deserialize, Clone, Debug)]
+pub struct DetectionGraphPayload {
+ pub nodes: Vec,
+ pub edges: Vec,
}
#[derive(Deserialize, Clone, Debug)]
@@ -75,13 +95,13 @@ pub struct ColorFilterPayload {
#[derive(Deserialize, Clone, Debug)]
pub struct ProcessPassPayload {
- pub pass_index: usize,
- pub layers: Vec,
- pub threshold: u8,
- pub min_area: u32,
- pub rdp_epsilon: f32,
- pub connectivity: String, // "four" | "eight"
- pub color_filter: ColorFilterPayload,
+ pub pass_index: usize,
+ pub graph: DetectionGraphPayload,
+ pub threshold: u8,
+ pub min_area: u32,
+ pub rdp_epsilon: f32,
+ pub connectivity: String, // "four" | "eight"
+ pub color_filter: ColorFilterPayload,
}
#[derive(Serialize, Clone, Default)]
@@ -95,6 +115,7 @@ pub struct ProcessResult {
pub hull_count: usize,
pub coverage_pct: usize,
pub viz_b64: String,
+ pub node_previews: std::collections::HashMap,
pub timings: Vec,
}
@@ -154,33 +175,52 @@ pub struct PassStrokesPayload {
// ── Helpers ────────────────────────────────────────────────────────────────────
-fn to_detection_params(layers: &[DetectionLayerPayload]) -> detect::DetectionParams {
+fn to_detection_graph(payload: &DetectionGraphPayload) -> detect::DetectionGraph {
use detect::DetectionKernel::*;
- detect::DetectionParams {
- layers: layers.iter().map(|l| {
- let kernel = match l.kernel.as_str() {
- "Sobel" => Sobel,
- "ColorGradient" => ColorGradient,
- "Laplacian" => Laplacian,
- "Canny" => Canny,
- "Saturation" => Saturation,
- "XDoG" => XDoG,
- _ => Luminance,
- };
- detect::DetectionLayer {
- kernel,
- weight: l.weight,
- invert: l.invert,
- blur_radius: l.blur_radius,
- sat_min_value: l.sat_min_value,
- canny_low: l.canny_low,
- canny_high: l.canny_high,
- xdog_sigma2: l.xdog_sigma2,
- xdog_tau: l.xdog_tau,
- xdog_phi: l.xdog_phi,
+ let nodes = payload.nodes.iter().map(|n| {
+ let kind = match n.kind.as_str() {
+ "Source" => detect::NodeKind::Source,
+ "Kernel" => {
+ let kernel = match n.kernel.as_deref().unwrap_or("Luminance") {
+ "Sobel" => Sobel,
+ "ColorGradient" => ColorGradient,
+ "Laplacian" => Laplacian,
+ "Canny" => Canny,
+ "Saturation" => Saturation,
+ "XDoG" => XDoG,
+ _ => Luminance,
+ };
+ detect::NodeKind::Kernel(detect::DetectionLayer {
+ kernel,
+ weight: n.weight.unwrap_or(1.0),
+ invert: n.invert.unwrap_or(false),
+ blur_radius: n.blur_radius.unwrap_or(0.0),
+ sat_min_value: n.sat_min_value.unwrap_or(0.1),
+ canny_low: n.canny_low.unwrap_or(50.0),
+ canny_high: n.canny_high.unwrap_or(150.0),
+ xdog_sigma2: n.xdog_sigma2.unwrap_or(1.6),
+ xdog_tau: n.xdog_tau.unwrap_or(0.98),
+ xdog_phi: n.xdog_phi.unwrap_or(10.0),
+ })
}
- }).collect(),
- }
+ "Combine" => {
+ let mode = detect::BlendMode::from_str(
+ n.blend_mode.as_deref().unwrap_or("Average")
+ );
+ detect::NodeKind::Combine(mode)
+ }
+ _ => detect::NodeKind::Output,
+ };
+ detect::GraphNode { id: n.id.clone(), kind }
+ }).collect();
+
+ let edges = payload.edges.iter().map(|e| detect::GraphEdge {
+ from: e.from.clone(),
+ to: e.to.clone(),
+ port: e.port,
+ }).collect();
+
+ detect::DetectionGraph { nodes, edges }
}
fn to_color_filter(p: &ColorFilterPayload) -> hulls::ColorFilter {
@@ -213,6 +253,24 @@ fn rgba_to_b64_png(rgba: &[u8], w: u32, h: u32) -> String {
B64.encode(buf.into_inner())
}
+fn map_to_b64_small(map: &[u8], w: u32, h: u32) -> String {
+ const MAX_DIM: u32 = 256;
+ let gray = image::GrayImage::from_raw(w, h, map.to_vec())
+ .expect("bad map buffer");
+ let out = if w > MAX_DIM || h > MAX_DIM {
+ let scale = MAX_DIM as f32 / w.max(h) as f32;
+ let (sw, sh) = ((w as f32 * scale) as u32, (h as f32 * scale) as u32);
+ image::imageops::resize(&gray, sw, sh, image::imageops::FilterType::Nearest)
+ } else {
+ gray
+ };
+ let mut buf = std::io::Cursor::new(Vec::new());
+ image::DynamicImage::ImageLuma8(out)
+ .write_to(&mut buf, image::ImageFormat::Jpeg)
+ .unwrap();
+ B64.encode(buf.into_inner())
+}
+
fn rgb_to_b64_jpeg(rgb: &image::RgbImage) -> String {
const MAX_DIM: u32 = 1024;
let (w, h) = rgb.dimensions();
@@ -240,7 +298,7 @@ fn process_pass_work(
let mut steps: Vec = Vec::new();
let (w, h) = rgb.dimensions();
- let det_params = to_detection_params(&payload.layers);
+ let det_graph = to_detection_graph(&payload.graph);
let hull_params = hulls::HullParams {
threshold: payload.threshold,
min_area: payload.min_area,
@@ -254,9 +312,14 @@ fn process_pass_work(
let color_filter = to_color_filter(&payload.color_filter);
let mut t = Instant::now();
- let response = detect::apply_stack(rgb, &det_params);
+ let (response, node_outputs) = detect::evaluate_graph(rgb, &det_graph);
t = lap!(steps, "detect", t);
+ let node_previews: std::collections::HashMap = node_outputs.iter()
+ .map(|(id, map)| (id.clone(), map_to_b64_small(map, w, h)))
+ .collect();
+ t = lap!(steps, "node previews", t);
+
let all_hulls = hulls::extract_hulls(&response, rgb, w, h, &hull_params);
t = lap!(steps, "hull extract", t);
@@ -280,7 +343,7 @@ fn process_pass_work(
steps.push(StepTime { label: "total".into(), ms: t0.elapsed().as_millis() as u64 });
- (extracted, ProcessResult { hull_count, coverage_pct, viz_b64, timings: steps })
+ (extracted, ProcessResult { hull_count, coverage_pct, viz_b64, node_previews, timings: steps })
}
fn generate_fill_work(
@@ -803,18 +866,17 @@ mod blocking_tests {
fn default_process_payload() -> ProcessPassPayload {
ProcessPassPayload {
pass_index: 0,
- layers: vec![DetectionLayerPayload {
- kernel: "Luminance".into(),
- weight: 1.0,
- invert: false,
- blur_radius: 0.0,
- sat_min_value: 0.0,
- canny_low: 0.1,
- canny_high: 0.3,
- xdog_sigma2: 1.5,
- xdog_tau: 0.98,
- xdog_phi: 10.0,
- }],
+ graph: DetectionGraphPayload {
+ nodes: vec![
+ GraphNodePayload { id: "source".into(), kind: "Source".into(), x: 0.0, y: 0.0, kernel: None, weight: None, invert: None, blur_radius: None, sat_min_value: None, canny_low: None, canny_high: None, xdog_sigma2: None, xdog_tau: None, xdog_phi: None, blend_mode: None },
+ GraphNodePayload { id: "k1".into(), kind: "Kernel".into(), x: 0.0, y: 0.0, kernel: Some("Luminance".into()), weight: Some(1.0), invert: Some(false), blur_radius: Some(0.0), sat_min_value: Some(0.0), canny_low: Some(50.0), canny_high: Some(150.0), xdog_sigma2: Some(1.6), xdog_tau: Some(0.98), xdog_phi: Some(10.0), blend_mode: None },
+ GraphNodePayload { id: "output".into(), kind: "Output".into(), x: 0.0, y: 0.0, kernel: None, weight: None, invert: None, blur_radius: None, sat_min_value: None, canny_low: None, canny_high: None, xdog_sigma2: None, xdog_tau: None, xdog_phi: None, blend_mode: None },
+ ],
+ edges: vec![
+ GraphEdgePayload { from: "source".into(), to: "k1".into(), port: 0 },
+ GraphEdgePayload { from: "k1".into(), to: "output".into(), port: 0 },
+ ],
+ },
threshold: 128,
min_area: 10,
rdp_epsilon: 2.0,
@@ -1181,7 +1243,18 @@ mod viz_tests {
xdog_tau: lj["xdog_tau"].as_f64().unwrap_or(0.98) as f32,
xdog_phi: lj["xdog_phi"].as_f64().unwrap_or(10.0) as f32,
};
- let response = detect::apply_stack(&img, &detect::DetectionParams { layers: vec![layer] });
+ let graph = detect::DetectionGraph {
+ nodes: vec![
+ detect::GraphNode { id: "source".into(), kind: detect::NodeKind::Source },
+ detect::GraphNode { id: "k1".into(), kind: detect::NodeKind::Kernel(layer) },
+ detect::GraphNode { id: "output".into(), kind: detect::NodeKind::Output },
+ ],
+ edges: vec![
+ detect::GraphEdge { from: "source".into(), to: "k1".into(), port: 0 },
+ detect::GraphEdge { from: "k1".into(), to: "output".into(), port: 0 },
+ ],
+ };
+ let (response, _) = detect::evaluate_graph(&img, &graph);
let params = HullParams { threshold, min_area, rdp_epsilon: rdp_eps,
connectivity: hulls::Connectivity::Four };
let hs = hulls::extract_hulls(&response, &img, w, h, ¶ms);
diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs
deleted file mode 100644
index 9e7aac68..00000000
--- a/src/vkprocessor.rs
+++ /dev/null
@@ -1,348 +0,0 @@
-use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
-use vulkano::device::{Device, DeviceExtensions, QueuesIter, Queue};
-use vulkano::instance::{Instance, PhysicalDevice};
-use vulkano::sync::{GpuFuture, FlushError, NowFuture};
-use vulkano::sync::now;
-use vulkano::sync;
-use std::sync::Arc;
-use vulkano::swapchain::{Swapchain, PresentMode, SurfaceTransform, Surface, SwapchainCreationError, AcquireError, Capabilities, FullscreenExclusive, ColorSpace};
-use vulkano::image::swapchain::SwapchainImage;
-use crate::compute::compu_state::CompuState;
-use vulkano::image::ImageUsage;
-use crate::compute::compu_frame::CompuFrame;
-use crate::canvas::canvas_frame::{CanvasFrame};
-use std::time::Duration;
-use vulkano::pipeline::depth_stencil::{DynamicStencilValue, StencilFaceFlags};
-use vulkano::pipeline::vertex::{OneVertexOneInstanceDefinition, SingleBufferDefinition};
-use crate::canvas::canvas_state::CanvasState;
-use crate::canvas::managed::shader::generic_shader::GenericShader;
-use crate::canvas::managed::shader::text_shader::TextShader;
-use crate::canvas::managed::handles::{CanvasTextureHandle, CompiledShaderHandle, CanvasFontHandle, CanvasImageHandle};
-use crate::compute::managed::handles::{CompuKernelHandle, CompuBufferHandle};
-use crate::util::vertex::{VertexType, ColorVertex3D, TextVertex3D, TextureVertex3D, ImageVertex3D};
-use vulkano_text::DrawText;
-use winit::window::{Window, WindowBuilder};
-use vulkano::instance::debug::DebugCallback;
-use winit::dpi::LogicalSize;
-use vulkano_win::VkSurfaceBuild;
-use winit::event_loop::EventLoop;
-
-
-/// VKProcessor holds the vulkan instance information, the swapchain,
-/// and the compute and canvas states
-pub struct VkProcessor {
- // Vulkan state fields
- //pub physical: PhysicalDevice<'a>,
- pub device: Arc,
- pub queues: QueuesIter,
- pub queue: Arc,
-
- pub swapchain: Option>>,
- pub swapchain_images: Option>>>,
-
- pub swapchain_recreate_needed: bool,
-
- /// State holding textures, images, and their related vertex buffers
- canvas_state: CanvasState,
-
- /// State holding
- compute_state: CompuState,
-
- capabilities: Capabilities,
-
-}
-
-
-impl VkProcessor {
- /// Creates a new VkProcessor from an instance and surface
- /// This includes the physical device, queues, compute and canvas state
- pub fn new(instance: Arc, surface: Arc>) -> VkProcessor {
-
- let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
-
- let queue_family = physical.queue_families().find(|&q| {
- // We take the first queue that supports drawing to our window.
- q.supports_graphics() &&
- surface.is_supported(q).unwrap_or(false) &&
- q.supports_compute()
- }).unwrap();
-
- let device_ext = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::none() };
-
- let (device, mut queues) = Device::new(physical,
- physical.supported_features(),
- &device_ext,
- [(queue_family, 0.5)].iter().cloned()).unwrap();
- let queue = queues.next().unwrap();
-
- let capabilities = surface.capabilities(physical).unwrap();
-
- VkProcessor {
- device: device.clone(),
- queue: queue.clone(),
- queues: queues,
- swapchain: None,
- swapchain_images: None,
- swapchain_recreate_needed: false,
- compute_state: CompuState::new(),
- capabilities: capabilities.clone(),
- canvas_state: CanvasState::new(queue, device, physical, capabilities),
- }
- }
-
- pub fn get_canvas_state(&self) -> &CanvasState {
- &self.canvas_state
- }
-
- /// Using the surface, we calculate the surface capabilities and create the swapchain and swapchain images
- pub fn create_swapchain(&mut self, instance: Arc, surface: Arc>) {
- let (mut swapchain, images) = {
- let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
- let capabilities = surface.capabilities(physical).unwrap();
- let usage = capabilities.supported_usage_flags;
- let alpha = capabilities.supported_composite_alpha.iter().next().unwrap();
- // Choosing the internal format that the images will have.
- let format = capabilities.supported_formats[0].0;
- let colorspace = capabilities.supported_formats[0].1;
-
- // Set the swapchains window dimensions
- let initial_dimensions = if let dimensions = surface.window().inner_size() {
- // convert to physical pixels
- let dimensions: (u32, u32) = dimensions.to_logical::(surface.window().scale_factor()).into();
- [dimensions.0, dimensions.1]
- } else {
- // The window no longer exists so exit the application.
- panic!("window closed");
- };
-
- Swapchain::new(self.device.clone(),
- surface.clone(),
- capabilities.min_image_count, // number of attachment images
- format,
- initial_dimensions,
- 1, // Layers
- ImageUsage::color_attachment(),
- &self.queue,
- SurfaceTransform::Identity,
- alpha,
- PresentMode::Immediate,
- FullscreenExclusive::Default, true,
- colorspace).unwrap()
- };
-
- self.swapchain = Some(swapchain);
- self.swapchain_images = Some(images);
- }
-
- /// On screen resizes, the swapchain and images must be recreated
- pub fn recreate_swapchain(&mut self, surface: &Arc>) {
- let dimensions = if let dimensions = surface.window().inner_size() {
- let dimensions: (u32, u32) = dimensions.to_logical::(surface.window().scale_factor()).into();
- [dimensions.0, dimensions.1]
- } else {
- return;
- };
-
- let (new_swapchain, new_images) = match self.swapchain.clone().unwrap().clone()
- .recreate_with_dimensions(dimensions) {
- Ok(r) => r,
- // This error tends to happen when the user is manually resizing the window.
- // Simply restarting the loop is the easiest way to fix this issue.
- Err(SwapchainCreationError::UnsupportedDimensions) => panic!("Uh oh"),
- Err(err) => panic!("{:?}", err)
- };
-
- self.swapchain = Some(new_swapchain);
- self.swapchain_images = Some(new_images);
- }
-
- /// A hardcoded list of textures which can be preloaded from this function
- pub fn preload_textures(&mut self) {
- self.canvas_state.load_texture(String::from("ford2.jpg"));
- self.canvas_state.load_texture(String::from("funky-bird.jpg"));
- self.canvas_state.load_texture(String::from("button.png"));
- self.canvas_state.load_texture(String::from("background.jpg"));
- self.canvas_state.load_texture(String::from("test2.png"));
- self.canvas_state.load_texture(String::from("sfml.png"));
- }
-
- /// A hardcoded list of kernels which can be preloaded from this function
- pub fn preload_kernels(&mut self) {
- self.compute_state.new_kernel(String::from("simple-homogenize.compute"), self.device.clone());
- self.compute_state.new_kernel(String::from("simple-edge.compute"), self.device.clone());
- }
-
- /// A hardcoded list of shaders which can be preloaded from this function
- pub fn preload_shaders(&mut self) {
- self.canvas_state.load_shader::(String::from("color-passthrough"), self.capabilities.clone());
- self.canvas_state.load_shader::(String::from("simple_texture"), self.capabilities.clone());
- self.canvas_state.load_shader::(String::from("simple_image"), self.capabilities.clone());
- self.canvas_state.load_shader::(String::from("simple_text"), self.capabilities.clone());
- }
-
- /// A hardcoded list of shaders which can be proloaded from this function
- pub fn preload_fonts(&mut self) {
- //self.canvas_state.load_font(String::from("sansation.ttf"));
- }
-
- /// O(n) Lookup for the matching texture string
- pub fn get_texture_handle(&self, texture_name: String) -> Option> {
- self.canvas_state.get_texture_handle(texture_name)
- }
-
- /// O(n) Lookup for the matching kernel string
- pub fn get_kernel_handle(&self, kernel_name: String) -> Option> {
- self.compute_state.get_kernel_handle(kernel_name)
- }
-
- /// O(n) Lookup for the matching shader string
- pub fn get_shader_handle(&self, shader_name: String) -> Option> {
- self.canvas_state.get_shader_handle(shader_name)
- }
-
- pub fn get_font_handle(&self, font_name: String) -> Option> {
- self.canvas_state.get_font_handle(font_name)
- }
-
- /// Create a new image which has the transfer usage
- pub fn new_swap_image(&mut self, dimensions: (u32, u32)) -> Arc {
- let mut usage = ImageUsage::none();
- usage.transfer_destination = true;
- usage.storage = true;
-
- self.canvas_state.create_image(dimensions, usage)
- }
-
- /// Builds a compute buffer and returns it's handle
- pub fn new_compute_buffer(&mut self, data: Vec, dimensions: (u32, u32), stride: u32) -> Arc {
- self.compute_state.new_compute_buffer(data, dimensions, stride, self.device.clone())
- }
-
- /// Takes a compute buffer handle and returns the read data
- pub fn read_compute_buffer(&mut self, handle: Arc) -> Vec {
- self.compute_state.read_compute_buffer(handle)
- }
-
- /// Takes a compute buffer handle and writes the received data
- pub fn write_compute_buffer(&self, handle: Arc, data: Vec) {
- self.compute_state.write_compute_buffer(handle, data)
- }
-
- /// Run the VKprocessor for a single frame, consuming the Canvas/Compu Frames
- pub fn run(&mut self,
- surface: &Arc>,
- canvas_frame: &CanvasFrame,
- compute_frame: &CompuFrame,
- ) {
- {
- let g = hprof::enter("Waiting at queue");
- self.queue.wait();
- }
-
- let g = hprof::enter("Frame buffer, future, swapchain recreate");
- let mut framebuffers =
- self.canvas_state.window_size_dependent_setup(&self.swapchain_images.clone().unwrap().clone());
-
- // Whenever the window resizes we need to recreate everything dependent on the window size.
- // In this example that includes the swapchain, the framebuffers and the dynamic state viewport.
- if self.swapchain_recreate_needed {
- self.recreate_swapchain(surface);
- framebuffers =
- self.canvas_state.window_size_dependent_setup(&self.swapchain_images.clone().unwrap().clone());
- self.swapchain_recreate_needed = false;
- }
-
- // This function can block if no image is available. The parameter is an optional timeout
- // after which the function call will return an error.
- let (image_num, suboptimal, acquire_future) =
- match vulkano::swapchain::acquire_next_image(
- self.swapchain.clone().unwrap().clone(),
- None,
- ) {
- Ok(r) => r,
- Err(AcquireError::OutOfDate) => {
- self.swapchain_recreate_needed = true;
- return;
- }
- Err(err) => panic!("{:?}", err)
- };
-
- if suboptimal {
- self.swapchain_recreate_needed = true;
- }
-
- drop(g);
-
- let allocated_buffers = {
- // take the canvas frame and create the vertex buffers
- // TODO: This performs gpu buffer creation. Shouldn't be in hotpath??
- let g = hprof::enter("Canvas creates GPU buffers");
- self.canvas_state.allocate(canvas_frame)
- };
-
- // let mut draw_text = DrawText::new(self.device.clone(), self.queue.clone(), self.swapchain.unwrap().clone(), &self.swapchain_images.images);
-
- let mut command_buffer =
- AutoCommandBufferBuilder::primary_one_time_submit(self.device.clone(), self.queue.family()).unwrap();
-
- let g = hprof::enter("Push compute commands to command buffer");
- // Add the compute commands
- self.compute_state.compute_commands(compute_frame, &mut command_buffer, &self.canvas_state);
- drop(g);
-
- let g = hprof::enter("Push draw commands to command buffer");
-
- // Add the draw commands
- //let mut command_buffer = self.canvas_state.draw_commands(command_buffer, framebuffers, image_num);
- self.canvas_state.draw_commands(&mut command_buffer, framebuffers, image_num, allocated_buffers);
-
- // And build
- let command_buffer = command_buffer.build().unwrap();
- drop(g);
-
- // Wait on the previous frame, then execute the command buffer and present the image
- {
- let g = hprof::enter("Joining on the framebuffer");
- let mut future = sync::now(self.device.clone())
- .join(acquire_future);
- drop(g);
-
- let g = hprof::enter("Running the kernel and waiting on the future");
-
- let future = future
- .then_execute(self.queue.clone(), command_buffer).unwrap()
- .then_swapchain_present(self.queue.clone(), self.swapchain.clone().unwrap().clone(), image_num)
- .then_signal_fence_and_flush();
-
- match future {
- Ok(future) => {
- future.wait(None).unwrap();
- }
- Err(FlushError::OutOfDate) => {
- self.swapchain_recreate_needed = true;
- }
- Err(e) => {
- println!("{:?}", e);
- }
- }
- }
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-