Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
jlfreund
Level 7

Performance problems on roScreen

I have a simple app that displays a few words of text, a small photo, on a fullscreen frame background. Then I want to smoothly animate a second full screen image on top of that. First, I tried roImageCanvas, but after advice from the forum, I rewrote the layout using roScreen, but am still having problems with animation.

Rendering the background (frame, photo, text) takes around 400ms, which is acceptable, except that I cannot simply create and animate a second roScreen on top of that. Both are double buffered, and drawing the second roScreen will leave a black background in place of the first roScreen, so I really need to render and animate everything in one roScreen which means I need all drawing and the swap to complete in 15ms. I profiled the contents of the first roScreen which paints a fullscreen frame, 3 DrawRect/DrawText pairs (very short text), plus one small image for a total of about 1.3X overdraw of the entire screen. Just this amount of painting already takes 400ms, so I'm a long way off.

The first thing the profiling told me was that the first DrawText seems to take a long time (I guess to init the font cache): average of 120ms. I guess if I keep the same roFont for the lifetime of the app, this won't be a problem, because subsequent DrawText using that font seems to be free.

However I found the same problem when measuring DrawRect (used to paint the background of each of the 3 short words on the screen): average of 150ms for the first tiny DrawRect, then 1ms for the 2nd and 3rd call. Not sure why the first DrawRect is always so slow or how I could workaround it.

Regarding the bitmaps, the frame background is free because I can re-use the same scaled bitmap for the lifetime of the app and DrawObject(bitmap) is always 0ms. However the other bitmaps in the layout are not-reused. The DrawObject on those bitmaps is free, but each one must be scaled, and unfortunately, I seem to get unpredictable performance results from DrawScaledObject: 67ms, 5ms, 10ms, 3ms, 5ms; even though they are all JPG, and very similar source and dest sizes.

Finally, SwapBuffers is extremely slow and pretty unpredictable: 17ms, 23ms, 13ms. This seems tragic, given that I only painted the screen ~1.3X times and the time to finish and swap is already so slow.

I'd really like to get the first layer of my scene from 400ms down to 15ms, but it seems like DrawRect and DrawScaledObject are unpredictable, and SwapBuffers is very slow. Does anyone have an idea how to do fast animation including scaled images using roScreen?

Thanks!
Jason
0 Kudos
39 Replies
TheEndless
Level 9

Re: Performance problems on roScreen

400ms for every update is a pretty good indication that you've got something wrong in your code. You should be able to get upwards of 60 fps, and easily 30 fps from roScreen (on the Roku 2, halve that for the Roku 1). First, make sure you're creating your roFont outside of your draw loop. As you noted, the first DrawText takes a bit longer, but all draws after that, using the same roFont object, should be infinitely faster. I've never seen anything like you're seeing with DrawRect, so I'd be inclined to think that's also related to the text you're drawing. As for DrawScaledObject, that's a very expensive operation, so unless you're scaling the image differently on every draw, you'd be better off scaling it to an in-memory bitmap initially, then just using that pre-scaled bitmap on subsequent draws, so you only incur the scaling penalty at initialization. If you're scaling differently on each draw, then you might want to use a SetScaleMode of 0. It'll result in a lower quality scale, but it should be a good bit faster.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
RokuMarkn
Level 8

Re: Performance problems on roScreen

Also note that SwapBuffers waits for the next vertical sync, so it's expected that it could take up to 16 ms. That's not a performance problem, it's the way SwapBuffers has to work.

--Mark
0 Kudos
EnTerr
Level 11

Re: Performance problems on roScreen

This is a profound note worth highlighting, so i'll paint it red:
"RokuMarkn" wrote:
Also note that SwapBuffers waits for the next vertical sync, so it's expected that it could take up to 16 ms. That's not a performance problem, it's the way SwapBuffers has to work.

What it means is that when using double-buffered roScreen, you cannot get frame rate higher than 60fps. And I mean never. Ever. Because that is how it works, vsync happening 60 times per second (silence procedure[/url:2r2dacel]), no more that 60 SwapBuffers() can pass per second.

I ran into this...




0 Kudos
TheEndless
Level 9

Re: Performance problems on roScreen

"EnTerr" wrote:
This is a profound note worth highlighting, so i'll paint it red:
"RokuMarkn" wrote:
Also note that SwapBuffers waits for the next vertical sync, so it's expected that it could take up to 16 ms. That's not a performance problem, it's the way SwapBuffers has to work.

What it means is that when using double-buffered roScreen, you cannot get frame rate higher than 60fps. And I mean never. Ever. Because that is how it works, vsync happening 60 times per second (silence procedure[/url:37xh8484]), no more that 60 SwapBuffers() can pass per second.

I ran into this...





It's also no longer guaranteed not to "tear" the visible image on the previous gen hardware (including the new stick). I believe this is a bug, and I've just about got the example worked out that I can pass along to Roku, so they can hopefully address it. In short, if you create an in-memory bitmap (i.e., not from a path) after you've initiated drawing to the back buffer, but before you call SwapBuffers(), you're more or less guaranteed a tear/flicker.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
EnTerr
Level 11

Re: Performance problems on roScreen

"TheEndless" wrote:
This operation can be extremely fast (that is, it never copies a bitmap from one location to another) but in double-buffered mode it is guaranteed not to "tear" the visible image, so it waits for the next vertical sync and can take up to 17ms.

It's also no longer guaranteed not to "tear" the visible image on the previous gen hardware (including the new stick). I believe this is a bug, and I've just about got the example worked out that I can pass along to Roku, so they can hopefully address it. In short, if you create an in-memory bitmap (i.e., not from a path) after you've initiated drawing to the back buffer, but before you call SwapBuffers(), you're more or less guaranteed a tear/flicker.

That is odd.
Do i understand right, if you call createObject("roBitmap") while in midst of painting to back buffer, it will prematurely show the back buffer, before SwapBuffers being called? Also if you keep painting after that createObject but before swapBuffers, where does that go?
0 Kudos
TheEndless
Level 9

Re: Performance problems on roScreen

"EnTerr" wrote:
"TheEndless" wrote:
This operation can be extremely fast (that is, it never copies a bitmap from one location to another) but in double-buffered mode it is guaranteed not to "tear" the visible image, so it waits for the next vertical sync and can take up to 17ms.

It's also no longer guaranteed not to "tear" the visible image on the previous gen hardware (including the new stick). I believe this is a bug, and I've just about got the example worked out that I can pass along to Roku, so they can hopefully address it. In short, if you create an in-memory bitmap (i.e., not from a path) after you've initiated drawing to the back buffer, but before you call SwapBuffers(), you're more or less guaranteed a tear/flicker.

That is odd.
Do i understand right, if you call createObject("roBitmap") while in midst of painting to back buffer, it will prematurely show the back buffer, before SwapBuffers being called? Also if you keep painting after that createObject but before swapBuffers, where does that go?

No, it doesn't confuse the buffers, it just causes a flicker/tear in the screen during the SwapBuffers(), like the draw never completes. Here's a quick sample that causes the issue. This should just create a red screen, but on the previous gen Roku 2s and the current streaming stick, you only get partial coverage. Replace it with content more complex/dynamic than just a red rect, and the vertical position of the tear is less predictable.
screen = CreateObject("roScreen", True, 1280, 720)
While True
' Initiate drawing the the back buffer by clearing it.
screen.Clear(0)
' Create a bitmap in memory. We're not going to use it for anything.
' I believe the allocation of memory is what causes the flicker/tear.
' Comment this line out, replace it with a roBitmap created from a local path, or just move it above the Clear() and the flicker goes away.
bitmap = CreateObject("roBitmap", { Width: 100, Height: 100, AlphaEnable: False }) 'Height and Width don't matter here
' Draw a red rect over the screen to make the issue more apparent
screen.DrawRect(0, 0, 1280, 720, &HFF0000FF)
screen.SwapBuffers()
End While
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
EnTerr
Level 11

Re: Performance problems on roScreen

"TheEndless" wrote:
it just causes a flicker/tear in the screen during the SwapBuffers(), like the draw never completes. Here's a quick sample that causes the issue.

Beautiful short example, it was very easy for me to experiment with it (and of course i observed the buggy behavior too). I found a couple of things more - you dont have even to do drawRect() - even doing second clear() shows the issue.

But the fun part is, bug does not happen when roBitmap is created but when it is disposed! To verify, just replace assignment bitmap = CreateObject(...) with appending the new bitmap to an array arr.push( createObject("roBitmap", ...) ) and... no blinking. So what happens is when you assign the new bitmap to the variable, old one is not used and freed - and there seems to lie the problem. Here is variation of the code that takes bitmap creation outside the loop, to confirm if disposal is the sole suspect:
    arr = []  'prep me 100 bitmaps to drop
for i = 0 to 100:
arr.push( createObject("roBitmap", { Width: 10, Height: 10, AlphaEnable: False }) )
end for
screen = CreateObject("roScreen", True)
While True
screen.Clear(0)
? arr.pop()
screen.Clear(&hFF0000FF)
screen.SwapBuffers()
End While
Run this and watch the console - screen "tears" only until the initial ammo of 100 bitmaps is exhausted and after that is all-red.

Have you flagged someone @Roku on this or should we start jumping up and down waving till someone notices?
This probably should be torn into separate thread... moderator, please?
0 Kudos
TheEndless
Level 9

Re: Performance problems on roScreen

"EnTerr" wrote:
But the fun part is, bug does not happen when roBitmap is created but when it is disposed!

I actually meant to mention that it happens on dispose, but I thought it was "as well" and not "instead" of creating. I was also wrong about it not happening with bitmaps created from a path, as I'm seeing it with that now, too. Perhaps my previous test was with a bitmap that took longer to load.

"EnTerr" wrote:
Run this and watch the console - screen "tears" only until the initial ammo of 100 bitmaps is exhausted and after that is all-red.

To test that properly, you probably need to create the bitmaps inside the loop as well. The way you've done it in this example excludes that variable by creating them in advance, so it could possibly be an "as well".

"EnTerr" wrote:
Have you flagged someone @Roku on this or should we start jumping up and down waving till someone notices?

Not yet. I've not had the free time to solidify the example (as you found by experimenting yourself), so I've not sent anything to Roku, yet.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
EnTerr
Level 11

Re: Performance problems on roScreen

"TheEndless" wrote:
"EnTerr" wrote:
Run this and watch the console - screen "tears" only until the initial ammo of 100 bitmaps is exhausted and after that is all-red.
To test that properly, you probably need to create the bitmaps inside the loop as well. The way you've done it in this example excludes that variable by creating them in advance, so it could possibly be an "as well".

I mentioned that already above in "To verify, just replace assignment bitmap = CreateObject(...) with appending the new bitmap to an array arr.push( createObject("roBitmap", ...) ) and... no blinking." - that is inside the loop; i tried it and to restate that, the bug does not occur if you hold onto the bitmaps created in the loop. So it is not "as well". Sorry being not clear enough, did not want to overwhelm bystanders with snippet of code _not_ demonstrating the bug.

PS. that it happens with bitmaps from file too is great - i mean it would be suspicious if GC were picky causing the bug only on disposing particular bitmaps. The more peculiar a bug is, the harder to track and shoot.
0 Kudos