A problem with graphics mode,

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
626788149
Member
Member
Posts: 25
Joined: Mon Oct 26, 2015 7:24 am
Location: Guilin,China

A problem with graphics mode,

Post by 626788149 »

When the screen is full,I need scroll down one row to show new information;
But this process is very slow;

For example, if i print 10000 rows on screen,it will take ten seconds of the time to complete this process under the qeum.
But it will take more than ten minutes under the boch or real machine.

How would i can do to fix it up?

Code: Select all

void init_vbe(uint32_t *p){

	multiboot_info_t* minf = (multiboot_info_t*)p;
	

	//find if there have info about vbe
        // 
	if((minf == NULL) || (minf->flags & 0x800) != 0x800)
		return;
	
	VbeInfoBlock* vbeib = (VbeInfoBlock*)minf->vbe_info.vbe_control_info;
	ModeInfoBlock* vebmb = (ModeInfoBlock*)minf->vbe_info.vbe_mode_info;
	
	//D7 = Linear frame buffer mode is available
	//0 = No
	//1 = Yes
	 
	if((vebmb == NULL) || (vebmb->attributes & 0x80) != 0x80){
		//Linear frame buffer mode is unavailable
		return;
	}
	if((vebmb->attributes & 0x10) == 0x10){
		//text mode
		video.mode = 0;	
	}else{
		video.mode = 1;
	}
	extern char _binary_font_bin_start[];

       	video.screen = (unsigned char*)vebmb->physbase;
	video.Xres = vebmb->Xres;
	video.Yres = vebmb->Yres;
	video.bpp = vebmb->bpp;
	video.pitch = vebmb->pitch;
	video.font_address = _binary_font_bin_start;
	video.buff_size = video.Xres * video.Yres * (video.bpp/8);
	video.crt_pos = 0;
	video.fonth = 16;	//font pixel height
	video.fontw = 8;
	video.xpos = video.ypos = 0;
	
	
	if(video.mode)
		video.numchars = video.Xres * video.Yres / video.fonth /video.fontw;
	else
		video.numchars = video.Xres * video.Yres;

	video.nchars_colum = video.Xres / video.fontw;
	video.nchars_line = video.Yres / video.fonth;

	cprintf("video.bpp:%d\n",video.bpp);
	cprintf("video.pitch:%d\n",video.pitch);
	cprintf("vbeib->TotalMemory:%dMB\n",vbeib->TotalMemory*64/1024);
	cprintf("vbeib.buff_size:%dkb\n",video.buff_size/1024);
	cprintf("vbeib->screen:%x\n",video.screen);

}

Code: Select all

void vga_putc(int c){
	// if no attribute given, then use black on white
	if (!(c & ~0xFF))
		c |= 0xFFFFFF00;
	
	switch (c & 0xff) {
	case '\n':
		video.ypos++;
		/* fallthru */
	case '\r':
		video.xpos = 0;
		break;
	case '\t':
		cons_putc(' ');
		cons_putc(' ');
		cons_putc(' ');
		cons_putc(' ');
		cons_putc(' ');
		break;
	default:
		//x * video.fontw, y * video.fontw
		draw_char((c & 0xFF),video.xpos * video.fontw, video.ypos * video.fonth ,(c>>8) & 0xFF, (c>>16) & 0xFF,(c>>24) & 0xFF);
		video.xpos++;
		break;
	}
	if (video.xpos >= video.nchars_colum ){
		video.xpos = 0;
		video.ypos++;
	}
	if(video.ypos >= video.nchars_line){
		//video.fonth is height of font
		memmove(video.screen, video.screen + video.Xres * video.bpp/8 * video.fonth,
				 (video.buff_size - video.Xres * video.bpp/8* video.fonth));
		video.ypos--;
		int i;
		for (i = 0; i < video.nchars_colum; i++){
			draw_char(' ',(video.xpos + i) * video.fontw, video.ypos * video.fonth ,(c>>8) & 0xFF, (c>>16) & 0xFF,(c>>24) & 0xFF);
			
		}
	}

}

Code: Select all

void draw_char(char ch, int x, int y,unsigned char r,unsigned char g,unsigned char b)  
{  
	int bar = video.bpp/8;
	char* font = video.font_address + ch*16;
	unsigned char *where = video.screen + x*bar + y*video.Xres*bar;
	int i, j;
	
	
	for (i = 0; i < 16; i++) {
		uint8_t bit = 0x80;
		for (j = 0; j < 8; j++) {
			if(*font & bit){
				
				where[j*bar] = b;
				where[j*bar + 1] = g;
				where[j*bar + 2] = r;
			}
			else{
				where[j*bar] = 0x00;
				where[j*bar + 1] = 0x00;
				where[j*bar + 2] = 0x00;
			}
			bit = bit >> 1;
		}
		where+=video.bpp*video.Xres/8;
		font ++;
	}
}

Code: Select all

static void putpixel(unsigned char* screen, int x,int y, int color) {
	int i = video.bpp/8;
	unsigned where = x*i + y*i*video.Xres;
	screen[where] = color & 255;              // BLUE
	screen[where + 1] = (color >> 8) & 255;   // GREEN
	screen[where + 2] = (color >> 16) & 255;  // RED
}
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: A problem with graphics mode,

Post by Brendan »

Hi,

Here's 4 rules to get you started:
  • Never read from display memory. Reading from display memory is extremely slow. Doing it in a "memcpy()" is worse because the memory itself is being "triple banged" (you're trying to read from it while you're trying to write to it while the video card is reading it to send pixels to the monitor), and the PCI bus (and its limited bandwidth) is being "double banged".
  • Never write individual bytes in display memory. For every write across a bus (like PCI) it's a little bit like a network packet (e.g. maybe consisting of a type, address, payload length, the data itself, then probably a checksum of some sort to detect bus errors). For that single byte of actual data you could be sending 16+ bytes. Instead you want to write larger amounts at once (e.g. 32-bit or larger).
  • Never use a "putpixel()" function. Not only is it bad because you only write one pixel at a time; it's bad because you have to calculate the address of every single pixel (your "where = video.screen + x*bar + y*video.Xres*bar" calculation, which is wrong anyway but that's a different problem). It's far better/faster to calculate "where" once (for the top/left pixel) and then do "where += bytes_per_pixel;" to find the next pixel to the right, or "where += bytes_per_line;" to find the next pixel down, and avoid a huge number of multiplications.
  • Avoid unnecessary writes. If you look at most "screens of text" you'll notice that about half of it is white space, there's a gap between lines, etc. There's even cases where (e.g.) a 'B' on one line is replaced by 'P' on the next line and only a small part (the bottom right part of the character) actually changes. When the screen scrolls, I'd guess that typically about 80% of pixels aren't changed at all, and by not writing those pixels you'd get a significant performance improvement.

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.
626788149
Member
Member
Posts: 25
Joined: Mon Oct 26, 2015 7:24 am
Location: Guilin,China

Re: A problem with graphics mode,

Post by 626788149 »

Brendan wrote:
  • Never read from display memory. Reading from display memory is extremely slow. Doing it in a "memcpy()" is worse because the memory itself is being "triple banged" (you're trying to read from it while you're trying to write to it while the video card is reading it to send pixels to the monitor), and the PCI bus (and its limited bandwidth) is being "double banged".
Thanks a lot.
But if I don't reading from display memory, how could I achieve scroll screen?
Can I reset display address? If i can, how?
Last edited by 626788149 on Fri Oct 30, 2015 12:28 am, edited 1 time in total.
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: A problem with graphics mode,

Post by alexfru »

626788149 wrote:But if I don't reading from display memory, how could I achieve scroll screen?
Buffer?
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: A problem with graphics mode,

Post by Antti »

Brendan wrote:you want to write larger amounts at once (e.g. 32-bit or larger).
On some rare cases, writing larger units may cause problems. This is quite irrelevant but may be worth taking into account if you want to be as defensive as possible.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: A problem with graphics mode,

Post by Brendan »

Hi,
626788149 wrote:
Brendan wrote:
  • Never read from display memory. Reading from display memory is extremely slow. Doing it in a "memcpy()" is worse because the memory itself is being "triple banged" (you're trying to read from it while you're trying to write to it while the video card is reading it to send pixels to the monitor), and the PCI bus (and its limited bandwidth) is being "double banged".
Thanks a lot.
But if I don't reading from display memory, how could I achieve scroll screen?
You'd have some sort of buffer in RAM; which might be raw pixel data, or might be "grid of characters", or might be a big string. It can even be multiple "things" (rather than just one buffer).

For an example, I'll describe what I'd probably do (and what I've done before).

Normally I have a big string in RAM, and just append new characters to it. Eventually (after boot) this string is written to disk (e.g. as the file "/sys/log/boot.txt" or something). When characters are appended they aren't displayed. Instead I have a "flushLog()" function, so that you can append many things (e.g. a list of 100 lines of stuff) and then call "flushLog()" to update the screen.

To keep track of what to display I use 2 pointers. One points to the position in the string for the start of the first character line previously displayed, and one points to the last character line previously displayed. When you call "flushLog()" it checks to see how many lines were appended to the big string since last time, then (using the "first line previously displayed" pointer) skip the same amount of lines to determine a "pointer to new first line to display".

Then I have an "X * Y grid of characters" (e.g. for 1024*768 and an 8*8 font you'd have a 128*96 array of "character and attribute"). From the previous step you know how many lines to scroll. This gives 3 possibilities:
  • The number of lines to scroll is zero. In this case you can just add appended characters using the "last line previously displayed" pointer.
  • The number of lines to scroll is greater than zero but less than the number of character rows on the screen. In this case you can do a single "memcpy();" then add appended characters using the "last line previously displayed" pointer (e.g. with 96 character rows, if 36 new lines were added you'd shift the bottom 60 character rows to the top of the screen then add the last/new 36 lines).
  • The number of lines to scroll is greater than the number of character rows on the screen. In this case everything that was on the screen will scroll off the top, so you can wipe the "X * Y grid of characters" and add appended characters starting from the "pointer to new first line to display" you already have.
I'd also have a "one bit per character row" to indicate if each row was modified or remained the same. If the old data (in the "X * Y grid of characters") was scrolled or wiped I set all these bits to indicate that all rows were modified; otherwise I only set the bits corresponding to the line/s that were modified.

After that, I generate pixel data in another buffer (from that "X * Y grid of characters"); but only for character rows that were actually modified.

Once the "pixel buffer" is done, I blit it to display memory; however I have another buffer to speed this up and actually do something like this:

Code: Select all

    for(x = 0; x < bytes_per_line/4; x++) {
        if(buffer1[src] != buffer2[src]) {
            // This dword is different to what's in display memory
            buffer2[src] = buffer1[src];
            displayMemory[dest] = buffer1[src];
        }
        src += 4;
        dest += 4;
    }
This extra buffer means more reads (from RAM) and more writes (to RAM), but a lot less writes to display memory; and because RAM is a lot faster than display memory it ends up being faster despite the extra reads/writes.

Of course if that "one bit per character row" says the row wasn't modified I'd skip the row during blitting too.


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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: A problem with graphics mode,

Post by Brendan »

Hi,
Antti wrote:
Brendan wrote:you want to write larger amounts at once (e.g. 32-bit or larger).
On some rare cases, writing larger units may cause problems. This is quite irrelevant but may be worth taking into account if you want to be as defensive as possible.
You wouldn't want to cripple performance on all video cards just because one rare video card is a buggy piece of crud. Instead you'd want to either detect if the video card is present and only use a work-around if it is, or (more likely) add it to your OS's "hardware that will never be supported" list and pretend it never existed. ;)


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.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: A problem with graphics mode,

Post by Antti »

Brendan wrote:You wouldn't want to cripple performance on all video cards just because one rare video card is a buggy piece of crud.
True. In this case I would accept if there were just one card. However, this caused me some slight discomfort i.e. how many those kind of issues there are in the wild? Taking this issue into account may be a good thing, but not because this specific problem matters (the one rare video card). Having a way to add something on the list of "hardware that will never be supported" in itself is the main advantage. This one rare video card could just be an entry on that list, which is, like you suggested, the "more likely" and the most correct solution.
626788149
Member
Member
Posts: 25
Joined: Mon Oct 26, 2015 7:24 am
Location: Guilin,China

Re: A problem with graphics mode,

Post by 626788149 »

Brendan wrote:Hi,
626788149 wrote:
Brendan wrote:
  • Never read from display memory. Reading from display memory is extremely slow. Doing it in a "memcpy()" is worse because the memory itself is being "triple banged" (you're trying to read from it while you're trying to write to it while the video card is reading it to send pixels to the monitor), and the PCI bus (and its limited bandwidth) is being "double banged".
Thanks a lot.
But if I don't reading from display memory, how could I achieve scroll screen?
You'd have some sort of buffer in RAM; which might be raw pixel data, or might be "grid of characters", or might be a big string. It can even be multiple "things" (rather than just one buffer).

For an example, I'll describe what I'd probably do (and what I've done before).
.....
.....
.....
This extra buffer means more reads (from RAM) and more writes (to RAM), but a lot less writes to display memory; and because RAM is a lot faster than display memory it ends up being faster despite the extra reads/writes.

Of course if that "one bit per character row" says the row wasn't modified I'd skip the row during blitting too.


Cheers,

Brendan

Thank you very much, now I know how to do it.
626788149
Member
Member
Posts: 25
Joined: Mon Oct 26, 2015 7:24 am
Location: Guilin,China

Re: A problem with graphics mode,

Post by 626788149 »

alexfru wrote:
626788149 wrote:But if I don't reading from display memory, how could I achieve scroll screen?
Buffer?
Thanks!~
Post Reply