Occasional "stuttering" in graphics scrolling

This forum is for general developer support questions.
Post Reply
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Thu Aug 04, 2011 11:08 pm
Location: Norway

Occasional "stuttering" in graphics scrolling

Post by jostein_aarbakk »

Hello,


I have the following issue on my AmigaOS4-system:
Occasional "stuttering" in graphics animations (f.ex. in 2D horisontal screen scrolling, using blitting operations).
I've been working on making a simple game for AmigaOS4 with focus on smooth animations (in full screen), but it seems "impossible" to make it run 100% glitch-free, unless I avoid running other programs in the background (Limpid clock, Transmission etc.).
Even then, it won't run glitch-free without using task priority +16 (yes, I know. I shouldn't...), and by starting it from Workbench instead of the Shell (The Icon attribute "Start from Workbench" needs to be set).
And it's not just my own programs that have this issue.

Observations from tests of mine/possible causes:
The "keys" for possible causes seems to be the following:
- The graphics.library function "WaitTOF()" (sometimes returns too late)
- Signals in Exec (which probably is the main cause for the above)

I've made testprograms that does the following:
1) Measure the time between WaitTOF() calls in a loop for 10 seconds.
2) Measures the time between each vertical blank (INTB_VERTB) interrupt occurrence for 10 seconds (using ITimer->GetSysTime() in the interrupt function).
3) Sends an Exec signal from each vertical blank interrupt (INTB_VERTB) occurrence to my main program, and measures the interval between each retrieved signal for a period of 10 seconds.

The testdata gets stored in an array, and there are no printf()'s etc. that might make my program or testresults unreliable.

Results from each test:
1) FAIL: The interval between 2 calls gets occasionally too high (up to 4500 microseconds). Delays of this size always leads to "stuttering" in gfs animations.
2) OK: The highest variance measured, was 14 microseconds (!!), but usually <10, even if I stressed my system with other background tasks. This proves that the Vertical Blanking interrupt itself is amazingly precise.
3) FAIL: Same result as for test #1.


Conclusion:
The "root cause" seem to be related to Exec signals.
(It often takes too long time between an Exec signal is being sent to it's received, and the time used varies too much).

I (and most likely other Amigans as well) would be very thankful if this issue could be solved somehow, as "smoothness" has traditionally been one of the biggest strengths of classic Amigas.

If some of you in the AmigaOS team have time to look at this issue, just let me know, and I'll send you my testprograms and test logs.



Thanks in advance for spending time on this.


My system:
Sam440ep (667MHz, 512MB RAM, Onboard Radeon M9 gfx-card), AmigaOS4.
spotUP
Posts: 9
Joined: Sat Jun 18, 2011 4:25 pm

Re: Occasional "stuttering" in graphics scrolling

Post by spotUP »

interesting observation, thanks for reporting and testing!
whose
Posts: 92
Joined: Sun Sep 04, 2011 3:11 pm

Re: Occasional "stuttering" in graphics scrolling

Post by whose »

This result isn't new at all ;-)

I made the same observations back in 2007 with Amijeweled on 68k RTG systems. I don't think that there is very much the OS developers could do against this, as it is mostly about tasks and their run priorities. If your task is of low priority (e.g. < 15), it will be delayed by other tasks with higher priorities and just made "ready" on signal receive, but will not run instantaneously.

Most of all this is input.device load, but there are some other tasks/processes running on higher priority (15 or higher). This is where the exec signals have the brake on (most of the time, but take heavy interrupt action into account, too). And it's the reason for the differences in signal run time.

As for the other "disturbances", there is e.g. screen bitmap locking (although it shouldn't lock other screen's bitmaps, it actually does). Might become avoidable in the future, but actually there is no proper way around it.

Actually, I experience these glitches myself, doing some tests with >350 moving objects. The glitches appear on the upper few lines only. I don't know if this is different for you?
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Thu Aug 04, 2011 11:08 pm
Location: Norway

Re: Occasional "stuttering" in graphics scrolling

Post by jostein_aarbakk »

When single buffering, the glitches appear on the top. How far down, depends on how delayed the vertical blanking finalization is.
With double buffering, it appear for the whole screen of course (as buffer swapping gets delayed).

The strange thing is that this happen's even when CPU-load is at idle.
And when I run with higher CPU-loads in the background, the glitches appear even if I set the task priority to +100 (in worst case scenario, it gets the resources for a few seconds, then it stops, continues, etc.).
--

(I haven't tried any 68k Amigas with graphics card, but I've been programming on classic Amiga's with built-in graphics chipset, and the above has never been an issue on my A1200. Always "Butterly smooth", unless there are no available HW-resources left..).


So, as the CPU on my Sam is much faster than the 020 & 030, and the "disturbances" occur even on low CPU-load, and the programs tested are simple, my Sam should ideally have no problems handling signals and WaitTOF() with minimal delay.
But unfortunately it has...
whose
Posts: 92
Joined: Sun Sep 04, 2011 3:11 pm

Re: Occasional "stuttering" in graphics scrolling

Post by whose »

Which kind of doublebuffering do you use? I do it using a simple backbuffer bitmap, blitted to the foreground bitmap after WaitTOF(), atm. I have the glitches only at the top of the picture.

I'm using a SAM440ep, onboard gfx. Maybe there is some driver glitch that you get "fullscreen distortions"?

Edit: Forgot something. Are you using "old style" blit operations (with masking) or Compositing?
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Thu Aug 04, 2011 11:08 pm
Location: Norway

Re: Occasional "stuttering" in graphics scrolling

Post by jostein_aarbakk »

I use this type of doublebuffering:
- 2 buffers (front & back), allocated by IIntuition->AllocScreenBuffer().
- Image blitting etc. only on the back buffer

Concept in the loop:
1. WaitTOF()
2. Makes the backbuffer visible by swapping the 2 buffers (using IIntuition->ChangeScreenBuffer())
3. Copies the Frontbuffers bitmap to the Backbuffer's bitmap (using IGraphics->BltBitMap())
4. ...does all the animations etc. on the Backbuffer bitmap

The screen is 8 bit CLUT, and for Blit'ing, I am using the "oldstyle" IGraphics->BltBitMap() function.
For blitting with masking, I use 2 separate Blit's with 2 different minterms.
I don't use compositing.

I guess the reason I get the distortions on the whole screen, while you get them only at the top, is because I change the Bitmap pointer pr. VBL,
while you keeps it unchanged, and instead just blits into the front bitmap.
whose
Posts: 92
Joined: Sun Sep 04, 2011 3:11 pm

Re: Occasional "stuttering" in graphics scrolling

Post by whose »

Uh oh... ChangeScreenBuffer() + WaitTOF() instead of using the "ready_to_change and read_to_draw" signals on appropriate ports is a rather bad idea. Try the recommended way first. For this, have a look into Hans´ Compositing demo to learn how to use the system double buffering. It´s a quite delicate thing with ChangeScreenBuffer() and if its timing is slightly distorted by anything it won´t work very well (it didn´t even on 68k RTG machines back then).

Your task might become delayed by other system tasks (even if there is very low CPU load) when WaitTOF() returns. I don´t know why you blit the complete foreground into your back buffer, but this might also be a source of the distortions you´re encountering. If possible, never do any blit operations involving the (now visible) foreground and do all operations within the back buffer. If necessary, use a third (invisible) bitmap containing ALL gfx you wish to blit into the back buffer. Take care that this third bitmap is located in the video RAM (e.g. friend bitmap).

I get the distortions because the ordinary blit to the foreground (after a WaitTOF(), btw.) isnt fast enough sometimes, because sometimes there is some other task delaying my tasks running phase. I don´t have the distortions on every frame, btw. Here the distortions become more frequent if something rather heavy like OWB, displaying some gif or png animation, is working in the background. But they are still in the upper half of the display. Might get better if I do the buffer change blit within the VBI, but I didn´t dare to do this, yet ;-)
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Thu Aug 04, 2011 11:08 pm
Location: Norway

Re: Occasional "stuttering" in graphics scrolling

Post by jostein_aarbakk »

I have actually tried the recommended way of double buffering "by the book" in the past, using messagePorts for each of the the 2 buffers ("Safe to display" and "Safe to write").
(Without any WaitTOF() at all in the loop..).
But it doesn't help; Same results.

As mentioned, I don't do any graphics processing at all, until the buffers actually are swapped (so it will never disturb the display part at all).
And never on the front buffer (if I did, there would be no point in double buffering).
I copy the Front to the back immediatly AFTER swapping buffers, in order to make the back buffer contain the same image as the displayed one before any graphics operations are put on the back buffer.

At each frame, making the "ready to display" background image visible as fast as possible is important.
This copy is necessary because I use double buffering with swapping of the 2 buffers pr. frame.

For making the back buffer visible, I avoid blitting from the background to foreground buffer, because blitting takes time, and "tearing" etc. could easily be visible on the screen.
And; If I did this, I wouldn't need to use ChangeScreenBuffer() at all.

And it's not just my programs that suffers with "occasional graphics stuttering".

PS: For experiment, I've also tried to use a blit inside a VBL interrupt, and that gets rid of the occasional disturbances.
However, there is a tearing effect when doing this, so it's not an option to do it like that (yet).


The key here, is that no matter how the display is being refreshed (single buffering, double buffering), the WaitTOF() returns too often too late.
And that is the key.
If that could be improved, the glitches would disappear.
whose
Posts: 92
Joined: Sun Sep 04, 2011 3:11 pm

Re: Occasional "stuttering" in graphics scrolling

Post by whose »

No question, improvement of this mechanism is always good ;-)

But I think that there is no "improvement" possible regarding the things that happen after the WaitTOF() resp. signal arrival. Every Wait() for signals doesn´t (and shouldn´t) guarantee that a task with normal priority is run immediately. Only guarantee is, that this task is switched to "Ready" state as soon as possible and will get a time share according to its priority. So, using a task priority as high as possible is the way to go for now. Nonetheless, there is still no guarantee that there isn´t any background task interrupting yours in the wrong time (result: gfx glitches).

The tearing effect that is seen if some bitmap manipulation is done within the VBI, seems to originate from "delayed blit". I don´t know exactly, but I could think of blitting operations being tied to the VBI. So, if the blit is started right after the VBI occurs, it might run behind (and overrun at last!) the (virtual) raster beam.

Moreover, for some hardware there is no real hardware "blitting" anymore. These blits are done by software or using the 3D hardware where possible. The drawback of the latter is, that the 3D hardware needs more operations for setup than it was the case for 2D hardware blitters.

Better solution would be to change the screen buffer pointers within the VBI server. I don´t know if this is done this way already. I would think so, as only a VBI server would be able to determine, if a screen buffer change actually happened or not and when to signal the drawing task.

Btw., on e.g. Windows you will see the same effect for 2D gfx done the traditional way. Most titles try to use 3D hardware acceleration for 2D gfx whereever possible, even OpenGL is used. But nonetheless, there are still slight interruptions very similar to the ones I experience, if you don´t have an utterly fast machine. Taking this into account, e.g. the SAM440ep does a quite good job ;-)

One could say, that modern gfx hardware became a bit lame regarding 2D gfx display...
whose
Posts: 92
Joined: Sun Sep 04, 2011 3:11 pm

Re: Occasional "stuttering" in graphics scrolling

Post by whose »

Ok, I did some tests with task priorities, and they show that it's valid: It's all about priorities.

With Pri +19, my task always gets the signal WaitTOF() gives fast enough to suppress most disturbances. Btw., it's running in the background while I'm writing this using MUI-OWB. While writing I switch back to it just to check. The only source of disturbance after a screen switch is input.device in conjunction with Intuition. After a single disturbance (caused by clicking into the invisible window, which in turn makes input.device throw some data around that Intuition has to check) the thing runs smoothly.

I know, higher priorities are "not nice" in a multitasking system, but there is some good reason sometimes to raise the priority. Stutter-free animation for a game running full screen is one of them ;-) As long as there is no delicate timing with I/O involved (and it shouldn't for any program that competes for the CPU against other tasks/processes! We have to always remember that we don't have a realtime system with guaranteed response times!), the (moderate) higher priority should do no harm to the system. If it does, the system is at fault.

One thing that is of great danger for such a system is software not behaving nice in the sense of CPU hogging (e.g. polling loops) combined with high priority. A developer of Amiga games should avoid such a thing under all circumstances. This is indeed feasible, so we should not fear that much to raise priorities moderately as long as our game is running. If our game is e.g. paused, there is nothing speaking against lowering the priority to "normal" level temporarily.

As for the good ol' times, there it was quite usual to take over the whole machine or suspend vital parts of the system in favour of selfmade replacements. Additionally, the timing wasn 't such a big concern (as the custom chips were quite slow compared to the CPU regarding bus access times, the timing was less critical than it is today). Depending on the complexity of the draw/display loop, it often wasn't possible to do full frame rate using the system double buffering, although the display refresh was in sync with the raster beam. Though you got 25 frames instead of 50. But it was absolutely possible that you got some stuttering if your system was filled up with background tasks, taking away CPU time from your task doing double buffering.

That's what we have today as standard. A standard OS4.x system is running lots of background tasks with priorities >0. So it's no wonder that your task is "a bit late" sometimes.

Btw., it's not only about priority, it's also about tight loops. If you wish to get stutter free animation, the switch after the "ready to change" signal should follow as soon as possible. Ideally, it should be located right after the Wait() with no additional jobs to do before. If there is something to do before ChangeScreenBuffer(), chances are high that a task with even higher priority "steals" some time that your task might miss.

Oh, and there is another thing: You won't get 100% accurate double buffering under all circumstances, as long as you don't take over the whole machine. As nice as multitasking is, it has some drawbacks. Like it is for everything in our universe. No good without bad ;-)
Post Reply