Continuing issue using pointers etc...

General FreeBASIC programming questions.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: Continuing issue using pointers etc...

Post by dafhi »

updated again. I tend to update frequently on new posts.

MaskColor is the transparent color
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Continuing issue using pointers etc...

Post by leopardpm »

ok, after looking over the code, here is my understanding:

Taking a raw image, and using the mask color, encode all the non-mask pixels into runs and put the runs and run info (length of run, start pos of run) into RLI

When blitting, first determine screen clipping, then blit the runs, pixel by pixel, using the clipping.

So this method is a more robust (clipping and variable mask color) blit than mine, uses the same idea of ignoring the transparency, but also RLE the image data. It doesn't appear to blend using the alpha to the background though, but I have trouble deciphering your actual blit statement:

Code: Select all

                    For xSrcS = xSrcS To xSrcE
                        des->p32[xSrcS + DestLeft+ yDst*des->pitchBy] = p32[xSrcS + ySrcS*pitchBy]
                    Next
this is very sophisticated pointer usage (for me) and I get the concept, but could not write the code! could you explain the use of the "->" operator in relation to this code for me?

Nicely done! Have you done any speed tests compared to various PUT statements? Since it blits pixel by pixel it will end up being comparatively slow (I think) compared to straight PUT(pset), but might fare well against PUT(trans). It should be faster than PUT(alpha) because it doesn't blend.

Your above blit section could be faster by changing it to using memcpy... but only for the runs that are longer than a few pixels long.

Very sweet code, Mr. Dafhi! If it is OK with you, I think i will use it as a basis for my attempt at blitting since it already incorporates clipping (which is nice). With very little modification, I think I can convert it to utilizing z-levels and alpha blending since it is already blitting on a pixel by pixel method. But, it will force me to understand pointer stuff ALOT better than I currently do... I gotta stretch my brain some... I will have to change the above actual blit code to pre-calcing the y math ("DestLeft+ yDst*des->pitchBy" and "ySrcS*pitchBy" since these computations result in the same thing for every pixel on a scanline... so drag the speed down when calced on a per pixel basis - I can't stand that kind of speed loss... lol!
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Continuing issue using pointers etc...

Post by leopardpm »

my own blit routine which just is supposed to blit an image (which has been transparency skip encoded) is not working, once again due to my lack of understanding of pointer math etc:

Code: Select all

'-------------------------------------------------------------------------------
sub SpeedSpriteBlit_Ver1(buffer as ulong ptr, dx as ulong, dy as ulong)
' This Blit decodes and blits a SpeedSprite_Ver1 buffer
' assumes first 'pixel' is transparent encoded....
' which it will be the case AS LONG AS the top-left pixel of image is transparent
'
    Union colour
        As ULong clr
        Type
            As UByte b   ' these are in reverse order BGRA instead of ARGB... is that correct? or should it be RGBA / ABGR ? I am so confused!
            As UByte g
            As UByte r
            AS UByte a
        End Type
    End Union

    dim as colour TempColor, tranny
    dim as ulong buf_index = 1

    dim as ulong ptr scr_row
    dim as ulong numBytes = 0

    dim as ulong imgpitch4 = ImagePitch\4, scrpitch4 = SCR_pitch\4
    
    dim as any ptr drowy = SCR_address + dx * 4
    
    'initialize - get first tranny
    tranny.clr = buffer[buf_index] 'first byte of the buffer 'should' be a transparent encoded pixel R,G,B = x,y,RL
    
    do
        ' decode tranny... R = x pos, G = y pos , B = pixels in strip (NOT bytes!), A = 0
        dim as ubyte xpos = tranny.r, ypos = tranny.g, strippixels = tranny.b
        scr_row = SCR_address + (tranny.g * SCR_pitch) + (tranny.r * 4)
        numBytes = tranny.b * 4
        buf_index += 1
        memcpy (scr_row, (buffer+buf_index), numBytes)
        buf_index += numbytes  '--- this is pixels in strip * 4...
        tranny.clr = buffer[buf_index]
    loop until tranny.clr = rgba(255,255,255,0)
end sub
In regards to a 'final' version, since the encoding of an image is not speed dependent, I am thinking it might be worth it to have the encoder test RLE pixel data vs. raw pixel data blit times (or, perhaps, estimated blit times) to determine which method is faster on a per image basis. This doubles the code in the encoding section, and will be twice as slow at encoding, but the actual blitting will end up using the fastest method depending upon the image pixel layout for each image.

Actually, thinking about it more, the RLE encoded image data version will ALWAYS be slower for the PSET version (because of memcpy being so fast for non-RLE data), but potentially faster only for the ALPHA blend version (since memcopy can't be used for a per pixel method).
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Continuing issue using pointers etc...

Post by leopardpm »

I forgot to mention, I really like your method of obtaining the image data and screen data... it is slick and handy!

Here is my entire 'Blit vs PUT comparison test' program that I have been using... it will crash on the last part where it tries to Blit using the above routine of mine. Still shows the times of the different PUT methods (pset, trans, alpha) and my raw basic blit... I am blitting the image 100 times per pixel movement to simulate 100 sprite blits per frame[between the screenlock/unlocks] (boromir mentioned 100 sprites per frame I think, so that was where the 100 came from...)

I find it interesting that my basic blit is about 10% slower than PUT(pset) on larger sprites, but can be many times faster on smaller ones... don't exactly know why...

once again, forgive my 'sloppy' unstructured coding style (especially compared to your sophisticated style!)...

Code: Select all

Const NULL As Any Ptr = 0

#define RGBA_R( c ) ( CUInt( c ) Shr 16 And 255 )
#define RGBA_G( c ) ( CUInt( c ) Shr  8 And 255 )
#define RGBA_B( c ) ( CUInt( c )        And 255 )
#define RGBA_A( c ) ( CUInt( c ) Shr 24         )

#pragma once
#include once "crt/stddef.bi"
extern "C"
    declare function memcpy (byval as any ptr, byval as const any ptr, byval as size_t) as any ptr
end extern

declare sub BasicBlit(img as any ptr, dx as ulong, dy as ulong)
declare sub EncodeSpeedSprite_Ver1(img as any ptr, buffer as ulong ptr)
declare sub SpeedSpriteBlit_Ver1(buffer as ulong ptr, dx as ulong, dy as ulong)

Function bmp_load( ByRef filename As Const String ) As Any Ptr
    Dim As ulong filenum, bmpwidth, bmpheight
    Dim As Any Ptr img
    '' open BMP file
        filenum = FreeFile()
        If Open( filename For Binary Access Read As #filenum ) <> 0 Then Return NULL
        '' retrieve BMP dimensions
            Get #filenum, 19, bmpwidth
            Get #filenum, 23, bmpheight
        Close #filenum
    '' create image with BMP dimensions
        img = ImageCreate( bmpwidth, Abs(bmpheight) )
        If img = NULL Then Return NULL
    '' load BMP file into image buffer
        If BLoad( filename, img ) <> 0 Then ImageDestroy( img ): Return NULL
        Return img
End Function


ScreenRes 640, 480, 32
color RGB(0,0,0), rgb(255,255,255)

    dim shared as ulong SCR_pitch
    dim shared as any ptr SCR_address

    Dim As Any Ptr img
    
    screeninfo ,,,, SCR_pitch
    SCR_address = screenptr
cls

' load the image
dim as string fname = "Test 5-64x31 flat tile.bmpx"
    img = bmp_load( fname )
    If img = NULL Then
        Print "Image failed to load"
        sleep
        ImageDestroy( img )
        end
    end if
    print
    print " Image Loaded: ";fname

' Analyze Image
    print
    dim shared as integer ImageWidth, ImageHeight, ImageBytesPerPixel, ImagePitch
    dim shared as any ptr ImageAddress

    If 0 <> ImageInfo( img, ImageWidth, ImageHeight, ImageBytesPerPixel, ImagePitch, ImageAddress ) Then
        Print "unable to retrieve image information."
        Sleep
        End
    End If
    print "         Width (";ImageWidth;")"
    print "        Height (";ImageHeight;")"
    print " Bytes / Pixel (";ImageBytesPerPixel;")"
    print "         Pitch (";ImagePitch;")"
    print " Num of Pixels (";ImageWidth*ImageHeight;")"
    print " raw size of image is ";ImageWidth*ImageHeight*ImageBytesPerPixel
    print
    print "  Press a key to begin test..."

    put (10,110),img,pset
    sleep

' Test 1 - Straight PUT with pset mode (fastest Freebasic blit command) - Transparency Ignored!
    dim as double t1 = timer
    
    for x as ulong = 10 to 409 ' move 400 pixel positions
        screenlock
        for tst as ulong = 1 to 100 ' blit the image 100 times per 'frame' to simulate 100 sprites on screen at once
            put (x,110),img,pset
        next tst
        screenunlock
        'sleep 1
    next x
    
    locate 2,40 : print "Test Results:"
    locate 3,40 : print using "PUT(pset)  = ##.###"; timer - t1
    sleep
    
    line (10,110)-(409+ImageWidth,110+ImageHeight), rgb(255,255,255),BF 'erase sprite area
    t1 = timer
    
    for x as ulong = 10 to 409 ' move 400 pixel positions
        screenlock
        for tst as ulong = 1 to 100 ' blit the image 100 times per 'frame' to simulate 100 sprites on screen at once
            put (x,110),img,trans
        next tst
        screenunlock
        'sleep 1
    next x

    locate 4,40 : print using "PUT(trans) = ##.###"; timer - t1
    sleep

    line (10,110)-(409+ImageWidth,110+ImageHeight), rgb(255,255,255),BF 'erase sprite area
    t1 = timer
    
    for x as ulong = 10 to 409 ' move 400 pixel positions
        screenlock
        for tst as ulong = 1 to 100 ' blit the image 100 times per 'frame' to simulate 100 sprites on screen at once
            put (x,110),img,alpha
        next tst
        screenunlock
        'sleep 1
    next x

    locate 5,40 : print using "PUT(alpha) = ##.###"; timer - t1
    sleep

    line (10,110)-(409+ImageWidth,110+ImageHeight), rgb(255,255,255),BF 'erase sprite area
    t1 = timer
    
    for x as ulong = 10 to 409 ' move 400 pixel positions
        screenlock
        for tst as ulong = 1 to 100 ' blit the image 100 times per 'frame' to simulate 100 sprites on screen at once
            BasicBlit(img,x,110)
        next tst
        screenunlock
        'sleep 1
    next x

    BasicBlit(img,50,310)
    locate 6,40 : print using "Basic Blit(pset) = ##.###"; timer - t1
    sleep

    ' Allocate and initialize space for size of image color elements.
    Dim SS1_buffer As ulong Ptr = CAllocate(ImageWidth*ImageHeight,4)
    EncodeSpeedSprite_Ver1(img, SS1_buffer)

    line (10,110)-(409+ImageWidth,110+ImageHeight), rgb(255,255,255),BF 'erase sprite area
    t1 = timer
    
    for x as ulong = 10 to 409 ' move 400 pixel positions
        screenlock
        for tst as ulong = 1 to 100 ' blit the image 100 times per 'frame' to simulate 100 sprites on screen at once
            SpeedSpriteBlit_Ver1(SS1_buffer, x, 110)
        next tst
        screenunlock
        'sleep 1
    next x

    SpeedSpriteBlit_Ver1(SS1_buffer, 90, 310)
    locate 7,40 : print using "Speed Blit(trans) = ##.###"; timer - t1
    sleep



    


'
' print out SS1_buffer
'
    dim as ulong buf_index = 0,tempcolor = 0
    do
    buf_index += 1
    tempcolor = SS1_buffer[buf_index]
    print using "#####: R### G### B### A###";buf_index;rgba_r(tempcolor);rgba_g(tempcolor);rgba_b(tempcolor);rgba_a(tempcolor)
    loop until SS1_buffer[buf_index] = rgba(255,255,255,0)
    print buf_index
    sleep
    end
    
'-------------------------------------------------------------------------------
'-------------------------------------------------------SUBROUTINES-------------
'-------------------------------------------------------------------------------
sub BasicBlit(img as any ptr, dx as ulong, dy as ulong)
' BasicBlit is just a pset
'
    dim as ulong ptr image_end, img_row, scr_row
    dim as ulong numBytes = ImageWidth * 4
    
    img_row = ImageAddress
    scr_row = SCR_address + (dy * SCR_pitch) + (dx * 4)
    image_end = ImageAddress + ((ImageHeight-1) * ImagePitch)
    
    dim as ulong imgpitch4 = ImagePitch\4, scrpitch4 = SCR_pitch\4
    do
        memcpy (scr_row, img_row, numBytes)
        img_row = img_row + imgpitch4
        scr_row = scr_row + scrpitch4
    loop until img_row > image_end
end sub

'-------------------------------------------------------------------------------
sub EncodeSpeedSprite_Ver1(img as any ptr, buffer as ulong ptr)
    ' This version just iterates through the image, copying over the RGBA values to the buffer,
    ' EXCEPT if the value has an Alpha of '0' (totally transparent) in which case it finds the
    ' next non-transparent pixel and saves this location in the R & G (x & y respectively) of
    ' the copied over transparent pixel... basically RLE's only the transparent pixels...
    '
    ' MUST CAllocate enough space into buffer before calling!
    '
    dim as ulong buf_index = 0, tranny = 0, reggy = 0, last_tranny = 0
    dim as ulong TempColor
    
    For y As ulong = 0 To ImageHeight - 1
        Dim row As ulong Ptr = ImageAddress + y * ImagePitch
        For x As ulong = 0 To ImageWidth - 1
            TempColor = row[x]
            if RGBA_A(TempColor) = 0 then
                ' if transparent then...count it
                tranny += 1
                if tranny = 1 then ' .. if first tranny then stop counting reggy and save it!
                    ' --- hopefully it is grabbing 4 bytes (32 bit) value here...???
                    TempColor = buffer[last_tranny]
                    ' this next line assumes that reggy is less than 255 - all the pixels on same line...
                    buffer[last_tranny] = rgba(RGBA_R(TempColor),RGBA_G(TempColor),reggy,0)
                    reggy = 0
                end if
                
            else
                ' if NOT transparent, then....
                if tranny > 0 then ' if currently counting transparent pixels...
                    ' then ran into a regular pixel... so make the Transparent coded pixel...
                    buf_index += 1
                    last_tranny = buf_index
                    buffer[buf_index] = rgba(x,y,0,0)
                    tranny = 0
                end if
                ' just a regular pixel color, so save it into buffer
                buf_index += 1
                reggy += 1
                buffer[buf_index] = TempColor
            end if
        next x
        if reggy > 0 then 'was still accumulating regular pixels when hit end of line.
            ' so make the last trans pixel say only reggy amount of pixels and make new trans pix for next line...
            TempColor = buffer[last_tranny] '--- hopefully it is grabbing 4 bytes (32 bit) value here...???
            ' this next line assumes that reggy is less than 255 - all the pixels on same line...
            buffer[last_tranny] = rgba(RGBA_R(TempColor),RGBA_G(TempColor),reggy,0)
            reggy = 0
            dim as ulong hold_y = y+1
            if hold_y < ImageHeight then
                buf_index += 1
                buffer[buf_index] = rgba(0,y+1,0,0) ' new trans pixel pointing to beginning of next line
                last_tranny = buf_index
            end if
        end if
    next y
    ' put in 'end of sprite' delimiter...
    buf_index += 1
    buffer[buf_index] = rgba(255,255,255,0) ' no other color in buffer will be transparent white...
end sub

'-------------------------------------------------------------------------------
sub SpeedSpriteBlit_Ver1(buffer as ulong ptr, dx as ulong, dy as ulong)
' This Blit decodes and blits a SpeedSprite_Ver1 buffer
' assumes first 'pixel' is transparent encoded....
' which it will be the case AS LONG AS the top-left pixel of image is transparent
'
    Union Colour
        As ULong clr
        ' these are in reverse order BGRA instead of ARGB... 
        ' is that correct? or should it be RGBA / ABGR ? I am so confused!
        Type
            As UByte b
            As UByte g
            As UByte r
            AS UByte a
        End Type
    End Union

    dim as colour TempColor, tranny
    dim as ulong buf_index = 1

    dim as ulong ptr scr_row
    dim as ulong numBytes = 0

    dim as ulong imgpitch4 = ImagePitch\4, scrpitch4 = SCR_pitch\4
    
    dim as any ptr drowy = SCR_address + dx * 4
    
    'initialize - get first tranny
    tranny.clr = buffer[buf_index]
    
    do
        ' decode tranny... R = x pos, G = y pos , B = pixels in strip (NOT bytes!), A = 0
        dim as ubyte xpos = tranny.r, ypos = tranny.g, strippixels = tranny.b
        scr_row = SCR_address + (tranny.g * SCR_pitch) + (tranny.r * 4)
        numBytes = tranny.b * 4
        buf_index += 1
        memcpy (scr_row, (buffer+buf_index), numBytes)
        buf_index += numbytes  '--- this is pixels in strip * 4...
        tranny.clr = buffer[buf_index]
    loop until tranny.clr = rgba(255,255,255,0)
end sub
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: Continuing issue using pointers etc...

Post by dafhi »

I coded the thing around 2006. I hardly understand it now but from looking at a few things and comparing with recent ideas, I've merged the best of 2 eras. Doubt I attempt a new one anytime soon though :D

I've updated the post with a few optimizations. Yes of course you're welcome to use it

With regard to why your blit is faster on smaller bitmaps, your blit probably has a small overhead

pointer info - (updated: thanks fxm)

Code: Select all

' 4 examples of pointer->member access

type mytype
  as single x=rnd,y=rnd
end type

dim as mytype     a(1)
dim as mytype ptr p = @a(0)


? "val", "address"
? " ---------", "--------"

'1
? p->x, @p->x

'2
? (*p).x, @(*p).x

'3
with *p
  ? .x, @.x
end with

'4 - next array element
with p[1]
  ? .x, @.x
end with

sleep
Last edited by dafhi on Jun 13, 2017 19:12, edited 1 time in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Continuing issue using pointers etc...

Post by leopardpm »

dafhi wrote:I've updated the post with a few optimizations. Yes of course you're welcome to use it
will look at your optimizations! thank you!
With regard to why your blit is faster on smaller bitmaps, your blit probably has a small overhead
that makes perfect sense - I don't have boundary checking/clipping going on and that in itself is a few clock cycles... I am quite sure this is the culprit.
pointer info
yay! will study and play... I need to learn this stuff because I think it lends itself to much faster algos... and I like speed...
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Continuing issue using pointers etc...

Post by MrSwiss »

leopardpm wrote:I think it lends itself to much faster algos... and I like speed...
Nope, not necessarily speed, more flexibility.
An example in ASM:

Code: Select all

ASM
    mov    rax,    qword ptr [value]	' direct variable access
' now with ptr access ... same operation
    mov    rax,    qword ptr [value]	' get address, then, get value
    mov    rax,    [rax]				' ptr needs 2 x mov
End ASM
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Continuing issue using pointers etc...

Post by leopardpm »

MrSwiss wrote:
leopardpm wrote:I think it lends itself to much faster algos... and I like speed...
Nope, not necessarily speed, more flexibility.
An example in ASM:

Code: Select all

ASM
    mov    rax,    qword ptr [value]	' direct variable access
' now with ptr access ... same operation
    mov    rax,    qword ptr [value]	' get address, then, get value
    mov    rax,    [rax]				' ptr needs 2 x mov
End ASM
hmmm, interesting... so are you saying that (even though my current blit routine is broken...) instead of using pointers in my routine, it could be faster by doing it another way? The whole pointer business drives me crazy, I never know if I am supposed to be 'dereferencing' a pointer first or what... if I can avoid using them AND make just as faster or faster code, then I will surely do that!
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Continuing issue using pointers etc...

Post by fxm »

dafhi wrote:pointer info

Code: Select all

' 4 examples of pointer->member access

type mytype
  as single x=rnd,y=rnd
end type

dim as mytype     a(1)
dim as mytype ptr p = @a(0)


? "val", "address"
? " ---------", "--------"

'1
? p->x, @p->x

'2
? *(p).x, @*(p).x

'3
with *p
  ? .x, @.x
end with

'4 - next array element
with p[1]
  ? .x, @.x
end with

sleep
? *(p).x, @*(p).x
is not the correct syntax.
The good syntax is:
? (*p).x, @(*p).x

See the bug report:
#811 *(ptr).field should give an error
(Operator Precedence)
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Continuing issue using pointers etc...

Post by MrSwiss »

Depends on your needs mainly, if you have a lot of allocate/deallocate stuff, ptr is maybe better.
Resizing is another such thing, because the MEM-Block, might be at a different location afterwards.
On more static defined allocations, arrays may be a advantage. (don't know any fixed rule)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Continuing issue using pointers etc...

Post by fxm »

About:
It seems that a similar parsing bug also exists for the pointers to procedure pointer:
Precedence rule between:
'operator ()' (procedure call)
and:
'operator *' (value of)
is not verified.

Simple example:

Code: Select all

Sub s (Byval i As Integer = 0)
End Sub

Dim As Sub (Byval As Integer = 0) ps = @s
Dim As Sub (Byval As Integer = 0) Ptr pps = @ps

   ps(0)       ' OK

   (*pps)(0)   ' OK
'  *pps(0)     ' NOK
'  *(pps(0))   ' NOK
   *(pps)(0)   ' should be NOK   <=== parsing bug ?

   (*pps)()    ' OK
   *pps()      ' should be NOK   <=== parsing bug ?
'  *(pps())    ' NOK
   *(pps)()    ' should be NOK   <=== parsing bug ?
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Continuing issue using pointers etc...

Post by fxm »

@Jeff,

I have not been able to test yet (due to lack of recent build) one of your last commits "fbc: fix sf.net # 811 *(ptr).field should give an error", but does it also take into account the post just above ("... similar parsing bug also exists for the pointers to procedure pointer") ?
SARG
Posts: 1756
Joined: May 27, 2005 7:15
Location: FRANCE

Re: Continuing issue using pointers etc...

Post by SARG »

@fxm
Sorry I forgot to upload lastest version. Done soon.

Done. https://users.freebasic-portal.de/sarg/ ... astest.zip
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Continuing issue using pointers etc...

Post by fxm »

fxm wrote: Dec 07, 2022 6:25 @Jeff,

I have not been able to test yet (due to lack of recent build) one of your last commits "fbc: fix sf.net # 811 *(ptr).field should give an error", but does it also take into account the post just above ("... similar parsing bug also exists for the pointers to procedure pointer") ?

I just tested it.
It's OK.
(Thanks SARG)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Continuing issue using pointers etc...

Post by fxm »

Attention users:

The old '#811 *(ptr).field should give an error' bug is now fixed for fbc version 1.10.0 (and later).
I noticed that some used this wrong expression '*(ptr).field' in their code.
They will need to replace it with the valid expression: '(*ptr).field'

Same problem for a pointeur to procedure-pointer 'Ptr_to_ProcPtr'.
Use:
'(*Ptr_to_ProcPtr)()' instead of '*Ptr_to_ProcPtr()'
'(*Ptr_to_ProcPtr)(...)' instead of '*(Ptr_to_ProcPtr)(...)'

(this post because who reads the 'changelog.txt' file besides me ?)
Post Reply