Page 1 of 1
Fast Pixel Demo
Posted: Mon Oct 22, 2012 4:11 am
by chris319
Here is some sample code demonstrating how to plot pixels faster than PB's native Plot() function. There is some unused code in here but you can deal with that
One wonders if PB's native Plot() function could be made faster.
Feel free to modify/improve this code.
Code: Select all
imWidth = 1024: imHeight = 768: imHeightm1 = imHeight - 1: imWidthm1 = imWidth - 1
Procedure PlotPixel(x, y)
Shared buffer, imHeightm1, pitch
hm1 = buffer + ((imHeightm1 - y) * pitch)
PokeL(hm1 + x * 4, $ff0000)
EndProcedure
LoadFont(0, "Arial", 14)
result = CreateImage(0, imWidth, imHeight, 32)
If result = 0: MessageRequester("Error", "Unable to create image."): End: EndIf
; Structure Pixel
; Pixel.l
; EndStructure
OpenWindow(0, 0, 0, imWidth, imHeight, "Fast Pixels", $CA0001)
; GetObject_(ImageID(0), SizeOf(BITMAP), @bmp.BITMAP)
; Width = bmp\bmWidth
; Height = bmp\bmHeight
; widthbytes = bmp\bmWidthbytes
; *colorbits = bmp\bmBits
; *pxout.LONG
;
; then = ElapsedMilliseconds()
; For rep = 1 To 50
; For y = 0 To imHeight - 1
; *pxout = *colorbits + (widthBytes * y)
; For x = 0 To widthbytes - 1 Step 3
; *pxout\l = RGB(33,$88,$44)
; *pxout + 3
; Next
; Next
; Next
; now = ElapsedMilliseconds()
StartDrawing(ImageOutput(0))
then2 = ElapsedMilliseconds()
For rep = 1 To 50
For y = 0 To imHeightm1
For x = 0 To imWidthm1
Plot(x, y, $448821)
Next
Next
Next
StopDrawing()
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
now2 = ElapsedMilliseconds()
StopDrawing()
StartDrawing(ImageOutput(0))
buffer = DrawingBuffer() ; Get the start address of the buffer
pitch = DrawingBufferPitch()
StopDrawing()
then3 = ElapsedMilliseconds()
For rep = 1 To 50
For y = 0 To imHeightm1
For x = 0 To imWidthm1
hm1 = buffer + ((imHeightm1 - y) * pitch)
PokeL(hm1 + x * 4, $ff0000)
Next
Next
Next
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
now3 = ElapsedMilliseconds()
StopDrawing()
;APPROXIMATELY 3 TIMES FASTER
StartDrawing(ImageOutput(0))
buffer = DrawingBuffer() ; Get the start address of the buffer
pitch = DrawingBufferPitch()
StopDrawing()
then4 = ElapsedMilliseconds()
For rep = 1 To 50
hm1 = buffer + (imHeightm1 * pitch)
For y = 0 To imHeightm1
; hm1 = buffer + ((imHeightm1 - y) * pitch)
For x = 0 To imWidthm1
PokeL(hm1 + x * 4, $0000ff)
Next
hm1 - pitch
Next
Next
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
now4 = ElapsedMilliseconds()
DrawingFont(FontID(0))
;DrawText(100,150,StrF((now - then) / 1,0)+ " milliseconds")
DrawText(100,175,StrF((now2 - then2) / 1,0)+ " milliseconds")
DrawText(100,200,StrF((now3 - then3) / 1,0)+ " milliseconds")
DrawText(100, 225, StrF((now4 - then4) / 1,0) + " milliseconds")
StopDrawing()
Repeat
Event = WaitWindowEvent()
Until Event = #WM_CLOSE
;DeleteObject_(bmp)
FreeImage(0)
End
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 10:55 am
by STARGÅTE
lol, nice fake your code ...
Why you use in the Plot demo the RGB(33,$88,$44) in each plot? You can write 4491297 for the color.
Plot(X,Y) is for plot one Pixel!
In your example ("APPROXIMATELY 3 TIMES FASTER") you plot a whole box.
So you have to compare it with Box():
Code: Select all
StartDrawing(ImageOutput(0))
then2 = ElapsedMilliseconds()
For rep = 1 To 50
Box(x, y, imWidthm1+1, imHeightm1+1, RGB(33,$88,$44))
Next
StopDrawing()
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
now2 = ElapsedMilliseconds()
StopDrawing()
93 milliseconds
234 milliseconds
172 milliseconds
in this case PB Box() function is 2 times faster than you Box- (not plot) function.
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 1:06 pm
by Fred
Using PokeL() is faster but you are restricting the plot to 32 bit bitmap (on 24 bit it can crash as you ploting one byte more).
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 6:34 pm
by IceSoft
Fred wrote:Using PokeL() is faster but you are restricting the plot to 32 bit bitmap (on 24 bit it can crash as you ploting one byte more).
Using pointers is 'the fastest'.
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 9:42 pm
by chris319
Why you use in the Plot demo the RGB(33,$88,$44) in each plot? You can write 4491297 for the color.
Good catch. I substituted 4491297 and eliminated the call to RGB() with not much improvement in speed.
Plot(X,Y) is for plot one Pixel!
In your example ("APPROXIMATELY 3 TIMES FASTER") you plot a whole box.
The idea is to compare painting a bitmap one pixel at a time. If you're working with a complex image, Box() isn't going to help you much. See here:
http://www.purebasic.fr/english/viewtop ... lm#p391944
Using pointers is 'the fastest'.
Someone suggested that some time ago in a different context and the benchmark results were inconclusive. Could you please modify the demo accordingly and share it with us?
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 10:05 pm
by STARGÅTE
In your code (
http://www.purebasic.fr/english/viewtop ... lm#p391944) you write a Long (PokeL) in a buffer for 24-Bit color!
Thats a problem like Fred writes.
If you have a Image with 4x4 Pixel the pitch in 24Bit is 12Byte
If you write at 0, 3, 6, 9 a Long, the last Pixel over writes the image buffer, and you can get an IMA.
The example with Pointer (32-Bit Image only)
Code: Select all
Structure LongArray
l.l[0]
EndStructure
*Pointer.LongArray = *Buffer
For Y = 0 To Height
For X = 0 To Width
*Pointer\l[X] = Color
Next
*Pointer + Pitch
Next
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 10:19 pm
by Fred
Better increment your pointer instead of using an array, it will be faster.
Re: Fast Pixel Demo
Posted: Mon Oct 22, 2012 10:49 pm
by STARGÅTE
ok, but faster is the ASM version (32Bit)
Code: Select all
Structure LongArray
l.l[0]
EndStructure
*Buffer = AllocateMemory(100*100*4)
Pitch = 100*4
Color = $FFC08040
Time = ElapsedMilliseconds()
For n = 1 To 10000
*PointerY = *Buffer
For Y = 0 To 99
*PointerX.Long = *PointerY
For X = 0 To 99
*PointerX\l = Color
*PointerX + 4
Next
*PointerY + Pitch
Next
Next
Time1 = ElapsedMilliseconds()-Time
Time = ElapsedMilliseconds()
For n = 1 To 10000
!mov eax, [v_Pitch]
!mov edx, 100
!mul edx
!mov ecx, [p_Buffer]
!add ecx, eax
loop1:
!sub ecx, [v_Pitch]
!mov edx, 400
loop2:
!sub edx, 4
!mov eax, [v_Color]
!mov [ecx+edx], eax
!jnz l_loop2
!cmp ecx, [p_Buffer]
!jne l_loop1
Next
Time2 = ElapsedMilliseconds()-Time
MessageRequester("", Str(Time1)+#LF$+Str(Time2))
chris319: in this case, I / we can create a ASM code for your code in the webcam topic?
Re: Fast Pixel Demo
Posted: Tue Oct 23, 2012 1:44 am
by chris319
chris319: in this case, I / we can create a ASM code for your code in the webcam topic?
Yes, of course. I would like to keep the PB version in place, however, for those who do not wish to venture into ASM.
If you're going to do speed comparisons the playing field has to be level. I modified your code so that the buffer equals my image size of 1024 x 768. The results on my machine with 500 iterations are:
Method using Poke(): 764 ms
Method using pointers: 764 ms
I haven't tried the ASM code yet.
Re: Fast Pixel Demo
Posted: Tue Oct 23, 2012 3:36 am
by chris319
Of course the ASM wins!
Code: Select all
imWidth = 1024: imHeight = 768
LoadFont(0, "Arial", 14)
result = CreateImage(0, imWidth, imHeight, 32)
If result = 0: MessageRequester("Error", "Unable to create image."): End: EndIf
StartDrawing(ImageOutput(0))
*myBuffer = DrawingBuffer() ; Get the start address of the buffer
myPitch = DrawingBufferPitch()
StopDrawing()
OpenWindow(0, 0, 0, imWidth, imHeight, "Fast Pixels", $CA0001)
Structure LongArray
l.l ;[0]
EndStructure
*Buffer = *myBuffer
Pitch = myPitch
Color = $FFC08040
color2 = $ff00ff00
Time = ElapsedMilliseconds()
For rep = 1 To 100
*PointerY = *Buffer
For Y = 0 To 767
*PointerX.Long = *PointerY
For X = 0 To 1023
*PointerX\l = color2
*PointerX + 4
Next
*PointerY + Pitch
Next
Next
Time1 = ElapsedMilliseconds()-Time
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
StopDrawing()
Delay(500) ;SHOW OFF WHAT WE HAVE DONE
Time = ElapsedMilliseconds()
For rep = 1 To 100
!mov eax, [v_Pitch]
!mov edx, 768
!mul edx
!mov ecx, [p_Buffer]
!add ecx, eax
loop1:
!sub ecx, [v_Pitch]
!mov edx, 1024 * 4
loop2:
!sub edx, 4
!mov eax, [v_Color]
!mov [ecx+edx], eax
!jnz l_loop2
!cmp ecx, [p_Buffer]
!jne l_loop1
Next
Time2 = ElapsedMilliseconds() - Time
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
DrawingFont(FontID(0))
DrawText(100,150,Str(time1)+ " milliseconds")
DrawText(100,175,Str(time2)+ " milliseconds")
StopDrawing()
Repeat
event = WaitWindowEvent()
Until event = #PB_Event_CloseWindow
Re: Fast Pixel Demo
Posted: Tue Oct 23, 2012 6:56 am
by wilbert
Two little remarks ...
For the ASM version, it's better to move !mov eax, [v_Color] just before loop1: .
There's no need to put that line inside the loop since eax doesn't change within the loop. It will be faster if you move it out of the loop.
It's better to use PB constants for the flags when you open a window.
The values of the constants may be different across different platforms.
$CA0001 for example doesn't open a visual window at all on OS X.
Re: Fast Pixel Demo
Posted: Tue Oct 23, 2012 8:45 am
by chris319
It's better to use PB constants for the flags when you open a window.
The values of the constants may be different across different platforms.
$CA0001 for example doesn't open a visual window at all on OS X.
That code was written by someone else. It's fixed now.
For the ASM version, it's better to move !mov eax, [v_Color] just before loop1: .
There's no need to put that line inside the loop since eax doesn't change within the loop. It will be faster if you move it out of the loop.
Like so (I have also made the colors constant for a fair speed comparison). This won't work for the webcam demo, however, where each pixel is presumed different and eax will have to be reloaded each time.
Code: Select all
imWidth = 1024: imHeight = 768
LoadFont(0, "Arial", 14)
result = CreateImage(0, imWidth, imHeight, 32)
If result = 0: MessageRequester("Error", "Unable to create image."): End: EndIf
StartDrawing(ImageOutput(0))
*myBuffer = DrawingBuffer() ; Get the start address of the buffer
myPitch = DrawingBufferPitch()
StopDrawing()
OpenWindow(0, 0, 0, imWidth, imHeight, "Fast Pixels", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
Structure LongArray
l.l ;[0]
EndStructure
*Buffer = *myBuffer
Pitch = myPitch
Time = ElapsedMilliseconds()
For rep = 1 To 100
*PointerY = *Buffer
For Y = 0 To 767
*PointerX.Long = *PointerY
For X = 0 To 1023
*PointerX\l = $ff00ff00
*PointerX + 4
Next
*PointerY + Pitch
Next
Next
Time1 = ElapsedMilliseconds()-Time
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
StopDrawing()
Delay(500) ;SHOW OFF WHAT WE HAVE DONE
Time = ElapsedMilliseconds()
For rep = 1 To 100
!mov eax, [v_Pitch]
!mov edx, 768
!mul edx
!mov ecx, [p_Buffer]
!add ecx, eax
loop1:
!sub ecx, [v_Pitch]
!mov edx, 1024 * 4
loop2:
!sub edx, 4
!mov eax, $ff08040
!mov [ecx+edx], eax
!jnz l_loop2
!cmp ecx, [p_Buffer]
!jne l_loop1
Next
Time2 = ElapsedMilliseconds() - Time
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
StopDrawing()
Delay(500)
Time = ElapsedMilliseconds()
For rep = 1 To 100
!mov eax, [v_Pitch]
!mov edx, 768
!mul edx
!mov ecx, [p_Buffer]
!add ecx, eax
!mov eax, $ffff040
loop3:
!sub ecx, [v_Pitch]
!mov edx, 1024 * 4
loop4:
!sub edx, 4
!mov [ecx+edx], eax
!jnz l_loop4
!cmp ecx, [p_Buffer]
!jne l_loop3
Next
Time3 = ElapsedMilliseconds() - Time
StartDrawing(WindowOutput(0))
DrawImage(ImageID(0), 0, 0)
DrawingFont(FontID(0))
DrawText(100,150,Str(time1)+ " milliseconds")
DrawText(100,175,Str(time2)+ " milliseconds")
DrawText(100,200,Str(time3)+ " milliseconds")
StopDrawing()
Repeat
event = WaitWindowEvent()
Until event = #PB_Event_CloseWindow
Re: Fast Pixel Demo
Posted: Sun Nov 18, 2012 2:22 pm
by Puffolino
For writing a program which should work on different machines and graphic cards, it may cause problems when using assembler code only, but what is the quickest safe possibility to draw dots?
Does anyone know, why Box(x,y,1,1,color) is so much faster than Plot(x,y,color)?
I am using the code below for drawing a pixel and the plot function takes 5 to 10 times longer than the box command. I also was wondering, that for both functions it has to be checked, if the coordinates are within the screen. So where's the advantage for using the plot function?
Code: Select all
Macro PlotXY(x,y,color)
CompilerIf #PlotSpeedup
Box(x,y,1,1,color)
CompilerElse
Plot(x,y,color)
CompilerEndIf
EndMacro
Macro PlotCheckXY(x,y,color)
; MOV eax,x
; MOV ecx,y
; CMP eax,ScreenX ; x >= ScreenX ?
; JGE exitMP
; CMP ecx,ScreenY ; y >= ScreenY ?
; JGE exitMP
; OR eax,ecx ; x OR y < 0?
; JS exitMP
If (x)>=0 And (x)<ScreenX And (y)>=0 And (y)<ScreenY
PlotXY(x,y,color)
EndIf
!exitMP:
EndMacro
Re: Fast Pixel Demo
Posted: Sun Nov 18, 2012 9:00 pm
by Thorium
Puffolino wrote:For writing a program which should work on different machines and graphic cards, it may cause problems when using assembler code only, but what is the quickest safe possibility to draw dots?
The asm code does not run on the graphics card. The only hardware dependence is the CPU, which need to be a x86 in 32bit mode in this case.
The fastest way to draw pixels was allready posted, both in PB and asm. Access the image buffer and calculate the pixel position and write the pixel with a pointer. If you need to draw a complete image write a loop that does not calculate the position but increment the pointer to save time. Just calculate the start of every line with the pitch and inside the line increment the pointer by the pixel size.