Double buffering using OS calls

GFA, ASM, STOS, ...

Moderators: exxos, simonsunnyboy, Mug UK, Zorro 2, Moderator Team

mikro
Atari God
Atari God
Posts: 1285
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Double buffering using OS calls

Postby mikro » Fri Aug 25, 2017 3:54 am

I'm deeply struggling with a seemingly trivial issue: double buffering. I wanted to explore the possibility of not setting own VBL handler and use OS calls instead. This is on Falcon.

Naturally, two choices comes into play: VsetScreen(log, phys, 3, resolution); and Vsync();

First I assumed that VsetScreen() waits for VBL, sets the screen and return. That would be ideal. However, it's not the case. It just sets the pointers (log -> $44e, phys -> $ff820x) and return. In case of the physical buffer the video base pointer is reloaded in the next VBL what would be fine unless you realise you need to set your logical pointers just at that time and not sooner or later (otherwise you switch phys/log buffers but the old buffer is still visible). So doing something like:

Code: Select all

VsetScreen(new_logbase, new_physbase, -1, -1);
...
mainloop:
    // ... my code writing to new_logbase
    VsetScreen(Physbase(), Logbase(), -1, -1);
    // swap new_logbase and new_physbase

isn't possible because "new_logbase" would contain value from "new_physbase" but this would still point to "live" picture until the next VBL.

So I thought all right, let's help the poor TOS a little:

Code: Select all

mainloop:
    // ... my code writing to new_logbase
    Vsync(); // wait for VBL explicitly here
    VsetScreen(Physbase(), Logbase(), -1, -1); // set the video base
    // swap new_logbase and new_physbase

and indeed, it does help. Most of the time. I don't know why but sometimes it seems like Vsync() is just too slow or too late or too whatever and the VBL is missed, crippling performance of the whole main loop.

Can anyone think of something better or spot a flaw?

User avatar
shoggoth
Nature
Nature
Posts: 853
Joined: Tue Aug 01, 2006 9:21 am
Location: Halmstad, Sweden
Contact:

Re: Double buffering using OS calls

Postby shoggoth » Fri Aug 25, 2017 5:12 am

mikro wrote:I'm deeply struggling with a seemingly trivial issue: double buffering. I wanted to explore the possibility of not setting own VBL handler and use OS calls instead. This is on Falcon.

Naturally, two choices comes into play: VsetScreen(log, phys, 3, resolution); and Vsync();

First I assumed that VsetScreen() waits for VBL, sets the screen and return. That would be ideal. However, it's not the case. It just sets the pointers (log -> $44e, phys -> $ff820x) and return. In case of the physical buffer the video base pointer is reloaded in the next VBL what would be fine unless you realise you need to set your logical pointers just at that time and not sooner or later (otherwise you switch phys/log buffers but the old buffer is still visible). So doing something like:

Code: Select all

VsetScreen(new_logbase, new_physbase, -1, -1);
...
mainloop:
    // ... my code writing to new_logbase
    VsetScreen(Physbase(), Logbase(), -1, -1);
    // swap new_logbase and new_physbase

isn't possible because "new_logbase" would contain value from "new_physbase" but this would still point to "live" picture until the next VBL.

So I thought all right, let's help the poor TOS a little:

Code: Select all

mainloop:
    // ... my code writing to new_logbase
    Vsync(); // wait for VBL explicitly here
    VsetScreen(Physbase(), Logbase(), -1, -1); // set the video base
    // swap new_logbase and new_physbase

and indeed, it does help. Most of the time. I don't know why but sometimes it seems like Vsync() is just too slow or too late or too whatever and the VBL is missed, crippling performance of the whole main loop.

Can anyone think of something better or spot a flaw?


Swapping logbase and physbase is a very brave thing to do.

There's a system variable somewhere iirc which is fetched on VBL, perhaps it could be used instead (less overhead and unwanted side effects, perhaps).
Last edited by shoggoth on Fri Aug 25, 2017 5:14 am, edited 1 time in total.
Ain't no space like PeP-space.

User avatar
shoggoth
Nature
Nature
Posts: 853
Joined: Tue Aug 01, 2006 9:21 am
Location: Halmstad, Sweden
Contact:

Re: Double buffering using OS calls

Postby shoggoth » Fri Aug 25, 2017 5:13 am

Edit: double post
Ain't no space like PeP-space.

mikro
Atari God
Atari God
Posts: 1285
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Re: Double buffering using OS calls

Postby mikro » Fri Aug 25, 2017 5:23 am

shoggoth wrote:There's a system variable somewhere iirc which is fetched on VBL, perhaps it could be used instead (less overhead and unwanted side effects, perhaps).

Yeah but then I'd need to be in Supervisor to fetch it. I can't use Supexec() to fetch it because FreeMiNT's way of doing Supexec() is super slow therefore I'd get outdated value again. So that kicks me back to the beginning.

User avatar
mfro
Atari Super Hero
Atari Super Hero
Posts: 674
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: Double buffering using OS calls

Postby mfro » Fri Aug 25, 2017 5:51 am

mikro wrote:
shoggoth wrote:There's a system variable somewhere iirc which is fetched on VBL, perhaps it could be used instead (less overhead and unwanted side effects, perhaps).

Yeah but then I'd need to be in Supervisor to fetch it. I can't use Supexec() to fetch it because FreeMiNT's way of doing Supexec() is super slow therefore I'd get outdated value again. So that kicks me back to the beginning.


I'd assume in MiNT, you'd need to do most of it in Supervisor mode anyway, at least the timing critical things.
In user mode, timing is unpredictable. If some stupid driver decides to busy wait on a slow I/O register just when you are supposed to switch screens, you lost.

User avatar
shoggoth
Nature
Nature
Posts: 853
Joined: Tue Aug 01, 2006 9:21 am
Location: Halmstad, Sweden
Contact:

Re: Double buffering using OS calls

Postby shoggoth » Fri Aug 25, 2017 6:02 am

mikro wrote:
shoggoth wrote:There's a system variable somewhere iirc which is fetched on VBL, perhaps it could be used instead (less overhead and unwanted side effects, perhaps).

Yeah but then I'd need to be in Supervisor to fetch it. I can't use Supexec() to fetch it because FreeMiNT's way of doing Supexec() is super slow therefore I'd get outdated value again. So that kicks me back to the beginning.


I bet you can access it with Setexc() or Ssystem()?
Ain't no space like PeP-space.

User avatar
ggn
Atari God
Atari God
Posts: 1132
Joined: Sat Dec 28, 2002 4:49 pm

Re: Double buffering using OS calls

Postby ggn » Fri Aug 25, 2017 6:03 am

TOS 4 VBL code does the following in this sequence:

[*]Bumps up frame clock
[*]Checks the lock semaphore vblsem ($452) to see if someone has locked the VBL and bails if yes
[*]Bumps up VBL clock
[*]Checks colorptr ($45a) and if not null, sets the palette and then clears the pointer
[*]Calls deferred interrupt vectors in _vblqueue ($456)
[*]Blinks the cursor
[*]Reloads display base register (i.e. sets up screen pointer) from _v_bas_ad ($44e)
[*]Checks for floppy drive select
[*]Checks for screen dump (alt+help) and calls screen dump

So there's quite a few things that happen before the VBL even attempts to set the screen. Maybe it would make sense to set a deferred vector and set the screen pointer yourself. Also, don't assume that the VBL occurs at exactly the vertical blank - the MFP has precedence over the VBL so if some long-ish interrupt happens then the VBL interrupt can be delayed massively. Same deal if you're loading large chunks of data off the disk.
is 73 Falcon patched atari games enough ? ^^

User avatar
mfro
Atari Super Hero
Atari Super Hero
Posts: 674
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: Double buffering using OS calls

Postby mfro » Fri Aug 25, 2017 6:18 am

I would try the other way round: stay in supervisor mode an do your stuff. Issue Syield() calls when you think you can afford it.

Not really nice to multitasking, but hey, a man has to do what ...

mikro
Atari God
Atari God
Posts: 1285
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Re: Double buffering using OS calls

Postby mikro » Fri Aug 25, 2017 6:31 am

ggn wrote:[Reloads display base register (i.e. sets up screen pointer) from _v_bas_ad ($44e)

Hey, this is good! First I was like "Heureka! This is why it fails!" because by default TOS provides the same address for both Logbase() and Physbase() so you wouldn't notice that Logbase() is promoted to Physbase() during VBL. However then I could see it's happening only if you set 'screenpt':

Code: Select all

*--- reload display base register
        tst.l   screenpt.w              ; if(screenpt == NULL) don't;
        beq     vbl5
        move.l  screenpt.w,_v_bas_ad.w  ; set OS variable

* This never worked on STe before, because dbasell was being written first.
* Now (9/91) it works, and the write to dbasell is unconditional because
* it's either necessary or harmless on all machines.

        move.b  _v_bas_ad+2.w,dbasel    ; load "low" pointer
        move.b  _v_bas_ad+1.w,dbaseh    ; load "high" pointer
        move.b  _v_bas_ad+3.w,dbasell   ; for STPLUS, TT use these 8 bits
                                        ; (harmless on ST)

*------ Floppy drive-select timeout:
vbl5:   bsr     _flopvbl                ; (no args)

TOS (Physbase()) doesn't need any support from VBL, it just sets dbase{l,h,ll} and let the Videl do its job.

And of course I agree guys, it's much easier and possible to do this via custom code with some help from Supervisor. I was just curious whether I'm not overlooking something obvious to make it 100% user code.

mikro
Atari God
Atari God
Posts: 1285
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Re: Double buffering using OS calls

Postby mikro » Fri Aug 25, 2017 6:42 am

shoggoth wrote:I bet you can access it with Setexc() or Ssystem()?

This is actually quite clever. :) Using Setexc() for setting a system variable is quite a novel idea! (to me)

In theory, one could do something as:

Code: Select all

Setexc(<screenpt vector>, new_physbase);
// wait for VBL *and* new physbase to be set
while (Setexc(<_v_bas_ad vector>, -1) != new_physbase);
// continue

Not too bad, not too bad at all.

AtariZoll
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 2797
Joined: Mon Feb 20, 2012 4:42 pm
Contact:

Re: Double buffering using OS calls

Postby AtariZoll » Fri Aug 25, 2017 6:59 am

Usual way for fast switch to supervisor mode is using some of traps 3-13. Fastest is when you do all accesses in same trap call.
Not exactly using OS calls, but if you want speed forget strict OS call mode.
Negative feedback has usually positive effect.

User avatar
shoggoth
Nature
Nature
Posts: 853
Joined: Tue Aug 01, 2006 9:21 am
Location: Halmstad, Sweden
Contact:

Re: Double buffering using OS calls

Postby shoggoth » Fri Aug 25, 2017 11:05 am

mikro wrote:
shoggoth wrote:I bet you can access it with Setexc() or Ssystem()?

This is actually quite clever. :) Using Setexc() for setting a system variable is quite a novel idea! (to me)

In theory, one could do something as:

Code: Select all

Setexc(<screenpt vector>, new_physbase);
// wait for VBL *and* new physbase to be set
while (Setexc(<_v_bas_ad vector>, -1) != new_physbase);
// continue

Not too bad, not too bad at all.


_v_bas_ad is the base address for the VDI, which will continue render stuff using that base address. I advice against it.

Private screens have their own private base address, and they're displayed by modifying the physical screen address. That way, the VDI is allowed to function properly in the background on its own private screen, which is pointed to by _v_bas_ad.
Ain't no space like PeP-space.

User avatar
Eero Tamminen
Atari God
Atari God
Posts: 1540
Joined: Sun Jul 31, 2011 1:11 pm

Re: Double buffering using OS calls

Postby Eero Tamminen » Fri Aug 25, 2017 8:58 pm

Here's C-code for a game I did, that uses double buffering:
http://tammat.mbnet.fi/hatari/programs.shtml#punssi

(There's also a bit of assembly for input handling.)

mikro
Atari God
Atari God
Posts: 1285
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Re: Double buffering using OS calls

Postby mikro » Sat Aug 26, 2017 12:01 am

Thanks Eero -- so it seems that VsetScreen() + Vsync() works for you. I guess that part of the problem is that my main loop is very demanding, scratching one frame's CPU time pretty tightly so the "hesitation" Vsync() adds doesn't help very much for me but for your game it's enough. Still, good to know it's usable.

shoggoth: on the contrary, using the same screen as VDI uses is exactly what I need. Will report results. :)

User avatar
shoggoth
Nature
Nature
Posts: 853
Joined: Tue Aug 01, 2006 9:21 am
Location: Halmstad, Sweden
Contact:

Re: Double buffering using OS calls

Postby shoggoth » Sat Aug 26, 2017 8:39 am

mikro wrote:shoggoth: on the contrary, using the same screen as VDI uses is exactly what I need. Will report results. :)


How is that clean? :) Anyhoo - I guess you can escape a good spanking if you atleast lock the AES screen.

I guess you really want offscreen bitmaps, which you flip through (but that means people would need NVDI, Enhancer, NovaVDI, MilanVDI, MagicVDI, etc)

The whole thing does sound exciting. Pictures or it didn't happen!
Ain't no space like PeP-space.

User avatar
Patrice Mandin
Atari User
Atari User
Posts: 36
Joined: Mon Aug 09, 2004 7:06 pm
Location: France
Contact:

Re: Double buffering using OS calls

Postby Patrice Mandin » Sat Aug 26, 2017 10:34 am

Hello,

I usually only changes the physical screen pointer using Setscreen(), because this is what is displayed on the screen. The logical screen pointer is only used by the VDI, so no need to change it.

I also noticed that when using NVDI, if you change screen pointer along with video mode, the palette is reset to the system palette, thus I usually make 2 calls, one to change screen pointer, and the second for video mode (for example if your game supports several video modes).

So this is the method I use for my apps, and that I also use inside SDL:

Setscreen(-1, new_screen_pointer,-1);
Setscreen(-1,-1,new_video_mode); /* or Vsetmode(new_video_mode) on Falcon */
/* You can also change palette here using SetPalette() before Vsync, if you need */
Vsync();

The changes you trigger with Setscreen, Setpalette, etc will only be 'validated' when the next VBL triggers, thus when VSync() is called.
Also, calls your program xx.tos or xx.ttp, you won't have the bee mouse waiting for you to hide/disable it (which happens when running as .prg)
Linux and Atari coder
Development tools, games


Social Media

     

Return to “Coding”

Who is online

Users browsing this forum: No registered users and 2 guests