Inaccurate vertical blank timing (VBL)

This forum is for general developer support questions.
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Fri Aug 05, 2011 12:08 am
Location: Norway

Inaccurate vertical blank timing (VBL)

Post by jostein_aarbakk »

I am currently programming a simple game for OS4 (non-commercial, just for fun), and are now working on a screen with 2D scrolling.
I have no problems making a glitch-free scrolltext, as long as it's far down on the screen.
However, if I put my scrolltext on the upper part of the screen (especially at the upper position 0), it doesn't scroll smooth anymore (graphics glitches and occasional 1-frame freezes now and then).

The program is made in C, and I use Intuition & Graphics library (no SDL, MiniGL or similar).
Scrolling is made by using the Graphics library "BltBitMapRastPort"/"BltBitMap"-procedures.
It's "single buffered", but the graphics are so light, that this shouldn't be a problem.

The graphics I "blit" is very small (672x32 pixels, 32 colours).
The screen resolution is 640x512 pixels, and of course fullscreen mode on a separate screen.


On my system, I have the following issues:
1) Graphics glitches when my program scrolls an object on the screen, using a loop with WaitTOF() in it:
=> See description of the issue above.

2) The Graphics.library function "VBeamPos" increases by 1 position for each vertical blank (after each WaitTOF-call).
=> See the output I got below. It's consistently +1 compared to the previous VBL. This is strange, as I would expect the value to be approximately fixed.
This must either be a bug in the VBeamPos-function, or it indicates a VBL synchronization issue that leads to the issue described in 1) above and 3) below.

3) Graphics glitches when my program scrolls an object upwards on the screen, using a VBL-interrupt containing a "BltBitMapRastPort"-call (through Exec's "AddIntServer"-function):
=> The glitches are different than for case 1). The scrolling runs smooth, except for a few fixed intervals (a horizontal stripe moving upwards on the screen).
---



Here is parts of the code I used to find the vertical beam position:

while (!done)
{
IGraphics->WaitTOF();
pos = IGraphics->VBeamPos();
IDOS->Printf("%ld ",pos);
.
.
}


...and the result (it's strange it's so consistent...):

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124



My system:
HW: Sam440ep (mini-ITX) w/512MB RAM and onboard Radeon M9 Graphics card. The monitor is a CRT type, synced at 60 Hz (Vertical).
OS: OS 4.1 update 4.


Thanks in advance for your assistance.
User avatar
Hans
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 703
Joined: Tue Dec 21, 2010 9:25 pm
Location: New Zealand
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by Hans »

jostein_aarbakk wrote:I am currently programming a simple game for OS4 (non-commercial, just for fun), and are now working on a screen with 2D scrolling.
I have no problems making a glitch-free scrolltext, as long as it's far down on the screen.
However, if I put my scrolltext on the upper part of the screen (especially at the upper position 0), it doesn't scroll smooth anymore (graphics glitches and occasional 1-frame freezes now and then).
Sounds like a good project.
jostein_aarbakk wrote:The program is made in C, and I use Intuition & Graphics library (no SDL, MiniGL or similar).
Scrolling is made by using the Graphics library "BltBitMapRastPort"/"BltBitMap"-procedures.
It's "single buffered", but the graphics are so light, that this shouldn't be a problem.

The graphics I "blit" is very small (672x32 pixels, 32 colours).
The screen resolution is 640x512 pixels, and of course fullscreen mode on a separate screen.


On my system, I have the following issues:
1) Graphics glitches when my program scrolls an object on the screen, using a loop with WaitTOF() in it:
=> See description of the issue above.
As a general rule, if you want smooth graphics without tearing, then you should use double-buffering. You can use true double-buffering via IIntuition->ChangeScreenBuffer(), or use vertical blanking interrupts and a blit from the back-buffer.

Also note that, in a multi-tasking environment, you're not guaranteed to get the CPU time that you need for your calculations and blits within the vertical-blanking period.
jostein_aarbakk wrote:2) The Graphics.library function "VBeamPos" increases by 1 position for each vertical blank (after each WaitTOF-call).
=> See the output I got below. It's consistently +1 compared to the previous VBL. This is strange, as I would expect the value to be approximately fixed.
This must either be a bug in the VBeamPos-function, or it indicates a VBL synchronization issue that leads to the issue described in 1) above and 3) below.
AFAIK, Picasso96 doesn't support VBeamPos; that's a classic Amiga chipset thing.
jostein_aarbakk wrote:3) Graphics glitches when my program scrolls an object upwards on the screen, using a VBL-interrupt containing a "BltBitMapRastPort"-call (through Exec's "AddIntServer"-function):
=> The glitches are different than for case 1). The scrolling runs smooth, except for a few fixed intervals (a horizontal stripe moving upwards on the screen).
Are you sure that you are catching the VBL interrupt? I don't think that you even have access to the information required to know which interrupt to use. Plus, what if VBL interrupts are disabled? WaitTOF() and ChangeScreenBuffer() still work; your interrupt routine won't.

Hans

P.S. This message board also has a forums for developer topics. You'll see down the bottom of theindex page.
http://hdrlab.org.nz/ - Amiga OS 4 projects, programming articles and more. Home of the RadeonHD driver for Amiga OS 4.x project.
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Fri Aug 05, 2011 12:08 am
Location: Norway

Re: Inaccurate vertical blank timing (VBL)

Post by jostein_aarbakk »

Thanks for your quick feedback, I appreciate this.
Hans wrote: As a general rule, if you want smooth graphics without tearing, then you should use double-buffering. You can use true double-buffering via IIntuition->ChangeScreenBuffer(), or use vertical blanking interrupts and a blit from the back-buffer.
Ok.
The reason why I started with single buffering, was to keep the complexity and overhead down, especially as my blits moved so tiny, small areas of pixels. The graphics card should have no problems doing these operations during the vertical blanking period, before anything were displayed on the screen.
Double buffering wasn't something I wanted to use before absolutely necessary.

I have now added double buffering to my program using ChangeScreenBuffer(), but the scrolling still "refuse" to run perfect.
It runs smooth a second or 2 (it moves 1 pixel pr. WaitTOF), but suddenly it "freezes" 1/60th sec, and at the next frame it scrolls again (but 2 pixels instead of 1). After that it runs smooth for a while, then freezes and so on.
It doesn't matter if the scrolltext is far up on the screen, or at the bottom; the same thing happens each time (although, the time for when the "freezes" happens are random).
With single buffering, the freezes happens when placed high up on the screen, but not at the bottom.

I have tested the program without any other programs running at all (incl. Limpid Clock and CPUInfo), but the same thing happens.
I have tried with high task priority (+20), and even added a Forbid()-call (I know, I shouldn't use it. But I needed to test if it would help).
Nothing of this helped.

So; I suspect WaitTOF() isn't precise enough.
To check this, I have made a test program, where I measure how many microseconds it takes between each WaitTOF().
I am using ITimer->GetSysTime() for this, and the program prints the results in the shell after exit.
I have included the output from it at the bottom of this post.
What I see, is that within a period of 10 seconds, there are always a few "WaitTOF's" that takes either too long or too short time.
When I modify the testprogram to scroll parts of the screen, I see there is a connection between the "stalls" in the animation I see on the screen and the numbers in my list.

If you need the testprogram, just tell me, and I'll send it to you.

Hans wrote: AFAIK, Picasso96 doesn't support VBeamPos; that's a classic Amiga chipset thing.
Ok, I didn't know this. I believed this function returned the beam position regardless of the graphics hardware underneath.

Hans wrote: Are you sure that you are catching the VBL interrupt? I don't think that you even have access to the information required to know which interrupt to use. Plus, what if VBL interrupts are disabled? WaitTOF() and ChangeScreenBuffer() still work; your interrupt routine won't.
I am sure I have done it right according to how the vertical blanking interrupts should be setuped on the Classic.
But if things have changed in OS4 compared to the classic, there might of course be something I have overlooked.
The interrupt part of my program is "ported" from a program I have made on my classic Amiga, and is programmed according to what the ROM Kernel Ref. manuals says.
It works perfect on my Classic, and it works on OS4 (but not perfect. Some "tearing" every 3 seconds or so. That is, a horizontal "stripe" moving slowly in the vertical direction.).
Technically, the "intNum"-parameter passed to the AddIntServer, is INTB_VERTB.
The Interrupt structure's "ln_Type" passed to AddIntServer, is set to NT_INTERRUPT.

I wasn't aware of that vertical blanking interrupts in the OS ever could be disabled (it's enabled on my system, at least), as my thoughts were that any monitor needs some kind of a refresh on a given interval, even if the vertical blanking technique we know from the classic Amigas w/PAL/NTSC monitors not necessarily will be needed in certain setups.

Hans wrote: P.S. This message board also has a forums for developer topics. You'll see down the bottom of theindex page.
Ok. I'll have that in mind for the future, even if they are like this one (to address possible bugs).




// The list below shows how long intervals there is between each WaitTOF().
// I believe the correct duration in microseconds between 2 TOF's is: 1 000 000 / 60 = 16667 microsec.
// The "MicrosecDiff" shows the difference of the duration compared to the previous frame.
// When this number is too high, I see "freezes" in my animations on the screen.
// With frame, I mean each time the screen is refreshed. On my system, that is 1/60th second.
//
// Differences of several 1000's Microseconds are clearly visible as "freezes" in my screen scrolling.
// Differences less than 100 Microsecs are excluded from the list. as I don't expect them to do any "harm".
// CPU usage while running was 2-4%.

// PS: It seems like when 1 WaitTOF() takes longer than normal, the next one is shorter so the 2 in total consumes the expected time to "catch up".

// Test 1: Only TOF (no blitting etc.), 600 frames in total (10 seconds), task pri 0.
// No running programs in the background.
Frame #: 81, Microsec: 18070, MicrosecDiff: 1307
Frame #: 82, Microsec: 15460, MicrosecDiff: -2610
Frame #: 83, Microsec: 16770, MicrosecDiff: 1310
Frame #: 183, Microsec: 17392, MicrosecDiff: 628
Frame #: 184, Microsec: 16144, MicrosecDiff: -1248
Frame #: 185, Microsec: 16761, MicrosecDiff: 617
Frame #: 260, Microsec: 17240, MicrosecDiff: 472
Frame #: 261, Microsec: 16293, MicrosecDiff: -947
Frame #: 262, Microsec: 16767, MicrosecDiff: 474
Frame #: 558, Microsec: 21445, MicrosecDiff: 4673
Frame #: 559, Microsec: 12083, MicrosecDiff: -9362
Frame #: 560, Microsec: 16762, MicrosecDiff: 4679

// Test 2: Same test over again, but run with task priority +20
Frame #: 10, Microsec: 17377, MicrosecDiff: 615
Frame #: 11, Microsec: 16157, MicrosecDiff: -1220
Frame #: 12, Microsec: 16770, MicrosecDiff: 613
Frame #: 308, Microsec: 21586, MicrosecDiff: 4821
Frame #: 309, Microsec: 11949, MicrosecDiff: -9637
Frame #: 310, Microsec: 16770, MicrosecDiff: 4821
Frame #: 487, Microsec: 20853, MicrosecDiff: 4089
Frame #: 488, Microsec: 12678, MicrosecDiff: -8175
Frame #: 489, Microsec: 16766, MicrosecDiff: 4088

// Test 3: Same test, but with task pri 0
Frame #: 135, Microsec: 18039, MicrosecDiff: 1274
Frame #: 136, Microsec: 15497, MicrosecDiff: -2542
Frame #: 137, Microsec: 16758, MicrosecDiff: 1261
Frame #: 268, Microsec: 16687, MicrosecDiff: -159
Frame #: 314, Microsec: 17225, MicrosecDiff: 461
Frame #: 315, Microsec: 16314, MicrosecDiff: -911
Frame #: 316, Microsec: 16757, MicrosecDiff: 443
User avatar
Hans
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 703
Joined: Tue Dec 21, 2010 9:25 pm
Location: New Zealand
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by Hans »

jostein_aarbakk wrote: ...
So; I suspect WaitTOF() isn't precise enough.
It is precise enough if interrupts are switched on. Check your monitor tooltypes, and make sure that INTERRUPT=Yes is set.
jostein_aarbakk wrote:
Hans wrote: Are you sure that you are catching the VBL interrupt? I don't think that you even have access to the information required to know which interrupt to use. Plus, what if VBL interrupts are disabled? WaitTOF() and ChangeScreenBuffer() still work; your interrupt routine won't.
I am sure I have done it right according to how the vertical blanking interrupts should be setuped on the Classic.
But if things have changed in OS4 compared to the classic, there might of course be something I have overlooked.
The interrupt part of my program is "ported" from a program I have made on my classic Amiga, and is programmed according to what the ROM Kernel Ref. manuals says.
It works perfect on my Classic, and it works on OS4 (but not perfect. Some "tearing" every 3 seconds or so. That is, a horizontal "stripe" moving slowly in the vertical direction.).
Technically, the "intNum"-parameter passed to the AddIntServer, is INTB_VERTB.
The Interrupt structure's "ln_Type" passed to AddIntServer, is set to NT_INTERRUPT.
Things that worked on the classic Amiga aren't guaranteed to work with RTG graphics cards. This is one of them. The horizontal stripe is a hint to you that the timing is not in sync with the monitor.
jostein_aarbakk wrote:
Hans wrote: P.S. This message board also has a forums for developer topics. You'll see down the bottom of theindex page.
Ok. I'll have that in mind for the future, even if they are like this one (to address possible bugs).
It sounded to me like you were asking for coding help. Besides, the problem is in your code/system, and not the OS (other people have games that are vsynced just fine). When developing software and something isn't working, you should assume that the bug is in your new code first.

Hans
http://hdrlab.org.nz/ - Amiga OS 4 projects, programming articles and more. Home of the RadeonHD driver for Amiga OS 4.x project.
User avatar
ssolie
Beta Tester
Beta Tester
Posts: 1010
Joined: Mon Dec 20, 2010 8:51 pm
Location: Canada
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by ssolie »

Moved to correct forum.
ExecSG Team Lead
User avatar
jostein_aarbakk
Beta Tester
Beta Tester
Posts: 37
Joined: Fri Aug 05, 2011 12:08 am
Location: Norway

Re: Inaccurate vertical blank timing (VBL)

Post by jostein_aarbakk »

@Hans:
Hello again,

Just a status:
I am happy to announce that after "playing" some more with my code, I have gotten the program to scroll smoothly 95% of the time during a 10 sec period, and sometimes 100%.
Much better than before.
Solution: To synchronize the changeScreenBuffer()-call, I needed to make use of Exec's message ports as described in the autodocs for IGraphics->AllocDBufInfo.

The scrolling runs smoothly most of the time, but there still are some minor occasional "stalls" in the upper part of the screen now and then (but way less severe than before).
However, if I avoid scrolling the upper 30-50 lines of pixels or so, the risk for getting interruptions is heavily reduced.
Avoiding use of the "Limpid clock" also helped (of some weird reason).
If there are ways to reduce the risk for making the remaining minor "stalls" appear, I would be happy (I am kind of obsessed by perfect, smooth scrolling).

The previous version of my program relied on WaitTOF() only, in order to synchronize the changeScreenBuffer()-call, and that just didn't do it (lead to random, clearly visible "stalls" in the scrolling now and then.).
The program code is ok.

Regarding the monitor tooltype; Interrupts was on.


PS: My post was written with the best intentions, and written with the details needed to use them sometime in the future to check if there is a bug somewhere (hardware-related? OS? ...).
I was not complaining in any way. I am an Amiga-enthusiast, and want the platform to be as good as possible.
Before I report bugs, asks for help etc., I always do proper research in advance. I don't want to cause unnecessary work for others.


Jostein
User avatar
Hans
AmigaOS Core Developer
AmigaOS Core Developer
Posts: 703
Joined: Tue Dec 21, 2010 9:25 pm
Location: New Zealand
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by Hans »

jostein_aarbakk wrote:@Hans:
Hello again,

Just a status:
I am happy to announce that after "playing" some more with my code, I have gotten the program to scroll smoothly 95% of the time during a 10 sec period, and sometimes 100%.
Much better than before.
Solution: To synchronize the changeScreenBuffer()-call, I needed to make use of Exec's message ports as described in the autodocs for IGraphics->AllocDBufInfo.
Good to hear.
jostein_aarbakk wrote:Avoiding use of the "Limpid clock" also helped (of some weird reason).
Interesting. Maybe "Limpid clock" is running at a higher priority level, and/or hogging the CPU for large chunks of time. That could delay the completion of your program's rendering until after the next VBlank, resulting in a one frame stall.
jostein_aarbakk wrote:If there are ways to reduce the risk for making the remaining minor "stalls" appear, I would be happy (I am kind of obsessed by perfect, smooth scrolling).
From what you've said so far, the most likely cause is some task on your system periodically hogging the CPU so long that you can't generate the next frame before the next VBlank period. You're using a Sam440ep, so it probably wouldn't take much to cause this. You could try shutting down as many unnecessary programs as possible to see if one of them is causing this. It might be worth double-checking that all your drives are using DMA modes. I don't think that this is the cause, but try disabling compositing for the Workbench screen. Rendering a composited screen does take a little extra work.

You could also try raising the task priority slightly (i.e., up by 1 or 2, you don't want to go too high, or you'll interfere with tasks that really are high priority). That will make it more likely that you'll get the CPU time that you need.

Another trick is to use triple buffering, but that should only be necessary if you're doing heavy processing/rendering. This reduces/eliminates the time that you wait for the back buffer to become available, giving your game/app more CPU time to generate the next frame in time.

Do you adapt your animation to the frame-rate? If not, it's worth doing, as it will minimise the visual effect of the occasional 1-2 frame delay. Since you're writing a game, it's worth reading this physics tutorial about getting the physics simulation running in constant time even with variable frame-rates. That tutorial helped me when writing the Composite3DDemo.
jostein_aarbakk wrote:The previous version of my program relied on WaitTOF() only, in order to synchronize the changeScreenBuffer()-call, and that just didn't do it (lead to random, clearly visible "stalls" in the scrolling now and then.).
The program code is ok.
That's because ChangeScreenBuffer() itself is interrupt triggered, so calling WaitTOF() beforehand can add an extra frame's delay.
jostein_aarbakk wrote:Regarding the monitor tooltype; Interrupts was on.
Okay, so that isn't causing timing issues.

jostein_aarbakk wrote:PS: My post was written with the best intentions, and written with the details needed to use them sometime in the future to check if there is a bug somewhere (hardware-related? OS? ...).
I was not complaining in any way. I am an Amiga-enthusiast, and want the platform to be as good as possible.
Before I report bugs, asks for help etc., I always do proper research in advance. I don't want to cause unnecessary work for others.
No worries. I assumed that you had the best intentions. However, it's pretty easy to overlook bugs in your own code and assume that the problem lies elsewhere, which is what I was trying to get across. My apologies if I came across a bit harsh.

I think that one of the problems here is lack of examples for smooth animation. One of the reasons that I released the source-code of the Composite3DDemo, was so that people would have some example code. However, it's in C++ which some people dislike or find scary, and it's a lot more complicated than necessary for demonstrating double-buffering. Something simpler and in plain C would definitely be worth having in the SDK.

Hans
http://hdrlab.org.nz/ - Amiga OS 4 projects, programming articles and more. Home of the RadeonHD driver for Amiga OS 4.x project.
User avatar
nbache
Beta Tester
Beta Tester
Posts: 1744
Joined: Mon Dec 20, 2010 7:25 pm
Location: Copenhagen, Denmark
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by nbache »

Hans wrote:
jostein_aarbakk wrote:Avoiding use of the "Limpid clock" also helped (of some weird reason).
Interesting. Maybe "Limpid clock" is running at a higher priority level, and/or hogging the CPU for large chunks of time. That could delay the completion of your program's rendering until after the next VBlank, resulting in a one frame stall.
Is LimpidClock showing seconds? Analog or digital clock (or even both)? Could be interesting to see if anything changes if you turn off seconds in LC.

Best regards,

Niels
User avatar
thomasrapp
Posts: 318
Joined: Sun Jun 19, 2011 12:22 am

Re: Inaccurate vertical blank timing (VBL)

Post by thomasrapp »

LimpidClock refreshes every second, no matter if the seconds hand or even the entire clock is switched of.
User avatar
nbache
Beta Tester
Beta Tester
Posts: 1744
Joined: Mon Dec 20, 2010 7:25 pm
Location: Copenhagen, Denmark
Contact:

Re: Inaccurate vertical blank timing (VBL)

Post by nbache »

thomasrapp wrote:LimpidClock refreshes every second, no matter if the seconds hand or even the entire clock is switched of.
Ah, I didn't know that. Then I guess it wouldn't make any difference. Unless of course there are some optimizations somewhere in the system which makes shortcuts when there are no graphical differences to render?


Best regards,

Niels
Post Reply