mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 16:18:23 +03:00
Render on demand. (#3397)
This commit is contained in:
parent
fbe28db1d7
commit
d5d5d3aac5
@ -18,13 +18,13 @@
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color::Rgba;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_gui_component::component;
|
||||
use ensogl_hardcoded_theme::application::component_browser::component_group as theme;
|
||||
use ensogl_list_view as list_view;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
|
||||
// === Non-Standard Linter Configuration ===
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
@ -17,3 +18,5 @@
|
||||
|
||||
pub use ide_view_component_group as component_group;
|
||||
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@ use crate::prelude::*;
|
||||
use crate::system::gpu::*;
|
||||
|
||||
use crate::display::render::pass;
|
||||
use crate::display::scene::UpdateStatus;
|
||||
|
||||
|
||||
|
||||
@ -67,9 +68,9 @@ impl {
|
||||
}
|
||||
|
||||
/// Run all the registered passes in this composer.
|
||||
pub fn run(&mut self) {
|
||||
pub fn run(&mut self, update_status: UpdateStatus) {
|
||||
for pass in &mut self.passes {
|
||||
pass.run();
|
||||
pass.run(update_status);
|
||||
}
|
||||
}
|
||||
}}
|
||||
@ -118,7 +119,7 @@ impl ComposerPass {
|
||||
}
|
||||
|
||||
/// Run the pass.
|
||||
pub fn run(&mut self) {
|
||||
self.pass.run(&self.instance);
|
||||
pub fn run(&mut self, update_status: UpdateStatus) {
|
||||
self.pass.run(&self.instance, update_status);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use crate::prelude::*;
|
||||
use crate::system::gpu::*;
|
||||
|
||||
use crate::display::scene::UpdateStatus;
|
||||
use crate::system::gpu::data::texture::class::TextureOps;
|
||||
|
||||
|
||||
@ -18,7 +19,7 @@ use crate::system::gpu::data::texture::class::TextureOps;
|
||||
#[allow(missing_docs)]
|
||||
pub trait Definition: CloneBoxedForDefinition + Debug + 'static {
|
||||
fn initialize(&mut self, _instance: &Instance) {}
|
||||
fn run(&mut self, _instance: &Instance);
|
||||
fn run(&mut self, _instance: &Instance, update_status: UpdateStatus);
|
||||
}
|
||||
|
||||
clone_boxed!(Definition);
|
||||
|
@ -5,6 +5,7 @@ use crate::system::gpu::*;
|
||||
use crate::system::js::*;
|
||||
|
||||
use crate::display::render::pass;
|
||||
use crate::display::scene::UpdateStatus;
|
||||
use crate::system::gpu::data::texture::class::TextureOps;
|
||||
|
||||
use web_sys::WebGlBuffer;
|
||||
@ -180,7 +181,7 @@ impl<T: JsTypedArrayItem> PixelReadPass<T> {
|
||||
}
|
||||
|
||||
impl<T: JsTypedArrayItem> pass::Definition for PixelReadPass<T> {
|
||||
fn run(&mut self, instance: &pass::Instance) {
|
||||
fn run(&mut self, instance: &pass::Instance, update_status: UpdateStatus) {
|
||||
if self.to_next_read > 0 {
|
||||
self.to_next_read -= 1;
|
||||
} else {
|
||||
@ -189,7 +190,8 @@ impl<T: JsTypedArrayItem> pass::Definition for PixelReadPass<T> {
|
||||
if let Some(sync) = self.sync.clone() {
|
||||
self.check_and_handle_sync(&instance.context, &sync);
|
||||
}
|
||||
if self.sync.is_none() {
|
||||
let need_sync = update_status.scene_was_dirty || update_status.pointer_position_changed;
|
||||
if need_sync && self.sync.is_none() {
|
||||
self.run_not_synced(&instance.context);
|
||||
if let Some(callback) = &self.sync_callback {
|
||||
callback()
|
||||
|
@ -4,6 +4,7 @@ use crate::prelude::*;
|
||||
|
||||
use crate::display::render::pass;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::display::scene::UpdateStatus;
|
||||
use crate::display::symbol::Screen;
|
||||
|
||||
|
||||
@ -27,7 +28,9 @@ impl ScreenRenderPass {
|
||||
}
|
||||
|
||||
impl pass::Definition for ScreenRenderPass {
|
||||
fn run(&mut self, _: &pass::Instance) {
|
||||
self.screen.render();
|
||||
fn run(&mut self, _: &pass::Instance, update_status: UpdateStatus) {
|
||||
if update_status.scene_was_dirty {
|
||||
self.screen.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use crate::display::render::pass;
|
||||
use crate::display::scene;
|
||||
use crate::display::scene::layer;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::display::scene::UpdateStatus;
|
||||
use crate::display::symbol::registry::SymbolRegistry;
|
||||
use crate::display::symbol::MaskComposer;
|
||||
|
||||
@ -90,25 +91,27 @@ impl pass::Definition for SymbolsRenderPass {
|
||||
self.framebuffers = Some(Framebuffers::new(composed_fb, mask_fb, layer_fb));
|
||||
}
|
||||
|
||||
fn run(&mut self, instance: &pass::Instance) {
|
||||
let framebuffers = self.framebuffers.as_ref().unwrap();
|
||||
fn run(&mut self, instance: &pass::Instance, update_status: UpdateStatus) {
|
||||
if update_status.scene_was_dirty {
|
||||
let framebuffers = self.framebuffers.as_ref().unwrap();
|
||||
|
||||
framebuffers.composed.bind();
|
||||
framebuffers.composed.bind();
|
||||
|
||||
let arr = vec![0.0, 0.0, 0.0, 0.0];
|
||||
instance.context.clear_bufferfv_with_f32_array(*Context::COLOR, 0, &arr);
|
||||
instance.context.clear_bufferfv_with_f32_array(*Context::COLOR, 1, &arr);
|
||||
let arr = vec![0.0, 0.0, 0.0, 0.0];
|
||||
instance.context.clear_bufferfv_with_f32_array(*Context::COLOR, 0, &arr);
|
||||
instance.context.clear_bufferfv_with_f32_array(*Context::COLOR, 1, &arr);
|
||||
|
||||
let mut scissor_stack = default();
|
||||
self.render_layer(instance, &self.layers.root.clone(), &mut scissor_stack, false);
|
||||
if !scissor_stack.is_empty() {
|
||||
warning!(
|
||||
&self.logger,
|
||||
"The scissor stack was not cleaned properly. \
|
||||
let mut scissor_stack = default();
|
||||
self.render_layer(instance, &self.layers.root.clone(), &mut scissor_stack, false);
|
||||
if !scissor_stack.is_empty() {
|
||||
warning!(
|
||||
&self.logger,
|
||||
"The scissor stack was not cleaned properly. \
|
||||
This is an internal bug that may lead to visual artifacts. Please report it."
|
||||
);
|
||||
);
|
||||
}
|
||||
instance.context.bind_framebuffer(*Context::FRAMEBUFFER, None);
|
||||
}
|
||||
instance.context.bind_framebuffer(*Context::FRAMEBUFFER, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -522,10 +522,10 @@ impl Renderer {
|
||||
}
|
||||
|
||||
/// Run the renderer.
|
||||
pub fn run(&self) {
|
||||
pub fn run(&self, update_status: UpdateStatus) {
|
||||
if let Some(composer) = &mut *self.composer.borrow_mut() {
|
||||
debug!(self.logger, "Running.", || {
|
||||
composer.run();
|
||||
composer.run(update_status);
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -705,35 +705,51 @@ impl Extensions {
|
||||
|
||||
|
||||
|
||||
// ====================
|
||||
// === UpdateStatus ===
|
||||
// ====================
|
||||
|
||||
/// Scene update status. Used to tell the renderer what is the minimal amount of per-frame
|
||||
/// processing. For example, if scene was not dirty (no animations), but pointer position changed,
|
||||
/// the scene should not be re-rendered, but the id-texture pixel under the mouse should be read.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct UpdateStatus {
|
||||
pub scene_was_dirty: bool,
|
||||
pub pointer_position_changed: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === SceneData ===
|
||||
// =================
|
||||
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct SceneData {
|
||||
pub display_object: display::object::Instance,
|
||||
pub dom: Dom,
|
||||
pub context: Rc<RefCell<Option<Context>>>,
|
||||
pub display_object: display::object::Instance,
|
||||
pub dom: Dom,
|
||||
pub context: Rc<RefCell<Option<Context>>>,
|
||||
pub context_lost_handler: Rc<RefCell<Option<ContextLostHandler>>>,
|
||||
pub symbols: SymbolRegistry,
|
||||
pub variables: UniformScope,
|
||||
pub current_js_event: CurrentJsEvent,
|
||||
pub mouse: Mouse,
|
||||
pub keyboard: Keyboard,
|
||||
pub uniforms: Uniforms,
|
||||
pub background: PointerTarget,
|
||||
pub shapes: ShapeRegistry,
|
||||
pub stats: Stats,
|
||||
pub dirty: Dirty,
|
||||
pub logger: Logger,
|
||||
pub renderer: Renderer,
|
||||
pub layers: HardcodedLayers,
|
||||
pub style_sheet: style::Sheet,
|
||||
pub bg_color_var: style::Var,
|
||||
pub bg_color_change: callback::Handle,
|
||||
pub frp: Frp,
|
||||
extensions: Extensions,
|
||||
disable_context_menu: Rc<EventListenerHandle>,
|
||||
pub symbols: SymbolRegistry,
|
||||
pub variables: UniformScope,
|
||||
pub current_js_event: CurrentJsEvent,
|
||||
pub mouse: Mouse,
|
||||
pub keyboard: Keyboard,
|
||||
pub uniforms: Uniforms,
|
||||
pub background: PointerTarget,
|
||||
pub shapes: ShapeRegistry,
|
||||
pub stats: Stats,
|
||||
pub dirty: Dirty,
|
||||
pub logger: Logger,
|
||||
pub renderer: Renderer,
|
||||
pub layers: HardcodedLayers,
|
||||
pub style_sheet: style::Sheet,
|
||||
pub bg_color_var: style::Var,
|
||||
pub bg_color_change: callback::Handle,
|
||||
pub frp: Frp,
|
||||
pub pointer_position_changed: Rc<Cell<bool>>,
|
||||
extensions: Extensions,
|
||||
disable_context_menu: Rc<EventListenerHandle>,
|
||||
}
|
||||
|
||||
impl SceneData {
|
||||
@ -784,6 +800,7 @@ impl SceneData {
|
||||
uniforms.pixel_ratio.set(dom.shape().pixel_ratio);
|
||||
let context = default();
|
||||
let context_lost_handler = default();
|
||||
let pointer_position_changed = default();
|
||||
Self {
|
||||
display_object,
|
||||
dom,
|
||||
@ -806,6 +823,7 @@ impl SceneData {
|
||||
bg_color_var,
|
||||
bg_color_change,
|
||||
frp,
|
||||
pointer_position_changed,
|
||||
extensions,
|
||||
disable_context_menu,
|
||||
}
|
||||
@ -843,7 +861,7 @@ impl SceneData {
|
||||
&self.symbols
|
||||
}
|
||||
|
||||
fn update_shape(&self) {
|
||||
fn update_shape(&self) -> bool {
|
||||
if self.dirty.shape.check_all() {
|
||||
let screen = self.dom.shape();
|
||||
self.resize_canvas(screen);
|
||||
@ -852,17 +870,24 @@ impl SceneData {
|
||||
});
|
||||
self.renderer.resize_composer();
|
||||
self.dirty.shape.unset_all();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn update_symbols(&self) {
|
||||
fn update_symbols(&self) -> bool {
|
||||
if self.dirty.symbols.check_all() {
|
||||
self.symbols.update();
|
||||
self.dirty.symbols.unset_all();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn update_camera(&self, scene: &Scene) {
|
||||
fn update_camera(&self, scene: &Scene) -> bool {
|
||||
let mut was_dirty = false;
|
||||
// Updating camera for DOM layers. Please note that DOM layers cannot use multi-camera
|
||||
// setups now, so we are using here the main camera only.
|
||||
let camera = self.camera();
|
||||
@ -871,6 +896,7 @@ impl SceneData {
|
||||
let welcome_screen_camera = self.layers.panel.camera();
|
||||
let changed = camera.update(scene);
|
||||
if changed {
|
||||
was_dirty = true;
|
||||
self.frp.camera_changed_source.emit(());
|
||||
self.symbols.set_camera(&camera);
|
||||
self.dom.layers.front.update_view_projection(&camera);
|
||||
@ -878,19 +904,26 @@ impl SceneData {
|
||||
}
|
||||
let fs_vis_camera_changed = fullscreen_vis_camera.update(scene);
|
||||
if fs_vis_camera_changed {
|
||||
was_dirty = true;
|
||||
self.dom.layers.fullscreen_vis.update_view_projection(&fullscreen_vis_camera);
|
||||
self.dom.layers.welcome_screen.update_view_projection(&welcome_screen_camera);
|
||||
}
|
||||
let node_searcher_camera = self.layers.node_searcher.camera();
|
||||
let node_searcher_camera_changed = node_searcher_camera.update(scene);
|
||||
if node_searcher_camera_changed {
|
||||
was_dirty = true;
|
||||
self.dom.layers.node_searcher.update_view_projection(&node_searcher_camera);
|
||||
}
|
||||
|
||||
// Updating all other cameras (the main camera was already updated, so it will be skipped).
|
||||
let sublayer_was_dirty = Rc::new(Cell::new(false));
|
||||
self.layers.iter_sublayers_and_masks_nested(|layer| {
|
||||
layer.camera().update(scene);
|
||||
let dirty = layer.camera().update(scene);
|
||||
sublayer_was_dirty.set(sublayer_was_dirty.get() || dirty);
|
||||
});
|
||||
was_dirty = was_dirty || sublayer_was_dirty.get();
|
||||
|
||||
was_dirty
|
||||
}
|
||||
|
||||
/// Resize the underlying canvas. This function should rather not be called
|
||||
@ -911,8 +944,8 @@ impl SceneData {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn render(&self) {
|
||||
self.renderer.run();
|
||||
pub fn render(&self, update_status: UpdateStatus) {
|
||||
self.renderer.run(update_status);
|
||||
// WebGL `flush` should be called when expecting results such as queries, or at completion
|
||||
// of a rendering frame. Flush tells the implementation to push all pending commands out
|
||||
// for execution, flushing them out of the queue, instead of waiting for more commands to
|
||||
@ -963,6 +996,7 @@ impl SceneData {
|
||||
let network = &self.frp.network;
|
||||
let shapes = &self.shapes;
|
||||
let target = &self.mouse.target;
|
||||
let pointer_position_changed = &self.pointer_position_changed;
|
||||
let pressed: Rc<RefCell<HashMap<mouse::Button, PointerTargetId>>> = default();
|
||||
|
||||
frp::extend! { network
|
||||
@ -971,6 +1005,7 @@ impl SceneData {
|
||||
pressed.borrow_mut().insert(*button,current_target);
|
||||
shapes.with_mouse_target(current_target, |t| t.mouse_down.emit(button));
|
||||
});
|
||||
|
||||
eval self.mouse.frp.up ([shapes,target,pressed](button) {
|
||||
let current_target = target.get();
|
||||
if let Some(last_target) = pressed.borrow_mut().remove(button) {
|
||||
@ -978,6 +1013,8 @@ impl SceneData {
|
||||
}
|
||||
shapes.with_mouse_target(current_target, |t| t.mouse_up.emit(button));
|
||||
});
|
||||
|
||||
eval_ self.mouse.frp.position (pointer_position_changed.set(true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1087,20 +1124,27 @@ impl Deref for Scene {
|
||||
|
||||
impl Scene {
|
||||
#[profile(Debug)]
|
||||
pub fn update(&self, time: animation::TimeInfo) {
|
||||
pub fn update(&self, time: animation::TimeInfo) -> UpdateStatus {
|
||||
if let Some(context) = &*self.context.borrow() {
|
||||
debug!(self.logger, "Updating.", || {
|
||||
let mut scene_was_dirty = false;
|
||||
self.frp.frame_time_source.emit(time.since_animation_loop_started.unchecked_raw());
|
||||
// Please note that `update_camera` is called first as it may trigger FRP events
|
||||
// which may change display objects layout.
|
||||
self.update_camera(self);
|
||||
scene_was_dirty = self.update_camera(self) || scene_was_dirty;
|
||||
self.display_object.update(self);
|
||||
self.layers.update();
|
||||
self.update_shape();
|
||||
self.update_symbols();
|
||||
scene_was_dirty = self.layers.update() || scene_was_dirty;
|
||||
scene_was_dirty = self.update_shape() || scene_was_dirty;
|
||||
scene_was_dirty = self.update_symbols() || scene_was_dirty;
|
||||
self.handle_mouse_over_and_out_events();
|
||||
context.shader_compiler.run(time);
|
||||
scene_was_dirty = context.shader_compiler.run(time) || scene_was_dirty;
|
||||
|
||||
let pointer_position_changed = self.pointer_position_changed.get();
|
||||
self.pointer_position_changed.set(false);
|
||||
UpdateStatus { scene_was_dirty, pointer_position_changed }
|
||||
})
|
||||
} else {
|
||||
default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -525,8 +525,9 @@ impl LayerModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume all dirty flags and update the ordering of elements if needed.
|
||||
pub fn update(&self) {
|
||||
/// Consume all dirty flags and update the ordering of elements if needed. Returns [`true`] if
|
||||
/// the layer or its sub-layers were modified during this call.
|
||||
pub fn update(&self) -> bool {
|
||||
self.update_internal(None)
|
||||
}
|
||||
|
||||
@ -534,23 +535,29 @@ impl LayerModel {
|
||||
pub(crate) fn update_internal(
|
||||
&self,
|
||||
global_element_depth_order: Option<&DependencyGraph<LayerItem>>,
|
||||
) {
|
||||
) -> bool {
|
||||
let mut was_dirty = false;
|
||||
|
||||
if self.depth_order_dirty.check() {
|
||||
was_dirty = true;
|
||||
self.depth_order_dirty.unset();
|
||||
self.depth_sort(global_element_depth_order);
|
||||
}
|
||||
|
||||
if self.sublayers.element_depth_order_dirty.check() {
|
||||
was_dirty = true;
|
||||
self.sublayers.element_depth_order_dirty.unset();
|
||||
for layer in self.sublayers() {
|
||||
layer.update_internal(Some(&*self.global_element_depth_order.borrow()))
|
||||
layer.update_internal(Some(&*self.global_element_depth_order.borrow()));
|
||||
}
|
||||
if let Some(layer) = &*self.mask.borrow() {
|
||||
if let Some(layer) = layer.upgrade() {
|
||||
layer.update_internal(Some(&*self.global_element_depth_order.borrow()))
|
||||
layer.update_internal(Some(&*self.global_element_depth_order.borrow()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
was_dirty
|
||||
}
|
||||
|
||||
/// Compute a combined [`DependencyGraph`] for the layer taking into consideration the global
|
||||
|
@ -299,9 +299,9 @@ impl WorldData {
|
||||
self.on.before_frame.run_all(time);
|
||||
self.uniforms.time.set(time.since_animation_loop_started.unchecked_raw());
|
||||
self.scene_dirty.unset_all();
|
||||
self.default_scene.update(time);
|
||||
let update_status = self.default_scene.update(time);
|
||||
self.garbage_collector.mouse_events_handled();
|
||||
self.default_scene.render();
|
||||
self.default_scene.render(update_status);
|
||||
self.on.after_frame.run_all(time);
|
||||
self.stats.end_frame();
|
||||
}
|
||||
|
@ -131,6 +131,20 @@ struct KhrProgram {
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Progress ===
|
||||
// ================
|
||||
|
||||
/// Shader compiler progress status.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Progress {
|
||||
StepProgress,
|
||||
NewShaderReady,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Compiler ===
|
||||
// ================
|
||||
@ -195,8 +209,9 @@ impl Compiler {
|
||||
self.rc.borrow_mut().submit(input, on_ready)
|
||||
}
|
||||
|
||||
/// Run the compiler. This should be run on every frame.
|
||||
pub fn run(&self, time: animation::TimeInfo) {
|
||||
/// Run the compiler. This should be run on every frame. Returns [`true`] if any new shaders
|
||||
/// finished the compilation process during this call.
|
||||
pub fn run(&self, time: animation::TimeInfo) -> bool {
|
||||
self.rc.borrow_mut().run(time)
|
||||
}
|
||||
}
|
||||
@ -226,14 +241,17 @@ impl CompilerData {
|
||||
}
|
||||
|
||||
#[profile(Debug)]
|
||||
fn run(&mut self, time: animation::TimeInfo) {
|
||||
fn run(&mut self, time: animation::TimeInfo) -> bool {
|
||||
let mut any_new_shaders_ready = false;
|
||||
if self.dirty {
|
||||
self.run_khr_completion_check_jobs();
|
||||
while self.dirty {
|
||||
match self.run_step() {
|
||||
Ok(made_progress) => {
|
||||
if !made_progress {
|
||||
break;
|
||||
Ok(progress) => {
|
||||
match progress {
|
||||
None => break,
|
||||
Some(Progress::NewShaderReady) => any_new_shaders_ready = true,
|
||||
Some(Progress::StepProgress) => {}
|
||||
}
|
||||
let now = (self.performance.now() as f32).ms();
|
||||
let current_frame_time =
|
||||
@ -256,21 +274,25 @@ impl CompilerData {
|
||||
}
|
||||
}
|
||||
}
|
||||
any_new_shaders_ready
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[profile(Detail)]
|
||||
fn run_step(&mut self) -> Result<bool, Error> {
|
||||
let ok_progress = |_| Ok(true);
|
||||
let no_progress = Ok(false);
|
||||
fn run_step(&mut self) -> Result<Option<Progress>, Error> {
|
||||
let ok_progress = |_| Ok(Some(Progress::StepProgress));
|
||||
let no_progress = Ok(None);
|
||||
let jobs = &self.jobs;
|
||||
let max_jobs = self.current_parallel_job_count() >= MAX_PARALLEL_COMPILE_JOBS;
|
||||
match () {
|
||||
_ if !max_jobs && !jobs.compile.is_empty() => ok_progress(self.run_next_compile_job()?),
|
||||
_ if !jobs.link.is_empty() => ok_progress(self.run_next_link_job()?),
|
||||
_ if !jobs.link_check.is_empty() => ok_progress(self.run_next_link_check_job()?),
|
||||
_ if !jobs.link_check.is_empty() => {
|
||||
self.run_next_link_check_job()?;
|
||||
Ok(Some(Progress::NewShaderReady))
|
||||
}
|
||||
_ => {
|
||||
if max_jobs {
|
||||
if !jobs.compile.is_empty() {
|
||||
|
Loading…
Reference in New Issue
Block a user