Page 1 of 1

Double Buffering problems

Posted: Fri Feb 24, 2017 4:37 am
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));
	}
}

Re: Double Buffering problems

Posted: Fri Feb 24, 2017 5:37 am
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.

Re: Double Buffering problems

Posted: Fri Feb 24, 2017 7:47 pm
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()

Re: Double Buffering problems

Posted: Sat Mar 11, 2017 1:44 am
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.

Re: Double Buffering problems

Posted: Sat Mar 11, 2017 2:43 am
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

Re: Double Buffering problems

Posted: Sat Mar 11, 2017 5:14 am
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.

Re: Double Buffering problems

Posted: Sun Mar 12, 2017 12:39 am
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

Re: Double Buffering problems

Posted: Fri Mar 17, 2017 5:56 pm
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 :?

Re: Double Buffering problems

Posted: Fri Mar 17, 2017 6:19 pm
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