Page 1 of 2
Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:20 am
by DavidBG
I am not sure how to google this question, so I'll ask here: As some of you may know, I am working on the GUI for my OS. Up until now, I made a VESA Pixel routine that writes to the video card at offset 0xA000. Then I made a horizontal line, vertical line, box, and filled box routine, all dependent on the original pixel routine. Simply put, I am looping for every pixel I draw. I suppose there is no faster way to blit them other than hardware acceleration? Also, I have heard horizontal lines are faster than vertical ones because of the way the video card is built. Is this true?
Thanks!
David
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:26 am
by DavidBG
Funny, I just got a google hit. It appears no, I need hardware acceleration?
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:39 am
by Solar
Note that there are two different ways of "hardware acceleration", 2D and 3D. The kind of acceleration you are looking for - drawing lines, rectangles and the like - is a distinct set of functions and not (really) related to the 3D acceleration kind.
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:40 am
by DavidBG
Thanks for the quick reply Solar, how do I access 2D acceleration functions? Is it through a BIOS interrupt, or must I dig deeper?
David
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:46 am
by Solar
I have no idea, really.
I never dug that deep from the application side, and I never got that far from the self-written-OS side.
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 10:48 am
by DavidBG
Well, the GPU seems to be what gives 2D hardware and blitting. I'll keep looking into it. BitBLT seems to be something to go by.
David
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 11:00 am
by DavidBG
The wiki
http://wiki.osdev.org/Accelerated_Graphic_Cards briefly goes into it, but it doesn't talk a lot about 2D. Still, from it it sounds like BitBLT is like the QBASIC PUT routine, used for sprites, not 2D drawing primitives.
David
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 11:15 am
by Solar
DavidBG wrote:Still, from it it sounds like BitBLT is like the QBASIC PUT routine, used for sprites, not 2D drawing primitives.
Think dragging of windows.
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 11:47 am
by bitshifter
There are two ways to speed it up.
The first way is plot multiple pixels per pass.
The second way is to write driver for your VC.
But there are so many VC and none are same...
This would take ten lifetimes to support common cards.
I prefer to use VGA since it is present on all modern boards.
And coding in assembler is a must if speed is your goal!
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 11:50 am
by Combuster
I have heard horizontal lines are faster than vertical ones because of the way the video card is built.
No, it's primarily because of how CPU's and memory are built. The effect is caused by Write Combining.
Also, hardware is especially adept at Blits and Fills, not lines. In practice they are rarely used, and even then only take up a comparatively little bit of the CPU's fill rate since they are only one-dimensional. Setting up a small line might even cost you more time than just doing it from the CPU.
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 12:04 pm
by DavidBG
Thanks everyone, I think I have my answer.
What I'll do is blit filling for rectangles and images. That should speed things up.
bitshifter wrote:
And coding in assembler is a must if speed is your goal!
I am coding in 100% NASM, the only language I know other than FreeBASIC and QBASIC. ASM is my favourite. However, C is pretty fast. Though I guess when it comes down to it, ASM is the absolute fastest.
Thanks all!
David
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 12:07 pm
by Creature
Combuster wrote:I have heard horizontal lines are faster than vertical ones because of the way the video card is built.
No, it's primarily because of how CPU's and memory are built. The effect is caused by Write Combining.
Also, hardware is especially adept at Blits and Fills, not lines. In practice they are rarely used, and even then only take up a comparatively little bit of the CPU's fill rate since they are only one-dimensional. Setting up a small line might even cost you more time than just doing it from the CPU.
Besides, suppose you have a 32-bits color depth, every pixel is 4 bytes and you've just coded 64-bit support for your OS. You could write 2 adjacent pixels at the same time horizontally using an 8-byte integer with your buffer.
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 4:38 pm
by Owen
Combuster wrote:I have heard horizontal lines are faster than vertical ones because of the way the video card is built.
No, it's primarily because of how CPU's and memory are built. The effect is caused by Write Combining.
Also, hardware is especially adept at Blits and Fills, not lines. In practice they are rarely used, and even then only take up a comparatively little bit of the CPU's fill rate since they are only one-dimensional. Setting up a small line might even cost you more time than just doing it from the CPU.
PCI/AGP/PCI-E transfer addresses in the same channel as data also, therefore, when doing something other than just incrementing the address, you have to spend an extra bus cycle writing the address (Or, for 64-bit PCI/[AGP (?)], two cycles)
Re: Is there any other way to draw other than pixel by pixel?
Posted: Tue Feb 09, 2010 11:00 pm
by Brendan
Hi,
DavidBG wrote:Then I made a horizontal line, vertical line, box, and filled box routine, all dependent on the original pixel routine. Simply put, I am looping for every pixel I draw.
I assume your using something like "putpixel(x, y, colour)", where every time you call putpixel it calculates the address of the pixel in display memory? That's insanely bad. You should only calculate the position of the starting pixel, and then update this address for each pixel you draw.
For example (assuming 8-bits per pixel):
Code: Select all
void do_vertical_line(x, y, length, colour) {
address = y * bytes_per_line + x + baseAddress;
end_address = address + length * bytes_per_line;
do {
*address = colour;
address += bytes_per_line;
} while(address != end_address);
}
In this case you do one addition per pixel, rather than 2 additions and a multiplication.
For horizontal lines you can do more than one pixel at a time, while making sure that
writes are aligned. For example (assuming 8-bits per pixel):
Code: Select all
void do_horizontal_line(x, y, length, colour) {
uint32_t packed_colour;
address = y * bytes_per_line + x + baseAddress;
/* Ensure alignment */
while( (address & 3) != 0) {
*address = colour;
address++;
length--;
if(length == 0) return;
}
/* Do 32-bits (4 pixels) at a time */
if(length >= 4) {
packed_colour = colour | (colour << 8) | (colour << 16) | (colour << 24);
do {
*(uint32_t *)address = packed_colour;
address += 4;
length -= 4;
} while(length >= 4);
}
/* Do any remaining pixels */
while(length > 0) {
*address = colour;
address++;
length--;
}
}
In this case, for long horizontal lines you get close to 1 addition for every 4 pixels. Of course the while loop in the middle should be converted into something like "rep stosd".
For rectangles, I'd start with something like this (assuming 8-bits per pixel):
Code: Select all
void do_rectangle_line(x, y, width, height, colour) {
address = y * bytes_per_line + x + baseAddress;
line_offset = bytes_per_line - width;
if( (address & 3) == 0) {
if( ( (address + width - 1) & 3) == 0) {
/* start and end is aligned */
packed_colour = colour | (colour << 8) | (colour << 16) | (colour << 24);
while(height > 0) {
temp_width = width;
while(temp_width >= 4) {
*(uint32_t *)address = packed_colour;
address += 4;
temp_width -= 4;
}
address += line_offset;
}
} else {
/* start is aligned but end isn't */
packed_colour = colour | (colour << 8) | (colour << 16) | (colour << 24);
while(height > 0) {
temp_width = width;
while(temp_width >= 4) {
*(uint32_t *)address = packed_colour;
address += 4;
temp_width -= 4;
}
while(temp_width > 0) {
*address = colour;
address++;
temp_width--;
}
address += line_offset;
}
}
} else {
if( ( (address + width - 1) & 3) == 0) {
/* end is aligned but start isn't */
packed_colour = colour | (colour << 8) | (colour << 16) | (colour << 24);
while(height > 0) {
temp_width = width;
while( ( (address & 3) != 0) && (temp_width != 0) ) {
*address = colour;
address++;
temp_width--;
}
while(temp_width >= 4) {
*(uint32_t *)address = packed_colour;
address += 4;
temp_width -= 4;
}
address += line_offset;
}
} else {
/* start and end aren't aligned */
packed_colour = colour | (colour << 8) | (colour << 16) | (colour << 24);
while(height > 0) {
temp_width = width;
while( ( (address & 3) != 0) && (temp_width != 0) ) {
*address = colour;
address++;
temp_width--;
}
while(temp_width >= 4) {
*(uint32_t *)address = packed_colour;
address += 4;
temp_width -= 4;
}
while(temp_width > 0) {
*address = colour;
address++;
temp_width--;
}
address += line_offset;
}
}
}
}
Also, you'd need to use a different routine for each colour depth you support. If you support 8-bpp, 16-bpp and 32-bpp then you'd want 3 routines for vertical lines, 3 routines for horizontal lines and 3 routines for rectangles. For this I'd use function pointers or indirect calls (e.g. "call [address_of_routine_to_do_vertical_lines]") so that you only need to decide which routine to use once during initialisation (and don't need "if(colour_depth == ?)" everywhere).
Mostly, you should get acceptable results on Bochs (which is much slower than real hardware) in all video modes without any hardware acceleration; and if you don't then your code needs to be optimised more...
Cheers,
Brendan
Re: Is there any other way to draw other than pixel by pixel?
Posted: Wed Feb 10, 2010 2:17 am
by quanganht
You have some really good snipets, Brendan! It would definetely help me alot later. Thanks