News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Function Pointers In Game Logic

Started by Caché GB, June 22, 2018, 08:14:44 AM

Previous topic - Next topic

Caché GB

Function Pointers in game logic

Hello everyone and welcome to my discussion on Function Pointers.

Function Pointers used in game code can be a very useful technique for speeding up loop
mechanisms execution time. They can do this by avoiding decision structured execution in 
game logic allowing for less instructions.

In the WNDCLASSEX STRUCT that is passed as a pointer in RegisterClassEx, the .lpfnWndProc
value is an example of a function pointer. This value points to your Window Procedure that
is used by Windows.

Although function pointer are a simple concept they do lead to complex code.

Here is how function pointer work.


lda MACRO Operand, Param    ;; lda => load AddressOf
    lea eax, Param
    mov Operand, eax
ENDM

;===============================================================>

.data

  swFunctionPointers   db   "Function Pointers", 0

.code
SomeFunction00 PROC
   invoke  MessageBoxA, null, CSTR("In Function 00"), addr swFunctionPointers, MB_OK
      ret
SomeFunction00 ENDP

SomeFunction01 PROC
   invoke  MessageBoxA, null, CSTR("In Function 01"), addr swFunctionPointers, MB_OK
      ret
SomeFunction01 ENDP

SomeFunction03 PROC
   invoke  MessageBoxA, null, CSTR("In Function 02"), addr swFunctionPointers, MB_OK
      ret
SomeFunction03 ENDP

etc.

;===============================================================>

.data
   g_pFunction DWORD 6 dup (0)

.code
SomeProc PROC

    local i:dword

          ...

          lda  g_pFunction[0*4], SomeFunction00
          lda  g_pFunction[1*4], SomeFunction01
          lda  g_pFunction[2*4], SomeFunction02
          lda  g_pFunction[3*4], SomeFunction03
          lda  g_pFunction[4*4], SomeFunction04
          lda  g_pFunction[5*4], SomeFunction05

          mov  i, 0
       .while (i < 6)
          mov  ecx, i
          mov  eax, 0
          .if (eax != g_pFunction[ecx*4])             
             call  g_pFunction[ecx*4]
          .endif
          inc  i
        .endw

          ...

          ret

SomeProc ENDP

;===============================================================>


In the zip file attachment is an asm file containing MASM32 code that demonstrates
how function pointers can be used to speed up loop execution.
Caché GB's 1 and 0-nly language:MASM

HSE

#1
Or just make the table:.data
   g_pFunction dd offset SomeFunction00
               dd offset SomeFunction01
               ...
               dd offset SomeFunction05
Equations in Assembly: SmplMath

felipe

Or just inline your functions... :idea:

felipe

What do you mean by decision structured execution? If you mean that this will avoid doing comparisons and jumps you should watch what .while really do. :idea:

Siekmanski

Just plain and simple, Jump Tables.

.data

RoutineJumpTable    dd offset Routine_0, offset Routine_1, offset Routine_2, ........... etc.


.code

    mov eax,0 ;routine_number
    jmp         [RoutineJumpTable+eax*4]
;    call         [RoutineJumpTable+eax*4] ; or call a routine

align 4
Routine_0:
; start of your awesome routine code
    ret

align 4
Routine_1:
; start of your awesome routine code
    ret

align 4
Routine_2:
; start of your awesome routine code
    ret
Creative coders use backward thinking techniques as a strategy.

Caché GB

Hello HSE.

Thank you very much for your knowledgeable advice.

Hello Siekmanski.

Thank you very much also for your knowledgeable advice.

Hello felipe.

Thank you very much for your input.

Yes  decision structured execution  == comparisons and jumps.

Yes the aim is to avoid doing comparisons and jumps, very much so.
Caché GB's 1 and 0-nly language:MASM

felipe

Quote from: Caché GB on June 22, 2018, 12:01:12 PM
Yes the aim is to avoid doing comparisons and jumps, very much so.

I'm not pretty sure, but i think the mmx instruction set has some "feature" to avoid this when making some computations. Someday i will take a look to mmx instruction set again but deeper. Maybe (probably) other members knows about this feature. Maybe there are similar features in newer instruction sets.   :idea:

The main problem with this feature is that you can use it just for specific computation types, but as i said before i really don't know, i just remember a little what i readed some long time ago about mmx.  :idea:

daydreamer

I think for finite state machine,its great to have a jumptable,just make a loop that loads array of State for 100's of enemies and use jumptable
also useful for the right image for 2d sprite for enemies/player character,depending on direction it points at and moves,action like fire a gun,jump,nonaction idle I seen in some games,the PC falls asleep after certain milliseconds inaction
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

Caché GB

Thanks felipe for the info.

Once again thanks to HSE and Siekmanski. Jump Tables are definitely the goto.

I see that hierarchical complexity can quickly be implemented and for deep nesting, it is possible to have
predetermined or calculated code paths (tree transversal) for rapidly dealing with course and effect. As
daydreamer pointed out "loads array of STATE for 100's of ... and use jump table".

Thanks daydreamer.

Awesome! Welcome to the JUNGLE, Table.



.data

szEntityZero   db "I am Entity Zero", 0
szEntityOne    db "I am Entity One", 0
    etc.

g_pBehaviour   dd offset Behaviour00, offset Behaviour00Routine00,  offset Behaviour00Routine01,  offset Behaviour00Routine02
               dd offset Behaviour01, offset Behaviour01Routine00,  offset Behaviour01Routine01,  offset Behaviour01Routine02
               dd offset Behaviour02, offset Behaviour02Routine00,  offset Behaviour02Routine01,  offset Behaviour02Routine02
               dd offset Behaviour03, offset Behaviour03Routine00,  offset Behaviour03Routine01,  offset Behaviour03Routine02
               dd offset Behaviour04, offset Behaviour04Routine00,  offset Behaviour04Routine01,  offset Behaviour04Routine02
               dd offset Behaviour05, offset Behaviour05Routine00,  offset Behaviour05Routine01,  offset Behaviour05Routine02

GameEntity     ENTITY  NumEntities dup (<>)

.code

;===========================================>

          ...
          lea  esi, GameEntity
       assume  esi:ptr ENTITY

          mov [esi].ENTITY.MyName, offset szEntityZero
          add  esi, sizeof(ENTITY)
          mov [esi].ENTITY.MyName, offset szEntityOne
          add  esi, sizeof(ENTITY)
             
          etc.

          mov  i, 0
       .while (i < NumEntities)

          invoke  RAND32, 6              ;; RNG by NaN (May 5, 2001)
            imul  eax, 4
             mov  edx, eax
             mAm [esi].ENTITY.BehaviourPtr, [g_pBehaviour+eax*4]
   
          invoke  RAND32, 3
             add  eax, 1
             add  eax, edx
             mAm [esi].ENTITY.RoutinePtr, [g_pBehaviour+eax*4]

             add  esi, sizeof(ENTITY)
             inc  i
        .endw
       assume  esi:
          ...
;===========================================>
          ...
         lea  esi, GameEntity
      assume  esi:ptr ENTITY
          mov  i, 0
       .while (i < NumEntities)         
            push  esi
            call [esi].ENTITY.BehaviourPtr
             add  esi, sizeof(ENTITY)
             inc  i
        .endw
       assume  esi:nothing
          ...

;===========================================>

Behaviour00 PROC  Who:PTR ENTITY

          mov  edx, Who
       assume  edx:ptr ENTITY
       invoke  MessageBoxA, null, CSTR("Acting out Behaviour 0"),  [edx].ENTITY.MyName, MB_OK   

          mov  edx, Who
         call [edx].ENTITY.RoutinePtr
       assume  edx:nothing
          ret

Behaviour00 ENDP

    Behaviour00Routine00 PROC
       invoke  MessageBoxA, null, CSTR("while doing 0s Routine 0"),  [edx].ENTITY.MyName, MB_OK
          ret
    Behaviour00Routine00 ENDP

    Behaviour00Routine01 PROC
       invoke  MessageBoxA, null, CSTR("while doing 0s Routine 1"),  [edx].ENTITY.MyName, MB_OK
          ret
    Behaviour00Routine01 ENDP

    Behaviour00Routine02 PROC
       invoke  MessageBoxA, null, CSTR("while doing 0s Routine 2"),  [edx].ENTITY.MyName, MB_OK
          ret
    Behaviour00Routine02 ENDP

  etc.

;==================================================================>

Caché GB's 1 and 0-nly language:MASM

mineiro

hello Caché GB;
Maybe you can gain some cycles if you do a mov and a jump; I think is more viable/quickly than a call/ret pairs.

Something like:
lea eax,[g_pBehaviour+??*4]  ;*4 because dwords addresses
jmp eax

align 4
Behaviour00:
...
jmp go_back_to_loop

go_back_to_loop is better if aligned to a multiple. (align 4,8,...)

The way to go is using tables, we often do this while on eletronics, only logic tables (or pre calculated tables) that are translated to logic circuits (diodes, transistors, ...).
I'd rather be this ambulant metamorphosis than to have that old opinion about everything

daydreamer

it would be nice to start with a simple solution,for example 2D topdown game for 100+ enemies and call simple PROC's for turning and moving that are coded oldschool style with trigo and simple 2D physics
and later combine with this:
https://docs.microsoft.com/en-us/windows/desktop/direct3d9/efficiently-drawing-multiple-instances-of-geometry
,changing your PROC for turning enemy alpha degrees,in pitch,roll,jaw angles and moving x,y,z is exchanged with wrapper for matrices code that do the same with instances

in a RTS game,you can have different colors for the two teams competing

this is because the alternative is probably fun to code SIMD that take 100+lowpoly 3dobjects and copy,3dtranslate/3drotate before sending to vertexbuffer ,but too much overhead so it lags and too much limitations for great graphics

my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

Caché GB

Hello mineiro.

"mov and a jump" although great for gaining some cycles, I don't see it possible to be used when called
from different parts of a program. " call/ret pairs" need to be implemented to return to point of call.

However thank you for your suggestion.


Hello daydreamer.

Nice read there. Thank you

In DX11.1 the Compute Shader is perfect for GPGPU programming. It is different from the other pipeline stages
because it works beside them. That is it does not explicitly have an input or output parameter for the previous
or next stage. Compute Shader is not limited to graphical applications. Algorithms for physics, animation, AI,
compression and cryptography or crypto mining can be implemented with Compute Shaders. It is awesome
for thread group processing.
Caché GB's 1 and 0-nly language:MASM