Page 1 of 1

vesa lfb - how to avoid flickering

Posted: Sun Sep 09, 2007 2:24 pm
by ezome
as the topic says...

is there any way to avoid flickering on vesa 2.0 using a linear framebuffer.
maybe something like double buffering ( painting to offscreen memory and flipping onscreen/offscreen memory)?

Re: vesa lfb - how to avoid flickering

Posted: Sun Sep 09, 2007 2:52 pm
by AndrewAPrice
ezome wrote:as the topic says...

is there any way to avoid flickering on vesa 2.0 using a linear framebuffer.
maybe something like double buffering ( painting to offscreen memory and flipping onscreen/offscreen memory)?
Their could be some sort of double buffering built in, but I'm no VESA expert. Until I found such a feature I would allocate a memory, do all my drawing in that buffer, and then to update what's on the screen I'd copy straight from my buffer to the frame buffer. That's essentially double buffering.

But I've read "VBE 3.0 has support for triple buffering" somewhere which makes me wonder if VBE 1/2 has in-built double buffering functions. This would save memory since you may not need to allocate more memory. I don't know.

Re: vesa lfb - how to avoid flickering

Posted: Sun Sep 09, 2007 3:06 pm
by ezome
MessiahAndrw wrote:I would allocate a memory, do all my drawing in that buffer, and then to update what's on the screen I'd copy straight from my buffer to the frame buffer. That's essentially double buffering.
i thought about that but the problem is to find the right moment to copy

if you copy your buffer while the old buffer is drawn to the screen you end up with half old/half new image on the screen

so in this case i would need some sort of sync

Posted: Sun Sep 09, 2007 4:18 pm
by AndrewAPrice
Do the copying inside your flip() (or similar) function, which you call after you have finished updating everything on the screen.

Posted: Sun Sep 09, 2007 8:23 pm
by Dex
You can test for Verticle Retrace, something like this:

Code: Select all

FullVertWait:

                        mov     dx,3dah
Vr:
                        in      al,dx
                        test    al,8
                        jnz     Vr                        ;wait until Verticle Retrace starts
Nvr:
                        IN      al,dx
                        test    al,8
                        jz      Nvr                        ;wait until Verticle Retrace Ends

                        ret
But i have found it make no differance for vesa or as it's for VGA, it does not work for vesa, but by double buffering, you will get rid of the flickering.

Posted: Sun Sep 09, 2007 11:15 pm
by ezome
MessiahAndrw wrote:Do the copying inside your flip() (or similar)
function, which you call after you have finished updating everything on
the screen.
i don't got a flip function
i just draw my stuff to the framebuffer and at some point i don't
know the graphic card updates the screen
Dex wrote:You can test for Verticle Retrace, something like this:

Code: Select all

FullVertWait:

                        mov     dx,3dah
Vr:
                        in      al,dx
                        test    al,8
                        jnz     Vr                        ;wait until Verticle Retrace starts
Nvr:
                        IN      al,dx
                        test    al,8
                        jz      Nvr                        ;wait until Verticle Retrace Ends

                        ret
But i have found it make no differance for vesa or as it's for VGA,
it does not work for vesa, but by double buffering, you will get rid
of the flickering.
what is Verticle Retrace?
however to implement double buffering i would need the know the
moment after an update is finished and be able to tell vesa which
memory address to use as framebuffer for the next update.
or am i missing something?

Posted: Sun Sep 09, 2007 11:42 pm
by pcmattman
Basically, all your functions that draw stuff draw to a RAM-based framebuffer (NOT the vesa one). Then, once you need them displayed, you call flip, which copies the RAM-based framebuffer into the vesa framebuffer - for example:

Code: Select all

// assume 0xE0000000 is the VESA lfb

// double buffer
char* dbuff;

void init_vesa()
{
	// .... do your vesa mode switch .... //

	// create a second buffer big enough
	dbuff = (char*) malloc( 640 * 480 * 8 ); // 32-bit
}

void draw_things()
{
	circle( 1, 2, 4 );
	square( 5, 6, 2 );
	rect( 9, 10, 11, 12 );
}

void flip()
{
	// faster ways to do this, but for example purposes
	// you get the idea
	memcpy( (char*) 0xE0000000, dbuff, 640 * 480 * 8 );
}

void main()
{
	while( 1 )
	{
		draw_things();
		flip();
	}
}
however to implement double buffering i would need the know the
moment after an update is finished and be able to tell vesa which
memory address to use as framebuffer for the next update.
By using the flip method I've shown above, you should be able to stop the flickering problem.

Posted: Sun Sep 09, 2007 11:43 pm
by Dex
When your VGA programing you call the function i posted than on return, you move you buffer to screen.
But i am not sure if this still works for vesa.
I have done alot of vesa programing, but have alway used a buffer in ram, than when i have finish updating buffer i write it to screen.
Now with vesa, if your using 32BPP, you need to test the BPP as some cards use 24bits and others use 32bits.
Now if you use double buffering as above, you will get no flicker or tearing, even without doing Verticle Retrace ( http://www.computerhope.com/jargon/v/vertretr.htm ).
Also note, that if you are coding a Gui, its best to update mouse pointer direct to screen, i have found it best that way anyway.

Posted: Sun Sep 09, 2007 11:58 pm
by ezome
Dex wrote:When your VGA programing you call the function i posted than on return, you move you buffer to screen.
But i am not sure if this still works for vesa.
ok ill try that
however i guess i don't understand how the graphic card uses the lfb to update the screen because in my understanding even if i use memcpy i could change the buffer while the graphic card is updating the screen.
is there any article about that?

i would also like an article about vga programming including compatibility issues.
currently i fixed my os on 800x600x24 VBE2 because it seems to be available on most systems and i'm more interested in os internals than in gui anyway

thanks ez

Posted: Mon Sep 10, 2007 12:21 am
by Combuster
ezome wrote:however i guess i don't understand how the graphic card uses the lfb to update the screen because in my understanding even if i use memcpy i could change the buffer while the graphic card is updating the screen.
Basically, the video card is a piece of memory and a GPU. The memory is shared between the GPU and the rest of the system - both can read and write to it at about the same time. Part of the GPU is responsible for generating the video output - it reads in bytes from video memory, converts them to colors, and put those on the output, then reads a new set of bytes and repeats the process. In essence, the video card can be trying to read from memory when you are writing to it, the effects are noticeable after only a few pixels. When doublebuffering, you copy the buffer in one piece. the video card will only change once from old to new data (if your code is uninterrupted). Tearing is reduced because there is only one scanline where the bottom and top half (original and new) do not match up.
i would also like an article about vga programming including compatibility issues.
VGA Hardware on the wiki, and there's an book called Graphics Programming Black Book which contains useful information on the subject (free download as pdf, google).

Posted: Mon Sep 10, 2007 1:46 am
by ezome
Combuster wrote: Basically, the video card is a piece of memory and a GPU. The memory is shared between the GPU and the rest of the system - both can read and write to it at about the same time. Part of the GPU is responsible for generating the video output - it reads in bytes from video memory, converts them to colors, and put those on the output, then reads a new set of bytes and repeats the process. In essence, the video card can be trying to read from memory when you are writing to it, the effects are noticeable after only a few pixels. When doublebuffering, you copy the buffer in one piece. the video card will only change once from old to new data (if your code is uninterrupted). Tearing is reduced because there is only one scanline where the bottom and top half (original and new) do not match up.
ok let me explain how i understood what you said

we got the cpu, ram, videomemory and the gpu
do you mean ram or video memory when you talk about memory?
when you say shared between gpu and the rest of the system do you
mean some block of memory in ram is shared between gpu and cpu?

if thats true than how is memory transfered to the gpu?
does the gpu paint data from ram or from video memory?
if the gpu paints from video memory does it copy the lfb in one piece or
in little chunks to video memory?

yeah alot of questions i know. maybe i'm stupid :wink:
but i just hate coding such stuff as long as i don't understand whats
going on

Posted: Mon Sep 10, 2007 2:49 am
by Combuster
ezome wrote:we got the cpu, ram, videomemory and the gpu do you mean ram or video memory when you talk about memory?
Video memory is a form of ram, only located on the card rather than on the motherboard. When programming VBE modes, the other differences are neglegible.
when you say shared between gpu and the rest of the system do you mean some block of memory in ram is shared between gpu and cpu?
Yes. In most cases, its the video ram, but IIRC intel chipsets share system ram.
if thats true than how is memory transfered to the gpu?
Like any other multiprocessor system. both the CPU and GPU want access to the memory, and the chipset of the video card will arrange things so they both get what they want.
does the gpu paint data from ram or from video memory?
See the notes above.
if the gpu paints from video memory does it copy the lfb in one piece or in little chunks to video memory?
The LFB is video memory.
but i just hate coding such stuff as long as i don't understand whats
going on
Compliments for the attitude.

A diagram:

Code: Select all

      CPU
       |
 ============ system bus
 |          |
RAM     PCI controller
            |
      =============  PCI bus
      |           |
   other     +--- | ---+    
   devices   |  =====  |
             |  |   |  | <- video card
             | GPU RAM |
             +- | -----+    
                |
             Monitor
From the CPU's perspective, everything is either a form of memory (including MMIO) or port I/O. The CPU is connected almost directly to system memory, and less directly to the video card (via the PCI Host controller) It can read and write to both ram and video memory without knowing the difference.

The GPU can read and write to memory just as good (usually, its better at it) as the processor can - and it can just as well access everything it is (indirectly) connected to. Normally it won't stick his nose out of his box, but if you tell him to, it will talk to the monitor, and if you know how, it will even read from system memory even though that is quite a bit away (that process is called DMA).

When in VESA mode, the GPU just reads bits from the memory, and forwards them to the monitor. That means for every frame, the contents of memory is sent to the display.
Since the CPU has also access to the same bit of RAM, the CPU might change the contents of it during one of the GPU's cycles. The GPU doesn't bother, but from the user's perspective it seems as if you see parts of several screens. Tearing results when one of those screens is a intermediate of the drawing process, i.e. it is not the screen you previously drew, nor is it the screen that you want the user to see next time around. By keeping those intermediates out of the area where the GPU finds its data for the screen, you can eliminate tearing. Many alternatives are available, including double-buffering, triple-buffering, page-flipping and using the vertical retrace interval. Double buffering is the easiest - you simply keep a copy of the screen somewhere the user can't see it, then copy it to the video card's visible area once its in a state ready for the user to view. That way intermediates are only present in non-visible memory and can not contribute to tearing effects.
The other three methods are more advanced, but require special support from the video card. With a VGA, you have all options at your disposal.

Posted: Mon Sep 10, 2007 5:22 am
by ezome
thanks great explanation

so if i get it right...
let's assume i got a pci graphic card with seperate video memory
and i'm using vbe

in this case when writing to the address provided as lfb i think i'm writing
to system memory but actually i'm writing to video memory on the pci card

ok now i got a last question
lets assume we loaded 2 bitmaps to system memory
the first bitmap is located at void* bmp1
the second bitmap is located at void* bmp2
and the lfb is located at void* lfb

so bmp1 and bmp2 point to different locations in system memory and lfb
points to video memory

now what happens if we do this:
-copy bmp1 to lfb
-do something till one half of bmp1 is displayed
-copy bmp2 to lfb

don't we end up with bmp1 on the top half of the screen and bmp2 on the
bottom half of the screen for one single frame?
on the next frame of course only bmp2 would be displayed.

something like this Verticle Retrace Dex mentioned before just for VBE

if so how do we make sure that on frame1 bmp1 is dislayed completely
and on frame2 bmp2 is displayed completely

Posted: Mon Sep 10, 2007 9:05 am
by Combuster
You got the idea :D
something like this Verticle Retrace Dex mentioned before just for VBE

if so how do we make sure that on frame1 bmp1 is dislayed completely
and on frame2 bmp2 is displayed completely
The Vertical Retrace is the period when the GPU does not send data to the screen, other than that it just finished one. If you wait for the Vertical Retrace before doing some operations, you know you can change the contents of video memory for a short time without anyone seeing it. Under normal circumstances, it is enough to copy the buffers over. However, to detect the Vertical Synchronization Pulse (which indicates the Vertical Retrace period) is a hardware-specific thing.

VBE can tell you if the video card supports the VGA register set, after which you can use some VGA knowledge to detect the retrace. If it isn't, triple buffering may be available that will allow you to a page flip during the vertical retrace. If both are unavailable, VBE can not help you.

Posted: Mon Sep 10, 2007 11:05 am
by Dex
I would just like to add one thing to the info so far posted, I in the old days programming VGA always used V retrace, before writing buffer to screen to avoid tear out.
But i have found with vesa that if you write use double buffering and write the back buffer to screen, you never get tear out,even without V-retrace, why i do not know.
So all you need to do is call something like this:

Code: Select all

 
 ;----------------------------------------------------;
 ; BuffToScreen.  ;Puts whats in the buffer to screen ;
 ;----------------------------------------------------;

BuffToScreen:
         pushad
         push  es
         mov   ax,linear_sel
         mov   es,ax
         mov   esi,[VesaBuffer]   ; Main Ram
         mov   edi,[ModeInfo_PhysBasePtr]   ; Graphic card Ram
         mov   ecx,640*480
         cmp   [ModeInfo_BitsPerPixel],24   ; You must test this
         jne   @f
         mov   ecx,0x38400
@@:
         cld
         cli
         rep   movsd
         sti
         pop   es
         popad
         ret

And you will get no flicking.

NOTE: The above code is unoptimised and hardcoded for one res, also the vesa buffer has been written to acording to the BPP.
Also you can test V-retrace by useing it as a simple timer in your code, example:
Works for none critical time, 60 = 1 second.

Code: Select all

BITS 16
ORG 100h

SECTION .text

START:
xor ah,ah
int 16h
mov  word[count],0
ddf:
call FullVertWait
cmp word [count],60
jne  ddf

;Do some thing in here

xor ah, ah
int 16h
mov ax, 04c00h
int 21h
;**************************

FullVertWait:
MOV  DX,3DAh
Vr:
IN   AL,DX
TEST    AL,8
JNZ    Vr  ;wait until Verticle Retrace starts
Nvr:
IN  AL,DX
TEST  AL,8
JZ Nvr     ;wait until Verticle Retrace Ends
ADD word[count],1
RET

;**************************

SECTION .data

count  dw 0