From 050d51ba63309ec2d9e34a0790dbbfe40d194dc1 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Thu, 9 Feb 2023 19:33:44 +0000 Subject: [PATCH 01/16] eager shader compilation --- lib/rust/ensogl/core/src/display/scene.rs | 12 +- .../src/display/shape/primitive/system.rs | 2 +- .../core/src/display/symbol/gpu/shader.rs | 4 +- lib/rust/ensogl/core/src/display/world.rs | 20 ++- .../core/src/system/gpu/shader/compiler.rs | 122 ++++++++++-------- 5 files changed, 97 insertions(+), 63 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index 91cf84d9a8af..c8d543d8e96f 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -719,6 +719,7 @@ pub struct SceneData { display_mode: Rc>, extensions: Extensions, disable_context_menu: Rc, + persistent_shaders: Rc>, } impl SceneData { @@ -727,6 +728,7 @@ impl SceneData { stats: &Stats, on_mut: OnMut, display_mode: &Rc>, + persistent_shaders: Vec, ) -> Self { debug!("Initializing."); let display_mode = display_mode.clone_ref(); @@ -766,6 +768,7 @@ impl SceneData { let context_lost_handler = default(); let pointer_position_changed = default(); let shader_compiler = default(); + let persistent_shaders = Rc::new(persistent_shaders); Self { display_object, display_mode, @@ -791,6 +794,7 @@ impl SceneData { shader_compiler, extensions, disable_context_menu, + persistent_shaders, } .init() } @@ -808,6 +812,11 @@ impl SceneData { *self.context.borrow_mut() = context.cloned(); self.dirty.shape.set(); self.renderer.set_context(context); + if let Some(context) = context { + for code in self.persistent_shaders.iter().cloned() { + context.shader_compiler.submit_background_job(code); + } + } } pub fn shape(&self) -> &frp::Sampler { @@ -1018,8 +1027,9 @@ impl Scene { stats: &Stats, on_mut: OnMut, display_mode: &Rc>, + persistent_shaders: Vec, ) -> Self { - let no_mut_access = SceneData::new(stats, on_mut, display_mode); + let no_mut_access = SceneData::new(stats, on_mut, display_mode, persistent_shaders); let this = Self { no_mut_access }; this } diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index 47205d84f0a7..f189a397a012 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -445,7 +445,7 @@ impl ShapeSystemModel { if let Some(shader) = crate::display::world::PRECOMPILED_SHADERS .with_borrow(|map| map.get(*self.definition_path).cloned()) { - let code = crate::display::shader::builder::CodeTemplate::new("", shader.fragment, ""); + let code = crate::display::shader::builder::CodeTemplate::new("", &shader.fragment, ""); self.material.borrow_mut().set_code(code); } else { if !display::world::with_context(|t| t.run_mode.get().is_shader_extraction()) { diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs index 04440ceb97c8..676e2c565933 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs @@ -62,7 +62,7 @@ pub struct ShaderData { geometry_material : Material, surface_material : Material, program : Rc>>, - shader_compiler_job : Option, + shader_compiler_job : Option, dirty : Dirty, stats : Stats, profiler : Option, @@ -196,7 +196,7 @@ impl { } fn cancel_previous_shader_compiler_job_and_use_new_one - (&mut self, handler: shader_compiler::JobHandler) { + (&mut self, handler: shader_compiler::JobHandle) { // Dropping the previous handler. self.shader_compiler_job = Some(handler); } diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index ffd097946a64..89532ff14e76 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -116,12 +116,9 @@ thread_local! { /// A precompiled shader definition. It contains the main function body for the vertex and fragment /// shaders. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Deref)] #[allow(missing_docs)] -pub struct PrecompiledShader { - pub vertex: String, - pub fragment: String, -} +pub struct PrecompiledShader(shader::Code); thread_local! { /// List of all precompiled shaders. They are registered here before main entry point is run by @@ -168,7 +165,7 @@ fn extract_shaders_from_js(value: JsValue) -> Result<(), JsValue> { let fragment_field = web::Reflect::get(&value, &"fragment".into())?; let vertex: String = vertex_field.dyn_into::()?.into(); let fragment: String = fragment_field.dyn_into::()?.into(); - let precompiled_shader = PrecompiledShader { vertex, fragment }; + let precompiled_shader = PrecompiledShader(shader::Code { vertex, fragment }); debug!("Registering precompiled shaders for '{key}'."); PRECOMPILED_SHADERS.with_borrow_mut(move |map| { map.insert(key, precompiled_shader); @@ -192,6 +189,13 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { map } +/// Returns the source code for shaders that should be compiled as soon as a context is available. +fn get_persistent_shaders() -> Vec { + PRECOMPILED_SHADERS.with_borrow(|shaders| { + shaders.values().cloned().map(|PrecompiledShader(shader)| shader).collect() + }) +} + // ================ @@ -272,6 +276,7 @@ impl<'t> From<&'t World> for &'t Scene { } + // =========== // === FRP === // =========== @@ -283,6 +288,7 @@ crate::define_endpoints_2! { } + // ========================= // === WorldDataWithLoop === // ========================= @@ -411,7 +417,7 @@ impl WorldData { let scene_dirty = dirty::SharedBool::new(()); let on_change = f!(scene_dirty.set()); let display_mode = Rc::>::default(); - let default_scene = Scene::new(&stats, on_change, &display_mode); + let default_scene = Scene::new(&stats, on_change, &display_mode, get_persistent_shaders()); let uniforms = Uniforms::new(&default_scene.variables); let debug_hotkeys_handle = default(); let garbage_collector = default(); diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index acc0c6cd36d2..e104c98b480f 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -76,16 +76,17 @@ const MAX_PARALLEL_COMPILE_JOBS: usize = 2; // =========== /// Compiler job. After the job is created it can be either transformed to another job, or, in case -/// that was the final job, the [`handler`] callback will be called. See the documentation of the +/// that was the final job, the [`on_ready`] callback will be called. See the documentation of the /// [`Compiler`] to learn more. #[derive(Derivative, Deref)] #[derivative(Debug)] pub struct Job { #[derivative(Debug = "ignore")] - on_ready: Box, + on_ready: Option>, #[deref] input: T, - handler: WeakJobHandler, + /// A cancellation handle. If none is present, the job cannot be cancelled. + handle: Option, /// A key used to cache the compiled program that this job is making progress on. When the job /// is finished or failed, the program or failure status is stored in the cache under this key. cache_key: ShaderCacheKey, @@ -95,42 +96,50 @@ pub struct Job { impl Job { fn map_input(self, f: impl FnOnce(T) -> S) -> Job { let on_ready = self.on_ready; - let handler = self.handler; + let handle = self.handle; let input = f(self.input); let profiler = self.profiler; let cache_key = self.cache_key; - Job { on_ready, input, handler, profiler, cache_key } + Job { on_ready, input, handle, profiler, cache_key } + } + + /// Return whether the job has been cancelled (by its handle being dropped). + fn is_cancelled(&self) -> bool { + match self.handle.as_ref() { + Some(handle) if !handle.exists() => true, + _ => false, + } } } -/// A handler to a job. After the handler is dropped, the job is invalidated and will no longer be +/// A handle to a job. After the handle is dropped, the job is invalidated and will no longer be /// scheduled for evaluation. #[derive(Debug, Clone, CloneRef)] -pub struct JobHandler { +pub struct JobHandle { rc: Rc<()>, } -/// A weak version of [`JobHandler`]. +/// A weak version of [`JobHandle`]. #[derive(Debug, Clone, CloneRef)] -pub struct WeakJobHandler { +pub struct WeakJobHandle { weak: Weak<()>, } -impl JobHandler { +impl JobHandle { /// Constructor. fn new() -> Self { Self { rc: default() } } - /// Get weak reference to this handler. - pub fn downgrade(&self) -> WeakJobHandler { + /// Get weak reference to this handle. + pub fn downgrade(&self) -> WeakJobHandle { let weak = Rc::downgrade(&self.rc); - WeakJobHandler { weak } + WeakJobHandle { weak } } } -impl WeakJobHandler { - /// Check whether the handler was dropped. +impl WeakJobHandle { + /// Check whether the handle was dropped. pub fn exists(&self) -> bool { self.weak.strong_count() > 0 } @@ -182,7 +191,7 @@ pub struct Jobs { /// Compiles and links GL shader programs, asynchronously. The compiler works in the following way: /// 1. A new shader code is submitted with the [`submit`] method. A new job is created in the -/// [`Jobs::compile`] queue and the job handler is returned to the user. +/// [`Jobs::compile`] queue and the job handle is returned to the user. /// /// 2. The following pseudo-algorithm is performed: /// ```text @@ -222,14 +231,25 @@ impl Compiler { Self { cell: RefCell::new(CompilerData::new(context)) } } - /// Submit shader for compilation. + /// Submit shader for compilation. The job will be cancelled if the returned handle is dropped. + #[must_use] pub fn submit( &self, input: shader::Code, profiler: profiler::Debug, on_ready: F, - ) -> JobHandler { - self.cell.borrow_mut().submit(input, profiler, on_ready) + ) -> JobHandle { + let strong_handle = JobHandle::new(); + let handle = strong_handle.downgrade(); + let on_ready = Box::new(on_ready); + self.cell.borrow_mut().submit(input, profiler, Some(on_ready), Some(handle)); + strong_handle + } + + /// Submit a shader for compilation, without awaiting its completion. + pub fn submit_background_job(&self, input: shader::Code) { + let profiler = profiler::create_debug!("submit_background_job"); + self.cell.borrow_mut().submit(input, profiler, None, None); } /// Returns `true` if the compiler has no jobs in progress or queued. @@ -258,15 +278,13 @@ impl CompilerData { Self { dirty, context, jobs, cache, performance } } - fn submit( + fn submit( &mut self, input: shader::Code, profiler: profiler::Debug, - on_ready: F, - ) -> JobHandler { - let strong_handler = JobHandler::new(); - let handler = strong_handler.downgrade(); - let on_ready = Box::new(on_ready); + on_ready: Option>, + handle: Option, + ) { let cache_key = ShaderCache::code_key(&input); match self.cache.get_program(cache_key) { @@ -274,7 +292,7 @@ impl CompilerData { debug!("Awaiting shader compilation {cache_key:?}."); // This shader is currently being processed by another job. Create a new job that // will wait for that job to finish. - let job = Job { input: (), handler, on_ready, profiler, cache_key }; + let job = Job { input: (), handle, on_ready, profiler, cache_key }; self.cache.add_to_waiting_list(job); self.dirty = true; } @@ -283,7 +301,7 @@ impl CompilerData { // This shader has been successfully compiled in the past. Spawn a cache lookup job, // so that `on_ready` callback is will be called from within the job runner during // next processing round. - let job = Job { input: (), handler, on_ready, profiler, cache_key }; + let job = Job { input: (), handle, on_ready, profiler, cache_key }; self.jobs.read_cache.push(job); self.dirty = true; } @@ -297,13 +315,11 @@ impl CompilerData { None => { debug!("Submitting shader for compilation {cache_key:?}."); self.cache.program_submitted(cache_key); - let job = Job { input, handler, on_ready, profiler, cache_key }; + let job = Job { input, handle, on_ready, profiler, cache_key }; self.jobs.compile.push(job); self.dirty = true; } }; - - strong_handler } #[profile(Debug)] @@ -416,10 +432,10 @@ impl CompilerData { })?; profiler.pause(); let input = shader::Sources { vertex, fragment }; - let handler = job.handler; + let handle = job.handle; let on_ready = job.on_ready; let cache_key = job.cache_key; - let link_job = Job { input, handler, on_ready, profiler, cache_key }; + let link_job = Job { input, handle, on_ready, profiler, cache_key }; this.jobs.link.push(link_job); Ok(()) }) @@ -437,17 +453,17 @@ impl CompilerData { this.context.link_program(&program); profiler.pause(); let input = shader::Program::new(shader, program); - let handler = job.handler; + let handle = job.handle; let on_ready = job.on_ready; let cache_key = job.cache_key; match this.context.extensions.khr_parallel_shader_compile { Some(khr) => { let input = KhrProgram { khr, program: input }; - let job = Job { input, handler, cache_key, on_ready, profiler }; + let job = Job { input, handle, cache_key, on_ready, profiler }; this.jobs.khr_completion_check.push(job) } None => { - let job = Job { input, handler, cache_key, on_ready, profiler }; + let job = Job { input, handle, cache_key, on_ready, profiler }; this.jobs.link_check.push(job) } } @@ -479,7 +495,9 @@ impl CompilerData { // processed. `Validated` is the only expected cache entry state at this point. match this.cache.get_program(job.cache_key) { Some(ProgramCacheEntry::Validated(program)) => { - (job.on_ready)(program.clone()); + if let Some(on_ready) = job.on_ready { + (on_ready)(program.clone()); + } Ok(()) } _ => panic!("Shader cache read was submitted for non-completed entry."), @@ -497,17 +515,17 @@ impl CompilerData { while let Some(mut job) = jobs(self).pop() { let cache_key = job.cache_key; - if !job.handler.exists() { + if job.is_cancelled() { // A compilation job has been cancelled, but there still might be other jobs waiting // for it to complete and write data to the cache. Take one of those waiting jobs // and resume the compilation process with its callback. if let Some(waiting_job) = self.cache.take_next_waiting_job(cache_key) { - job.handler = waiting_job.handler; + job.handle = waiting_job.handle; job.on_ready = waiting_job.on_ready; } else { // No waiting jobs, cancel the compilation process. self.cache.program_canceled(cache_key); - trace!("Job handler dropped, skipping."); + trace!("Job handle dropped, skipping."); continue; } } @@ -581,16 +599,12 @@ impl ShaderCache { /// cancelled will be removed from the waiting list to avoid processing them in the future. fn take_next_waiting_job(&mut self, key: ShaderCacheKey) -> Option> { let jobs = self.waiting_jobs.get_mut(&key)?; - if let Some(first_active) = jobs.iter().position(|job| job.handler.exists()) { - // Remove all leading cancelled jobs from the list and take the active in single pass. - // This ensures the minimum number of necessary memory moves and preserves the order of - // submitted jobs. - jobs.drain(0..=first_active).next_back() - } else { - // All jobs in the waiting list were cancelled. - self.waiting_jobs.clear(); - None + while let Some(job) = jobs.pop() { + if !job.is_cancelled() { + return Some(job); + } } + None } fn program_submitted(&mut self, key: ShaderCacheKey) { @@ -611,7 +625,7 @@ impl ShaderCache { &mut self, key: ShaderCacheKey, program: shader::Program, - on_ready: impl FnOnce(shader::Program), + on_ready: Option, ) { self.programs.insert(key, ProgramCacheEntry::Validated(program.clone())); @@ -619,11 +633,15 @@ impl ShaderCache { // entry is updated, in case the job callback schedules another compilation of the same // shader program. We want those submissions to recognize cache status as validated and // dispatch a read from cache. - on_ready(program.clone()); + if let Some(on_ready) = on_ready { + on_ready(program.clone()); + } if let Some(jobs) = self.waiting_jobs.remove(&key) { for job in jobs { - if job.handler.exists() { - (job.on_ready)(program.clone()); + if !job.is_cancelled() { + if let Some(on_ready) = job.on_ready { + (on_ready)(program.clone()); + } } } } From 04b0164a63fa3d2e68559fc242650e29d3b7d23f Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Fri, 10 Feb 2023 01:39:34 +0000 Subject: [PATCH 02/16] support compiling unpaired shaders --- lib/rust/ensogl/core/src/display/scene.rs | 6 +- .../src/display/shape/primitive/system.rs | 2 +- .../core/src/system/gpu/context/extension.rs | 14 +- lib/rust/ensogl/core/src/system/gpu/shader.rs | 30 +- .../core/src/system/gpu/shader/compiler.rs | 271 +++++++++++------- 5 files changed, 221 insertions(+), 102 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index c8d543d8e96f..c236178f8595 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -813,7 +813,11 @@ impl SceneData { self.dirty.shape.set(); self.renderer.set_context(context); if let Some(context) = context { - for code in self.persistent_shaders.iter().cloned() { + for code in self.persistent_shaders.iter() { + let code = shader::Sources { + fragment: Some(code.fragment.clone()), + vertex: None, + }; context.shader_compiler.submit_background_job(code); } } diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index f189a397a012..6b3f79a781a6 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -445,7 +445,7 @@ impl ShapeSystemModel { if let Some(shader) = crate::display::world::PRECOMPILED_SHADERS .with_borrow(|map| map.get(*self.definition_path).cloned()) { - let code = crate::display::shader::builder::CodeTemplate::new("", &shader.fragment, ""); + let code = crate::display::shader::builder::CodeTemplate::from_main(&shader.fragment); self.material.borrow_mut().set_code(code); } else { if !display::world::with_context(|t| t.run_mode.get().is_shader_extraction()) { diff --git a/lib/rust/ensogl/core/src/system/gpu/context/extension.rs b/lib/rust/ensogl/core/src/system/gpu/context/extension.rs index 99afed4981c7..35a3f9ed75e3 100644 --- a/lib/rust/ensogl/core/src/system/gpu/context/extension.rs +++ b/lib/rust/ensogl/core/src/system/gpu/context/extension.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use crate::system::gpu::data::GlEnum; -use web_sys::WebGl2RenderingContext; +use web_sys::{WebGl2RenderingContext, WebGlShader}; use web_sys::WebGlProgram; @@ -68,11 +68,21 @@ impl KhrParallelShaderCompile { /// Asynchronously check if the job is ready. Returns [`None`] if it was impossible to get this /// information. This can happen during context loss or driver failure. - pub fn is_ready( + pub fn is_program_ready( &self, context: &WebGl2RenderingContext, program: &WebGlProgram, ) -> Option { context.get_program_parameter(program, *self.completion_status_khr).as_bool() } + + /// Asynchronously check if the job is ready. Returns [`None`] if it was impossible to get this + /// information. This can happen during context loss or driver failure. + pub fn is_shader_ready( + &self, + context: &WebGl2RenderingContext, + program: &WebGlShader, + ) -> Option { + context.get_shader_parameter(program, *self.completion_status_khr).as_bool() + } } diff --git a/lib/rust/ensogl/core/src/system/gpu/shader.rs b/lib/rust/ensogl/core/src/system/gpu/shader.rs index 76a937702c17..357a3e1f8290 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader.rs @@ -60,7 +60,7 @@ impl ToGlEnum for Type { /// Abstractions for shader sources (vertex and fragment one). #[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Sources { pub vertex: Vertex, pub fragment: Fragment, @@ -72,6 +72,34 @@ pub type Code = Sources; /// Shader sources as compiled shaders that are not linked yet. pub type CompiledCode = Sources, Shader>; +impl Sources { + /// Apply a [`Into::into`] to the [`vertex`] and [`fragment`] elements and return the result. + pub fn into(self) -> Sources where V: Into, F: Into { + let Sources { vertex, fragment } = self; + let vertex = vertex.into(); + let fragment = fragment.into(); + Sources { vertex, fragment } + } + + /// Return references to the [`vertex`] and [`fragment`] elements. + pub fn as_ref(&self) -> Sources<&V, &F> { + let Sources { vertex, fragment } = self; + let vertex = &vertex; + let fragment = &fragment; + Sources { vertex, fragment } + } +} + +impl Sources { + /// Apply a function to the [`vertex`] and [`fragment`] elements and return the result. + pub fn map(self, f: F) -> Sources where F: Fn(VF) -> VF1 { + let Sources { vertex, fragment } = self; + let vertex = f(vertex); + let fragment = f(fragment); + Sources { vertex, fragment } + } +} + // ============== diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index e104c98b480f..fde5906f6faf 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -67,7 +67,8 @@ const FRAME_TIME_THRESHOLD: Duration = (1000.0 / FPS_THRESHOLD).ms(); /// a crude form of backpressure by blocking until all pending jobs complete. We are not sure if it /// is a feature or a bug. To learn more about our findings so far, see: /// https://github.com/enso-org/enso/pull/3378#issuecomment-1090958946 -const MAX_PARALLEL_COMPILE_JOBS: usize = 2; +//const MAX_PARALLEL_COMPILE_JOBS: usize = 2; +const MAX_PARALLEL_COMPILE_JOBS: usize = 1000; @@ -147,16 +148,16 @@ impl WeakJobHandle { -// ================== -// === KhrProgram === -// ================== +// =============== +// === WithKhr === +// =============== /// A program together with the [`KhrParallelShaderCompile`] extension. Used as one of the /// [`Compiler`] jobs to provide nice API. #[derive(Debug)] -struct KhrProgram { +struct WithKhr { khr: KhrParallelShaderCompile, - program: shader::Program, + program: T, } @@ -182,11 +183,12 @@ pub enum Progress { /// Compiler job queues. See the documentation of [`Compiler`] to learn more. #[derive(Debug, Default)] pub struct Jobs { - compile: Vec>, - link: Vec>, - read_cache: Vec>, - khr_completion_check: Vec>, - link_check: Vec>, + compile: Vec, Option>>>, + compile_poll: Vec>>, + compile_check: Vec>, + link: Vec>, + link_poll: Vec>>, + link_check: Vec>, } /// Compiles and links GL shader programs, asynchronously. The compiler works in the following way: @@ -223,6 +225,7 @@ struct CompilerData { jobs: Jobs, cache: ShaderCache, performance: web::Performance, + callbacks: Vec, } impl Compiler { @@ -233,7 +236,7 @@ impl Compiler { /// Submit shader for compilation. The job will be cancelled if the returned handle is dropped. #[must_use] - pub fn submit( + pub fn submit( &self, input: shader::Code, profiler: profiler::Debug, @@ -241,13 +244,13 @@ impl Compiler { ) -> JobHandle { let strong_handle = JobHandle::new(); let handle = strong_handle.downgrade(); - let on_ready = Box::new(on_ready); - self.cell.borrow_mut().submit(input, profiler, Some(on_ready), Some(handle)); + let on_ready: Box = Box::new(on_ready); + self.cell.borrow_mut().submit(input.into(), profiler, on_ready.into(), handle.into()); strong_handle } /// Submit a shader for compilation, without awaiting its completion. - pub fn submit_background_job(&self, input: shader::Code) { + pub fn submit_background_job(&self, input: shader::Sources, Option>) { let profiler = profiler::create_debug!("submit_background_job"); self.cell.borrow_mut().submit(input, profiler, None, None); } @@ -275,18 +278,18 @@ impl CompilerData { let jobs = default(); let cache = default(); let performance = web::window.performance_or_panic(); - Self { dirty, context, jobs, cache, performance } + let callbacks = default(); + Self { dirty, context, jobs, cache, performance, callbacks } } fn submit( &mut self, - input: shader::Code, + input: shader::Sources, Option>, profiler: profiler::Debug, on_ready: Option>, handle: Option, ) { let cache_key = ShaderCache::code_key(&input); - match self.cache.get_program(cache_key) { Some(ProgramCacheEntry::Submitted) => { debug!("Awaiting shader compilation {cache_key:?}."); @@ -296,17 +299,17 @@ impl CompilerData { self.cache.add_to_waiting_list(job); self.dirty = true; } - Some(ProgramCacheEntry::Validated(_)) => { + Some(ProgramCacheEntry::Validated(program)) => { debug!("Reusing cached shader {cache_key:?}."); - // This shader has been successfully compiled in the past. Spawn a cache lookup job, - // so that `on_ready` callback is will be called from within the job runner during - // next processing round. - let job = Job { input: (), handle, on_ready, profiler, cache_key }; - self.jobs.read_cache.push(job); - self.dirty = true; + // This shader has been successfully compiled in the past. Queue the `on_ready` + // callback to be called from within the job runner during next processing round. + if let Some(callback) = on_ready { + let program = program.clone(); + self.callbacks.push(DeferredCallback { callback, program }) + } } Some(ProgramCacheEntry::Failed) => { - debug!("Submitted a shader that previously failed to compile {cache_key:?}."); + warn!("Submitted a shader that previously failed to compile {cache_key:?}."); // This shader failed to compile in the past. Don't spawn any job, as the `on_ready` // callback is never supposed to be called anyway when the compilation fails. @@ -325,7 +328,9 @@ impl CompilerData { #[profile(Debug)] fn run(&mut self, time: animation::TimeInfo) -> bool { let mut any_new_shaders_ready = false; - self.run_khr_completion_check_jobs(); + any_new_shaders_ready = self.run_callbacks() || any_new_shaders_ready; + self.poll_shaders_ready(); + self.poll_programs_ready(); while self.dirty { match self.run_step() { Ok(progress) => { @@ -356,8 +361,7 @@ impl CompilerData { } /// Runs the next compiler job if there is any left and if it will not cause too many jobs being - /// run in parallel. The result [`bool`] indicates if the call to this function did any - /// progress. + /// run in parallel. The result indicates if the call to this function made any progress. fn run_step(&mut self) -> Result, Error> { let ok_progress = |_| Ok(Some(Progress::StepProgress)); let no_progress = Ok(None); @@ -370,10 +374,7 @@ impl CompilerData { self.run_next_link_check_job()?; Ok(Some(Progress::NewShaderReady)) } - _ if !jobs.read_cache.is_empty() => { - self.run_next_read_cache_job()?; - Ok(Some(Progress::NewShaderReady)) - } + _ if !jobs.compile_check.is_empty() => ok_progress(self.run_next_compile_check_job()?), _ => { if max_jobs { if !jobs.compile.is_empty() { @@ -381,7 +382,7 @@ impl CompilerData { let msg2 = "Skipping spawning new ones."; trace!("{msg1} {msg2}"); } - } else if jobs.khr_completion_check.is_empty() { + } else if jobs.compile_poll.is_empty() && jobs.link_poll.is_empty() { trace!("All shaders compiled."); self.dirty = false; } @@ -395,52 +396,97 @@ impl CompilerData { /// it is costly and can prevent the parallelism altogether. To learn more, see: /// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#dont_check_shader_compile_status_unless_linking_fails fn current_parallel_job_count(&self) -> usize { - self.jobs.link.len() + self.jobs.khr_completion_check.len() + self.jobs.link_check.len() + [ + self.jobs.compile.len(), + self.jobs.compile_poll.len(), + self.jobs.compile_check.len(), + self.jobs.link.len(), + self.jobs.link_poll.len(), + self.jobs.link_check.len(), + ] + .iter() + .sum() } #[profile(Debug)] - fn run_khr_completion_check_jobs(&mut self) { - trace!("Running KHR parallel shader compilation check job."); - let jobs = &mut self.jobs.khr_completion_check; - let ready_jobs = - jobs.drain_filter(|job| match job.khr.is_ready(&self.context, &job.program) { - Some(val) => val, - None => { - if !self.context.is_context_lost() { - reportable_warn!( - "context.getProgramParameter returned non-bool value for KHR Parallel \ - Shader Compile status check. This should never happen, however, it \ - should not cause visual artifacts. Reverting to non-parallel mode." - ); - } - true - } - }); + fn poll_programs_ready(&mut self) { + let ready_jobs = self.jobs.link_poll.drain_filter(|job| { + job.khr.is_program_ready(&self.context, &job.program).unwrap_or(true) + }); self.jobs.link_check.extend(ready_jobs.map(|job| job.map_input(|t| t.program))); } + #[profile(Debug)] + fn poll_shaders_ready(&mut self) { + let ready_jobs = self.jobs.compile_poll.drain_filter(|job| { + job.khr.is_shader_ready(&self.context, &job.program).unwrap_or(true) + }); + self.jobs.compile_check.extend(ready_jobs.map(|job| job.map_input(|t| t.program))); + } + #[allow(unused_parens)] fn run_next_compile_job(&mut self) -> Result<(), Error> { self.with_next_job("shader compilation", (|t| &mut t.jobs.compile), |this, job| { let profiler = job.profiler; profiler.resume(); - let vertex = this.cache.get_or_insert_vertex_shader(job.cache_key, || { - CompilerData::compile_shader(&this.context, Vertex, job.input.vertex) - })?; - let fragment = this.cache.get_or_insert_fragment_shader(job.cache_key, || { - CompilerData::compile_shader(&this.context, Fragment, job.input.fragment) - })?; + let vertex = job.input.vertex.and_then(|code| { + this.cache.get_or_insert_vertex_shader(job.cache_key, || { + CompilerData::compile_shader(&this.context, Vertex, code) + }) + }); + let fragment = job.input.fragment.and_then(|code| { + this.cache.get_or_insert_fragment_shader(job.cache_key, || { + CompilerData::compile_shader(&this.context, Fragment, code) + }) + }); profiler.pause(); - let input = shader::Sources { vertex, fragment }; - let handle = job.handle; - let on_ready = job.on_ready; - let cache_key = job.cache_key; - let link_job = Job { input, handle, on_ready, profiler, cache_key }; - this.jobs.link.push(link_job); + let vertex = match vertex { + Some(vertex) => Some(vertex?), + None => None, + }; + let fragment = match fragment { + Some(fragment) => Some(fragment?), + None => None, + }; + match (vertex, fragment) { + (Some(vertex), Some(fragment)) => { + let input = shader::Sources { vertex, fragment }; + let handle = job.handle; + let on_ready = job.on_ready; + let cache_key = job.cache_key; + let link_job = Job { input, handle, on_ready, profiler, cache_key }; + this.jobs.link.push(link_job); + } + (Some(vertex), None) => { + let input = vertex.native; + let handle = job.handle; + let on_ready = job.on_ready; + let cache_key = job.cache_key; + let job = Job { input, handle, cache_key, on_ready, profiler }; + this.queue_shader_check_job(job); + } + (None, Some(fragment)) => { + let input = fragment.native; + let handle = job.handle; + let on_ready = job.on_ready; + let cache_key = job.cache_key; + let job = Job { input, handle, cache_key, on_ready, profiler }; + this.queue_shader_check_job(job); + } + _ => (), + } Ok(()) }) } + fn queue_shader_check_job(&mut self, job: Job) { + match self.context.extensions.khr_parallel_shader_compile { + Some(khr) => + self.jobs.compile_poll.push(job.map_input(|program| WithKhr { khr, program })), + None => self.jobs.compile_check.push(job), + } + } + #[allow(unused_parens)] fn run_next_link_job(&mut self) -> Result<(), Error> { self.with_next_job("shader linking", (|t| &mut t.jobs.link), |this, job| { @@ -458,9 +504,9 @@ impl CompilerData { let cache_key = job.cache_key; match this.context.extensions.khr_parallel_shader_compile { Some(khr) => { - let input = KhrProgram { khr, program: input }; + let input = WithKhr { khr, program: input }; let job = Job { input, handle, cache_key, on_ready, profiler }; - this.jobs.khr_completion_check.push(job) + this.jobs.link_poll.push(job) } None => { let job = Job { input, handle, cache_key, on_ready, profiler }; @@ -473,7 +519,7 @@ impl CompilerData { #[allow(unused_parens)] fn run_next_link_check_job(&mut self) -> Result<(), Error> { - self.with_next_job("shader validation", (|t| &mut t.jobs.link_check), |this, job| { + self.with_next_job("program validation", (|t| &mut t.jobs.link_check), |this, job| { let program = job.input; let param = WebGl2RenderingContext::LINK_STATUS; job.profiler.resume(); @@ -489,21 +535,27 @@ impl CompilerData { } #[allow(unused_parens)] - fn run_next_read_cache_job(&mut self) -> Result<(), Error> { - self.with_next_job("shader cache read", (|t| &mut t.jobs.read_cache), |this, job| { - // This job is only scheduled after this cache entry was determined to be successfully - // processed. `Validated` is the only expected cache entry state at this point. - match this.cache.get_program(job.cache_key) { - Some(ProgramCacheEntry::Validated(program)) => { - if let Some(on_ready) = job.on_ready { - (on_ready)(program.clone()); - } - Ok(()) - } - _ => panic!("Shader cache read was submitted for non-completed entry."), + fn run_next_compile_check_job(&mut self) -> Result<(), Error> { + self.with_next_job("shader validation", (|t| &mut t.jobs.compile_check), |this, job| { + let shader = job.input; + let param = WebGl2RenderingContext::COMPILE_STATUS; + job.profiler.resume(); + let status = this.context.get_shader_parameter(&shader, param); + job.profiler.finish(); + if !status.as_bool().unwrap_or(false) { + error!("Failed to compile shader: {:?}", status); + return Err(Error::ShaderCompilingError(shader)); } - })?; - Ok(()) + Ok(()) + }) + } + + fn run_callbacks(&mut self) -> bool { + let any_new_shaders_ready = !self.callbacks.is_empty(); + for DeferredCallback { callback, program } in self.callbacks.drain(..) { + (callback)(program); + } + any_new_shaders_ready } fn with_next_job( @@ -554,6 +606,18 @@ impl CompilerData { } +// === DeferredCallback === + +/// A callback waiting to be invoked. +#[derive(Derivative)] +#[derivative(Debug)] +struct DeferredCallback { + #[derivative(Debug = "ignore")] + callback: Box, + program: shader::Program, +} + + // =================== // === ShaderCache === @@ -582,8 +646,8 @@ enum ProgramCacheEntry { impl ShaderCache { /// Generate new shader cache key - fn code_key(code: &shader::Code) -> ShaderCacheKey { - (ShaderHash::new(&code.vertex), ShaderHash::new(&code.fragment)) + fn code_key(code: &shader::Sources, Option>) -> ShaderCacheKey { + code.as_ref().map(|shader| shader.as_ref().map(ShaderHash::new)) } fn get_program(&self, key: ShaderCacheKey) -> Option<&ProgramCacheEntry> { @@ -651,22 +715,23 @@ impl ShaderCache { &mut self, key: ShaderCacheKey, compile: impl FnOnce() -> Result, Error>, - ) -> Result, Error> { - Ok(self.vertex_shaders.get_or_insert_with_result(key.0, compile)?.clone()) + ) -> Option, Error>> { + key.vertex.map(|key| self.vertex_shaders.get_or_insert_with_result(key, compile).cloned()) } fn get_or_insert_fragment_shader( &mut self, key: ShaderCacheKey, compile: impl FnOnce() -> Result, Error>, - ) -> Result, Error> { - Ok(self.fragment_shaders.get_or_insert_with_result(key.1, compile)?.clone()) + ) -> Option, Error>> { + key.fragment + .map(|key| self.fragment_shaders.get_or_insert_with_result(key, compile).cloned()) } } /// Shader cache entry key. Allows retrieving linked programs or individual compiled vertex and /// fragment shaders from [`ShaderCache`]. -type ShaderCacheKey = (ShaderHash, ShaderHash); +type ShaderCacheKey = shader::Sources, Option>; /// A shader source hash value used for cache storage. /// We do not want to store and compare very long shader source strings in the hashmap. Instead, we @@ -680,7 +745,7 @@ struct ShaderHash(u64); impl ShaderHash { - fn new(code: &str) -> Self { + fn new(code: impl Hash) -> Self { let mut hasher = DefaultHasher::new(); code.hash(&mut hasher); Self(hasher.finish()) @@ -698,6 +763,7 @@ impl ShaderHash { #[allow(missing_docs)] pub enum Error { ShaderCreationError, + ShaderCompilingError(web_sys::WebGlShader), ProgramCreationError, ProgramLinkingError(shader::CompiledCode), } @@ -706,17 +772,26 @@ impl Error { /// Report the error. This function uses GPU blocking API and thus will cause a significant /// performance hit. Use it only when necessary. pub fn blocking_report(self, context: &WebGl2RenderingContext) -> String { + let unwrap_error = |name: &str, err: Option| { + let header = format!("----- {name} Shader -----"); + err.map(|t| format!("\n\n{header}\n\n{t}")).unwrap_or_else(|| "".into()) + }; match self { Self::ShaderCreationError => "WebGL was unable to create a new shader.".into(), Self::ProgramCreationError => "WebGl was unable to create a new program shader.".into(), - Self::ProgramLinkingError(shader) => { - let unwrap_error = |name: &str, err: Option| { - let header = format!("----- {name} Shader -----"); - err.map(|t| format!("\n\n{header}\n\n{t}")).unwrap_or_else(|| "".into()) + Self::ShaderCompilingError(shader) => { + let shader = Shader::<()> { + native: shader, + code: Default::default(), + tp: Default::default(), }; - - let vertex = &shader.vertex; - let fragment = &shader.fragment; + let log = context.blocking_format_error_log(&shader); + let error = unwrap_error("Shader", log); + format!("Unable to compile shader.\n{error}") + } + Self::ProgramLinkingError(program) => { + let vertex = &program.vertex; + let fragment = &program.fragment; let vertex_log = context.blocking_format_error_log(vertex); let fragment_log = context.blocking_format_error_log(fragment); let vertex_error = unwrap_error("Vertex", vertex_log); @@ -741,7 +816,9 @@ impl Error { let dbg_msg = format!("{}\n{}", run_msg("vertex"), run_msg("fragment")); - format!("Unable to compile shader.\n{dbg_msg}\n{vertex_error}{fragment_error}") + format!( + "Unable to compile shader program.\n{dbg_msg}\n{vertex_error}{fragment_error}" + ) } } } From 9cb79bca54bc5abd2e3818ed8b4b66f10d35386a Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Fri, 10 Feb 2023 18:17:18 +0000 Subject: [PATCH 03/16] clean up --- lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index fde5906f6faf..cf85d11b46c3 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -67,8 +67,7 @@ const FRAME_TIME_THRESHOLD: Duration = (1000.0 / FPS_THRESHOLD).ms(); /// a crude form of backpressure by blocking until all pending jobs complete. We are not sure if it /// is a feature or a bug. To learn more about our findings so far, see: /// https://github.com/enso-org/enso/pull/3378#issuecomment-1090958946 -//const MAX_PARALLEL_COMPILE_JOBS: usize = 2; -const MAX_PARALLEL_COMPILE_JOBS: usize = 1000; +const MAX_PARALLEL_COMPILE_JOBS: usize = 32; @@ -106,10 +105,7 @@ impl Job { /// Return whether the job has been cancelled (by its handle being dropped). fn is_cancelled(&self) -> bool { - match self.handle.as_ref() { - Some(handle) if !handle.exists() => true, - _ => false, - } + self.handle.as_ref().map(|| !handle.exists()).unwrap_or_default() } } From 3f364a3655a24b286a8cf1def75cc54bb1e4f8ac Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Fri, 10 Feb 2023 18:19:18 +0000 Subject: [PATCH 04/16] fix --- lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index cf85d11b46c3..a23dab26c48a 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -105,7 +105,7 @@ impl Job { /// Return whether the job has been cancelled (by its handle being dropped). fn is_cancelled(&self) -> bool { - self.handle.as_ref().map(|| !handle.exists()).unwrap_or_default() + self.handle.as_ref().map(|handle| !handle.exists()).unwrap_or_default() } } From 6648441f3ed48fe51f5a505138726ad86e325302 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 13 Feb 2023 22:33:09 +0000 Subject: [PATCH 05/16] fix log-level changing bug --- lib/rust/logging/macros/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rust/logging/macros/build.rs b/lib/rust/logging/macros/build.rs index ac34acf59f5e..67031e82e547 100644 --- a/lib/rust/logging/macros/build.rs +++ b/lib/rust/logging/macros/build.rs @@ -15,8 +15,8 @@ fn main() { - declare_env_dependence("ENSO_MAX_LOGGING_LEVEL"); - declare_env_dependence("ENSO_UNCOLLAPSED_LOGGING_LEVEL"); + declare_env_dependence("ENSO_MAX_LOG_LEVEL"); + declare_env_dependence("ENSO_UNCOLLAPSED_LOG_LEVEL"); } /// Make cargo aware that the result of compiling this crate depends on an environment variable. From a20e168452529c188b36096ce5f1a4c34d3e32cb Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Fri, 10 Feb 2023 20:53:52 +0000 Subject: [PATCH 06/16] cleanup --- lib/rust/ensogl/core/src/gui/component.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/rust/ensogl/core/src/gui/component.rs b/lib/rust/ensogl/core/src/gui/component.rs index 5aff02adce3f..d2bc5bfb2a4a 100644 --- a/lib/rust/ensogl/core/src/gui/component.rs +++ b/lib/rust/ensogl/core/src/gui/component.rs @@ -168,8 +168,6 @@ impl ShapeViewModel { self.add_to_scene_layer(scene, &layer) } } else { - // Bug in clippy: https://github.com/rust-lang/rust-clippy/issues/9763 - #[allow(clippy::explicit_auto_deref)] let (shape, _) = scene.layers.DETACHED.instantiate(&*self.data.borrow()); self.shape.swap(&shape); } @@ -189,8 +187,6 @@ impl ShapeViewModel { } impl ShapeViewModel { - // Clippy error: https://github.com/rust-lang/rust-clippy/issues/9763 - #[allow(clippy::explicit_auto_deref)] fn add_to_scene_layer(&self, scene: &Scene, layer: &scene::Layer) { let (shape, instance) = layer.instantiate(&*self.data.borrow()); scene.pointer_target_registry.insert(instance.global_instance_id, self.events.clone_ref()); @@ -200,8 +196,6 @@ impl ShapeViewModel { } impl ShapeViewModel { - // Clippy error: https://github.com/rust-lang/rust-clippy/issues/9763 - #[allow(clippy::explicit_auto_deref)] fn unregister_existing_mouse_targets(&self) { for global_instance_id in mem::take(&mut *self.pointer_targets.borrow_mut()) { scene().pointer_target_registry.remove(global_instance_id); From 40c674d7d19ad0040e38204adaa045a54481a547 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 13 Feb 2023 20:46:52 +0000 Subject: [PATCH 07/16] cleanup --- .../src/display/shape/primitive/system.rs | 4 ++-- .../ensogl/core/src/display/symbol/gpu.rs | 12 +++++------ .../core/src/display/symbol/gpu/shader.rs | 21 ++++++++++--------- lib/rust/ensogl/core/src/gui/component.rs | 12 +++++------ 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index 6b3f79a781a6..6e1c0a29d145 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -632,7 +632,7 @@ macro_rules! _shape_old { // ============= /// The type of the shape. It also contains the parameters of the shape. The parameters - /// are stored in this type in order to simplify bounds for utlities managing shape + /// are stored in this type in order to simplify bounds for utilities managing shape /// systems. For example, if we would like to handle any shape with given parameters, /// we will be processing [`ShapeSystem`] and we can add bounds to [`S`] to reflect /// what parameters it should contain. @@ -788,7 +788,7 @@ macro_rules! _shape { // ============= /// The type of the shape. It also contains the parameters of the shape. The parameters - /// are stored in this type in order to simplify bounds for utlities managing shape + /// are stored in this type in order to simplify bounds for utilities managing shape /// systems. For example, if we would like to handle any shape with given parameters, /// we will be processing [`ShapeSystem`] and we can add bounds to [`S`] to reflect /// what parameters it should contain. diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu.rs b/lib/rust/ensogl/core/src/display/symbol/gpu.rs index 06c8babf7a3f..9582a6dd8473 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu.rs @@ -409,15 +409,13 @@ impl Symbol { &self, global_variables: &UniformScope, ) -> Vec { - let mut vars = self.shader.collect_variables(); - for binding in &mut vars { - let scope = self.lookup_variable(&binding.name, global_variables); + self.shader.collect_variables().map(|(name, decl)| { + let scope = self.lookup_variable(&name, global_variables); if scope.is_none() { - warn!("Unable to bind variable '{}' to geometry buffer.", binding.name); + warn!("Unable to bind variable '{name}' to geometry buffer."); } - binding.scope = scope; - } - vars + shader::VarBinding::new(name, decl, scope) + }).collect() } /// Runs the provided function in a context of active program and active VAO. After the function diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs index 676e2c565933..ffcd065a6533 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs @@ -122,9 +122,10 @@ impl { /// Get the shader code in GLSL 310 format. The shader parameters will not be bound to any /// particular mesh and thus this code can be used for optimization purposes only. pub fn abstract_shader_code_in_glsl_310(&self) -> crate::system::gpu::shader::Code { - let bindings = self.collect_variables().into_iter().map(|mut binding| { - binding.scope = Some(ScopeType::Mesh(crate::display::symbol::geometry::primitive::mesh::ScopeType::Instance)); - binding + let bindings = self.collect_variables().map(|(name, decl)| { + let instance = crate::display::symbol::geometry::primitive::mesh::ScopeType::Instance; + let scope = Some(ScopeType::Mesh(instance)); + VarBinding::new(name, decl, scope) }).collect_vec(); self.gen_gpu_code(glsl::Version::V310, &bindings) } @@ -202,13 +203,13 @@ impl { } /// Traverses the shader definition and collects all attribute names. - pub fn collect_variables(&self) -> Vec { - let mut out = vec![]; - let vars = self.geometry_material.inputs().iter().chain(self.surface_material.inputs()); - for (name,decl) in vars { - out.push(VarBinding::new(name.clone(), decl.clone(), None)); - } - out + pub fn collect_variables(&self) -> impl Iterator { + let geometry_inputs = self.geometry_material.inputs().iter(); + let surface_inputs = self.surface_material.inputs().iter(); + geometry_inputs.chain(surface_inputs) + .map(|(s, d)| (s.clone(), d.clone())) + .collect_vec() + .into_iter() } }} diff --git a/lib/rust/ensogl/core/src/gui/component.rs b/lib/rust/ensogl/core/src/gui/component.rs index d2bc5bfb2a4a..5abfd5583ebf 100644 --- a/lib/rust/ensogl/core/src/gui/component.rs +++ b/lib/rust/ensogl/core/src/gui/component.rs @@ -228,8 +228,8 @@ impl display::Object for ShapeView { struct WidgetData { app: Application, display_object: display::object::Instance, - frp: std::mem::ManuallyDrop, - model: std::mem::ManuallyDrop>, + frp: mem::ManuallyDrop, + model: mem::ManuallyDrop>, } impl WidgetData { @@ -242,8 +242,8 @@ impl WidgetData { Self { app: app.clone_ref(), display_object, - frp: std::mem::ManuallyDrop::new(frp), - model: std::mem::ManuallyDrop::new(model), + frp: mem::ManuallyDrop::new(frp), + model: mem::ManuallyDrop::new(model), } } } @@ -255,8 +255,8 @@ impl Drop for WidgetData { // This is clearly the case, because the structure will be soon dropped anyway. #[allow(unsafe_code)] unsafe { - let frp = std::mem::ManuallyDrop::take(&mut self.frp); - let model = std::mem::ManuallyDrop::take(&mut self.model); + let frp = mem::ManuallyDrop::take(&mut self.frp); + let model = mem::ManuallyDrop::take(&mut self.model); self.app.display.collect_garbage(frp); self.app.display.collect_garbage(model); } From b56fcb028c6a5ca0413b04e9c05c17185515ea12 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 13 Feb 2023 22:33:29 +0000 Subject: [PATCH 08/16] cleanup --- app/gui/src/model/undo_redo.rs | 2 +- lib/rust/ensogl/core/src/system/gpu/data/buffer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/gui/src/model/undo_redo.rs b/app/gui/src/model/undo_redo.rs index f4d1b408c4af..f4be92bd37dc 100644 --- a/app/gui/src/model/undo_redo.rs +++ b/app/gui/src/model/undo_redo.rs @@ -132,7 +132,7 @@ impl Drop for Transaction { urm.push_to(Stack::Undo, self.frame.borrow().clone()); urm.clear(Stack::Redo); } else { - info!( + debug!( "Dropping the ignored transaction '{}' without pushing a frame to repository.", self.name() ) diff --git a/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs b/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs index a0a8d2b83d46..5318d0dbcd93 100644 --- a/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs +++ b/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs @@ -280,7 +280,7 @@ impl BufferData { /// Upload the provided data range to the GPU buffer. In case the local buffer was resized, /// it will be re-created on the GPU. fn upload_data(&mut self, opt_range: &Option>) { - info_span!("Uploading buffer data.").in_scope(|| { + debug_span!("Uploading buffer data.").in_scope(|| { self.stats.inc_data_upload_count(); match opt_range { None => self.replace_gpu_buffer(), From cbfb37510089e9e211ea75e8b6abc4220c58b031 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Fri, 10 Feb 2023 20:53:12 +0000 Subject: [PATCH 09/16] set up shader-program inputs correctly --- lib/rust/ensogl/core/src/display/scene.rs | 16 +++++++------ .../ensogl/core/src/display/symbol/gpu.rs | 2 +- lib/rust/ensogl/core/src/display/world.rs | 23 ++++++++++++++----- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index c236178f8595..7990c702e092 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -719,7 +719,7 @@ pub struct SceneData { display_mode: Rc>, extensions: Extensions, disable_context_menu: Rc, - persistent_shaders: Rc>, + persistent_shaders: Rc>>, } impl SceneData { @@ -728,7 +728,6 @@ impl SceneData { stats: &Stats, on_mut: OnMut, display_mode: &Rc>, - persistent_shaders: Vec, ) -> Self { debug!("Initializing."); let display_mode = display_mode.clone_ref(); @@ -768,7 +767,7 @@ impl SceneData { let context_lost_handler = default(); let pointer_position_changed = default(); let shader_compiler = default(); - let persistent_shaders = Rc::new(persistent_shaders); + let persistent_shaders = default(); Self { display_object, display_mode, @@ -813,10 +812,10 @@ impl SceneData { self.dirty.shape.set(); self.renderer.set_context(context); if let Some(context) = context { - for code in self.persistent_shaders.iter() { + for code in self.persistent_shaders.borrow().iter() { let code = shader::Sources { fragment: Some(code.fragment.clone()), - vertex: None, + vertex: Some(code.vertex.clone()), }; context.shader_compiler.submit_background_job(code); } @@ -950,6 +949,10 @@ impl SceneData { let world_space = camera.inversed_view_projection_matrix() * clip_space; (inv_object_matrix * world_space).xy() } + + pub fn set_persistent_shaders(&self, shaders: Vec) { + *self.persistent_shaders.borrow_mut() = shaders; + } } @@ -1031,9 +1034,8 @@ impl Scene { stats: &Stats, on_mut: OnMut, display_mode: &Rc>, - persistent_shaders: Vec, ) -> Self { - let no_mut_access = SceneData::new(stats, on_mut, display_mode, persistent_shaders); + let no_mut_access = SceneData::new(stats, on_mut, display_mode); let this = Self { no_mut_access }; this } diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu.rs b/lib/rust/ensogl/core/src/display/symbol/gpu.rs index 9582a6dd8473..e08d269572ce 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu.rs @@ -405,7 +405,7 @@ impl Symbol { } /// For each variable from the shader definition, looks up its position in geometry scopes. - fn discover_variable_bindings( + pub fn discover_variable_bindings( &self, global_variables: &UniformScope, ) -> Vec { diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 89532ff14e76..93069dfc9769 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -190,10 +190,19 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { } /// Returns the source code for shaders that should be compiled as soon as a context is available. -fn get_persistent_shaders() -> Vec { - PRECOMPILED_SHADERS.with_borrow(|shaders| { - shaders.values().cloned().map(|PrecompiledShader(shader)| shader).collect() - }) +fn get_persistent_shaders(variables: &UniformScope) -> Vec { + let mut shaders = vec![]; + SHAPES_DEFINITIONS.with_borrow(|shapes| { + for shape_cons in shapes { + let symbol = &shape_cons().sprite().symbol; + let bindings = symbol.discover_variable_bindings(variables); + let shader = symbol.shader(); + let version = crate::system::gpu::shader::glsl::Version::V300; + let code = shader.gen_gpu_code(version, &bindings); + shaders.push(code); + } + }); + shaders } @@ -417,8 +426,10 @@ impl WorldData { let scene_dirty = dirty::SharedBool::new(()); let on_change = f!(scene_dirty.set()); let display_mode = Rc::>::default(); - let default_scene = Scene::new(&stats, on_change, &display_mode, get_persistent_shaders()); - let uniforms = Uniforms::new(&default_scene.variables); + let default_scene = Scene::new(&stats, on_change, &display_mode); + let variables = &default_scene.variables; + let uniforms = Uniforms::new(variables); + default_scene.set_persistent_shaders(get_persistent_shaders(variables)); let debug_hotkeys_handle = default(); let garbage_collector = default(); let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) { From 177f105e03b03ebc1f54a680180e8801f2dc2ab7 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Tue, 14 Feb 2023 20:27:01 +0000 Subject: [PATCH 10/16] program inputs --- lib/rust/ensogl/core/src/display/scene.rs | 9 +++ .../ensogl/core/src/display/scene/layer.rs | 13 ++-- .../src/display/shape/primitive/system.rs | 72 +++++++++++++++++-- lib/rust/ensogl/core/src/display/world.rs | 34 +++++++-- lib/rust/ensogl/core/src/lib.rs | 1 + .../core/src/system/gpu/shader/compiler.rs | 28 +++----- 6 files changed, 120 insertions(+), 37 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index 7990c702e092..7b02746ba0d5 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -812,6 +812,11 @@ impl SceneData { self.dirty.shape.set(); self.renderer.set_context(context); if let Some(context) = context { + /* + if self.persistent_shaders.borrow().is_empty() { + *self.persistent_shaders.borrow_mut() = world::get_persistent_shaders(); + } + */ for code in self.persistent_shaders.borrow().iter() { let code = shader::Sources { fragment: Some(code.fragment.clone()), @@ -953,6 +958,10 @@ impl SceneData { pub fn set_persistent_shaders(&self, shaders: Vec) { *self.persistent_shaders.borrow_mut() = shaders; } + + pub fn debug_layer_contents(&self) { + warn!("{:?}", &self.layers); + } } diff --git a/lib/rust/ensogl/core/src/display/scene/layer.rs b/lib/rust/ensogl/core/src/display/scene/layer.rs index b4a427ebd847..b054a732e4a8 100644 --- a/lib/rust/ensogl/core/src/display/scene/layer.rs +++ b/lib/rust/ensogl/core/src/display/scene/layer.rs @@ -19,7 +19,6 @@ use crate::display::symbol::SymbolId; use enso_data_structures::dependency_graph::DependencyGraph; use enso_shapely::shared; use smallvec::alloc::collections::BTreeSet; -use std::any::TypeId; @@ -1143,8 +1142,8 @@ shared! { ShapeSystemRegistry /// the same type on the same layer. Read the docs of [`ShapeProxy`] to learn more. #[derive(Default,Debug)] pub struct ShapeSystemRegistryData { - shape_system_map : HashMap<(TypeId, ShapeSystemFlavor),ShapeSystemRegistryEntry>, - shape_system_flavors: HashMap>, + shape_system_map : HashMap<(ShapeSystemId, ShapeSystemFlavor),ShapeSystemRegistryEntry>, + shape_system_flavors: HashMap>, } impl { @@ -1184,7 +1183,7 @@ impl { // Intentional short-circuit - avoid computing `total_system_instances` when we know there // are still more instances in the currently processed entry. - let no_more_instances = entry_is_empty && self.total_system_instances(*system_id) == 0; + let no_more_instances = entry_is_empty && self.total_system_instances(system_id) == 0; (no_more_instances, system_id, PhantomData) } @@ -1198,7 +1197,7 @@ impl ShapeSystemRegistryData { where S: Shape, { - let id = TypeId::of::(); + let id = ShapeSystemId::of::(); self.shape_system_map.get_mut(&(id, flavor)).and_then(|t| { let shape_system = t.shape_system.downcast_mut::>(); let instance_count = &mut t.instance_count; @@ -1217,7 +1216,7 @@ impl ShapeSystemRegistryData { where S: Shape, { - let id = TypeId::of::(); + let id = ShapeSystemId::of::(); let flavor = S::flavor(data); let system = ShapeSystem::::new(data); let any = Box::new(system); @@ -1229,7 +1228,7 @@ impl ShapeSystemRegistryData { } /// Get total number of shape instances from shape systems of given type and all flavors. - fn total_system_instances(&self, system_id: TypeId) -> usize { + fn total_system_instances(&self, system_id: ShapeSystemId) -> usize { let Some(flavors) = self.shape_system_flavors.get(&system_id) else { return 0 }; flavors.iter().map(|f| self.shape_system_map[&(system_id, *f)].instance_count).sum() } diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index 6e1c0a29d145..c5ac019d6d61 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -60,6 +60,7 @@ //! shape system will be detached from the parent display object (and not attached to another one), //! it will also be moved to the `DETACHED` layer. +use std::fmt::Formatter; use crate::prelude::*; use crate::system::gpu::types::*; @@ -82,9 +83,67 @@ use super::def; // === ShapeSystemId === // ===================== -newtype_prim_no_default_no_display! { - /// The ID of a user generated shape system. - ShapeSystemId(std::any::TypeId); +/// The ID of a user generated shape system. +#[derive(Copy, Clone, CloneRef, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] +pub struct ShapeSystemId { + definition_path: DistinctStr, +} + +impl ShapeSystemId { + #[inline(always)] + pub fn of() -> Self { + let definition_path = DistinctStr::new_unchecked(S::definition_path()); + Self { definition_path } + } +} + + +//////// + +#[derive(Copy, Clone, Eq, Ord)] +pub struct DistinctStr(&'static str); + +impl DistinctStr { + /// Cast the input to a [`DistinctStr`]. + #[inline(always)] + const fn new_unchecked(s: &'static str) -> Self { + Self(s) + } +} + +impl PartialEq for DistinctStr { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + self.0.as_ptr() == other.0.as_ptr() + } +} + +impl PartialOrd for DistinctStr { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + self.0.as_ptr().partial_cmp(&other.0.as_ptr()) + } +} + +impl Hash for DistinctStr { + #[inline(always)] + fn hash(&self, state: &mut H) { + self.0.as_ptr().hash(state) + } +} + +impl Debug for DistinctStr { + #[inline(always)] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl CloneRef for DistinctStr { + #[inline(always)] + fn clone_ref(&self) -> Self { + *self + } } @@ -249,8 +308,8 @@ pub struct ShapeSystemStandardData { impl ShapeSystem { /// The ID of this shape system. - pub const fn id() -> ShapeSystemId { - ShapeSystemId::new(std::any::TypeId::of::()) + pub fn id() -> ShapeSystemId { + ShapeSystemId::of::() } /// Reference to the underlying sprite system. @@ -874,7 +933,8 @@ macro_rules! _shape { #[before_main] pub fn register_shape() { $crate::display::world::SHAPES_DEFINITIONS.with(|shapes| { - shapes.borrow_mut().push(Box::new(|| Box::new(View::new()))); + let path = Shape::definition_path(); + shapes.borrow_mut().push((path, Box::new(|| Box::new(View::new())))); }); } diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 93069dfc9769..fc0f5f575602 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -101,7 +101,7 @@ pub struct CachedShapeDefinition { thread_local! { /// All shapes defined with the `shape!` macro. They will be populated on the beginning of /// program execution, before the `main` function is called. - pub static SHAPES_DEFINITIONS: RefCell> = default(); + pub static SHAPES_DEFINITIONS: RefCell> = default(); /// All shapes defined with the `cached_shape!` macro. They will be populated on the beginning /// of program execution, before the `main` function is called. @@ -178,7 +178,7 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { with_context(|t| t.run_mode.set(RunMode::ShaderExtraction)); let mut map = HashMap::new(); SHAPES_DEFINITIONS.with(|shapes| { - for shape_cons in shapes.borrow().iter() { + for (_, shape_cons) in shapes.borrow().iter() { let shape = shape_cons(); let path = shape.definition_path(); let code = shape.abstract_shader_code_in_glsl_310(); @@ -190,8 +190,30 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { } /// Returns the source code for shaders that should be compiled as soon as a context is available. -fn get_persistent_shaders(variables: &UniformScope) -> Vec { +pub fn get_persistent_shaders() -> Vec { // variables: &UniformScope let mut shaders = vec![]; + //PRECOMPILED_SHADERS.with_borrow(|shaders| info!("PRECOMPILED_SHADERS: {shaders:?}")); + SHAPES_DEFINITIONS.with_borrow(|shapes| { + with_context(|t| { + //let layer = &t.layers.root; + for (path, shape_cons) in shapes { + let do_it = match path { + //_ if path.starts_with("app/gui/view/component-browser") => true, + _ => true, + }; + if do_it { + let shape = shape_cons(); + } + //scene().add_child(&*shape); + //layer.add(&*shape); + //mem::forget(shape); + //let shape: Box = shape_cons(); + //layer.remove(&*shape); + //mem::forget(shape); + } + }); + }); + /* SHAPES_DEFINITIONS.with_borrow(|shapes| { for shape_cons in shapes { let symbol = &shape_cons().sprite().symbol; @@ -202,6 +224,7 @@ fn get_persistent_shaders(variables: &UniformScope) -> Vec { shaders.push(code); } }); + */ shaders } @@ -419,6 +442,7 @@ pub struct WorldData { impl WorldData { /// Create and initialize new world instance. pub fn new(frp: &api::private::Output) -> Self { + get_persistent_shaders(); let frp = frp.clone_ref(); let stats = Stats::new(web::window.performance_or_panic()); let stats_monitor = debug::monitor::Monitor::new(); @@ -429,7 +453,6 @@ impl WorldData { let default_scene = Scene::new(&stats, on_change, &display_mode); let variables = &default_scene.variables; let uniforms = Uniforms::new(variables); - default_scene.set_persistent_shaders(get_persistent_shaders(variables)); let debug_hotkeys_handle = default(); let garbage_collector = default(); let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) { @@ -475,6 +498,7 @@ impl WorldData { let display_mode = self.display_mode.clone_ref(); let display_mode_uniform = self.uniforms.display_mode.clone_ref(); let emit_measurements_handle = self.emit_measurements_handle.clone_ref(); + let scene = self.default_scene.clone_ref(); let closure: Closure = Closure::new(move |val: JsValue| { let event = val.unchecked_into::(); let digit_prefix = "Digit"; @@ -497,6 +521,8 @@ impl WorldData { } else if key == "KeyQ" { enso_debug_api::save_profile(&profiler::internal::get_log()); enso_debug_api::LifecycleController::new().map(|api| api.quit()); + } else if key == "KeyL" { + scene.debug_layer_contents(); } else if key.starts_with(digit_prefix) { let code_value = key.trim_start_matches(digit_prefix).parse().unwrap_or(0); if let Some(mode) = glsl::codes::DisplayModes::from_value(code_value) { diff --git a/lib/rust/ensogl/core/src/lib.rs b/lib/rust/ensogl/core/src/lib.rs index f53d9a8116fa..a59da12729d3 100644 --- a/lib/rust/ensogl/core/src/lib.rs +++ b/lib/rust/ensogl/core/src/lib.rs @@ -25,6 +25,7 @@ #![feature(auto_traits)] #![feature(int_roundings)] #![feature(let_chains)] +#![feature(trait_upcasting)] // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index a23dab26c48a..ee1457de29dc 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -67,7 +67,7 @@ const FRAME_TIME_THRESHOLD: Duration = (1000.0 / FPS_THRESHOLD).ms(); /// a crude form of backpressure by blocking until all pending jobs complete. We are not sure if it /// is a feature or a bug. To learn more about our findings so far, see: /// https://github.com/enso-org/enso/pull/3378#issuecomment-1090958946 -const MAX_PARALLEL_COMPILE_JOBS: usize = 32; +const MAX_PARALLEL_COMPILE_JOBS: usize = 2000; @@ -238,6 +238,7 @@ impl Compiler { profiler: profiler::Debug, on_ready: F, ) -> JobHandle { + //info!("submit: {}", &input.fragment); let strong_handle = JobHandle::new(); let handle = strong_handle.downgrade(); let on_ready: Box = Box::new(on_ready); @@ -301,7 +302,8 @@ impl CompilerData { // callback to be called from within the job runner during next processing round. if let Some(callback) = on_ready { let program = program.clone(); - self.callbacks.push(DeferredCallback { callback, program }) + self.callbacks.push(DeferredCallback { callback, program }); + self.dirty = true; } } Some(ProgramCacheEntry::Failed) => { @@ -436,36 +438,22 @@ impl CompilerData { }) }); profiler.pause(); - let vertex = match vertex { - Some(vertex) => Some(vertex?), - None => None, - }; - let fragment = match fragment { - Some(fragment) => Some(fragment?), - None => None, - }; - match (vertex, fragment) { + let handle = job.handle; + let on_ready = job.on_ready; + let cache_key = job.cache_key; + match (vertex.transpose()?, fragment.transpose()?) { (Some(vertex), Some(fragment)) => { let input = shader::Sources { vertex, fragment }; - let handle = job.handle; - let on_ready = job.on_ready; - let cache_key = job.cache_key; let link_job = Job { input, handle, on_ready, profiler, cache_key }; this.jobs.link.push(link_job); } (Some(vertex), None) => { let input = vertex.native; - let handle = job.handle; - let on_ready = job.on_ready; - let cache_key = job.cache_key; let job = Job { input, handle, cache_key, on_ready, profiler }; this.queue_shader_check_job(job); } (None, Some(fragment)) => { let input = fragment.native; - let handle = job.handle; - let on_ready = job.on_ready; - let cache_key = job.cache_key; let job = Job { input, handle, cache_key, on_ready, profiler }; this.queue_shader_check_job(job); } From 4332f686b9d3c5cbd4fc872ece88c2a00186298b Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Tue, 14 Feb 2023 21:00:24 +0000 Subject: [PATCH 11/16] fix & cleanup --- lib/rust/ensogl/core/src/display/scene.rs | 25 ------ .../src/display/shape/primitive/system.rs | 65 ++++------------ .../ensogl/core/src/display/symbol/gpu.rs | 19 +++-- lib/rust/ensogl/core/src/display/world.rs | 76 +++++++++---------- lib/rust/ensogl/core/src/lib.rs | 1 - .../core/src/system/gpu/context/extension.rs | 3 +- lib/rust/ensogl/core/src/system/gpu/shader.rs | 10 ++- .../core/src/system/gpu/shader/compiler.rs | 46 +++++------ 8 files changed, 87 insertions(+), 158 deletions(-) diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index 7b02746ba0d5..91cf84d9a8af 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -719,7 +719,6 @@ pub struct SceneData { display_mode: Rc>, extensions: Extensions, disable_context_menu: Rc, - persistent_shaders: Rc>>, } impl SceneData { @@ -767,7 +766,6 @@ impl SceneData { let context_lost_handler = default(); let pointer_position_changed = default(); let shader_compiler = default(); - let persistent_shaders = default(); Self { display_object, display_mode, @@ -793,7 +791,6 @@ impl SceneData { shader_compiler, extensions, disable_context_menu, - persistent_shaders, } .init() } @@ -811,20 +808,6 @@ impl SceneData { *self.context.borrow_mut() = context.cloned(); self.dirty.shape.set(); self.renderer.set_context(context); - if let Some(context) = context { - /* - if self.persistent_shaders.borrow().is_empty() { - *self.persistent_shaders.borrow_mut() = world::get_persistent_shaders(); - } - */ - for code in self.persistent_shaders.borrow().iter() { - let code = shader::Sources { - fragment: Some(code.fragment.clone()), - vertex: Some(code.vertex.clone()), - }; - context.shader_compiler.submit_background_job(code); - } - } } pub fn shape(&self) -> &frp::Sampler { @@ -954,14 +937,6 @@ impl SceneData { let world_space = camera.inversed_view_projection_matrix() * clip_space; (inv_object_matrix * world_space).xy() } - - pub fn set_persistent_shaders(&self, shaders: Vec) { - *self.persistent_shaders.borrow_mut() = shaders; - } - - pub fn debug_layer_contents(&self) { - warn!("{:?}", &self.layers); - } } diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index c5ac019d6d61..a0e9e1fa3ea4 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -60,7 +60,6 @@ //! shape system will be detached from the parent display object (and not attached to another one), //! it will also be moved to the `DETACHED` layer. -use std::fmt::Formatter; use crate::prelude::*; use crate::system::gpu::types::*; @@ -84,63 +83,21 @@ use super::def; // ===================== /// The ID of a user generated shape system. -#[derive(Copy, Clone, CloneRef, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] pub struct ShapeSystemId { - definition_path: DistinctStr, + type_id: std::any::TypeId, } impl ShapeSystemId { + /// Return an identifier unique to the given [`Shape`] type. #[inline(always)] pub fn of() -> Self { - let definition_path = DistinctStr::new_unchecked(S::definition_path()); - Self { definition_path } + let type_id = std::any::TypeId::of::(); + Self { type_id } } } - -//////// - -#[derive(Copy, Clone, Eq, Ord)] -pub struct DistinctStr(&'static str); - -impl DistinctStr { - /// Cast the input to a [`DistinctStr`]. - #[inline(always)] - const fn new_unchecked(s: &'static str) -> Self { - Self(s) - } -} - -impl PartialEq for DistinctStr { - #[inline(always)] - fn eq(&self, other: &Self) -> bool { - self.0.as_ptr() == other.0.as_ptr() - } -} - -impl PartialOrd for DistinctStr { - #[inline(always)] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ptr().partial_cmp(&other.0.as_ptr()) - } -} - -impl Hash for DistinctStr { - #[inline(always)] - fn hash(&self, state: &mut H) { - self.0.as_ptr().hash(state) - } -} - -impl Debug for DistinctStr { - #[inline(always)] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl CloneRef for DistinctStr { - #[inline(always)] +impl CloneRef for ShapeSystemId { fn clone_ref(&self) -> Self { *self } @@ -933,8 +890,14 @@ macro_rules! _shape { #[before_main] pub fn register_shape() { $crate::display::world::SHAPES_DEFINITIONS.with(|shapes| { - let path = Shape::definition_path(); - shapes.borrow_mut().push((path, Box::new(|| Box::new(View::new())))); + let definition_path = Shape::definition_path(); + let cons = Box::new(|| { + let view: Box = + Box::new(View::new()); + view + }); + let def = $crate::display::world::ShapeDefinition { definition_path, cons }; + shapes.borrow_mut().push(def); }); } diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu.rs b/lib/rust/ensogl/core/src/display/symbol/gpu.rs index e08d269572ce..7c68954af2af 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu.rs @@ -405,17 +405,20 @@ impl Symbol { } /// For each variable from the shader definition, looks up its position in geometry scopes. - pub fn discover_variable_bindings( + fn discover_variable_bindings( &self, global_variables: &UniformScope, ) -> Vec { - self.shader.collect_variables().map(|(name, decl)| { - let scope = self.lookup_variable(&name, global_variables); - if scope.is_none() { - warn!("Unable to bind variable '{name}' to geometry buffer."); - } - shader::VarBinding::new(name, decl, scope) - }).collect() + self.shader + .collect_variables() + .map(|(name, decl)| { + let scope = self.lookup_variable(&name, global_variables); + if scope.is_none() { + warn!("Unable to bind variable '{name}' to geometry buffer."); + } + shader::VarBinding::new(name, decl, scope) + }) + .collect() } /// Runs the provided function in a context of active program and active VAO. After the function diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index fc0f5f575602..03b9e6b7f3b2 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -87,6 +87,17 @@ fn init_context() { /// A constructor of view of some specific shape. pub type ShapeCons = Box Box>; +/// The definition of shapes created with the `shape!` macro. +#[derive(Derivative)] +#[derivative(Debug)] +pub struct ShapeDefinition { + /// Location in the source code that the shape was defined. + pub definition_path: &'static str, + /// A constructor of single shape view. + #[derivative(Debug = "ignore")] + pub cons: ShapeCons, +} + /// The definition of shapes created with the `cached_shape!` macro. #[derive(Derivative)] #[derivative(Debug)] @@ -101,7 +112,7 @@ pub struct CachedShapeDefinition { thread_local! { /// All shapes defined with the `shape!` macro. They will be populated on the beginning of /// program execution, before the `main` function is called. - pub static SHAPES_DEFINITIONS: RefCell> = default(); + pub static SHAPES_DEFINITIONS: RefCell> = default(); /// All shapes defined with the `cached_shape!` macro. They will be populated on the beginning /// of program execution, before the `main` function is called. @@ -178,8 +189,8 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { with_context(|t| t.run_mode.set(RunMode::ShaderExtraction)); let mut map = HashMap::new(); SHAPES_DEFINITIONS.with(|shapes| { - for (_, shape_cons) in shapes.borrow().iter() { - let shape = shape_cons(); + for shape in shapes.borrow().iter() { + let shape = (shape.cons)(); let path = shape.definition_path(); let code = shape.abstract_shader_code_in_glsl_310(); map.insert(path, code); @@ -189,43 +200,28 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { map } -/// Returns the source code for shaders that should be compiled as soon as a context is available. -pub fn get_persistent_shaders() -> Vec { // variables: &UniformScope - let mut shaders = vec![]; - //PRECOMPILED_SHADERS.with_borrow(|shaders| info!("PRECOMPILED_SHADERS: {shaders:?}")); + + +// =================================== +// === Eager shaders instantiation === +// =================================== + +/// Begin compilation of all shaders that will be needed for the default scene. +#[profile(Task)] +pub fn instantiate_shaders() { SHAPES_DEFINITIONS.with_borrow(|shapes| { - with_context(|t| { - //let layer = &t.layers.root; - for (path, shape_cons) in shapes { - let do_it = match path { - //_ if path.starts_with("app/gui/view/component-browser") => true, - _ => true, - }; - if do_it { - let shape = shape_cons(); - } - //scene().add_child(&*shape); - //layer.add(&*shape); - //mem::forget(shape); - //let shape: Box = shape_cons(); - //layer.remove(&*shape); - //mem::forget(shape); + for shape in shapes { + let path = &shape.definition_path; + let preload = match path { + _ if path.starts_with("app/gui/view/debug_scene/") => false, + _ if path.starts_with("lib/rust/ensogl/example/") => false, + _ => true, + }; + if preload { + let _shape = (shape.cons)(); } - }); - }); - /* - SHAPES_DEFINITIONS.with_borrow(|shapes| { - for shape_cons in shapes { - let symbol = &shape_cons().sprite().symbol; - let bindings = symbol.discover_variable_bindings(variables); - let shader = symbol.shader(); - let version = crate::system::gpu::shader::glsl::Version::V300; - let code = shader.gen_gpu_code(version, &bindings); - shaders.push(code); } }); - */ - shaders } @@ -442,7 +438,6 @@ pub struct WorldData { impl WorldData { /// Create and initialize new world instance. pub fn new(frp: &api::private::Output) -> Self { - get_persistent_shaders(); let frp = frp.clone_ref(); let stats = Stats::new(web::window.performance_or_panic()); let stats_monitor = debug::monitor::Monitor::new(); @@ -451,8 +446,7 @@ impl WorldData { let on_change = f!(scene_dirty.set()); let display_mode = Rc::>::default(); let default_scene = Scene::new(&stats, on_change, &display_mode); - let variables = &default_scene.variables; - let uniforms = Uniforms::new(variables); + let uniforms = Uniforms::new(&default_scene.variables); let debug_hotkeys_handle = default(); let garbage_collector = default(); let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) { @@ -486,6 +480,7 @@ impl WorldData { self.init_environment(); self.init_composer(); self.init_debug_hotkeys(); + instantiate_shaders(); self } @@ -498,7 +493,6 @@ impl WorldData { let display_mode = self.display_mode.clone_ref(); let display_mode_uniform = self.uniforms.display_mode.clone_ref(); let emit_measurements_handle = self.emit_measurements_handle.clone_ref(); - let scene = self.default_scene.clone_ref(); let closure: Closure = Closure::new(move |val: JsValue| { let event = val.unchecked_into::(); let digit_prefix = "Digit"; @@ -521,8 +515,6 @@ impl WorldData { } else if key == "KeyQ" { enso_debug_api::save_profile(&profiler::internal::get_log()); enso_debug_api::LifecycleController::new().map(|api| api.quit()); - } else if key == "KeyL" { - scene.debug_layer_contents(); } else if key.starts_with(digit_prefix) { let code_value = key.trim_start_matches(digit_prefix).parse().unwrap_or(0); if let Some(mode) = glsl::codes::DisplayModes::from_value(code_value) { diff --git a/lib/rust/ensogl/core/src/lib.rs b/lib/rust/ensogl/core/src/lib.rs index a59da12729d3..f53d9a8116fa 100644 --- a/lib/rust/ensogl/core/src/lib.rs +++ b/lib/rust/ensogl/core/src/lib.rs @@ -25,7 +25,6 @@ #![feature(auto_traits)] #![feature(int_roundings)] #![feature(let_chains)] -#![feature(trait_upcasting)] // === Standard Linter Configuration === #![deny(non_ascii_idents)] #![warn(unsafe_code)] diff --git a/lib/rust/ensogl/core/src/system/gpu/context/extension.rs b/lib/rust/ensogl/core/src/system/gpu/context/extension.rs index 35a3f9ed75e3..87f733caab78 100644 --- a/lib/rust/ensogl/core/src/system/gpu/context/extension.rs +++ b/lib/rust/ensogl/core/src/system/gpu/context/extension.rs @@ -4,8 +4,9 @@ use crate::prelude::*; use crate::system::gpu::data::GlEnum; -use web_sys::{WebGl2RenderingContext, WebGlShader}; +use web_sys::WebGl2RenderingContext; use web_sys::WebGlProgram; +use web_sys::WebGlShader; diff --git a/lib/rust/ensogl/core/src/system/gpu/shader.rs b/lib/rust/ensogl/core/src/system/gpu/shader.rs index 357a3e1f8290..4d05c1b63fc0 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader.rs @@ -73,8 +73,11 @@ pub type Code = Sources; pub type CompiledCode = Sources, Shader>; impl Sources { - /// Apply a [`Into::into`] to the [`vertex`] and [`fragment`] elements and return the result. - pub fn into(self) -> Sources where V: Into, F: Into { + /// Apply [`Into::into`] to the [`vertex`] and [`fragment`] elements and return the result. + pub fn into(self) -> Sources + where + V: Into, + F: Into, { let Sources { vertex, fragment } = self; let vertex = vertex.into(); let fragment = fragment.into(); @@ -92,7 +95,8 @@ impl Sources { impl Sources { /// Apply a function to the [`vertex`] and [`fragment`] elements and return the result. - pub fn map(self, f: F) -> Sources where F: Fn(VF) -> VF1 { + pub fn map(self, f: F) -> Sources + where F: Fn(VF) -> VF1 { let Sources { vertex, fragment } = self; let vertex = f(vertex); let fragment = f(fragment); diff --git a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs index ee1457de29dc..94fde29afc63 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader/compiler.rs @@ -67,7 +67,7 @@ const FRAME_TIME_THRESHOLD: Duration = (1000.0 / FPS_THRESHOLD).ms(); /// a crude form of backpressure by blocking until all pending jobs complete. We are not sure if it /// is a feature or a bug. To learn more about our findings so far, see: /// https://github.com/enso-org/enso/pull/3378#issuecomment-1090958946 -const MAX_PARALLEL_COMPILE_JOBS: usize = 2000; +const MAX_PARALLEL_COMPILE_JOBS: usize = 16; @@ -238,7 +238,6 @@ impl Compiler { profiler: profiler::Debug, on_ready: F, ) -> JobHandle { - //info!("submit: {}", &input.fragment); let strong_handle = JobHandle::new(); let handle = strong_handle.downgrade(); let on_ready: Box = Box::new(on_ready); @@ -288,13 +287,13 @@ impl CompilerData { ) { let cache_key = ShaderCache::code_key(&input); match self.cache.get_program(cache_key) { - Some(ProgramCacheEntry::Submitted) => { + Some(ProgramCacheEntry::Submitted { waiters }) => { debug!("Awaiting shader compilation {cache_key:?}."); // This shader is currently being processed by another job. Create a new job that // will wait for that job to finish. let job = Job { input: (), handle, on_ready, profiler, cache_key }; - self.cache.add_to_waiting_list(job); - self.dirty = true; + waiters.push(job); + debug_assert!(self.dirty); } Some(ProgramCacheEntry::Validated(program)) => { debug!("Reusing cached shader {cache_key:?}."); @@ -394,8 +393,8 @@ impl CompilerData { /// it is costly and can prevent the parallelism altogether. To learn more, see: /// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#dont_check_shader_compile_status_unless_linking_fails fn current_parallel_job_count(&self) -> usize { + // [`compile`] is the only excluded queue, because those jobs are waiting to start. [ - self.jobs.compile.len(), self.jobs.compile_poll.len(), self.jobs.compile_check.len(), self.jobs.link.len(), @@ -561,7 +560,7 @@ impl CompilerData { } else { // No waiting jobs, cancel the compilation process. self.cache.program_canceled(cache_key); - trace!("Job handle dropped, skipping."); + info!("Job handle dropped, skipping."); continue; } } @@ -615,13 +614,12 @@ struct ShaderCache { vertex_shaders: HashMap>, fragment_shaders: HashMap>, programs: HashMap, - waiting_jobs: HashMap>>, } -#[derive(Debug, Clone)] +#[derive(Debug)] enum ProgramCacheEntry { /// Shader program is already submitted for compilation. - Submitted, + Submitted { waiters: Vec> }, /// Shader program was already compiled and linked successfully. Validated(shader::Program), /// Compilation of this shader program already failed. @@ -634,38 +632,33 @@ impl ShaderCache { code.as_ref().map(|shader| shader.as_ref().map(ShaderHash::new)) } - fn get_program(&self, key: ShaderCacheKey) -> Option<&ProgramCacheEntry> { - self.programs.get(&key) - } - - fn add_to_waiting_list(&mut self, job: Job<()>) { - self.waiting_jobs.entry(job.cache_key).or_default().push(job); + fn get_program(&mut self, key: ShaderCacheKey) -> Option<&mut ProgramCacheEntry> { + self.programs.get_mut(&key) } /// Get single job that is awaiting for the completed compilation under specific cache entry. /// Only returns jobs that were not cancelled yet. All jobs that were scanned and found to be /// cancelled will be removed from the waiting list to avoid processing them in the future. fn take_next_waiting_job(&mut self, key: ShaderCacheKey) -> Option> { - let jobs = self.waiting_jobs.get_mut(&key)?; - while let Some(job) = jobs.pop() { - if !job.is_cancelled() { - return Some(job); + if let Some(ProgramCacheEntry::Submitted { waiters }) = self.get_program(key) { + while let Some(job) = waiters.pop() { + if !job.is_cancelled() { + return Some(job); + } } } None } fn program_submitted(&mut self, key: ShaderCacheKey) { - self.programs.insert(key, ProgramCacheEntry::Submitted); + self.programs.insert(key, ProgramCacheEntry::Submitted { waiters: default() }); } fn program_failed(&mut self, key: ShaderCacheKey) { - self.waiting_jobs.remove(&key); self.programs.insert(key, ProgramCacheEntry::Failed); } fn program_canceled(&mut self, key: ShaderCacheKey) { - self.waiting_jobs.remove(&key); self.programs.remove(&key); } @@ -675,8 +668,7 @@ impl ShaderCache { program: shader::Program, on_ready: Option, ) { - self.programs.insert(key, ProgramCacheEntry::Validated(program.clone())); - + let entry = self.programs.insert(key, ProgramCacheEntry::Validated(program.clone())); // Complete all jobs that were scheduled for this program. This must be done after the cache // entry is updated, in case the job callback schedules another compilation of the same // shader program. We want those submissions to recognize cache status as validated and @@ -684,8 +676,8 @@ impl ShaderCache { if let Some(on_ready) = on_ready { on_ready(program.clone()); } - if let Some(jobs) = self.waiting_jobs.remove(&key) { - for job in jobs { + if let Some(ProgramCacheEntry::Submitted { waiters }) = entry { + for job in waiters { if !job.is_cancelled() { if let Some(on_ready) = job.on_ready { (on_ready)(program.clone()); From efcd01de6d5cd1f399c532d6e43bdbbd36cff78c Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 20 Feb 2023 17:05:41 +0000 Subject: [PATCH 12/16] Apply review. --- Cargo.toml | 2 +- app/gui/Cargo.toml | 2 +- app/gui/docs/CONTRIBUTING.md | 2 +- build/intellij-run-config-gen/src/main.rs | 2 +- .../src/display/shape/primitive/system.rs | 8 +------ .../core/src/display/symbol/gpu/shader.rs | 2 +- lib/rust/ensogl/core/src/display/world.rs | 23 +++++++++++-------- .../ensogl/{example => examples}/Cargo.toml | 0 .../animation/Cargo.toml | 0 .../animation/src/lib.rs | 0 .../auto-layout/Cargo.toml | 0 .../auto-layout/src/lib.rs | 0 .../cached-shape/Cargo.toml | 0 .../cached-shape/src/lib.rs | 0 .../complex-shape-system/Cargo.toml | 0 .../complex-shape-system/src/lib.rs | 0 .../custom-shape-system/Cargo.toml | 0 .../custom-shape-system/src/lib.rs | 0 .../dom-symbols/Cargo.toml | 0 .../dom-symbols/src/lib.rs | 0 .../drop-down/Cargo.toml | 0 .../drop-down/src/lib.rs | 0 .../drop-manager/Cargo.toml | 0 .../drop-manager/src/lib.rs | 0 .../easing-animator/Cargo.toml | 0 .../easing-animator/src/lib.rs | 0 .../focus-management/Cargo.toml | 0 .../focus-management/src/lib.rs | 0 .../grid-view/Cargo.toml | 0 .../grid-view/src/lib.rs | 0 .../list-view/Cargo.toml | 0 .../list-view/src/lib.rs | 0 .../mouse-events/Cargo.toml | 0 .../mouse-events/src/lib.rs | 0 .../profiling-run-graph/Cargo.toml | 0 .../profiling-run-graph/src/lib.rs | 0 .../render-profile-flamegraph/Cargo.toml | 0 .../render-profile-flamegraph/src/lib.rs | 0 .../scroll-area/Cargo.toml | 0 .../scroll-area/src/lib.rs | 0 .../{example => examples}/slider/Cargo.toml | 0 .../{example => examples}/slider/src/lib.rs | 0 .../sprite-system-benchmark/Cargo.toml | 0 .../sprite-system-benchmark/src/lib.rs | 0 .../sprite-system/Cargo.toml | 0 .../sprite-system/src/lib.rs | 0 .../ensogl/{example => examples}/src/lib.rs | 0 .../text-area/Cargo.toml | 0 .../text-area/src/lib.rs | 0 49 files changed, 19 insertions(+), 22 deletions(-) rename lib/rust/ensogl/{example => examples}/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/animation/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/animation/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/auto-layout/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/auto-layout/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/cached-shape/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/cached-shape/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/complex-shape-system/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/complex-shape-system/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/custom-shape-system/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/custom-shape-system/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/dom-symbols/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/dom-symbols/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/drop-down/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/drop-down/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/drop-manager/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/drop-manager/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/easing-animator/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/easing-animator/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/focus-management/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/focus-management/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/grid-view/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/grid-view/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/list-view/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/list-view/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/mouse-events/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/mouse-events/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/profiling-run-graph/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/profiling-run-graph/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/render-profile-flamegraph/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/render-profile-flamegraph/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/scroll-area/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/scroll-area/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/slider/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/slider/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/sprite-system-benchmark/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/sprite-system-benchmark/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/sprite-system/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/sprite-system/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/src/lib.rs (100%) rename lib/rust/ensogl/{example => examples}/text-area/Cargo.toml (100%) rename lib/rust/ensogl/{example => examples}/text-area/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index edd028090a0c..19626d1583fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" # Listing only the "root" crates of each app/library. All path dependencies are included in the workspace automatically. -# If you want to add sub crate (like `app/gui/config` or `lib/rust/ensogl/example`), just add it as a path dependency +# If you want to add sub crate (like `app/gui/config` or `lib/rust/ensogl/examples`), just add it as a path dependency # where plausible. members = [ "app/gui", diff --git a/app/gui/Cargo.toml b/app/gui/Cargo.toml index 1361dab8cd06..124f1657f23d 100644 --- a/app/gui/Cargo.toml +++ b/app/gui/Cargo.toml @@ -25,7 +25,7 @@ enso-text = { path = "../../lib/rust/text" } enso-web = { path = "../../lib/rust/web" } enso-suggestion-database = { path = "suggestion-database" } ensogl = { path = "../../lib/rust/ensogl" } -ensogl-examples = { path = "../../lib/rust/ensogl/example" } +ensogl-examples = { path = "../../lib/rust/ensogl/examples" } ensogl-component = { path = "../../lib/rust/ensogl/component" } ensogl-text-msdf = { path = "../../lib/rust/ensogl/component/text/src/font/msdf" } ensogl-hardcoded-theme = { path = "../../lib/rust/ensogl/app/theme/hardcoded" } diff --git a/app/gui/docs/CONTRIBUTING.md b/app/gui/docs/CONTRIBUTING.md index 068d7266298c..d4f04fcaef47 100644 --- a/app/gui/docs/CONTRIBUTING.md +++ b/app/gui/docs/CONTRIBUTING.md @@ -210,7 +210,7 @@ below: - **Selective mode** In order to compile only part of the project, and thus drastically shorten the incremental compile time, you are advised to use the selective compilation mode by passing the `--crate-path` option to the `build` - or `watch` command, e.g. `./run ide watch --crate-path ensogl/example` to + or `watch` command, e.g. `./run ide watch --crate-path ensogl/examples` to compile only the renderer-related example scenes. Please note, that in order to run a scene in a web-browser, the scene has to be compiled and has to expose a public function with a name starting with `entry_point_`. Thus, if diff --git a/build/intellij-run-config-gen/src/main.rs b/build/intellij-run-config-gen/src/main.rs index ba174311698f..f642189b1b4e 100644 --- a/build/intellij-run-config-gen/src/main.rs +++ b/build/intellij-run-config-gen/src/main.rs @@ -83,7 +83,7 @@ const DIR_BLACK_LIST: &[&str] = &["target", "dist"]; const FOLDER_TO_HEADER_MAP: &[(&str, &str, usize)] = &[ ("build/", "🚧 Build", 10), ("tools/", "🔨 Tools", 9), - ("lib/rust/ensogl/example/", "🧸 Example", 4), + ("lib/rust/ensogl/examples/", "🧸 Example", 4), ("lib/rust/ensogl/", "🎨 EnsoGl", 6), ("lib/rust/ensogl", "🎨 EnsoGl", 0), ("lib/rust/parser/", "👓 Parser", 7), diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index a0e9e1fa3ea4..35216f877a5a 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -83,7 +83,7 @@ use super::def; // ===================== /// The ID of a user generated shape system. -#[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] +#[derive(Copy, Clone, CloneRef, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] pub struct ShapeSystemId { type_id: std::any::TypeId, } @@ -97,12 +97,6 @@ impl ShapeSystemId { } } -impl CloneRef for ShapeSystemId { - fn clone_ref(&self) -> Self { - *self - } -} - // ============= diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs index ffcd065a6533..dfe4564a7401 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs @@ -207,7 +207,7 @@ impl { let geometry_inputs = self.geometry_material.inputs().iter(); let surface_inputs = self.surface_material.inputs().iter(); geometry_inputs.chain(surface_inputs) - .map(|(s, d)| (s.clone(), d.clone())) + .map(|(name, declaration)| (name.clone(), declaration.clone())) .collect_vec() .into_iter() } diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index 03b9e6b7f3b2..e7e76e743d51 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -98,6 +98,14 @@ pub struct ShapeDefinition { pub cons: ShapeCons, } +impl ShapeDefinition { + /// Return `true` if it is possible that this shape is used by the main application. + fn is_main_application_shape(&self) -> bool { + // Shapes defined in `examples` directories are not used in the main application. + !self.definition_path.contains("/examples/") + } +} + /// The definition of shapes created with the `cached_shape!` macro. #[derive(Derivative)] #[derivative(Debug)] @@ -210,16 +218,11 @@ fn gather_shaders() -> HashMap<&'static str, shader::Code> { #[profile(Task)] pub fn instantiate_shaders() { SHAPES_DEFINITIONS.with_borrow(|shapes| { - for shape in shapes { - let path = &shape.definition_path; - let preload = match path { - _ if path.starts_with("app/gui/view/debug_scene/") => false, - _ if path.starts_with("lib/rust/ensogl/example/") => false, - _ => true, - }; - if preload { - let _shape = (shape.cons)(); - } + for shape in shapes.iter().filter(|shape| shape.is_main_application_shape()) { + // Instantiate shape so that its shader program will be submitted to the shader + // compiler. The runtime compiles the shaders in background threads, and starting early + // ensures they will be ready when we want to render them. + let _shape = (shape.cons)(); } }); } diff --git a/lib/rust/ensogl/example/Cargo.toml b/lib/rust/ensogl/examples/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/Cargo.toml rename to lib/rust/ensogl/examples/Cargo.toml diff --git a/lib/rust/ensogl/example/animation/Cargo.toml b/lib/rust/ensogl/examples/animation/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/animation/Cargo.toml rename to lib/rust/ensogl/examples/animation/Cargo.toml diff --git a/lib/rust/ensogl/example/animation/src/lib.rs b/lib/rust/ensogl/examples/animation/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/animation/src/lib.rs rename to lib/rust/ensogl/examples/animation/src/lib.rs diff --git a/lib/rust/ensogl/example/auto-layout/Cargo.toml b/lib/rust/ensogl/examples/auto-layout/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/auto-layout/Cargo.toml rename to lib/rust/ensogl/examples/auto-layout/Cargo.toml diff --git a/lib/rust/ensogl/example/auto-layout/src/lib.rs b/lib/rust/ensogl/examples/auto-layout/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/auto-layout/src/lib.rs rename to lib/rust/ensogl/examples/auto-layout/src/lib.rs diff --git a/lib/rust/ensogl/example/cached-shape/Cargo.toml b/lib/rust/ensogl/examples/cached-shape/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/cached-shape/Cargo.toml rename to lib/rust/ensogl/examples/cached-shape/Cargo.toml diff --git a/lib/rust/ensogl/example/cached-shape/src/lib.rs b/lib/rust/ensogl/examples/cached-shape/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/cached-shape/src/lib.rs rename to lib/rust/ensogl/examples/cached-shape/src/lib.rs diff --git a/lib/rust/ensogl/example/complex-shape-system/Cargo.toml b/lib/rust/ensogl/examples/complex-shape-system/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/complex-shape-system/Cargo.toml rename to lib/rust/ensogl/examples/complex-shape-system/Cargo.toml diff --git a/lib/rust/ensogl/example/complex-shape-system/src/lib.rs b/lib/rust/ensogl/examples/complex-shape-system/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/complex-shape-system/src/lib.rs rename to lib/rust/ensogl/examples/complex-shape-system/src/lib.rs diff --git a/lib/rust/ensogl/example/custom-shape-system/Cargo.toml b/lib/rust/ensogl/examples/custom-shape-system/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/custom-shape-system/Cargo.toml rename to lib/rust/ensogl/examples/custom-shape-system/Cargo.toml diff --git a/lib/rust/ensogl/example/custom-shape-system/src/lib.rs b/lib/rust/ensogl/examples/custom-shape-system/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/custom-shape-system/src/lib.rs rename to lib/rust/ensogl/examples/custom-shape-system/src/lib.rs diff --git a/lib/rust/ensogl/example/dom-symbols/Cargo.toml b/lib/rust/ensogl/examples/dom-symbols/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/dom-symbols/Cargo.toml rename to lib/rust/ensogl/examples/dom-symbols/Cargo.toml diff --git a/lib/rust/ensogl/example/dom-symbols/src/lib.rs b/lib/rust/ensogl/examples/dom-symbols/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/dom-symbols/src/lib.rs rename to lib/rust/ensogl/examples/dom-symbols/src/lib.rs diff --git a/lib/rust/ensogl/example/drop-down/Cargo.toml b/lib/rust/ensogl/examples/drop-down/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/drop-down/Cargo.toml rename to lib/rust/ensogl/examples/drop-down/Cargo.toml diff --git a/lib/rust/ensogl/example/drop-down/src/lib.rs b/lib/rust/ensogl/examples/drop-down/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/drop-down/src/lib.rs rename to lib/rust/ensogl/examples/drop-down/src/lib.rs diff --git a/lib/rust/ensogl/example/drop-manager/Cargo.toml b/lib/rust/ensogl/examples/drop-manager/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/drop-manager/Cargo.toml rename to lib/rust/ensogl/examples/drop-manager/Cargo.toml diff --git a/lib/rust/ensogl/example/drop-manager/src/lib.rs b/lib/rust/ensogl/examples/drop-manager/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/drop-manager/src/lib.rs rename to lib/rust/ensogl/examples/drop-manager/src/lib.rs diff --git a/lib/rust/ensogl/example/easing-animator/Cargo.toml b/lib/rust/ensogl/examples/easing-animator/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/easing-animator/Cargo.toml rename to lib/rust/ensogl/examples/easing-animator/Cargo.toml diff --git a/lib/rust/ensogl/example/easing-animator/src/lib.rs b/lib/rust/ensogl/examples/easing-animator/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/easing-animator/src/lib.rs rename to lib/rust/ensogl/examples/easing-animator/src/lib.rs diff --git a/lib/rust/ensogl/example/focus-management/Cargo.toml b/lib/rust/ensogl/examples/focus-management/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/focus-management/Cargo.toml rename to lib/rust/ensogl/examples/focus-management/Cargo.toml diff --git a/lib/rust/ensogl/example/focus-management/src/lib.rs b/lib/rust/ensogl/examples/focus-management/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/focus-management/src/lib.rs rename to lib/rust/ensogl/examples/focus-management/src/lib.rs diff --git a/lib/rust/ensogl/example/grid-view/Cargo.toml b/lib/rust/ensogl/examples/grid-view/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/grid-view/Cargo.toml rename to lib/rust/ensogl/examples/grid-view/Cargo.toml diff --git a/lib/rust/ensogl/example/grid-view/src/lib.rs b/lib/rust/ensogl/examples/grid-view/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/grid-view/src/lib.rs rename to lib/rust/ensogl/examples/grid-view/src/lib.rs diff --git a/lib/rust/ensogl/example/list-view/Cargo.toml b/lib/rust/ensogl/examples/list-view/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/list-view/Cargo.toml rename to lib/rust/ensogl/examples/list-view/Cargo.toml diff --git a/lib/rust/ensogl/example/list-view/src/lib.rs b/lib/rust/ensogl/examples/list-view/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/list-view/src/lib.rs rename to lib/rust/ensogl/examples/list-view/src/lib.rs diff --git a/lib/rust/ensogl/example/mouse-events/Cargo.toml b/lib/rust/ensogl/examples/mouse-events/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/mouse-events/Cargo.toml rename to lib/rust/ensogl/examples/mouse-events/Cargo.toml diff --git a/lib/rust/ensogl/example/mouse-events/src/lib.rs b/lib/rust/ensogl/examples/mouse-events/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/mouse-events/src/lib.rs rename to lib/rust/ensogl/examples/mouse-events/src/lib.rs diff --git a/lib/rust/ensogl/example/profiling-run-graph/Cargo.toml b/lib/rust/ensogl/examples/profiling-run-graph/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/profiling-run-graph/Cargo.toml rename to lib/rust/ensogl/examples/profiling-run-graph/Cargo.toml diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/examples/profiling-run-graph/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/profiling-run-graph/src/lib.rs rename to lib/rust/ensogl/examples/profiling-run-graph/src/lib.rs diff --git a/lib/rust/ensogl/example/render-profile-flamegraph/Cargo.toml b/lib/rust/ensogl/examples/render-profile-flamegraph/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/render-profile-flamegraph/Cargo.toml rename to lib/rust/ensogl/examples/render-profile-flamegraph/Cargo.toml diff --git a/lib/rust/ensogl/example/render-profile-flamegraph/src/lib.rs b/lib/rust/ensogl/examples/render-profile-flamegraph/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/render-profile-flamegraph/src/lib.rs rename to lib/rust/ensogl/examples/render-profile-flamegraph/src/lib.rs diff --git a/lib/rust/ensogl/example/scroll-area/Cargo.toml b/lib/rust/ensogl/examples/scroll-area/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/scroll-area/Cargo.toml rename to lib/rust/ensogl/examples/scroll-area/Cargo.toml diff --git a/lib/rust/ensogl/example/scroll-area/src/lib.rs b/lib/rust/ensogl/examples/scroll-area/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/scroll-area/src/lib.rs rename to lib/rust/ensogl/examples/scroll-area/src/lib.rs diff --git a/lib/rust/ensogl/example/slider/Cargo.toml b/lib/rust/ensogl/examples/slider/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/slider/Cargo.toml rename to lib/rust/ensogl/examples/slider/Cargo.toml diff --git a/lib/rust/ensogl/example/slider/src/lib.rs b/lib/rust/ensogl/examples/slider/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/slider/src/lib.rs rename to lib/rust/ensogl/examples/slider/src/lib.rs diff --git a/lib/rust/ensogl/example/sprite-system-benchmark/Cargo.toml b/lib/rust/ensogl/examples/sprite-system-benchmark/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/sprite-system-benchmark/Cargo.toml rename to lib/rust/ensogl/examples/sprite-system-benchmark/Cargo.toml diff --git a/lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs b/lib/rust/ensogl/examples/sprite-system-benchmark/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs rename to lib/rust/ensogl/examples/sprite-system-benchmark/src/lib.rs diff --git a/lib/rust/ensogl/example/sprite-system/Cargo.toml b/lib/rust/ensogl/examples/sprite-system/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/sprite-system/Cargo.toml rename to lib/rust/ensogl/examples/sprite-system/Cargo.toml diff --git a/lib/rust/ensogl/example/sprite-system/src/lib.rs b/lib/rust/ensogl/examples/sprite-system/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/sprite-system/src/lib.rs rename to lib/rust/ensogl/examples/sprite-system/src/lib.rs diff --git a/lib/rust/ensogl/example/src/lib.rs b/lib/rust/ensogl/examples/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/src/lib.rs rename to lib/rust/ensogl/examples/src/lib.rs diff --git a/lib/rust/ensogl/example/text-area/Cargo.toml b/lib/rust/ensogl/examples/text-area/Cargo.toml similarity index 100% rename from lib/rust/ensogl/example/text-area/Cargo.toml rename to lib/rust/ensogl/examples/text-area/Cargo.toml diff --git a/lib/rust/ensogl/example/text-area/src/lib.rs b/lib/rust/ensogl/examples/text-area/src/lib.rs similarity index 100% rename from lib/rust/ensogl/example/text-area/src/lib.rs rename to lib/rust/ensogl/examples/text-area/src/lib.rs From da7bb1b46480733b310c71cc06b774d85f8ee5d7 Mon Sep 17 00:00:00 2001 From: Kaz Wesley Date: Mon, 20 Feb 2023 17:50:12 +0000 Subject: [PATCH 13/16] Apply review. --- .../clippy-debug-scene-component-list-panel-view-wasm-linux.xml | 2 +- .../clippy-debug-scene-component-list-panel-view-wasm-macos.xml | 2 +- ...lippy-debug-scene-component-list-panel-view-wasm-windows.xml | 2 +- .../clippy-debug-scene-component-list-panel-view-wasm.xml | 2 +- .idea/runConfigurations/clippy-debug-scene-icons-wasm-linux.xml | 2 +- .idea/runConfigurations/clippy-debug-scene-icons-wasm-macos.xml | 2 +- .../runConfigurations/clippy-debug-scene-icons-wasm-windows.xml | 2 +- .idea/runConfigurations/clippy-debug-scene-icons-wasm.xml | 2 +- .../clippy-debug-scene-interface-wasm-linux.xml | 2 +- .../clippy-debug-scene-interface-wasm-macos.xml | 2 +- .../clippy-debug-scene-interface-wasm-windows.xml | 2 +- .idea/runConfigurations/clippy-debug-scene-interface-wasm.xml | 2 +- .../clippy-debug-scene-text-grid-visualization-wasm-linux.xml | 2 +- .../clippy-debug-scene-text-grid-visualization-wasm-macos.xml | 2 +- .../clippy-debug-scene-text-grid-visualization-wasm-windows.xml | 2 +- .../clippy-debug-scene-text-grid-visualization-wasm.xml | 2 +- .../clippy-debug-scene-visualization-wasm-linux.xml | 2 +- .../clippy-debug-scene-visualization-wasm-macos.xml | 2 +- .../clippy-debug-scene-visualization-wasm-windows.xml | 2 +- .../runConfigurations/clippy-debug-scene-visualization-wasm.xml | 2 +- .idea/runConfigurations/clippy-enso-debug-scene-wasm-linux.xml | 2 +- .idea/runConfigurations/clippy-enso-debug-scene-wasm-macos.xml | 2 +- .../runConfigurations/clippy-enso-debug-scene-wasm-windows.xml | 2 +- .idea/runConfigurations/clippy-enso-debug-scene-wasm.xml | 2 +- .../doc-test-debug-scene-component-list-panel-view-native.xml | 2 +- .idea/runConfigurations/doc-test-debug-scene-icons-native.xml | 2 +- .../runConfigurations/doc-test-debug-scene-interface-native.xml | 2 +- .../doc-test-debug-scene-text-grid-visualization-native.xml | 2 +- .../doc-test-debug-scene-visualization-native.xml | 2 +- .idea/runConfigurations/doc-test-enso-debug-scene-native.xml | 2 +- .../test-debug-scene-component-list-panel-view-native.xml | 2 +- .idea/runConfigurations/test-debug-scene-icons-native.xml | 2 +- .idea/runConfigurations/test-debug-scene-interface-native.xml | 2 +- .../test-debug-scene-text-grid-visualization-native.xml | 2 +- .../runConfigurations/test-debug-scene-visualization-native.xml | 2 +- .idea/runConfigurations/test-enso-debug-scene-native.xml | 2 +- app/gui/Cargo.toml | 2 +- app/gui/view/{debug_scene => examples}/Cargo.toml | 0 .../component-list-panel-view/Cargo.toml | 0 .../component-list-panel-view/src/lib.rs | 0 app/gui/view/{debug_scene => examples}/documentation/Cargo.toml | 0 app/gui/view/{debug_scene => examples}/documentation/src/lib.rs | 0 app/gui/view/{debug_scene => examples}/icons/Cargo.toml | 0 app/gui/view/{debug_scene => examples}/icons/src/lib.rs | 0 app/gui/view/{debug_scene => examples}/interface/Cargo.toml | 0 app/gui/view/{debug_scene => examples}/interface/src/lib.rs | 0 app/gui/view/{debug_scene => examples}/src/lib.rs | 0 .../text-grid-visualization/Cargo.toml | 0 .../text-grid-visualization/src/lib.rs | 0 app/gui/view/{debug_scene => examples}/visualization/Cargo.toml | 0 app/gui/view/{debug_scene => examples}/visualization/src/lib.rs | 0 51 files changed, 37 insertions(+), 37 deletions(-) rename app/gui/view/{debug_scene => examples}/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/component-list-panel-view/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/component-list-panel-view/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/documentation/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/documentation/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/icons/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/icons/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/interface/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/interface/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/text-grid-visualization/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/text-grid-visualization/src/lib.rs (100%) rename app/gui/view/{debug_scene => examples}/visualization/Cargo.toml (100%) rename app/gui/view/{debug_scene => examples}/visualization/src/lib.rs (100%) diff --git a/.idea/runConfigurations/clippy-debug-scene-component-list-panel-view-wasm-linux.xml b/.idea/runConfigurations/clippy-debug-scene-component-list-panel-view-wasm-linux.xml index 0bec8612bd87..7d80334bfd6c 100644 --- a/.idea/runConfigurations/clippy-debug-scene-component-list-panel-view-wasm-linux.xml +++ b/.idea/runConfigurations/clippy-debug-scene-component-list-panel-view-wasm-linux.xml @@ -5,7 +5,7 @@ name="clippy/debug-scene-component-list-panel-view [wasm, linux]" type="CargoCommandRunConfiguration" factoryName="Cargo Command" - folderName="🚥 GUI view/debug_scene/component-list-panel-view" + folderName="🚥 GUI view/examples/component-list-panel-view" >