mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Improving Performance Monitor (#5895)
This commit is contained in:
parent
c9806496ee
commit
abb0b447d5
@ -172,11 +172,16 @@
|
||||
generated shaders differ per theme (only light theme is available, the dark
|
||||
theme has been disabled). We will support multiple themes in the future, but
|
||||
this is not on our priority list right now.
|
||||
- [Performance monitor was extended with the ability to print details of actions
|
||||
performed in a given frame][5895]. In particular, you can now inspect names of
|
||||
all symbols rendered in a given frame. You can also pause the performance
|
||||
monitor and inspect results recorded in the past.
|
||||
|
||||
[3857]: https://github.com/enso-org/enso/pull/3857
|
||||
[3985]: https://github.com/enso-org/enso/pull/3985
|
||||
[4047]: https://github.com/enso-org/enso/pull/4047
|
||||
[4003]: https://github.com/enso-org/enso/pull/4003
|
||||
[5895]: https://github.com/enso-org/enso/pull/5895
|
||||
|
||||
#### Enso Standard Library
|
||||
|
||||
|
@ -43,8 +43,6 @@ pub enum Metadata {
|
||||
RpcRequest(json_rpc::log::RpcRequest),
|
||||
/// A message between the Language Server and the Engine.
|
||||
BackendMessage(backend::Message),
|
||||
/// Performance stats gathered from the EnsoGL rendering engine.
|
||||
RenderStats(ensogl_core::debug::StatsData),
|
||||
/// Any other metadata type.
|
||||
///
|
||||
/// The types defined above are handled specially by `enso-profiler-enso-data` tools: E.g. the
|
||||
@ -63,7 +61,6 @@ impl Display for Metadata {
|
||||
Metadata::RpcEvent(name) => f.collect_str(name),
|
||||
Metadata::RpcRequest(method) => f.collect_str(&method.to_string()),
|
||||
Metadata::BackendMessage(backend::Message { endpoint, .. }) => f.collect_str(endpoint),
|
||||
Metadata::RenderStats(stats) => f.collect_str(&format!("{stats:#?}")),
|
||||
Metadata::Other => f.collect_str("<value>"),
|
||||
}
|
||||
}
|
||||
|
@ -370,17 +370,9 @@ impl View {
|
||||
|
||||
frp.source.is_hovered <+ model.overlay.events.mouse_over.constant(true);
|
||||
frp.source.is_hovered <+ model.overlay.events.mouse_out.constant(false);
|
||||
let mouse_up = scene.mouse.frp.up.clone_ref();
|
||||
let mouse_down = scene.mouse.frp.down.clone_ref();
|
||||
let mouse_wheel = scene.mouse.frp.wheel.clone_ref();
|
||||
let mouse_position = scene.mouse.frp.position.clone_ref();
|
||||
caught_mouse <- any_(mouse_up,mouse_down,mouse_wheel,mouse_position);
|
||||
pass_to_dom <- caught_mouse.gate(&frp.source.is_hovered);
|
||||
eval_ pass_to_dom(scene.current_js_event.pass_to_dom.emit(()));
|
||||
}
|
||||
init.emit(());
|
||||
style.init.emit(());
|
||||
visualization.pass_events_to_dom_if_active(scene, network);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,6 @@ class Histogram extends Visualization {
|
||||
.attr('width', canvas.inner.width)
|
||||
.attr('height', canvas.inner.height)
|
||||
.style('fill', 'none')
|
||||
.style('pointer-events', 'all')
|
||||
.call(zoom)
|
||||
|
||||
const self = this
|
||||
@ -396,6 +395,18 @@ class Histogram extends Visualization {
|
||||
return { zoomElem, zoom, transformedScale }
|
||||
}
|
||||
|
||||
/** Removing `pointer-events` handling from brush element, as we want it to be inherited. D3 inserts
|
||||
* `pointer-events: all` in the brush element and some of its children on brush creation and after brushing ends.
|
||||
* There is no documentation on that topic as far as we are aware, so this was observed and tested manually. */
|
||||
removePointerEventsAttrsFromBrush(brushElem) {
|
||||
brushElem.attr('pointer-events', null)
|
||||
brushElem.select(function () {
|
||||
for (const child of this.childNodes) {
|
||||
child.removeAttribute('pointer-events')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise brushing functionality on the visualization.
|
||||
*
|
||||
@ -417,7 +428,7 @@ class Histogram extends Visualization {
|
||||
// The brush element must be child of zoom element - this is only way we found to have both
|
||||
// zoom and brush events working at the same time. See https://stackoverflow.com/a/59757276 .
|
||||
const brushElem = zoom.zoomElem.append('g').attr('class', brushClass).call(brush)
|
||||
|
||||
this.removePointerEventsAttrsFromBrush(brushElem)
|
||||
const self = this
|
||||
|
||||
/**
|
||||
@ -463,6 +474,7 @@ class Histogram extends Visualization {
|
||||
selectedZoomBtn.style.display = 'none'
|
||||
selectedZoomBtn.removeEventListener('click', zoomIn, true)
|
||||
document.removeEventListener('keydown', zoomInKeyEvent, true)
|
||||
this.removePointerEventsAttrsFromBrush(brushElem)
|
||||
}
|
||||
|
||||
let endEvents = ['click', 'auxclick', 'contextmenu', 'scroll']
|
||||
|
@ -234,7 +234,6 @@ class ScatterPlot extends Visualization {
|
||||
.attr('width', boxWidth)
|
||||
.attr('height', boxHeight)
|
||||
.style('fill', 'none')
|
||||
.style('pointer-events', 'all')
|
||||
.call(zoom)
|
||||
|
||||
let transformedScale = Object.assign({}, scaleAndAxis)
|
||||
@ -355,6 +354,18 @@ class ScatterPlot extends Visualization {
|
||||
return { zoomElem, zoom, transformedScale }
|
||||
}
|
||||
|
||||
/** Removing `pointer-events` handling from brush element, as we want it to be inherited. D3 inserts
|
||||
* `pointer-events: all` in the brush element and some of its children on brush creation and after brushing ends.
|
||||
* There is no documentation on that topic as far as we are aware, so this was observed and tested manually. */
|
||||
removePointerEventsAttrsFromBrush(brushElem) {
|
||||
brushElem.attr('pointer-events', null)
|
||||
brushElem.select(function () {
|
||||
for (const child of this.childNodes) {
|
||||
child.removeAttribute('pointer-events')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds brushing functionality to the plot.
|
||||
*
|
||||
@ -377,6 +388,7 @@ class ScatterPlot extends Visualization {
|
||||
// events working at the same time. See https://stackoverflow.com/a/59757276 .
|
||||
let brushElem = zoom.zoomElem.append('g').attr('class', brushClass).call(brush)
|
||||
|
||||
this.removePointerEventsAttrsFromBrush(brushElem)
|
||||
let self = this
|
||||
|
||||
/**
|
||||
@ -426,6 +438,7 @@ class ScatterPlot extends Visualization {
|
||||
selectedZoomBtn.style.display = 'none'
|
||||
selectedZoomBtn.removeEventListener('click', zoomIn, true)
|
||||
document.removeEventListener('keydown', zoomInKeyEvent, true)
|
||||
this.removePointerEventsAttrsFromBrush(brushElem)
|
||||
}
|
||||
|
||||
let endEvents = ['click', 'auxclick', 'contextmenu', 'scroll']
|
||||
|
@ -115,7 +115,6 @@ impl InstanceModel {
|
||||
let bg_blue = bg_color.blue * 255.0;
|
||||
let bg_hex = format!("rgba({},{},{},{})", bg_red, bg_green, bg_blue, bg_color.alpha);
|
||||
root_node.dom().set_style_or_warn("background", bg_hex);
|
||||
|
||||
Ok(root_node)
|
||||
}
|
||||
|
||||
@ -247,6 +246,11 @@ impl InstanceModel {
|
||||
fn set_layer(&self, layer: Layer) {
|
||||
layer.apply_for_html_component(&self.scene, &self.root_node)
|
||||
}
|
||||
|
||||
fn set_active(&self, active: bool) {
|
||||
let attribute = if active { "all" } else { "none " };
|
||||
self.root_node.set_style_or_warn("pointer-events", attribute);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -273,10 +277,11 @@ impl Instance {
|
||||
let frp = visualization::instance::Frp::new(&network);
|
||||
let model = InstanceModel::from_class(class, scene)?;
|
||||
model.set_dom_layer(&scene.dom.layers.back);
|
||||
Ok(Instance { model, frp, network }.init_frp(scene).init_preprocessor_change_callback())
|
||||
model.set_active(false);
|
||||
Ok(Instance { model, frp, network }.init_frp().init_preprocessor_change_callback())
|
||||
}
|
||||
|
||||
fn init_frp(self, scene: &Scene) -> Self {
|
||||
fn init_frp(self) -> Self {
|
||||
let network = &self.network;
|
||||
let model = self.model.clone_ref();
|
||||
let frp = self.frp.clone_ref();
|
||||
@ -288,8 +293,8 @@ impl Instance {
|
||||
}
|
||||
});
|
||||
eval frp.set_layer ((layer) model.set_layer(*layer));
|
||||
eval frp.is_active ((is_active) model.set_active(*is_active));
|
||||
}
|
||||
frp.pass_events_to_dom_if_active(scene, network);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ use crate::data::enso;
|
||||
use enso_frp as frp;
|
||||
use ensogl::display;
|
||||
use ensogl::display::DomSymbol;
|
||||
use ensogl::display::Scene;
|
||||
|
||||
|
||||
|
||||
@ -179,7 +178,7 @@ impl Frp {
|
||||
def preprocessor_change = any_mut();
|
||||
on_preprocessor_change <- preprocessor_change.sampler();
|
||||
def data_receive_error = source();
|
||||
is_active <- bool(&inputs.deactivate,&inputs.activate);
|
||||
is_active <- bool(&inputs.deactivate, &inputs.activate);
|
||||
};
|
||||
preprocessor_change.emit(PreprocessorConfiguration::default());
|
||||
let on_data_receive_error = data_receive_error.clone_ref().into();
|
||||
@ -192,26 +191,6 @@ impl Frp {
|
||||
preprocessor_change,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the FRP network with mechanism of passing all mouse and keyboard event to DOM when
|
||||
/// visualization is active.
|
||||
///
|
||||
/// Used mainly in visualizations based on DOM elements (e.g. JavaScript visualization).
|
||||
pub fn pass_events_to_dom_if_active(&self, scene: &Scene, network: &frp::Network) {
|
||||
frp::extend! { network
|
||||
let mouse_up = scene.mouse.frp.up.clone_ref();
|
||||
let mouse_down = scene.mouse.frp.down.clone_ref();
|
||||
let mouse_wheel = scene.mouse.frp.wheel.clone_ref();
|
||||
let mouse_position = scene.mouse.frp.position.clone_ref();
|
||||
let keyboard_up = scene.keyboard.frp.up.clone_ref();
|
||||
let keyboard_down = scene.keyboard.frp.down.clone_ref();
|
||||
caught_mouse <- any_(mouse_up,mouse_down,mouse_wheel,mouse_position);
|
||||
caught_keyboard <- any_(keyboard_up,keyboard_down);
|
||||
caught_event <- any(caught_mouse,caught_keyboard);
|
||||
should_process <- caught_event.gate(&self.is_active);
|
||||
eval_ should_process (scene.current_js_event.pass_to_dom.emit(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,6 +80,7 @@ body {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
#root {
|
||||
@ -93,9 +94,6 @@ body {
|
||||
.visualization {
|
||||
z-index: 2;
|
||||
border-radius: 14px;
|
||||
/* visualizations may be put into the fullscreen-vis layer which has pointer-events set to none, so we need to
|
||||
override it */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
|
@ -284,6 +284,8 @@ impl IsTarget for Wasm {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Clone, Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct WatchInput {
|
||||
@ -343,11 +345,18 @@ impl IsWatchable for Wasm {
|
||||
|
||||
let mut watch_cmd = Cargo.cmd()?;
|
||||
|
||||
let (watch_cmd_name, mut watch_cmd_opts) = match std::env::var("USE_CARGO_WATCH_PLUS") {
|
||||
Ok(_) => ("watch-plus", vec!["--why"]),
|
||||
Err(_) => ("watch", vec![]),
|
||||
};
|
||||
watch_cmd_opts.push("--ignore");
|
||||
watch_cmd_opts.push("README.md");
|
||||
|
||||
watch_cmd
|
||||
.kill_on_drop(true)
|
||||
.current_dir(&context.repo_root)
|
||||
.arg("watch")
|
||||
.args(["--ignore", "README.md"])
|
||||
.arg(watch_cmd_name)
|
||||
.args(watch_cmd_opts)
|
||||
.args(cargo_watch_flags)
|
||||
.arg("--");
|
||||
|
||||
|
@ -131,7 +131,7 @@ The following operating systems are supported for developing Enso:
|
||||
- macOS 10.14 and above
|
||||
- Linux 4.4 and above
|
||||
|
||||
Currently we support `x86_64` (all mentioned OS) and `arm64` (Mac only)
|
||||
Currently, we support `x86_64` (all mentioned OS) and `arm64` (Mac only)
|
||||
architectures. You may be able to develop Enso on other systems, but issues
|
||||
arising from unsupported configurations will not be fixed by the core team.
|
||||
|
||||
@ -228,6 +228,15 @@ enso$ cargo +stable install cargo-watch # To enable `./run wasm watch` utility
|
||||
The previous three steps shall be enough to build the IDE via
|
||||
`./run wasm build run wasm build --wasm-profile dev`.
|
||||
|
||||
### Using Cargo Watch Plus
|
||||
|
||||
Currently, `cargo-watch` has
|
||||
[many issues](https://github.com/enso-org/cargo-watch-plus), including not
|
||||
working on modern macOS properly. Thus, we've developed a replacement, the
|
||||
[Cargo Watch Plus](https://github.com/enso-org/cargo-watch-plus). To use it,
|
||||
simply export the `USE_CARGO_WATCH_PLUS=1` in your shell and the build system
|
||||
will pick it up instead of the `cargo-watch`.
|
||||
|
||||
### Getting Set Up (JVM)
|
||||
|
||||
In order to properly build the `runtime` component, the JVM running SBT needs to
|
||||
|
@ -12,9 +12,9 @@ use ensogl_core::system::web::Map;
|
||||
// =================
|
||||
|
||||
/// Path within the asset directory to store the vertex shader.
|
||||
const VERTEX_FILE: &'static str = "vertex.glsl";
|
||||
const VERTEX_FILE: &str = "vertex.glsl";
|
||||
/// Path within the asset directory to store the fragment shader.
|
||||
const FRAGMENT_FILE: &'static str = "fragment.glsl";
|
||||
const FRAGMENT_FILE: &str = "fragment.glsl";
|
||||
|
||||
|
||||
|
||||
|
@ -105,13 +105,13 @@ impl Application {
|
||||
let data = &self.inner;
|
||||
let network = self.frp.network();
|
||||
enso_frp::extend! { network
|
||||
eval self.display.default_scene.frp.focused ((t) data.show_system_cursor(!t));
|
||||
frp.private.output.tooltip <+ frp.private.input.set_tooltip;
|
||||
eval_ frp.private.input.show_system_cursor(data.show_system_cursor(true));
|
||||
eval_ frp.private.input.hide_system_cursor(data.show_system_cursor(false));
|
||||
}
|
||||
// We hide the system cursor to replace it with the EnsoGL-provided one.
|
||||
self.frp.hide_system_cursor();
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,13 @@ pub struct MouseManager {
|
||||
pub type MouseEventJsClosure = Closure<dyn FnMut(JsValue)>;
|
||||
|
||||
macro_rules! define_bindings {
|
||||
( $( $js_event:ident :: $js_name:ident => $name:ident ($target:ident) ),* $(,)? ) => {
|
||||
(
|
||||
$target:ident,
|
||||
$global_target:ident,
|
||||
$( $js_event:ident :: $js_name:ident =>
|
||||
$name:ident ($event_target:ident, $event:ident)
|
||||
),* $(,)?
|
||||
) => {
|
||||
|
||||
/// Keeps references to JavaScript closures in order to keep them alive.
|
||||
#[derive(Debug)]
|
||||
@ -53,22 +59,30 @@ macro_rules! define_bindings {
|
||||
#[derive(Clone,CloneRef,Debug,Default)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct MouseManagerDispatchers {
|
||||
$(pub $name : callback::registry::Ref1<$target>),*
|
||||
$(pub $name : callback::registry::Ref1<$event>),*
|
||||
}
|
||||
|
||||
impl MouseManager {
|
||||
/// Constructor.
|
||||
pub fn new(dom:&web::dom::WithKnownShape<web::EventTarget>) -> Self {
|
||||
Self::new_separated(dom,dom.deref())
|
||||
}
|
||||
|
||||
/// Constructor which takes the exact element to set listener as a separate argument.
|
||||
/// This is the constructor for mouse listeners which takes three arguments:
|
||||
///
|
||||
/// Sometimes we want to listen for mouse event for element without ResizeObserver.
|
||||
/// Thus, some html element may be passed as a size provider, and another one where we
|
||||
/// attach listeners (for example `body` and `window` respectively).
|
||||
pub fn new_separated
|
||||
(dom:&web::dom::WithKnownShape<web::EventTarget>,target:&web::EventTarget) -> Self {
|
||||
/// 1. A DOM object to set resize observer on. This object should cover the entire screen.
|
||||
/// Since EnsoGL's scene origin is positioned in the left-bottom corner, the size of
|
||||
/// the DOM object is used to translate mouse coordinates from HTML to the EnsoGL space.
|
||||
///
|
||||
/// 2. A DOM object to set the 'mousedown', 'mousewheel', and 'mouseleave' listeners on.
|
||||
/// In most cases, this should be the canvas used by EnsoGL. Alternatively, you can set
|
||||
/// this argument to the window object if you want EnsoGL to capture all events, even if
|
||||
/// it is placed behind another DOM element.
|
||||
///
|
||||
/// 3. A DOM object to set the 'mouseup' and 'mousemove' listeners on. In most cases,
|
||||
/// this should be the window object. It is common for the element drag action to be
|
||||
/// initiated by a 'mousedown' event on one element and finished by a 'mouseup' event
|
||||
/// on another element. Handling these events globally covers such situations.
|
||||
pub fn new(
|
||||
dom: &web::dom::WithKnownShape<web::EventTarget>,
|
||||
$target: &web::EventTarget,
|
||||
$global_target: &web::EventTarget,
|
||||
) -> Self {
|
||||
let dispatchers = MouseManagerDispatchers::default();
|
||||
let dom = dom.clone();
|
||||
$(
|
||||
@ -81,11 +95,12 @@ macro_rules! define_bindings {
|
||||
);
|
||||
let shape = shape.value();
|
||||
let event = event.unchecked_into::<web::$js_event>();
|
||||
dispatcher.run_all(&event::$target::new(event,shape))
|
||||
dispatcher.run_all(&event::$event::new(event,shape))
|
||||
});
|
||||
let js_name = stringify!($js_name);
|
||||
let opt = event_listener_options();
|
||||
let $name = web::add_event_listener_with_options(&target,js_name,closure,&opt);
|
||||
let $name = web::add_event_listener_with_options
|
||||
(&$event_target, js_name, closure, &opt);
|
||||
)*
|
||||
let handles = Rc::new(MouseManagerEventListenerHandles {$($name),*});
|
||||
Self {dispatchers,handles,dom}
|
||||
@ -98,18 +113,20 @@ macro_rules! define_bindings {
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||
fn event_listener_options() -> web::AddEventListenerOptions {
|
||||
let mut options = web::AddEventListenerOptions::new();
|
||||
// We listen for events in capture phase, so we can decide ourself if it should be passed
|
||||
// further.
|
||||
options.capture(true);
|
||||
// We listen for events in the bubbling phase. If we ever would like to listen in the capture
|
||||
// phase, it would need to be set to "bubbling" for the "mouseleave" and "mouseenter" events,
|
||||
// as they provide incorrect events for the "capture" phase.
|
||||
options.capture(false);
|
||||
// We want to prevent default action on wheel events, thus listener cannot be passive.
|
||||
options.passive(false);
|
||||
options
|
||||
}
|
||||
|
||||
define_bindings! {
|
||||
MouseEvent::mousedown => on_down (OnDown),
|
||||
MouseEvent::mouseup => on_up (OnUp),
|
||||
MouseEvent::mousemove => on_move (OnMove),
|
||||
MouseEvent::mouseleave => on_leave (OnLeave),
|
||||
WheelEvent::wheel => on_wheel (OnWheel),
|
||||
define_bindings! { target, gloabl_target,
|
||||
MouseEvent::mousedown => on_down (target, OnDown),
|
||||
MouseEvent::mouseup => on_up (gloabl_target, OnUp),
|
||||
MouseEvent::mousemove => on_move (gloabl_target, OnMove),
|
||||
MouseEvent::mouseleave => on_leave (target, OnLeave),
|
||||
MouseEvent::mouseenter => on_enter (target, OnEnter),
|
||||
WheelEvent::wheel => on_wheel (target, OnWheel),
|
||||
}
|
||||
|
@ -98,5 +98,6 @@ define_events! {
|
||||
MouseEvent::OnUp,
|
||||
MouseEvent::OnMove,
|
||||
MouseEvent::OnLeave,
|
||||
MouseEvent::OnEnter,
|
||||
WheelEvent::OnWheel,
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
267
lib/rust/ensogl/core/src/debug/monitor/sampler.rs
Normal file
267
lib/rust/ensogl/core/src/debug/monitor/sampler.rs
Normal file
@ -0,0 +1,267 @@
|
||||
//! Definition of performance monitor sampler and a set of predefined samplers. Sampler is a utility
|
||||
//! capable of displaying a single performance metric, like the current FPS or number of draw calls
|
||||
//! per frame.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::debug::stats::StatsData;
|
||||
|
||||
use num_traits::cast::AsPrimitive;
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === ValueCheck ===
|
||||
// ==================
|
||||
|
||||
/// Values drawn in the monitor can be assigned with a check: `Correct`, `Warning`, and `Error`.
|
||||
/// It affects the way they are visually displayed.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ValueCheck {
|
||||
Correct,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Default for ValueCheck {
|
||||
fn default() -> Self {
|
||||
Self::Correct
|
||||
}
|
||||
}
|
||||
|
||||
// To be removed after this gets resolved: https://github.com/rust-lang/rust-clippy/issues/4971
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
impl ValueCheck {
|
||||
/// Construct the check by comparing the provided value to two threshold values.
|
||||
pub fn from_threshold(warn_threshold: f64, err_threshold: f64, value: f64) -> Self {
|
||||
if warn_threshold > err_threshold {
|
||||
if value >= warn_threshold {
|
||||
ValueCheck::Correct
|
||||
} else if value >= err_threshold {
|
||||
ValueCheck::Warning
|
||||
} else {
|
||||
ValueCheck::Error
|
||||
}
|
||||
} else {
|
||||
if value <= warn_threshold {
|
||||
ValueCheck::Correct
|
||||
} else if value <= err_threshold {
|
||||
ValueCheck::Warning
|
||||
} else {
|
||||
ValueCheck::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Sampler ===
|
||||
// ===============
|
||||
|
||||
/// Sampler is an utility to gather performance-related data and expose it in a way understandable
|
||||
/// by the performance monitor.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sampler {
|
||||
/// Label of the sampler to be displayed in the performance monitor window.
|
||||
pub label: &'static str,
|
||||
/// Get the newest value of the sampler. The value will be displayed in the monitor panel.
|
||||
pub expr: fn(&StatsData) -> f64,
|
||||
/// Get the details to be displayed in the details view.
|
||||
pub details: Option<fn(&StatsData) -> &[&'static str]>,
|
||||
/// If the value crosses this threshold, the graph will be drawn in the warning color.
|
||||
pub warn_threshold: f64,
|
||||
/// If the value crosses this threshold, the graph will be drawn in the error color.
|
||||
pub err_threshold: f64,
|
||||
/// The value will be divided by this number before being displayed.
|
||||
pub value_divisor: f64,
|
||||
/// The minimum expected value in order to set proper scaling of the monitor plots. If the real
|
||||
/// value will be smaller than this parameter, it will be clamped.
|
||||
pub min_value: Option<f64>,
|
||||
/// The maximum expected value in order to set proper scaling of the monitor plots. If the real
|
||||
/// value will be bigger than this parameter, it will be clamped.
|
||||
pub max_value: Option<f64>,
|
||||
/// The number of digits after the dot which should be displayed in the monitor panel.
|
||||
pub precision: usize,
|
||||
}
|
||||
|
||||
impl Debug for Sampler {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Sampler")
|
||||
}
|
||||
}
|
||||
|
||||
impl const Default for Sampler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
label: "Unlabeled",
|
||||
expr: |_| 0.0,
|
||||
details: None,
|
||||
warn_threshold: 0.0,
|
||||
err_threshold: 0.0,
|
||||
value_divisor: 1.0,
|
||||
min_value: None,
|
||||
max_value: None,
|
||||
precision: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler {
|
||||
/// The current sampler value.
|
||||
pub fn value(&self, stats: &StatsData) -> f64 {
|
||||
let raw_value: f64 = (self.expr)(stats).as_();
|
||||
raw_value / self.value_divisor
|
||||
}
|
||||
|
||||
/// Check the current value in order to draw it with warning or error if it exceeds the allowed
|
||||
/// thresholds.
|
||||
pub fn check(&self, stats: &StatsData) -> ValueCheck {
|
||||
let value = self.value(stats);
|
||||
ValueCheck::from_threshold(self.warn_threshold, self.err_threshold, value)
|
||||
}
|
||||
|
||||
/// Minimum size of the size the sampler should occupy in the performance monitor view.
|
||||
pub fn min_size(&self) -> Option<f64> {
|
||||
Some(self.warn_threshold)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Samplers ===
|
||||
// ================
|
||||
|
||||
const MB: f64 = (1024 * 1024) as f64;
|
||||
|
||||
const DEFAULT_SAMPLER: Sampler = Default::default();
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const FPS: Sampler = Sampler {
|
||||
label: "Frames per second",
|
||||
expr: |s| s.fps,
|
||||
warn_threshold: 55.0,
|
||||
err_threshold: 25.0,
|
||||
precision: 2,
|
||||
max_value: Some(60.0),
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const FRAME_TIME: Sampler = Sampler {
|
||||
label: "Frame time (ms)",
|
||||
expr: |s| s.frame_time,
|
||||
warn_threshold: 1000.0 / 55.0,
|
||||
err_threshold: 1000.0 / 25.0,
|
||||
precision: 2,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const WASM_MEMORY_USAGE: Sampler = Sampler {
|
||||
label: "WASM memory usage (Mb)",
|
||||
expr: |s| s.wasm_memory_usage as f64,
|
||||
warn_threshold: 50.0,
|
||||
err_threshold: 100.0,
|
||||
precision: 2,
|
||||
value_divisor: MB,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const GPU_MEMORY_USAGE: Sampler = Sampler {
|
||||
label: "GPU memory usage (Mb)",
|
||||
expr: |s| s.gpu_memory_usage as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
precision: 2,
|
||||
value_divisor: MB,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const DRAW_CALL_COUNT: Sampler = Sampler {
|
||||
label: "Draw call count",
|
||||
expr: |s| s.draw_calls.len() as f64,
|
||||
details: Some(|s| &s.draw_calls),
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const BUFFER_COUNT: Sampler = Sampler {
|
||||
label: "Buffer count",
|
||||
expr: |s| s.buffer_count as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const DATA_UPLOAD_COUNT: Sampler = Sampler {
|
||||
label: "Data upload count",
|
||||
expr: |s| s.data_upload_count as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const DATA_UPLOAD_SIZE: Sampler = Sampler {
|
||||
label: "Data upload size (Mb)",
|
||||
expr: |s| s.data_upload_size as f64,
|
||||
warn_threshold: 1.0,
|
||||
err_threshold: 10.0,
|
||||
precision: 2,
|
||||
value_divisor: MB,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SPRITE_SYSTEM_COUNT: Sampler = Sampler {
|
||||
label: "Sprite system count",
|
||||
expr: |s| s.sprite_system_count as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SYMBOL_COUNT: Sampler = Sampler {
|
||||
label: "Symbol count",
|
||||
expr: |s| s.symbol_count as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SPRITE_COUNT: Sampler = Sampler {
|
||||
label: "Sprite count",
|
||||
expr: |s| s.sprite_count as f64,
|
||||
warn_threshold: 100_000.0,
|
||||
err_threshold: 500_000.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SHADER_COUNT: Sampler = Sampler {
|
||||
label: "Shader count",
|
||||
expr: |s| s.shader_count as f64,
|
||||
warn_threshold: 100.0,
|
||||
err_threshold: 500.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SHADER_COMPILE_COUNT: Sampler = Sampler {
|
||||
label: "Shader compile count",
|
||||
expr: |s| s.shader_compile_count as f64,
|
||||
warn_threshold: 10.0,
|
||||
err_threshold: 100.0,
|
||||
..DEFAULT_SAMPLER
|
||||
};
|
@ -15,6 +15,9 @@
|
||||
|
||||
use enso_prelude::*;
|
||||
|
||||
use crate::display::world;
|
||||
use crate::display::SymbolId;
|
||||
|
||||
use enso_types::unit2::Duration;
|
||||
use enso_web::Performance;
|
||||
use enso_web::TimeProvider;
|
||||
@ -42,7 +45,7 @@ pub type Stats = StatsWithTimeProvider<Performance>;
|
||||
/// Contains all the gathered stats, and provides methods for modifying and retrieving their
|
||||
/// values.
|
||||
/// Uses [`T`] to access current time for calculating time-dependent stats (e.g. FPS).
|
||||
#[derive(Debug, CloneRef)]
|
||||
#[derive(Debug, Deref, CloneRef)]
|
||||
pub struct StatsWithTimeProvider<T> {
|
||||
rc: Rc<RefCell<FramedStatsData<T>>>,
|
||||
}
|
||||
@ -61,16 +64,17 @@ impl<T: TimeProvider> StatsWithTimeProvider<T> {
|
||||
Self { rc }
|
||||
}
|
||||
|
||||
/// Starts tracking data for a new animation frame.
|
||||
/// Also, calculates the [`fps`] stat.
|
||||
/// Returns a snapshot of statistics data for the previous frame.
|
||||
///
|
||||
/// Note: the code works under an assumption that [`begin_frame()`] and [`end_frame()`] are
|
||||
/// called, respectively, at the beginning and end of every frame. The very first time
|
||||
/// [`begin_frame()`] is called, it returns `None`, because it does not have complete
|
||||
/// statistics data for the preceding frame.
|
||||
pub fn begin_frame(&self, time: Duration) -> Option<StatsData> {
|
||||
self.rc.borrow_mut().begin_frame(time)
|
||||
/// Calculate FPS for the last frame. This function should be called on the very beginning of
|
||||
/// every frame. Please note, that it does not clean the per-frame statistics. You want to run
|
||||
/// the [`reset_per_frame_statistics`] function before running rendering operations.
|
||||
pub fn calculate_prev_frame_fps(&self, time: Duration) {
|
||||
self.rc.borrow_mut().calculate_prev_frame_fps(time)
|
||||
}
|
||||
|
||||
/// Clean the per-frame statistics, such as the per-frame number of draw calls. This function
|
||||
/// should be called before any rendering calls were made.
|
||||
pub fn reset_per_frame_statistics(&self) {
|
||||
self.rc.borrow_mut().reset_per_frame_statistics()
|
||||
}
|
||||
|
||||
/// Ends tracking data for the current animation frame.
|
||||
@ -78,6 +82,14 @@ impl<T: TimeProvider> StatsWithTimeProvider<T> {
|
||||
pub fn end_frame(&self) {
|
||||
self.rc.borrow_mut().end_frame();
|
||||
}
|
||||
|
||||
/// Register a new draw call for the given symbol.
|
||||
pub fn register_draw_call(&self, symbol_id: SymbolId) {
|
||||
let label = world::with_context(|ctx| {
|
||||
ctx.get_symbol(symbol_id).map(|t| t.label).unwrap_or("Unknown")
|
||||
});
|
||||
self.rc.borrow_mut().stats_data.register_draw_call(label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -86,10 +98,12 @@ impl<T: TimeProvider> StatsWithTimeProvider<T> {
|
||||
// === FramedStatsData ===
|
||||
// =======================
|
||||
|
||||
/// Internal representation of [`StatsWithTimeProvider`].
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug)]
|
||||
struct FramedStatsData<T> {
|
||||
pub struct FramedStatsData<T> {
|
||||
time_provider: T,
|
||||
stats_data: StatsData,
|
||||
pub stats_data: StatsData,
|
||||
frame_begin_time: Option<f64>,
|
||||
}
|
||||
|
||||
@ -101,19 +115,14 @@ impl<T: TimeProvider> FramedStatsData<T> {
|
||||
Self { time_provider, stats_data, frame_begin_time }
|
||||
}
|
||||
|
||||
/// Starts tracking data for a new animation frame. The time is provided explicitly from the
|
||||
/// JS `requestAnimationFrame` callback in order to be sure that we measure all the time,
|
||||
/// including operations happening before calling this function.
|
||||
fn begin_frame(&mut self, time: Duration) -> Option<StatsData> {
|
||||
/// Calculate FPS for the last frame. This function should be called on the very beginning of
|
||||
/// every frame. Please note, that it does not clean the per-frame statistics. You want to run
|
||||
/// the [`reset_per_frame_statistics`] function before running rendering operations.
|
||||
fn calculate_prev_frame_fps(&mut self, time: Duration) {
|
||||
let time = time.unchecked_raw() as f64;
|
||||
let mut previous_frame_stats = self.stats_data;
|
||||
self.reset_per_frame_statistics();
|
||||
let previous_frame_begin_time = self.frame_begin_time.replace(time);
|
||||
previous_frame_begin_time.map(|begin_time| {
|
||||
let end_time = time;
|
||||
previous_frame_stats.fps = 1000.0 / (end_time - begin_time);
|
||||
previous_frame_stats
|
||||
})
|
||||
if let Some(previous_frame_begin_time) = self.frame_begin_time.replace(time) {
|
||||
self.stats_data.fps = 1000.0 / (time - previous_frame_begin_time);
|
||||
}
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
@ -131,8 +140,10 @@ impl<T: TimeProvider> FramedStatsData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean the per-frame statistics, such as the per-frame number of draw calls. This function
|
||||
/// should be called before any rendering calls were made.
|
||||
fn reset_per_frame_statistics(&mut self) {
|
||||
self.stats_data.draw_call_count = 0;
|
||||
self.stats_data.draw_calls = default();
|
||||
self.stats_data.shader_compile_count = 0;
|
||||
self.stats_data.data_upload_count = 0;
|
||||
self.stats_data.data_upload_size = 0;
|
||||
@ -150,7 +161,7 @@ impl<T: TimeProvider> FramedStatsData<T> {
|
||||
macro_rules! emit_if_integer {
|
||||
(u32, $($block:tt)*) => ($($block)*);
|
||||
(usize, $($block:tt)*) => ($($block)*);
|
||||
(f64, $($block:tt)*) => ();
|
||||
($other:ty, $($block:tt)*) => ();
|
||||
}
|
||||
|
||||
/// Emits the StatsData struct, and extends StatsWithTimeProvider with accessors to StatsData
|
||||
@ -162,7 +173,7 @@ macro_rules! gen_stats {
|
||||
// === StatsData ===
|
||||
|
||||
/// Raw data of all the gathered stats.
|
||||
#[derive(Debug,Default,Clone,Copy,serde::Serialize,serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct StatsData {
|
||||
$(pub $field : $field_type),*
|
||||
@ -174,7 +185,7 @@ macro_rules! gen_stats {
|
||||
impl<T: TimeProvider> StatsWithTimeProvider<T> { $(
|
||||
/// Field getter.
|
||||
pub fn $field(&self) -> $field_type {
|
||||
self.rc.borrow().stats_data.$field
|
||||
self.rc.borrow().stats_data.$field.clone()
|
||||
}
|
||||
|
||||
/// Field setter.
|
||||
@ -189,7 +200,6 @@ macro_rules! gen_stats {
|
||||
self.[<set _ $field>](value);
|
||||
}
|
||||
|
||||
// FIXME: saturating_add is proper solution, but even without it it should not crash, but it does. To be investigated.
|
||||
emit_if_integer!($field_type,
|
||||
/// Increments field's value.
|
||||
pub fn [<inc _ $field>](&self) {
|
||||
@ -211,7 +221,7 @@ gen_stats! {
|
||||
fps : f64,
|
||||
wasm_memory_usage : u32,
|
||||
gpu_memory_usage : u32,
|
||||
draw_call_count : usize,
|
||||
draw_calls : Vec<&'static str>,
|
||||
buffer_count : usize,
|
||||
data_upload_count : usize,
|
||||
data_upload_size : u32,
|
||||
@ -223,6 +233,13 @@ gen_stats! {
|
||||
shader_compile_count : usize,
|
||||
}
|
||||
|
||||
impl StatsData {
|
||||
/// Register a new draw call for the given symbol.
|
||||
pub fn register_draw_call(&mut self, symbol_name: &'static str) {
|
||||
self.draw_calls.push(symbol_name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps the body if the `statistics` compilation flag was enabled.
|
||||
#[macro_export]
|
||||
macro_rules! if_compiled_with_stats {
|
||||
|
@ -238,9 +238,6 @@ impl NavigatorEvents {
|
||||
let data = Rc::downgrade(&self.data);
|
||||
let listener = self.mouse_manager.on_wheel.add(move |event: &mouse::OnWheel| {
|
||||
if let Some(data) = data.upgrade() {
|
||||
if data.is_navigator_enabled() {
|
||||
event.prevent_default();
|
||||
}
|
||||
if event.ctrl_key() {
|
||||
// Prevent zoom event to be handed to the browser. This avoids browser scaling
|
||||
// being applied to the whole IDE, thus we need to do this always when ctrl is
|
||||
@ -314,10 +311,6 @@ impl NavigatorEvents {
|
||||
let data = Rc::downgrade(&self.data);
|
||||
let listener = self.mouse_manager.on_move.add(move |event: &mouse::OnMove| {
|
||||
if let Some(data) = data.upgrade() {
|
||||
if data.is_navigator_enabled() {
|
||||
event.prevent_default();
|
||||
}
|
||||
|
||||
let position = event.position_relative_to_event_handler();
|
||||
data.set_mouse_position(position);
|
||||
let movement = data.mouse_position() - data.last_mouse_position();
|
||||
|
@ -30,7 +30,7 @@ use crate::system::web;
|
||||
use crate::system::web::EventListenerHandle;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_frp::io::js::CurrentJsEvent;
|
||||
use enso_frp::io::js::JsEvent;
|
||||
use enso_shapely::shared;
|
||||
use std::any::TypeId;
|
||||
use web::HtmlElement;
|
||||
@ -126,7 +126,7 @@ pub struct Mouse {
|
||||
pub click_count: Uniform<i32>,
|
||||
pub hover_rgba: Uniform<Vector4<u32>>,
|
||||
pub target: Rc<Cell<PointerTargetId>>,
|
||||
pub handles: Rc<[callback::Handle; 4]>,
|
||||
pub handles: Rc<[callback::Handle; 6]>,
|
||||
pub frp: enso_frp::io::Mouse,
|
||||
pub scene_frp: Frp,
|
||||
}
|
||||
@ -136,37 +136,41 @@ impl Mouse {
|
||||
scene_frp: &Frp,
|
||||
root: &web::dom::WithKnownShape<web::HtmlDivElement>,
|
||||
variables: &UniformScope,
|
||||
current_js_event: &CurrentJsEvent,
|
||||
js_event: &JsEvent,
|
||||
display_mode: &Rc<Cell<glsl::codes::DisplayModes>>,
|
||||
) -> Self {
|
||||
let scene_frp = scene_frp.clone_ref();
|
||||
let target = PointerTargetId::default();
|
||||
let last_position = Rc::new(Cell::new(Vector2::new(0, 0)));
|
||||
let position = variables.add_or_panic("mouse_position", Vector2(0, 0));
|
||||
let last_position = Rc::new(Cell::new(Vector2::default()));
|
||||
let position = variables.add_or_panic("mouse_position", Vector2::default());
|
||||
let click_count = variables.add_or_panic("mouse_click_count", 0);
|
||||
let hover_rgba = variables.add_or_panic("mouse_hover_ids", Vector4(0, 0, 0, 0));
|
||||
let hover_rgba = variables.add_or_panic("mouse_hover_ids", Vector4::default());
|
||||
let target = Rc::new(Cell::new(target));
|
||||
let mouse_manager = MouseManager::new_separated(&root.clone_ref().into(), &web::window);
|
||||
let shaped_dom = root.clone_ref().into();
|
||||
let mouse_manager = MouseManager::new(&shaped_dom, root, &web::window);
|
||||
let frp = frp::io::Mouse::new();
|
||||
let on_move = mouse_manager.on_move.add(current_js_event.make_event_handler(
|
||||
let on_move = mouse_manager.on_move.add(js_event.handler(
|
||||
f!([frp, scene_frp, position, last_position] (event: &mouse::OnMove) {
|
||||
let shape = scene_frp.shape.value();
|
||||
let pixel_ratio = shape.pixel_ratio;
|
||||
let screen_x = event.client_x();
|
||||
let screen_y = event.client_y();
|
||||
|
||||
let new_pos = Vector2::new(screen_x,screen_y);
|
||||
let pos_changed = new_pos != last_position.get();
|
||||
if pos_changed {
|
||||
last_position.set(new_pos);
|
||||
let new_canvas_position = new_pos.map(|v| (v as f32 * pixel_ratio) as i32);
|
||||
position.set(new_canvas_position);
|
||||
let position = Vector2(new_pos.x as f32,new_pos.y as f32) - shape.center();
|
||||
let position_bottom_left = Vector2(new_pos.x as f32, new_pos.y as f32);
|
||||
let position_top_left = Vector2(new_pos.x as f32, shape.height - new_pos.y as f32);
|
||||
let position = position_bottom_left - shape.center();
|
||||
frp.position_bottom_left.emit(position_bottom_left);
|
||||
frp.position_top_left.emit(position_top_left);
|
||||
frp.position.emit(position);
|
||||
}
|
||||
}),
|
||||
));
|
||||
let on_down = mouse_manager.on_down.add(current_js_event.make_event_handler(
|
||||
let on_down = mouse_manager.on_down.add(js_event.handler(
|
||||
f!([frp, click_count, display_mode] (event:&mouse::OnDown) {
|
||||
click_count.modify(|v| *v += 1);
|
||||
if display_mode.get().allow_mouse_events() {
|
||||
@ -174,21 +178,27 @@ impl Mouse {
|
||||
}
|
||||
}),
|
||||
));
|
||||
let on_up = mouse_manager.on_up.add(current_js_event.make_event_handler(
|
||||
let on_up = mouse_manager.on_up.add(js_event.handler(
|
||||
f!([frp, display_mode] (event:&mouse::OnUp) {
|
||||
if display_mode.get().allow_mouse_events() {
|
||||
frp.up.emit(event.button())
|
||||
}
|
||||
}),
|
||||
));
|
||||
let on_wheel = mouse_manager.on_wheel.add(current_js_event.make_event_handler(
|
||||
f_!([frp, display_mode] {
|
||||
if display_mode.get().allow_mouse_events() {
|
||||
frp.wheel.emit(())
|
||||
}
|
||||
}),
|
||||
));
|
||||
let handles = Rc::new([on_move, on_down, on_up, on_wheel]);
|
||||
let on_wheel = mouse_manager.on_wheel.add(js_event.handler(f_!([frp, display_mode] {
|
||||
if display_mode.get().allow_mouse_events() {
|
||||
frp.wheel.emit(())
|
||||
}
|
||||
})));
|
||||
|
||||
let on_leave = mouse_manager.on_leave.add(js_event.handler(f_!(
|
||||
scene_frp.focused_source.emit(false);
|
||||
)));
|
||||
let on_enter = mouse_manager.on_enter.add(js_event.handler(f_!(
|
||||
scene_frp.focused_source.emit(true);
|
||||
)));
|
||||
|
||||
let handles = Rc::new([on_move, on_down, on_up, on_wheel, on_leave, on_enter]);
|
||||
Self {
|
||||
mouse_manager,
|
||||
last_position,
|
||||
@ -243,7 +253,7 @@ pub struct Keyboard {
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new(current_event: &CurrentJsEvent) -> Self {
|
||||
pub fn new(current_event: &JsEvent) -> Self {
|
||||
let frp = enso_frp::io::keyboard::Keyboard::default();
|
||||
let bindings = Rc::new(enso_frp::io::keyboard::DomBindings::new(&frp, current_event));
|
||||
Self { frp, bindings }
|
||||
@ -624,8 +634,10 @@ pub struct Frp {
|
||||
pub shape: frp::Sampler<Shape>,
|
||||
pub camera_changed: frp::Stream,
|
||||
pub frame_time: frp::Stream<f32>,
|
||||
pub focused: frp::Stream<bool>,
|
||||
camera_changed_source: frp::Source,
|
||||
frame_time_source: frp::Source<f32>,
|
||||
focused_source: frp::Source<bool>,
|
||||
}
|
||||
|
||||
impl Frp {
|
||||
@ -633,18 +645,22 @@ impl Frp {
|
||||
pub fn new(shape: &frp::Sampler<Shape>) -> Self {
|
||||
frp::new_network! { network
|
||||
camera_changed_source <- source();
|
||||
frame_time_source <- source();
|
||||
frame_time_source <- source();
|
||||
focused_source <- source();
|
||||
}
|
||||
let shape = shape.clone_ref();
|
||||
let camera_changed = camera_changed_source.clone_ref().into();
|
||||
let frame_time = frame_time_source.clone_ref().into();
|
||||
let focused = focused_source.clone_ref().into();
|
||||
Self {
|
||||
network,
|
||||
shape,
|
||||
camera_changed,
|
||||
frame_time,
|
||||
focused,
|
||||
camera_changed_source,
|
||||
frame_time_source,
|
||||
focused_source,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -701,7 +717,7 @@ pub struct SceneData {
|
||||
pub context: Rc<RefCell<Option<Context>>>,
|
||||
pub context_lost_handler: Rc<RefCell<Option<ContextLostHandler>>>,
|
||||
pub variables: UniformScope,
|
||||
pub current_js_event: CurrentJsEvent,
|
||||
pub js_event: JsEvent,
|
||||
pub mouse: Mouse,
|
||||
pub keyboard: Keyboard,
|
||||
pub uniforms: Uniforms,
|
||||
@ -743,11 +759,11 @@ impl SceneData {
|
||||
let uniforms = Uniforms::new(&variables);
|
||||
let renderer = Renderer::new(&dom, &variables);
|
||||
let style_sheet = world::with_context(|t| t.style_sheet.clone_ref());
|
||||
let current_js_event = CurrentJsEvent::new();
|
||||
let js_event = JsEvent::new();
|
||||
let frp = Frp::new(&dom.root.shape);
|
||||
let mouse = Mouse::new(&frp, &dom.root, &variables, ¤t_js_event, &display_mode);
|
||||
let mouse = Mouse::new(&frp, &dom.root, &variables, &js_event, &display_mode);
|
||||
let disable_context_menu = Rc::new(web::ignore_context_menu(&dom.root));
|
||||
let keyboard = Keyboard::new(¤t_js_event);
|
||||
let keyboard = Keyboard::new(&js_event);
|
||||
let network = &frp.network;
|
||||
let extensions = Extensions::default();
|
||||
let bg_color_var = style_sheet.var("application.background");
|
||||
@ -776,7 +792,7 @@ impl SceneData {
|
||||
context,
|
||||
context_lost_handler,
|
||||
variables,
|
||||
current_js_event,
|
||||
js_event,
|
||||
mouse,
|
||||
keyboard,
|
||||
uniforms,
|
||||
@ -822,8 +838,8 @@ impl SceneData {
|
||||
self.layers.main.camera()
|
||||
}
|
||||
|
||||
pub fn new_symbol(&self) -> Symbol {
|
||||
world::with_context(|t| t.new())
|
||||
pub fn new_symbol(&self, label: &'static str) -> Symbol {
|
||||
world::with_context(|t| t.new(label))
|
||||
}
|
||||
|
||||
fn update_shape(&self) -> bool {
|
||||
|
@ -367,7 +367,7 @@ impl ShapeSystemModel {
|
||||
/// Constructor.
|
||||
#[profile(Detail)]
|
||||
pub fn new(shape: def::AnyShape, pointer_events: bool, definition_path: &'static str) -> Self {
|
||||
let sprite_system = SpriteSystem::new();
|
||||
let sprite_system = SpriteSystem::new(definition_path);
|
||||
let material = Rc::new(RefCell::new(Self::default_material()));
|
||||
let geometry_material = Rc::new(RefCell::new(Self::default_geometry_material()));
|
||||
let pointer_events = Immutable(pointer_events);
|
||||
|
@ -323,6 +323,7 @@ impl Symbol {
|
||||
/// Constructor.
|
||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||
stats: &Stats,
|
||||
label: &'static str,
|
||||
id: SymbolId,
|
||||
global_id_provider: &GlobalInstanceIdProvider,
|
||||
on_mut: OnMut,
|
||||
@ -332,7 +333,7 @@ impl Symbol {
|
||||
let shader_dirty = ShaderDirty::new(Box::new(on_mut));
|
||||
let shader_on_mut = Box::new(f!(shader_dirty.set()));
|
||||
let shader = Shader::new(stats, shader_on_mut);
|
||||
let data = Rc::new(SymbolData::new(stats, id, global_id_provider, on_mut2));
|
||||
let data = Rc::new(SymbolData::new(stats, label, id, global_id_provider, on_mut2));
|
||||
Self { data, shader_dirty, shader }
|
||||
})
|
||||
}
|
||||
@ -393,7 +394,7 @@ impl Symbol {
|
||||
let count = self.surface.point_scope().size() as i32;
|
||||
let instance_count = self.surface.instance_scope().size() as i32;
|
||||
|
||||
self.stats.inc_draw_call_count();
|
||||
self.stats.register_draw_call(self.id);
|
||||
if instance_count > 0 {
|
||||
context.draw_arrays_instanced(*mode, first, count, instance_count);
|
||||
} else {
|
||||
@ -536,6 +537,7 @@ impl WeakElement for WeakSymbol {
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct SymbolData {
|
||||
pub label: &'static str,
|
||||
pub id: SymbolId,
|
||||
global_id_provider: GlobalInstanceIdProvider,
|
||||
display_object: display::object::Instance,
|
||||
@ -553,6 +555,7 @@ impl SymbolData {
|
||||
/// Create new instance with the provided on-dirty callback.
|
||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||
stats: &Stats,
|
||||
label: &'static str,
|
||||
id: SymbolId,
|
||||
global_id_provider: &GlobalInstanceIdProvider,
|
||||
on_mut: OnMut,
|
||||
@ -572,6 +575,7 @@ impl SymbolData {
|
||||
let global_instance_id = instance_scope.add_buffer("global_instance_id");
|
||||
|
||||
Self {
|
||||
label,
|
||||
id,
|
||||
global_id_provider,
|
||||
display_object,
|
||||
|
@ -23,7 +23,7 @@ impl Screen {
|
||||
/// Constructor.
|
||||
#[profile(Detail)]
|
||||
pub fn new(surface_material: Material) -> Self {
|
||||
let sprite_system = SpriteSystem::new();
|
||||
let sprite_system = SpriteSystem::new("screen");
|
||||
sprite_system.set_geometry_material(Self::geometry_material());
|
||||
sprite_system.set_material(surface_material);
|
||||
let sprite = sprite_system.new_instance();
|
||||
|
@ -288,8 +288,8 @@ pub struct SpriteSystem {
|
||||
impl SpriteSystem {
|
||||
/// Constructor.
|
||||
#[profile(Detail)]
|
||||
pub fn new() -> Self {
|
||||
let (stats, symbol) = world::with_context(|t| (t.stats.clone_ref(), t.new()));
|
||||
pub fn new(label: &'static str) -> Self {
|
||||
let (stats, symbol) = world::with_context(|t| (t.stats.clone_ref(), t.new(label)));
|
||||
let mesh = symbol.surface();
|
||||
let point_scope = mesh.point_scope();
|
||||
let instance_scope = mesh.instance_scope();
|
||||
|
@ -137,19 +137,23 @@ impl SymbolRegistry {
|
||||
|
||||
/// Creates a new `Symbol`.
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(&self) -> Symbol {
|
||||
pub fn new(&self, label: &'static str) -> Symbol {
|
||||
let dirty = self.dirty.clone();
|
||||
let stats = &self.stats;
|
||||
let id_value = self.next_id.get();
|
||||
self.next_id.set(id_value + 1);
|
||||
let id = SymbolId::new(id_value);
|
||||
let on_mut = move || dirty.set(id);
|
||||
let symbol = Symbol::new(stats, id, &self.global_id_provider, on_mut);
|
||||
let symbol = Symbol::new(stats, label, id, &self.global_id_provider, on_mut);
|
||||
symbol.set_context(self.context.borrow().as_ref());
|
||||
self.symbols.borrow_mut().insert(id, symbol.clone_ref());
|
||||
symbol
|
||||
}
|
||||
|
||||
pub fn get_symbol(&self, id: SymbolId) -> Option<Symbol> {
|
||||
self.symbols.borrow().get(&id)
|
||||
}
|
||||
|
||||
/// Set the GPU context. In most cases, this happens during app initialization or during context
|
||||
/// restoration, after the context was lost. See the docs of [`Context`] to learn more.
|
||||
pub fn set_context(&self, context: Option<&Context>) {
|
||||
|
@ -230,13 +230,6 @@ impl Uniforms {
|
||||
}
|
||||
|
||||
|
||||
// =========================
|
||||
// === Metadata Profiler ===
|
||||
// =========================
|
||||
|
||||
profiler::metadata_logger!("RenderStats", log_render_stats(StatsData));
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === World ===
|
||||
@ -436,7 +429,6 @@ impl WorldData {
|
||||
let garbage_collector = default();
|
||||
let stats_draw_handle = on.prev_frame_stats.add(f!([stats_monitor] (stats: &StatsData) {
|
||||
stats_monitor.sample_and_draw(stats);
|
||||
log_render_stats(*stats)
|
||||
}));
|
||||
let themes = with_context(|t| t.theme_manager.clone_ref());
|
||||
let update_themes_handle = on.before_frame.add(f_!(themes.update()));
|
||||
@ -536,10 +528,12 @@ impl WorldData {
|
||||
}
|
||||
|
||||
fn run_stats(&self, time: Duration) {
|
||||
let previous_frame_stats = self.stats.begin_frame(time);
|
||||
if let Some(stats) = previous_frame_stats {
|
||||
self.on.prev_frame_stats.run_all(&stats);
|
||||
self.stats.calculate_prev_frame_fps(time);
|
||||
{
|
||||
let stats_borrowed = self.stats.borrow();
|
||||
self.on.prev_frame_stats.run_all(&stats_borrowed.stats_data);
|
||||
}
|
||||
self.stats.reset_per_frame_statistics();
|
||||
}
|
||||
|
||||
/// Begin incrementally submitting [`profiler`] data to the User Timing web API.
|
||||
|
@ -82,7 +82,7 @@ pub fn main() {
|
||||
let scene = &world.default_scene;
|
||||
let camera = scene.camera();
|
||||
let navigator = Navigator::new(scene, &camera);
|
||||
let sprite_system = SpriteSystem::new();
|
||||
let sprite_system = SpriteSystem::new("test_sprite_system");
|
||||
world.add_child(&sprite_system);
|
||||
|
||||
let dom_front_layer = &scene.dom.layers.front;
|
||||
|
@ -22,6 +22,7 @@ use ensogl_core::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use enso_profiler_data::parse_multiprocess_profile;
|
||||
use enso_profiler_data::Class;
|
||||
use enso_profiler_data::Profile;
|
||||
use enso_profiler_enso_data::Metadata;
|
||||
use enso_profiler_flame_graph as profiler_flame_graph;
|
||||
@ -178,7 +179,6 @@ fn make_marks_from_profile(profile: &Profile<Metadata>) -> Vec<profiler_flame_gr
|
||||
profile
|
||||
.metadata()
|
||||
.filter_map(|metadata: &enso_profiler_data::Timestamped<Metadata>| match metadata.data {
|
||||
Metadata::RenderStats(_) => None,
|
||||
Metadata::RpcEvent(_) if !SHOW_RPC_EVENT_MARKS => None,
|
||||
Metadata::BackendMessage(_) if !SHOW_BACKEND_MESSAGE_MARKS => None,
|
||||
_ => {
|
||||
@ -190,28 +190,29 @@ fn make_marks_from_profile(profile: &Profile<Metadata>) -> Vec<profiler_flame_gr
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn make_rendering_performance_blocks(
|
||||
profile: &Profile<Metadata>,
|
||||
fn make_rendering_performance_blocks<M>(
|
||||
profile: &Profile<M>,
|
||||
) -> Vec<profiler_flame_graph::Block<Performance>> {
|
||||
let mut blocks = Vec::default();
|
||||
let render_stats = profile.metadata().filter_map(|metadata| match metadata.data {
|
||||
Metadata::RenderStats(data) => Some(metadata.as_ref().map(|_| data)),
|
||||
_ => None,
|
||||
});
|
||||
for (prev, current) in render_stats.tuple_windows() {
|
||||
let start = prev.time.into_ms();
|
||||
let end = current.time.into_ms();
|
||||
let row = -1;
|
||||
let label = format!("{:#?}", current.data);
|
||||
let block_type = match current.data.fps {
|
||||
fps if fps > 55.0 => Performance::Good,
|
||||
fps if fps > 25.0 => Performance::Medium,
|
||||
_ => Performance::Bad,
|
||||
};
|
||||
let block = profiler_flame_graph::Block { start, end, row, label, block_type };
|
||||
blocks.push(block);
|
||||
}
|
||||
blocks
|
||||
profile
|
||||
.intervals
|
||||
.iter()
|
||||
.filter(|interval| matches!(profile[interval.measurement].classify(), Class::OnFrame))
|
||||
.map(|interval| interval.interval.start.into_ms())
|
||||
.tuple_windows()
|
||||
.map(|(start, end)| {
|
||||
let label = "<frame>".to_string();
|
||||
let row = -1;
|
||||
let block_type = match end - start {
|
||||
// 60 FPS
|
||||
dt if dt < 17.0 => Performance::Good,
|
||||
// 30 FPS
|
||||
dt if dt < 34.0 => Performance::Medium,
|
||||
// <30 FPS
|
||||
_ => Performance::Bad,
|
||||
};
|
||||
profiler_flame_graph::Block { start, end, row, label, block_type }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ pub fn main() {
|
||||
let scene = &world.default_scene;
|
||||
let camera = scene.camera().clone_ref();
|
||||
let navigator = Navigator::new(scene, &camera);
|
||||
let sprite_system = SpriteSystem::new();
|
||||
let sprite_system = SpriteSystem::new("test_sprite_system");
|
||||
|
||||
let sprite1 = sprite_system.new_instance();
|
||||
sprite1.set_size(Vector2::new(10.0, 10.0));
|
||||
|
@ -32,7 +32,7 @@ use ensogl_core::display::symbol::geometry::SpriteSystem;
|
||||
pub fn main() {
|
||||
let world = World::new().displayed_in("root");
|
||||
let navigator = Navigator::new(&world.default_scene, &world.default_scene.camera());
|
||||
let sprite_system = SpriteSystem::new();
|
||||
let sprite_system = SpriteSystem::new("test_sprite_system");
|
||||
|
||||
let sprite2 = sprite_system.new_instance();
|
||||
let sprite1 = sprite_system.new_instance();
|
||||
|
@ -85,88 +85,44 @@ fn event_listener_options() -> enso_web::AddEventListenerOptions {
|
||||
// === JsEventHandler ===
|
||||
// ======================
|
||||
|
||||
/// Handler of currently processed js event.
|
||||
///
|
||||
/// Managed js event by this structure will be NOT propagated further to DOM elements, unless the
|
||||
/// `pass_to_dom` event will be emitted.
|
||||
///
|
||||
/// To make this class manage js event, you should wrap the closure passed as event listener using
|
||||
/// `make_event_handler` function.
|
||||
/// FRP wrapper for JS events. You can use the [`Self::handler`] method to generate a new event
|
||||
/// handler closure. When event is fired, it will be emitted as [`Self::event`] stream. After the
|
||||
/// event stops propagating, [`None`] will be emitted instead.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct CurrentJsEvent {
|
||||
/// Currently handled js event.
|
||||
pub event: frp::Stream<Option<enso_web::Event>>,
|
||||
/// Emitting this signal while handling js event (`current_js_event` is Some) makes this event
|
||||
/// pass to the DOM elements. Otherwise the js event propagation will be stopped.
|
||||
pub pass_to_dom: frp::Source,
|
||||
event_source: frp::Source<Option<enso_web::Event>>,
|
||||
network: frp::Network,
|
||||
pub struct JsEvent {
|
||||
pub event: frp::Stream<Option<enso_web::Event>>,
|
||||
event_source: frp::Source<Option<enso_web::Event>>,
|
||||
network: frp::Network,
|
||||
}
|
||||
|
||||
impl Default for CurrentJsEvent {
|
||||
impl Default for JsEvent {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CurrentJsEvent {
|
||||
impl JsEvent {
|
||||
/// Constructor
|
||||
pub fn new() -> Self {
|
||||
frp::new_network! { network
|
||||
event_source <- source();
|
||||
pass_to_dom <- source();
|
||||
event_is_passed_to_dom <- any(...);
|
||||
event_is_passed_to_dom <+ pass_to_dom.constant(true);
|
||||
event <- any(...);
|
||||
|
||||
new_event <- event_source.map3(&event,&event_is_passed_to_dom,Self::on_event_change);
|
||||
|
||||
event_is_passed_to_dom <+ new_event.constant(false);
|
||||
event <+ new_event;
|
||||
event_source <- source();
|
||||
}
|
||||
let event = event.into();
|
||||
Self { event, pass_to_dom, event_source, network }
|
||||
let event = event_source.clone().into();
|
||||
Self { event, event_source, network }
|
||||
}
|
||||
|
||||
/// A helper function for creating mouse event handlers.
|
||||
///
|
||||
/// This wraps the `processing_fn` so before processing the current js event is
|
||||
/// set to the received js event, and after processing it is set back to `None`.
|
||||
pub fn make_event_handler<Event>(
|
||||
&self,
|
||||
mut processing_fn: impl FnMut(&Event),
|
||||
) -> impl FnMut(&Event)
|
||||
where
|
||||
Event: AsRef<enso_web::Event>,
|
||||
{
|
||||
let event_source = self.event_source.clone_ref();
|
||||
move |event| {
|
||||
/// Creates an event handler which wraps the event in an FRP network. The event will be emitted
|
||||
/// on the `event` output stream. After the event is emitted, `None` will be emitted.
|
||||
pub fn handler<Event>(&self, mut processing_fn: impl FnMut(&Event)) -> impl FnMut(&Event)
|
||||
where Event: AsRef<enso_web::Event> {
|
||||
let event_source = &self.event_source;
|
||||
f!([event_source] (event) {
|
||||
let _profiler = profiler::start_debug!(profiler::APP_LIFETIME, "event_handler");
|
||||
let js_event = event.as_ref().clone();
|
||||
event_source.emit(Some(js_event));
|
||||
processing_fn(event);
|
||||
event_source.emit(None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The bool is passed by reference to match the signatures expected by FRP eval.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn on_event_change(
|
||||
new: &Option<enso_web::Event>,
|
||||
current: &Option<enso_web::Event>,
|
||||
is_passed: &bool,
|
||||
) -> Option<enso_web::Event> {
|
||||
// Whenever the current js event change, we pass the processed one to the dom if someone
|
||||
// asked to.
|
||||
if let Some(e) = current {
|
||||
if !is_passed {
|
||||
// Prevent events from propagating to user agent, so default browser actions will
|
||||
// not be triggered.
|
||||
e.prevent_default();
|
||||
e.stop_propagation();
|
||||
}
|
||||
}
|
||||
new.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate as frp;
|
||||
use crate::io::js::CurrentJsEvent;
|
||||
use crate::io::js::JsEvent;
|
||||
use crate::io::js::Listener;
|
||||
|
||||
use enso_web::KeyboardEvent;
|
||||
@ -440,15 +440,16 @@ pub struct DomBindings {
|
||||
|
||||
impl DomBindings {
|
||||
/// Create new Keyboard and Frp bindings.
|
||||
pub fn new(keyboard: &Keyboard, current_event: &CurrentJsEvent) -> Self {
|
||||
let key_down = Listener::new_key_down(current_event.make_event_handler(
|
||||
pub fn new(keyboard: &Keyboard, current_event: &JsEvent) -> Self {
|
||||
let key_down = Listener::new_key_down(current_event.handler(
|
||||
f!((event:&KeyboardEvent) keyboard.source.down.emit(KeyWithCode::from(event))),
|
||||
));
|
||||
let key_up = Listener::new_key_up(current_event.make_event_handler(
|
||||
f!((event:&KeyboardEvent) keyboard.source.up.emit(KeyWithCode::from(event))),
|
||||
));
|
||||
let key_up =
|
||||
Listener::new_key_up(current_event.handler(
|
||||
f!((event:&KeyboardEvent) keyboard.source.up.emit(KeyWithCode::from(event))),
|
||||
));
|
||||
let blur = Listener::new_blur(
|
||||
current_event.make_event_handler(f_!(keyboard.source.window_defocused.emit(()))),
|
||||
current_event.handler(f_!(keyboard.source.window_defocused.emit(()))),
|
||||
);
|
||||
Self { key_down, key_up, blur }
|
||||
}
|
||||
|
@ -185,49 +185,51 @@ impl From<&ButtonMask> for ButtonMask {
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Mouse {
|
||||
pub network: frp::Network,
|
||||
pub up: frp::Source<Button>,
|
||||
pub down: frp::Source<Button>,
|
||||
pub wheel: frp::Source,
|
||||
pub up_0: frp::Stream,
|
||||
pub up_1: frp::Stream,
|
||||
pub up_2: frp::Stream,
|
||||
pub up_3: frp::Stream,
|
||||
pub up_4: frp::Stream,
|
||||
pub up_primary: frp::Stream,
|
||||
pub up_middle: frp::Stream,
|
||||
pub up_secondary: frp::Stream,
|
||||
pub down_0: frp::Stream,
|
||||
pub down_1: frp::Stream,
|
||||
pub down_2: frp::Stream,
|
||||
pub down_3: frp::Stream,
|
||||
pub down_4: frp::Stream,
|
||||
pub down_primary: frp::Stream,
|
||||
pub down_middle: frp::Stream,
|
||||
pub down_secondary: frp::Stream,
|
||||
pub is_up_0: frp::Stream<bool>,
|
||||
pub is_up_1: frp::Stream<bool>,
|
||||
pub is_up_2: frp::Stream<bool>,
|
||||
pub is_up_3: frp::Stream<bool>,
|
||||
pub is_up_4: frp::Stream<bool>,
|
||||
pub is_up_primary: frp::Stream<bool>,
|
||||
pub is_up_middle: frp::Stream<bool>,
|
||||
pub is_up_secondary: frp::Stream<bool>,
|
||||
pub is_down_0: frp::Stream<bool>,
|
||||
pub is_down_1: frp::Stream<bool>,
|
||||
pub is_down_2: frp::Stream<bool>,
|
||||
pub is_down_3: frp::Stream<bool>,
|
||||
pub is_down_4: frp::Stream<bool>,
|
||||
pub is_down_primary: frp::Stream<bool>,
|
||||
pub is_down_middle: frp::Stream<bool>,
|
||||
pub is_down_secondary: frp::Stream<bool>,
|
||||
pub position: frp::Source<Vector2<f32>>,
|
||||
pub prev_position: frp::Stream<Vector2<f32>>,
|
||||
pub translation: frp::Stream<Vector2<f32>>,
|
||||
pub distance: frp::Stream<f32>,
|
||||
pub ever_moved: frp::Stream<bool>,
|
||||
pub button_mask: frp::Stream<ButtonMask>,
|
||||
pub prev_button_mask: frp::Stream<ButtonMask>,
|
||||
pub network: frp::Network,
|
||||
pub up: frp::Source<Button>,
|
||||
pub down: frp::Source<Button>,
|
||||
pub wheel: frp::Source,
|
||||
pub up_0: frp::Stream,
|
||||
pub up_1: frp::Stream,
|
||||
pub up_2: frp::Stream,
|
||||
pub up_3: frp::Stream,
|
||||
pub up_4: frp::Stream,
|
||||
pub up_primary: frp::Stream,
|
||||
pub up_middle: frp::Stream,
|
||||
pub up_secondary: frp::Stream,
|
||||
pub down_0: frp::Stream,
|
||||
pub down_1: frp::Stream,
|
||||
pub down_2: frp::Stream,
|
||||
pub down_3: frp::Stream,
|
||||
pub down_4: frp::Stream,
|
||||
pub down_primary: frp::Stream,
|
||||
pub down_middle: frp::Stream,
|
||||
pub down_secondary: frp::Stream,
|
||||
pub is_up_0: frp::Stream<bool>,
|
||||
pub is_up_1: frp::Stream<bool>,
|
||||
pub is_up_2: frp::Stream<bool>,
|
||||
pub is_up_3: frp::Stream<bool>,
|
||||
pub is_up_4: frp::Stream<bool>,
|
||||
pub is_up_primary: frp::Stream<bool>,
|
||||
pub is_up_middle: frp::Stream<bool>,
|
||||
pub is_up_secondary: frp::Stream<bool>,
|
||||
pub is_down_0: frp::Stream<bool>,
|
||||
pub is_down_1: frp::Stream<bool>,
|
||||
pub is_down_2: frp::Stream<bool>,
|
||||
pub is_down_3: frp::Stream<bool>,
|
||||
pub is_down_4: frp::Stream<bool>,
|
||||
pub is_down_primary: frp::Stream<bool>,
|
||||
pub is_down_middle: frp::Stream<bool>,
|
||||
pub is_down_secondary: frp::Stream<bool>,
|
||||
pub position: frp::Source<Vector2<f32>>,
|
||||
pub position_top_left: frp::Source<Vector2<f32>>,
|
||||
pub position_bottom_left: frp::Source<Vector2<f32>>,
|
||||
pub prev_position: frp::Stream<Vector2<f32>>,
|
||||
pub translation: frp::Stream<Vector2<f32>>,
|
||||
pub distance: frp::Stream<f32>,
|
||||
pub ever_moved: frp::Stream<bool>,
|
||||
pub button_mask: frp::Stream<ButtonMask>,
|
||||
pub prev_button_mask: frp::Stream<ButtonMask>,
|
||||
}
|
||||
|
||||
impl Mouse {
|
||||
@ -283,6 +285,8 @@ impl Default for Mouse {
|
||||
down <- source();
|
||||
wheel <- source();
|
||||
position <- source();
|
||||
position_top_left <- source();
|
||||
position_bottom_left <- source();
|
||||
prev_position <- position.previous();
|
||||
translation <- position.map2(&prev_position,|t,s|t-s);
|
||||
distance <- translation.map(|t:&Vector2<f32>|t.norm());
|
||||
@ -381,6 +385,8 @@ impl Default for Mouse {
|
||||
is_down_middle,
|
||||
is_down_secondary,
|
||||
position,
|
||||
position_top_left,
|
||||
position_bottom_left,
|
||||
prev_position,
|
||||
translation,
|
||||
distance,
|
||||
|
@ -526,6 +526,7 @@ ops! { DocumentOps for Document
|
||||
trait {
|
||||
fn body_or_panic(&self) -> HtmlElement;
|
||||
fn create_element_or_panic(&self, local_name: &str) -> Element;
|
||||
fn create_html_element_or_panic(&self, local_name: &str) -> HtmlElement;
|
||||
fn create_div_or_panic(&self) -> HtmlDivElement;
|
||||
fn create_canvas_or_panic(&self) -> HtmlCanvasElement;
|
||||
fn get_html_element_by_id(&self, id: &str) -> Option<HtmlElement>;
|
||||
@ -541,6 +542,10 @@ ops! { DocumentOps for Document
|
||||
self.create_element(local_name).unwrap()
|
||||
}
|
||||
|
||||
fn create_html_element_or_panic(&self, local_name: &str) -> HtmlElement {
|
||||
self.create_element(local_name).unwrap().unchecked_into()
|
||||
}
|
||||
|
||||
fn create_div_or_panic(&self) -> HtmlDivElement {
|
||||
self.create_element_or_panic("div").unchecked_into()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user