Double Buffering problems

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
ComputerFido
Member
Member
Posts: 44
Joined: Fri Sep 09, 2016 5:52 pm
Location: Australia
Contact:

Double Buffering problems

Post by ComputerFido »

I have been running into some really weird problems when i tried implementing Double Buffering in my graphics driver in my operating system. When i don't use the double buffering my functions work fine. When i do however, if I use malloc to allocate space nothing happens and the screen is black but when i don't it's really weird and it draws my first yellow 200x200 rectangle, but it wont draw anything else and there is an exception (VirtualBox won't tell me much else).
Here is my graphics code:

Code: Select all

namespace graphics{
	vesa_mode_info_t *modeInfo;
	uint32_t vram = 0;

	unsigned char* dblBuffer;
	unsigned char* screen;
	bool initialized = false;

	vesa_mode_info_t * initGfx(){
		if(!initialized){
			modeInfo = EnterGraphicsMode();
			vram = modeInfo->physbase;
			//if(vram == 0)
			//	panic("ERR_GFX_MODE_FAILED", "Error whilst setting graphics mode.",false);

			screen = (unsigned char*)vram;
			//dblBuffer = (unsigned char*)malloc(modeInfo->width*modeInfo->height*modeInfo->bpp/8);

			UpdateScreen();

			initialized = true;
		}

		return modeInfo;
	}

	void putpixel(int x,int y, uint8_t r, uint8_t g, uint8_t b) {
		putpixel(x,y,(r << 16) + (g << 8) + b);
	}

	/* Using Double Buffer
       void putpixel(int x,int y, uint32_t colour) {
		unsigned where = y * modeInfo->pitch + (x * (modeInfo->bpp/8));
		dblBuffer[where] = colour & 255;              // BLUE
		dblBuffer[where + 1] = (colour >> 8) & 255;   // GREEN
		dblBuffer[where + 2] = (colour >> 16) & 255;  // RED
	}

	void fillrect(int x, int y, int w, int h, uint32_t colour) {
		int bpp = modeInfo->bpp;
		int pitch = modeInfo->pitch;
		uint32_t where = 0;
		for(int i=0;i<h;i++){
			for(int j=0;j<w;j++){
				where = (y+i) * pitch + ((x+j) * (bpp/8));
				dblBuffer[where] = colour & 255;             // BLUE
				dblBuffer[where + 1] = (colour >> 8) & 255;  // GREEN
				dblBuffer[where + 2] = (colour >> 16) & 255; // RED
			}
		}
	}*/

        /* Without Double Buffer*/
	void putpixel(int x,int y, uint32_t colour) {
		unsigned where = y * modeInfo->pitch + (x * (modeInfo->bpp/8));
		screen[where] = colour & 255;              // BLUE
		screen[where + 1] = (colour >> 8) & 255;   // GREEN
		screen[where + 2] = (colour >> 16) & 255;  // RED
	}

	void fillrect(int x, int y, int w, int h, uint32_t colour) {
		int bpp = modeInfo->bpp;
		int pitch = modeInfo->pitch;
		uint32_t where = 0;
		for(int i=0;i<h;i++){
			for(int j=0;j<w;j++){
				where = (y+i) * pitch + ((x+j) * (bpp/8));
				screen[where] = colour & 255;             // BLUE
				screen[where + 1] = (colour >> 8) & 255;  // GREEN
				screen[where + 2] = (colour >> 16) & 255; // RED
			}
		}
	}

	void fillrect(int x, int y, int w, int h, unsigned char r, unsigned char g, unsigned char b){
		fillrect(x,y,w,h,(r << 16) + (g << 8) + b);
	}

        // Copy back buffer to video memory 
	void UpdateScreen(){
		memcpy(screen,dblBuffer,modeInfo->width*modeInfo->height*(modeInfo->bpp/8));
	}
}
User avatar
iansjack
Member
Member
Posts: 4690
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Double Buffering problems

Post by iansjack »

If you don't allocate memory for the buffer you are bound to have problems. So the question you need to answer is why things don't work when you malloc() the buffer; time for a little debugging.
ComputerFido
Member
Member
Posts: 44
Joined: Fri Sep 09, 2016 5:52 pm
Location: Australia
Contact:

Re: Double Buffering problems

Post by ComputerFido »

iansjack wrote:If you don't allocate memory for the buffer you are bound to have problems. So the question you need to answer is why things don't work when you malloc() the buffer; time for a little debugging.
Ok thanks i will try to find out the problem with malloc()
ComputerFido
Member
Member
Posts: 44
Joined: Fri Sep 09, 2016 5:52 pm
Location: Australia
Contact:

Re: Double Buffering problems

Post by ComputerFido »

iansjack wrote:If you don't allocate memory for the buffer you are bound to have problems. So the question you need to answer is why things don't work when you malloc() the buffer; time for a little debugging.
Malloc is working but i am still having problems, i tried drawing a rectangle to fill the screen and it only filled about 50 pixels at the top of the screen.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Double Buffering problems

Post by Brendan »

Hi,
ComputerFido wrote:
iansjack wrote:If you don't allocate memory for the buffer you are bound to have problems. So the question you need to answer is why things don't work when you malloc() the buffer; time for a little debugging.
Malloc is working but i am still having problems, i tried drawing a rectangle to fill the screen and it only filled about 50 pixels at the top of the screen.
Try to figure out if it's a problem with the code that draws stuff in the double buffer, and/or if it's a problem with the code that copies the double buffer to display memory.

Looking at your old (and likely no longer relevant) code; I'd be tempted to expect multiple problems (e.g. that "fillrect()" is buggy and that "UpdateScreen()" is buggy too), but it's hard to say without seeing any of the new/relevant code.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
ComputerFido
Member
Member
Posts: 44
Joined: Fri Sep 09, 2016 5:52 pm
Location: Australia
Contact:

Re: Double Buffering problems

Post by ComputerFido »

Brendan wrote:Hi,
ComputerFido wrote:
iansjack wrote:If you don't allocate memory for the buffer you are bound to have problems. So the question you need to answer is why things don't work when you malloc() the buffer; time for a little debugging.
Malloc is working but i am still having problems, i tried drawing a rectangle to fill the screen and it only filled about 50 pixels at the top of the screen.
Try to figure out if it's a problem with the code that draws stuff in the double buffer, and/or if it's a problem with the code that copies the double buffer to display memory.

Looking at your old (and likely no longer relevant) code; I'd be tempted to expect multiple problems (e.g. that "fillrect()" is buggy and that "UpdateScreen()" is buggy too), but it's hard to say without seeing any of the new/relevant code.


Cheers,

Brendan
I have not changed my fillrect code (as it worked without the doublebuffer and i got the same problem when i used memset) i also tried using a loop instead of memcpy and i still got the same problem.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Double Buffering problems

Post by Brendan »

Hi,
ComputerFido wrote:Looking at your old (and likely no longer relevant) code; I'd be tempted to expect multiple problems (e.g. that "fillrect()" is buggy and that "UpdateScreen()" is buggy too), but it's hard to say without seeing any of the new/relevant code.
I have not changed my fillrect code (as it worked without the doublebuffer and i got the same problem when i used memset) i also tried using a loop instead of memcpy and i still got the same problem.[/quote]

The first problem is your handling of "pitch".

Think of the video card's display memory as rows, where each row consists of pixel data followed by none or more unused bytes (for padding). For example, for "800*600 with 32-bpp" each row might be 800*4 bytes (3200 bytes) of pixel data followed by 896 bytes of padding, that adds up to a total of 4096 bytes per row (and so "modeInfo->pitch" would be 4096).

Of course your double buffer has no padding.

Your "UpdateScreen()" assumes that there is no padding for the double buffer (correct) and that there's no padding for display memory (incorrect). This can cause pixels in the double buffer to be written to unused padding and be invisible.

Your "fillrect()" code assumes that there is padding when it draws into the double buffer (that has no padding). This is a potential "index out of range" bug where data in memory after the buffer get trashed. For example, for "800*600 with 32-bpp" with "modeInfo->pitch" is 4096, you allocate 800*600*4 bytes (or 1920000 bytes), and for the bottom left pixel you would write to "where = 799*4096+599*4 = 3275100" , which is 1355100 bytes past the end of the memory you allocated.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
ComputerFido
Member
Member
Posts: 44
Joined: Fri Sep 09, 2016 5:52 pm
Location: Australia
Contact:

Re: Double Buffering problems

Post by ComputerFido »

Brendan wrote: The first problem is your handling of "pitch".

Think of the video card's display memory as rows, where each row consists of pixel data followed by none or more unused bytes (for padding). For example, for "800*600 with 32-bpp" each row might be 800*4 bytes (3200 bytes) of pixel data followed by 896 bytes of padding, that adds up to a total of 4096 bytes per row (and so "modeInfo->pitch" would be 4096).

Of course your double buffer has no padding.

Your "UpdateScreen()" assumes that there is no padding for the double buffer (correct) and that there's no padding for display memory (incorrect). This can cause pixels in the double buffer to be written to unused padding and be invisible.
Okay i have tried many different things to try and fix UpdateScreen() this being among them.

Code: Select all

void UpdateScreen(){
		for(int i=0;i<modeInfo->height;i++){
			for(int j=0;j<modeInfo->width;j++)
				screen[i*modeInfo->width*modeInfo->pitch+j] = dblBuffer[i*modeInfo->width+j];
		}
	}
But it still isn't working and i have no idea why. It's probably just something obvious i am missing but i am not sure. It would be really helpful if you could point me in right direction with the really annoying equation to copy the buffer :?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Double Buffering problems

Post by Brendan »

Hi,
ComputerFido wrote:Okay i have tried many different things to try and fix UpdateScreen() this being among them.

Code: Select all

void UpdateScreen(){
		for(int i=0;i<modeInfo->height;i++){
			for(int j=0;j<modeInfo->width;j++)
				screen[i*modeInfo->width*modeInfo->pitch+j] = dblBuffer[i*modeInfo->width+j];
		}
	}
But it still isn't working and i have no idea why. It's probably just something obvious i am missing but i am not sure. It would be really helpful if you could point me in right direction with the really annoying equation to copy the buffer :?
That's not right either. The "modeInfo->pitch" is the number of bytes in a horizontal line and should not be multiplied by the horizontal resolution ("modeInfo->width").

It also won't work "as is" for arrays in C - you'd have to use something like "modeInfo->pitch/sizeof(screen[0])" instead. A better alternative is something like:

Code: Select all

void UpdateScreen(){
		unsigned int visibleBytesPerLine = modeInfo->width * modeInfo->bpp / 8;
		uint8_t *dest = screen;
		uint8_t *src = dblBuffer;

		for(int y = 0; y < modeInfo->height; y++) {
			memcpy(dest, src, visibleBytesPerLine);
			dest += modeInfo->pitch;
			src += visibleBytesPerLine;
		}
	}
Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply