How to achieve butter smooth motion in pixi.js (without any lags / spikes) - 2d-games

I'm trying to achieve butter smooth motion using pixi.js. It should be a simple thing I suppose but it seems I can't obtain it.
Sometimes I get spikes / micro lags. I've tested it on my Ubuntu 20.04 dev machine using the latest Chrome version and the latest PIXI js (5.3.3 actually). Same thing happens on the latest Chrome on my old Macbook pro (late 2013)
This is the code I'm using:
const app = new PIXI.Application({
view: document.querySelector("#view"),
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x0,
antialias: false
})
const ticker = PIXI.Ticker.system
const box = new PIXI.Graphics()
box.lineStyle(1, 0xff0000)
.drawRect(
100,
100,
100,
100)
app.stage.addChild(box)
let speed = 4
app.ticker.add( (dt) => {
if (box.x < 0) {
speed *= -1
box.x += speed * dt
}
if (box.x > 800) {
speed *= -1
box.x += speed * dt
}
box.x += speed * dt
})
Here a running version https://codepen.io/ferama/pen/LYZVBMx
What I'm missing?

You will never be able to eliminate occasional stuttering in a WebGL animation. Underneath every web rendering framework (including PixiJS) is a call to requestRenderFrame(). Which, as its name implies, is a request to the browser to repaint the window. You will not always get back a "sure, do it right now!" from the browser. Sometimes the browser is busy. Sometimes, your javascript garbage collector has to run. Sometimes a butterfly flaps its wings in China...
Point is, the only way to completely control your framerate is to use a technology such as DirectX that allows complete control over the actual rendering and display hardware (mostly!). WebGL is simply not geared for that.

It was running just fine for me, however you can try setting antialias: true and resolution: window.devicePixelRatio.

Related

Adjust QTableView Height to its content (few lines)

Below a screenshot of my application. I want to get rid of the white spaces after the last tables lines marked by red rectangles :
The horizontal size policy is expanding and the vertical one is minimum and for other tables it is both set to expanding.
I'm using this method I found in another SO question but as you can see the result is not flawless.
void verticalResizeTableViewToContents(QTableView* tableView)
{
tableView->resizeRowsToContents();
// does it work ?
tableView->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
int rowTotalHeight = 0;
// Rows height
int count = tableView->verticalHeader()->count();
for (int i = 0; i < count; ++i) {
// 2018-03 edit: only account for row if it is visible
if (!tableView->verticalHeader()->isSectionHidden(i)) {
rowTotalHeight += tableView->verticalHeader()->sectionSize(i);
}
}
// Check for scrollbar visibility
if (!tableView->horizontalScrollBar()->isHidden())
{
rowTotalHeight += tableView->horizontalScrollBar()->height();
}
// Check for header visibility
if (!tableView->horizontalHeader()->isHidden())
{
rowTotalHeight += tableView->horizontalHeader()->height();
}
tableView->setMaximumHeight(rowTotalHeight);
}
Somewhere, I'm using this code to setup one of the tables :
m_Internals->Ui.Measures->setModel(mm->getPh66MeasuresModel());
m_Internals->Ui.Measures->horizontalHeader()->setSectionsMovable(true);
m_Internals->Ui.Measures->horizontalHeader()->setHighlightSections(false);
m_Internals->Ui.Measures->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
m_Internals->Ui.Measures->horizontalHeader()->setStretchLastSection(true);
m_Internals->Ui.Measures->verticalHeader()->hide();
m_Internals->Ui.Measures->setSelectionBehavior(QAbstractItemView::SelectRows);
verticalResizeTableViewToContents(m_Internals->Ui.Measures);
I'm using Qt ModelView pattern to populate/update the tables.
Update : I made a small example to reproduce this issue with QTableView : https://github.com/embeddedmz/QTableViewAdjustPolicyNotWorkingProperly
Using the latest Qt version (from Qt official installer), there's no issue. However, using the Qt library provided by vcpkg (outdated for sure) the issue is there.
With Qt provided by vcpkg :
With the latest Qt provided by the Qt Company (update not the latest, it's 5.12.11) :
If you have something fully buildable on Ubuntu 20.04 LTS, post a link to the complete project (strip it down to just this part) and I will take an actual stab at it.
My gut, having worked with Qt for years, is telling me you are being burned by Margins.
https://doc.qt.io/qt-5/qwidget.html#contentsMargins
You probably need to set a bottom margin of Zero.
https://doc.qt.io/qt-5/qmargins.html#setBottom
If you retrieve the margins for those widgets you will probably find they are non-zero.
you can also fix your problem with one trick, this problem happens for you because your data is lower than the table size. I clone your project and change sizepolicy
Under Qt 5.12.11, the bug does not exist. So I took a look at the QAbstractScrollArea::sizeHint code of this version and compared it with the implementation used in recent versions of Qt and found that setting verticalScrollBarPolicy to "ScrollBarAlwaysOff" the "AdjustToContents" adjustment policy works. The default value was "ScrollBarAsNeeded", in fact we can see that this value is not handled but since in Qt 5.12.11 we only compare the vertical|horizontal]scrollBarPolicy to Qt::ScrollBarAlwaysOn it prevents this bug from appearing.

Bug in IMFSinkWriter?

I implemented an encoder in 2 ways.
1) based on the SDK Transcoder example, which uses topology and transcoding profile
2) based on IMFSourceReader and IMFSinkWriter, where the Sinkwriter delivers the samples to the Sourcewriter for transcoding
I tested both implementations on Windows 8.1 with Nvidia (Quadro K2200) and Intel GPU (P4600/P4700)
But bizarrly only the topology implementation uses GPU (on both).
In 2) I both I set "MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS", which has not to be set I guess, because 1) works with GPU with and without this flag set for the container type.
Is there a trick to enable GPU with IMFSinkWriter or is this a bug in the media foundation?
I had initially ran into the same issue. You don't mention how you configured the output type of the source reader (and the input type of the sink), but I found that if you allow the system to handle it (by selecting the output type of the reader to be RGB32), the performance will be horrible and all CPU bound. (error checking omitted for brevity)
hr = videoMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
hr = videoMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
hr = reader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, videoMediaType);
reader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, true);
And the documentation agrees, indicating that this configuration is useful for getting a single snapshot from the video. As a result, if you configure the reader to deliver the native media type, performance is excellent, but you now have to transform the format yourself.
reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, videoMode->GetIndex(), videoMediaType);
From here, if you are dealing with simple color conversion (like YUY2 or YUV from a webcam) then there are a few options. I originally tried writing my own converter, and pushing that off to the GPU using HLSL with DirectCompute. This works very well, but in your case, the format isn't as trivial.
Ultimately, creating and configuring an instance of the color converter (as an IMFTransform) works perfectly.
Microsoft::WRL::ComPtr<IMFMediaType> mediaTransform;
hr = ::CoCreateInstance(CLSID_CColorConvertDMO, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMFTransform), reinterpret_cast<void**>(mediaTransform.GetAddressOf());
// set the input type of the transform to the NATIVE output type of the reader
hr = mediaTransform->SetInputType(0u, videoMediaType.Get(), 0u);
Create and configure a separate sample and buffer.
IMFSample* transformSample;
hr = ::MFCreateSample(&transformSample);
hr = ::MFCreateMemoryBuffer(RGB_MFT_OUTPUT_BUFFER_SIZE, &_transformBuffer);
hr = transformSample->AddBuffer(transformBuffer);
MFT_OUTPUT_DATA_BUFFER* transformDataBuffer;
transformDataBuffer = new MFT_OUTPUT_DATA_BUFFER();
transformDataBuffer->pSample = _transformSample;
transformDataBuffer->dwStreamID = 0u;
transformDataBuffer->dwStatus = 0u;
transformDataBuffer->pEvents = nullptr;
When receiving samples from the source, hand them off to the transform to be converted.
hr = mediaTransform->ProcessInput(0u, sample, 0u));
hr = mediaTransform->ProcessOutput(0u, 1u, transformDataBuffer, &outStatus));
hr = transformDataBuffer->pSample->GetBufferByIndex(0, &mediaBuffer);
Then of course, finally hand off the transformed sample to the sink just as you do today. I am confident that this will work, and you will be a very happy person. For me, I went from 20% CPU utilization (originally implementation) down to 2% (I am concurrently displaying the video). Good luck. I hope you enjoy your project.

Cannot find overload for '/', '*', Failure when running an app on anything but iPhone 5s simulator

My game builds and runs successfully on the iPhone 5s simulator, but when I try on any other version I get the following two errors:
`Could not find an overload for '*' that accepts the supplied arguments`
`Could not find an overload for '/' that accepts the supplied arguments`
I'm writing my game entirely in Swift, and the deployment target is iOS 7.1
The die rolls in the picture are defined as
let lengthDiceroll = Double(arc4random()) / 0x100000000
let sideDiceroll = Int(arc4random_uniform(UInt32(4)))
Your problem is a difference between 32- and 64-bit architecture. Note that the target architecture you're compiling your target for is determined by the selected deviceā€”if you've got the iPhone 4S simulator selected as your target in Xcode, for example, you'll be building for 32 bit; if you've got the iPhone 5S simulator selected, you'll be building for 64-bit.
You haven't included enough code to help us figure out what exactly is going on (we'd need to know the types of the variable you're assigning to) but here's my theory. In your first error, sprite.speed is probably a CGFloat. CGFloat is 32-bit ("float") on 32-bit targets, 64-bit ("double") on 64-bit targets. So this, for example:
var x:CGFloat = Double(arc4random()) / 0x100000000
...will compile fine on a 64-bit target, because you're putting a double into a double. But when compiling for a 32-bit target, it'll give you the error that you're getting, because you're losing precision by trying to stuff a double into a float.
This will work on both:
var x:CGFloat = CGFloat(arc4random()) / 0x100000000
Your other errors are caused by the same issue (though again, I can't reproduce them accurately without knowing what type you've declared width and height as.) For example, this will fail to compile for a 32-bit architecture:
let lengthDiceroll = Double(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * lengthDiceroll, 0)
...because lengthDiceroll is a Double, so width * lengthDiceroll is a Double. CGPointMake takes CGFloat arguments, so you're trying to stuff a Double (64-bit) into a float (32-bit.)
This will compile on both architectures:
let lengthDiceroll = Double(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * CGFloat(lengthDiceroll), 0)
...or possibly better, declare lengthDiceroll as CGFloat in the first place. It won't be as accurate on 32-bit architectures, but that's sort of the point of CGFloat:
let lengthDiceroll = CGFloat(arc4random()) / 0x100000000
let width:CGFloat = 5
var y:CGPoint = CGPointMake(width * lengthDiceroll, 0)
I've experienced similar errors where debug builds work and release builds fail for example. My advice would be make all your types explicit:
let lengthDiceroll = Double(arc4random()) / Double(0x100000000)
I've also had similar problems with CGFloat and CGPoint, make sure you explicitly use CGFloat, e.g. CGFloat(2.0)

Multiple script/paperscripts in the same paperscope

I'm starting with paper.js. I like the fact that it introduces the possibility to have a script with a text/paperscript mime type, which runs in its on scope. However, scripts can become large pretty soon, so I want to be able to divide it in multiple scripts for readability. I thought I could just add more than one script tag and have them all run in the same scope, but apparently this isn't the case.
Both scripts are loaded and do run, but the second script doesn't seem to be in the paper scope.
I've set up an example here: http://barbata.nl/SO/Maps/ This example has some code, but I'll point out the important bits.
It contains two paperscripts:
Maps.js is the main script, which rasterizes the image and allows moving it around. You can ignore the code in this script, for it works fine so far.
Zoom.js is the script in which I wanted to isolate zooming functionality. It uses jq.mobi to capture the scroll wheel of the mouse, since Paper.js doesn't seem to have that event. It then translates that to a call to onMouseScroll, in a similar way Paper does it.
So far so good. The actual problem arises with the zoomIn and zoomOut functions in zoom.js.
It works if I explicity use the paper object to reference the view I want to zoom:
function zoomIn()
{
if (paper.view.zoom < 2)
{
paper.view.zoom = paper.view.zoom * 2;
}
}
But it fails when I remove paper and just reference the view:
function zoomIn()
{
if (view.zoom < 2)
{
view.zoom = view.zoom * 2;
}
}
This surprises me, as I expected the script to be a Paperscript, running in the Paperscope. It works fine if I put this code in Maps.js, so it seems that although zoom.js is loaded by Paper.js (the developer tools in the browser confirm this), it isn't run in the Paperscope.
My question is: are my findings correct? Am I doing something wrong? What is the right way to divide a Paper.js application into multiple units for readability?
Of course I can get it running, but I want to make sure I do it right.
This is indeed how it works. I've opened an issue on GitHub
I found that the "cleanest" way is to do it with this.install(window). It also makes error finding with Chrome developer tools easier since it is more adapted to reporting on the line errors in java-script than "paperscript".
in index.html (for example):
<script type="text/javascript" src='js/other_lib.js'></script>
<script type="text/paperscript" canvas="canvas">
this.install(window);
/*no code 'required' here */
</script>
<body>
<canvas id="canvas" width="1500" height="500"></canvas>
</body>
Then in the js/other_lib.js i just add code as normal:
var r = new Path.Rectangle([100,100],[200,200]);
r.fillColor = 'black';
/*more code here*/
This should generate a rectangle.
What DOES'T NOT WORK for me (as of Paper.js v0.10.2 Release Date: 9. July 2016) is the object operators. Such as adding vecrots pointc = pointa + pointb; for me this is giving a lot of NaN values.
I have had to edit a few libs to get this working, but the change is simple:
var pointc = new Point(pointa.x+pointb.x,pointa.y + pointb.y);

Javafx 2d text rendering speed up

I am a little surprised that JavaFX do consume my CPU by showing simple floating text on a screen.
My question is there any option tweaks to turn on hardware acceleration for nodes like Text? To Use GPU and not CPU when rendering 2D primitives?
Here is the simple example that consume up to 40% cpu on my 2.53Mhz core 2 duo + Nvidia 9600M GT. OS: Mac Os X. JavaFX 1.2; JRE 1.5
Edit: I put animation in the example to just simulate text scrolling. You can try and achieve the same CPU consuming by scrolling ListBox or some picture with no stopping.
package text2dacceleration;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.transform.Transform;
import javafx.scene.text.Text;
import javafx.animation.*;
def longLine = for (i in [1..45]) "{i}";
def textNodes = for (i in [1..64]) Text{content: "{longLine} line number {i}"};
var yoffset = 0.0;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
KeyFrame { time: 0s values: [yoffset => 0.0]}
KeyFrame { time: 1s values: [yoffset => 10.0]}]
}.play();
Stage {
title: "Text nodes"
width: 800
height: 600
resizable: false
scene: Scene {
content: [
VBox {
content: textNodes
transforms: bind Transform.translate(0, yoffset);
}]}}
Have you played with Timeline's framerate variable? Lowering that might help.
(The framerate description: The maximum framerate at which this animation will run, in frames per second.)
Probably won't have an impact but KeyFrame has a "canSkip" option.
Sun did a lot of work on this starting at 1.5 and through 6 but I don't know how much of it filtered through to Apple's JRE implementation. For comparison is it possible for you to update to a newer JRE such as 1.6? If you can't run the latest version of OSX and therefore can't do you have a Windows machine available on boot camp or whatever it might be worth trying one of the later Sun reference ones to see how you get on.
It might also be worth a post to the JavaFX forum - http://forums.sun.com/forum.jspa?forumID=932
Sun are usually pretty good at responding to these.
VBox {
cache: true
content: textNodes
transforms: bind Transform.translate(0, yoffset);
...
--
cache: A performance hint to the system to indicate that this Node should be cached as a bitmap.

Resources