question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

WASM event callback binds blocks browser (Chrome) to re-render (CRP)

See original GitHub issue

#19203 # The issue

Okay, so I’ve started developing this svg rendering “engine” in blazor and found kinda strange behavior. I am rendering SVG’s and moving them around with css translate3d transform, called via JS Interop (literally the only thing that my engine uses from javascript, otherwise it’s all C# - wasm).

First of all, I’ve found out that every event callback invokes blazor re-render and it fires AfterRenderAsync and stuff, that makes sense because you want to render new data on some event change, however I am using events like onmousemove / ontouchmove that only change position which is being invoked via javascript, so I’ve disabled ShouldRender and enabling it by myself on data change.

So right now i basically hook onmousemove / ontouchmove in blazor, and without re-rendering the component I call JSInterop on clientside to move the elements around, worked almost perfect with mouse so far, but when I’ve added ontouchmove (that probably fires more frequently?) things started to mess up.

image

As you can see on this log taken during touch movement, I buffer CSS changes via JSInterop -> requestAnimationFrame and the browser (Chrome) does CRP after I slow my movement down (event is called less frequently) or I completely stop.

(btw don’t mind the Re-rendering canvas thing, i log this on mouseup / touchend and it has nothing to do with browser CRP rendering, tried this, don’t worry)

image

Here you can even see screenshot, where I back enabled ShouldRender making the component re-render each mouse/touch move and buffer CSS change AfterRenderAsync

You can check the live demo here: http://blaze.majdatrpkos.cz/, however there is no console logging enabled, works perfectly with mouse move on desktop, the bug occurs only if you have touch-enabled laptop (like I do) or smartphone.

You can check the DOM and see that CSS values on canvas aren’t changing that frequently as move occurs when you drag the canvas itself.

src: https://github.com/majda107/blaze-cards

Knowing this approach has no issues in pure js, I feel like there is something in WASM <-> JS binds that prevent the browser from CRP re-render and fire the event instead, since this loop finishes browser fires another event and this whole thing continues.

Also, this only happens in desktop version Chrome and many android browser, it seems to work perfectly fine in desktop Firefox and desktop touch support (I use touch-enabled laptop).

I am not sure if this is bug or feature, but is there any way to fix this? I think that my ‘app’ has an potential to be WASM built working css svg renderer, but this issue totally buries the idea.

To Reproduce

  • Hook ontouchmove on blazor component with either ShouldRender enabled or disabled
  • Call JSInterop callback that changes CSS property and should be css trigger
  • Try to use requestAnimationFrame?
  • Use Google Chrome on desktop?

Details

  • Blazor clientside (WASM) 3.2.0-preview3.20168.3
  • Version 81.0.4044.129 (Official Build) (64-bit)

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
majda107commented, May 5, 2020

I made a prototype app that does a lot of draw operations on a canvas in a tight loop (25-100ms), and I experienced the same performance degradation. I have not dug deeper as I suspected the issue came from using a Threading.Timer in C# and not something like a WebWorker or like you animationFrame.

There might be a real issue in heavy Blazor/Javascript interop, Vakhtangi Abashidze created JS fast data exchanger. I have not compared your repro with his solution, but perhaps you can investigate if this might help? https://github.com/Lupusa87/BlazorJsFastDataExchanger

Once more, thanks a ton.

I’ve downloaded .NET 5 and linked .NET 5 preview Mono-wasm nugget, using MonoWebAssemblyJSRuntime in .NET 5 razor library and it seems to be almost as performant as rust/c++ version.

It’s definitelly faster, than JSInterop bindinds, probably due to marshalling.

You can check demo here: https://blaze.majdatrpkos.cz/ (use CTRL+SHIFT+R to reload service worker), you can drag canvas, elements and multi-select with SHIFT

It’s not cleanest solution, but seems to work so far, makes me wondering, could guys in microsoft somehow encapsulate InvokeUnmarshalled function? If it makes any sense, I can fork blazor and add it as a feature, submitting merge request.

1reaction
majda107commented, May 3, 2020

Thanks for detailed issue report, @majda107. We have multiple SVG support related issues which we plan to handle all together during 5.0 timeframe. For now, parking this issue in the next-sprint-planning milestone to handle it together with the rest when we get to them.

Okay so correct me now, I’ve removed everything that relates to SVG and even JS Interop and the issue still persists

I mean, this is thing that should blazor and wasm handle without any issues and it may even be on a list of features but it’s clearly broken.

Broken code:

@page "/"
@inject IJSRuntime IJSRuntime;

<h1>Hello, world!</h1>

<div class="canvas"
     @onmousedown="((e) => this.MouseDown((float)e.ClientX, (float)e.ClientY))"
     @onmousemove="((e) => this.MouseMove((float)e.ClientX, (float)e.ClientY))"
     @onmouseup="((e) => this.MouseUp((float)e.ClientX, (float)e.ClientY))"
     @ontouchstart="((e) => this.MouseDown((float)e.Touches[0].ClientX, (float)e.Touches[0].ClientY))"
     @ontouchmove="((e) => this.MouseMove((float)e.Touches[0].ClientX, (float)e.Touches[0].ClientY))"
     @ontouchend="((e) => this.MouseUp(0, 0))"
     @ontouchstart:stopPropagation="true"
     @ontouchmove:stopPropagation="true">

    @*<rect @ref="rectRef" x="@this.ToCoordString(this.movx)" y="@this.ToCoordString(this.movy)" width="40px" height="40px"></rect>*@

    @*<div @ref="rectRef" width="40px" height="40px"></div>*@

    <div @ref="rectRef" class="rect-div" style="@this.GetCssOffset()"></div>
</div>

@code {
    private float lastx;
    private float lasty;

    private float movx;
    private float movy;

    private bool mouseDown;

    private ElementReference rectRef;

    private string GetCssOffset()
    {
        return $"left: {this.movx.ToString("0")}px; top: {this.movy.ToString("0")}px;";
    }

    private string ToCoordString(float val)
    {
        return $"{val.ToString("0")}px";
    }

    private void MouseDown(float x, float y)
    {
        this.mouseDown = true;

        this.lastx = x;
        this.lasty = y;
    }

    private void MouseUp(float x, float y)
    {
        this.mouseDown = false;

        this.lastx = x;
        this.lasty = y;
    }

    private void MouseMove(float x, float y)
    {
        if (!this.mouseDown) return;

        var devx = x - this.lastx;
        var devy = y - this.lasty;

        movx += devx;
        movy += devy;

        this.lastx = x;
        this.lasty = y;
    }
}

https://github.com/majda107/blaze-touch-issue you can examine the entire repo here.

Same issue persists, no SVGs, no JS interop, pure wasm issue. I’ve rewritten entire scenario to JS + Rust wasm and it works flawlessly, so this is definitelly not wasm + chrome issue (can provide the code if interested)

Just as I said and thought, this has to be blazor <-> wasm binding issue and it’s definitelly much bigger thing than ‘svg bug that is planned to fix next sprint’, it affects entire rendering tree -> DOM.

Just plug that code in and see it running on some touch-enabled device… If you further investigate what I’ve written in previous comments, you’ll find out it’s not touchevent or something else related stuff btw…

Blazor / WASM just seems to block browser rendering at all in some way,

Read more comments on GitHub >

github_iconTop Results From Across the Web

Force DOM redraw/refresh on Chrome/Mac
I ran into the same problem a few minutes ago. I change the element for a div (it was a span) and now...
Read more >
How does the browser work when rendering a web page? ...
The browser will download JS files and executes them at the same time immediately when parsing <script>. <script> will block HTML parsing. It's ......
Read more >
Use addPageBinding in Playwright Internal With Examples
Use the addPageBinding method in your next Playwright Internal project with LambdaTest Automation Testing Advisor. Learn how to set up and run automated ......
Read more >
Mudselect set value. 0: … The only way to do that is to call ...
You can bind the click event of a button to the GetDataByValue(TValue) ... a callback when the market value is changed, you update...
Read more >
Xterm cursor position. I've heard that this can be done using ...
If anyone is looking, this was my solution in the xterm. }); // Moves the cursor two rows up and to the left...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found