Fast Pixel Demo

Share your advanced PureBasic knowledge/code with the community.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Fast Pixel Demo

Post 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 :D

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
Last edited by chris319 on Mon Oct 22, 2012 11:14 pm, edited 1 time in total.
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Fast Pixel Demo

Post 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Fred
Administrator
Administrator
Posts: 18247
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Fast Pixel Demo

Post 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).
User avatar
IceSoft
Addict
Addict
Posts: 1695
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: Fast Pixel Demo

Post 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'.
Belive! C++ version of Puzzle of Mystralia
Bug Planet
<Wrapper>4PB, PB<game>, =QONK=, PetriDish, Movie2Image, PictureManager,...
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: Fast Pixel Demo

Post 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?
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Fast Pixel Demo

Post 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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Fred
Administrator
Administrator
Posts: 18247
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Fast Pixel Demo

Post by Fred »

Better increment your pointer instead of using an array, it will be faster.
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Fast Pixel Demo

Post 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?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: Fast Pixel Demo

Post 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.
Last edited by chris319 on Tue Oct 23, 2012 10:22 am, edited 1 time in total.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: Fast Pixel Demo

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Fast Pixel Demo

Post 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.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: Fast Pixel Demo

Post 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
Puffolino
User
User
Posts: 49
Joined: Thu Jan 05, 2012 12:27 am

Re: Fast Pixel Demo

Post 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
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: Fast Pixel Demo

Post 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.
Post Reply