diff --git a/Cargo.lock b/Cargo.lock index 5541319e7b..cf63a5aff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "anyhow" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" + [[package]] name = "approx" version = "0.3.2" @@ -329,6 +335,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "cmake" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.20.2" @@ -1016,6 +1031,12 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "glyph_brush" version = "0.7.0" @@ -2374,9 +2395,12 @@ dependencies = [ name = "roc_editor" version = "0.1.0" dependencies = [ + "anyhow", "bumpalo", "env_logger 0.7.1", + "fs_extra", "futures", + "glob", "glyph_brush", "im", "im-rc", @@ -2404,6 +2428,7 @@ dependencies = [ "roc_types", "roc_unify", "roc_uniq", + "shaderc", "target-lexicon", "tokio", "wgpu", @@ -2855,6 +2880,26 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "shaderc" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b8aeaae10b9bda5cba66736a7e265f67698e912e1cc6a4678acba286e22be9" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b12d7c62d6732884c9dfab587503fa3a795b108df152415a89da23812d4737e" +dependencies = [ + "cmake", + "libc", +] + [[package]] name = "signal-hook-registry" version = "1.2.1" diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 35f7338629..de633e1c9f 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -63,3 +63,9 @@ maplit = "1.0.1" indoc = "0.3.3" quickcheck = "0.8" quickcheck_macros = "0.8" + +[build-dependencies] +anyhow = "1.0" +fs_extra = "1.1" +glob = "0.3" +shaderc = "0.6" \ No newline at end of file diff --git a/editor/build.rs b/editor/build.rs new file mode 100644 index 0000000000..04f65c3307 --- /dev/null +++ b/editor/build.rs @@ -0,0 +1,81 @@ +use anyhow::*; +use glob::glob; +use std::fs::{read_to_string, write}; +use std::path::PathBuf; + +// Build script for shaders used from: +// https://sotrh.github.io/learn-wgpu/beginner/tutorial3-pipeline/#compiling-shaders-and-include-spirv + +struct ShaderData { + src: String, + src_path: PathBuf, + spv_path: PathBuf, + kind: shaderc::ShaderKind, +} + +impl ShaderData { + pub fn load(src_path: PathBuf) -> Result { + let extension = src_path + .extension() + .context("File has no extension")? + .to_str() + .context("Extension cannot be converted to &str")?; + let kind = match extension { + "vert" => shaderc::ShaderKind::Vertex, + "frag" => shaderc::ShaderKind::Fragment, + "comp" => shaderc::ShaderKind::Compute, + _ => bail!("Unsupported shader: {}", src_path.display()), + }; + + let src = read_to_string(src_path.clone())?; + let spv_path = src_path.with_extension(format!("{}.spv", extension)); + + Ok(Self { + src, + src_path, + spv_path, + kind, + }) + } +} + +fn main() -> Result<()> { + // Collect all shaders recursively within /src/ + let mut shader_paths = [ + glob("./src/**/*.vert")?, + glob("./src/**/*.frag")?, + glob("./src/**/*.comp")?, + ]; + + // This could be parallelized + let shaders = shader_paths + .iter_mut() + .flatten() + .map(|glob_result| ShaderData::load(glob_result?)) + .collect::>>() + .into_iter() + .collect::>>()?; + + let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?; + + // This can't be parallelized. The [shaderc::Compiler] is not + // thread safe. Also, it creates a lot of resources. You could + // spawn multiple processes to handle this, but it would probably + // be better just to only compile shaders that have been changed + // recently. + for shader in shaders { + // This tells cargo to rerun this script if something in /src/ changes. + println!("cargo:rerun-if-changed={:?}", shader.src_path); + + let compiled = compiler.compile_into_spirv( + &shader.src, + shader.kind, + &shader.src_path.to_str().unwrap(), + "main", + None, + )?; + write(shader.spv_path, compiled.as_binary_u8())?; + } + + Ok(()) +} diff --git a/editor/shaders/triangle.frag b/editor/shaders/triangle.frag deleted file mode 100644 index a6fbc1a628..0000000000 --- a/editor/shaders/triangle.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) in vec4 vertex_color; - -layout(location = 0) out vec4 fragment_color; - -void main() { - fragment_color = vertex_color; -} diff --git a/editor/shaders/triangle.vert b/editor/shaders/triangle.vert deleted file mode 100644 index 1c8ff27e99..0000000000 --- a/editor/shaders/triangle.vert +++ /dev/null @@ -1,25 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(push_constant) uniform PushConstants { - vec4 color; - vec2 pos; - vec2 scale; -} push_constants; - -layout(location = 0) out vec4 vertex_color; - -void main() { - vec2 position; - if (gl_VertexIndex == 0) { - position = vec2(0.0, -0.5); - } else if (gl_VertexIndex == 1) { - position = vec2(-0.5, 0.5); - } else if (gl_VertexIndex == 2) { - position = vec2(0.5, 0.5); - } - - vec2 pos = position * push_constants.scale; - vertex_color = push_constants.color; - gl_Position = vec4((pos + push_constants.pos), 0.0, 1.0); -} diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 572b109e8c..3fdf1fd3c8 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -76,7 +76,6 @@ fn run_event_loop() -> Result<(), Box> { // Prepare swap chain let render_format = wgpu::TextureFormat::Bgra8UnormSrgb; let mut size = window.inner_size(); - let mut swap_chain = device.create_swap_chain( &surface, &wgpu::SwapChainDescriptor { @@ -88,6 +87,43 @@ fn run_event_loop() -> Result<(), Box> { }, ); + // Prepare Triangle Pipeline + let triangle_vs_module = + device.create_shader_module(wgpu::include_spirv!("shaders/rect.vert.spv")); + let triangle_fs_module = + device.create_shader_module(wgpu::include_spirv!("shaders/rect.frag.spv")); + + let triangle_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let triangle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&triangle_pipeline_layout), + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &triangle_vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &triangle_fs_module, + entry_point: "main", + }), + // Use the default rasterizer state: no culling, no depth bias + rasterization_state: None, + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[wgpu::TextureFormat::Bgra8UnormSrgb.into()], + depth_stencil_state: None, + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + }, + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + // Prepare glyph_brush let inconsolata = ab_glyph::FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?; @@ -182,7 +218,7 @@ fn run_event_loop() -> Result<(), Box> { // Clear frame { - let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &frame.view, resolve_target: None, @@ -198,6 +234,9 @@ fn run_event_loop() -> Result<(), Box> { }], depth_stencil_attachment: None, }); + + render_pass.set_pipeline(&triangle_pipeline); + render_pass.draw(0..3, 0..1); } glyph_brush.queue(Section { diff --git a/editor/src/shaders/rect.frag b/editor/src/shaders/rect.frag new file mode 100644 index 0000000000..48576aefd4 --- /dev/null +++ b/editor/src/shaders/rect.frag @@ -0,0 +1,7 @@ +#version 450 + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/editor/src/shaders/rect.vert b/editor/src/shaders/rect.vert new file mode 100644 index 0000000000..bb654d6cea --- /dev/null +++ b/editor/src/shaders/rect.vert @@ -0,0 +1,10 @@ +#version 450 + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + vec2 position = vec2(gl_VertexIndex, (gl_VertexIndex & 1) * 2) - 1; + gl_Position = vec4(position, 0.0, 1.0); +} \ No newline at end of file