The game offers to all entities the following interface which returns many interfaces to handle any part of the game behaviour, like this nobody is aware of the implementations of the classes.
IGameServices = interface
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSession: ISession;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetEvents: IEvents;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetPlayers: IPlayers;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCoins: ICoins;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetMap: IMap;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetDrawer: IGraphicDrawer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetGraphicInfo: IGraphicInfo;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLog: ILog;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetProfile: IProfile;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFont: IFont;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSmallFont: IFont;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetWindow: IWindow;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLayer(aIndex: integer): ILayer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCameraHelper: ICameraHelper;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetHud: IHud;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetEffects: IEffects;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSound: ISoundDispatcher;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetMusic: IMusic;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFileSystem: IFileSystem;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetOptimizer: IOptimizer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFactory: ISpriteFactory;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetBack: IBackground;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSettings: ISettings;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetHighscore: IHighscore;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLevels: ILevels;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetVars: IMemVar;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetAchievements: IAchievements;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetRunes: IRunes;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetShop: IShopHandler;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetTimer: ICrono;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetInput: IInput;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCurrentScreen: IScreen;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetTrigger: ITriggerDispatcher;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetChat: IChat;
end;
So a typical enemy would do GameServices.GetSound.Play() in order to play a sound.
GameSprite.pas.4293: begin
00A6BA50 53 push ebx
00A6BA51 56 push esi
00A6BA52 8BF2 mov esi,edx
00A6BA54 8BD8 mov ebx,eax
GameSprite.pas.4294: Exit(FGameServices);
00A6BA56 8BC6 mov eax,esi
00A6BA58 8B93A4030000 mov edx,[ebx+$000003a4]
00A6BA5E E8D5739AFF call @IntfCopy
GameSprite.pas.4295: end;
00A6BA63 5E pop esi
00A6BA64 5B pop ebx
00A6BA65 C3 ret
The classic IntfCopy() is there and of course their counterparts InfClear(). When you have a system which continuously passes interfaces then is quite an overhead.
In the experimental version we get:
GameSprite.pas.2834: result := GameServices.GetSound.Play(aFileName, aLoop, True);
00A63218 6A01 push $01
00A6321A 8BC6 mov eax,esi
00A6321C E863390000 call TGameSprite.GetGameServices
GameSprite.pas.4293: begin
00A66B84 51 push ecx
GameSprite.pas.4294: Exit(FGameServices);
00A66B85 8B80A4030000 mov eax,[eax+$000003a4]
00A66B8B 890424 mov [esp],eax
GameSprite.pas.4295: end;
00A66B8E 8B0424 mov eax,[esp]
00A66B91 5A pop edx
00A66B92 C3 ret
Hooray!, all that overhead is gone. The problem is that the compliler very often makes a mess and the code explodes as an AV (I haven't found a pattern), so I change the way the calls are done and then it no longer crashes.
For example this crashes (in some places of the code some others not):
GameServices.GetPlayers.Kill(pOne);
changing the code a bit as:
players := GameServices.GetPlayers;
players.Kill(pOne);
Not a big deal but quite cumbersome to do all over the code, so I change it when it crashes.
Still not convinced?
Performance test with classic interfaces:
About 40 FPS for 50000 triangles.
Now with this UNSAFE stuff:
Almost 48 FPS for 50000 triangles.
I believe if worth the effort. It is important to note that interfaces should all be declared as const and never as inline.
So once I'm happy with the results I will publish this version.
Cheers.
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSession: ISession;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetEvents: IEvents;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetPlayers: IPlayers;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCoins: ICoins;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetMap: IMap;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetDrawer: IGraphicDrawer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetGraphicInfo: IGraphicInfo;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLog: ILog;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetProfile: IProfile;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFont: IFont;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSmallFont: IFont;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetWindow: IWindow;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLayer(aIndex: integer): ILayer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCameraHelper: ICameraHelper;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetHud: IHud;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetEffects: IEffects;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSound: ISoundDispatcher;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetMusic: IMusic;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFileSystem: IFileSystem;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetOptimizer: IOptimizer;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetFactory: ISpriteFactory;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetBack: IBackground;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetSettings: ISettings;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetHighscore: IHighscore;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetLevels: ILevels;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetVars: IMemVar;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetAchievements: IAchievements;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetRunes: IRunes;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetShop: IShopHandler;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetTimer: ICrono;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetInput: IInput;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetCurrentScreen: IScreen;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetTrigger: ITriggerDispatcher;
{$IFDEF _UNSAFE_}[result: unsafe]{$ENDIF}function GetChat: IChat;
end;
So a typical enemy would do GameServices.GetSound.Play() in order to play a sound.
GameSprite.pas.4293: begin
00A6BA50 53 push ebx
00A6BA51 56 push esi
00A6BA52 8BF2 mov esi,edx
00A6BA54 8BD8 mov ebx,eax
GameSprite.pas.4294: Exit(FGameServices);
00A6BA56 8BC6 mov eax,esi
00A6BA58 8B93A4030000 mov edx,[ebx+$000003a4]
00A6BA5E E8D5739AFF call @IntfCopy
GameSprite.pas.4295: end;
00A6BA63 5E pop esi
00A6BA64 5B pop ebx
00A6BA65 C3 ret
The classic IntfCopy() is there and of course their counterparts InfClear(). When you have a system which continuously passes interfaces then is quite an overhead.
In the experimental version we get:
GameSprite.pas.2834: result := GameServices.GetSound.Play(aFileName, aLoop, True);
00A63218 6A01 push $01
00A6321A 8BC6 mov eax,esi
00A6321C E863390000 call TGameSprite.GetGameServices
GameSprite.pas.4293: begin
00A66B84 51 push ecx
GameSprite.pas.4294: Exit(FGameServices);
00A66B85 8B80A4030000 mov eax,[eax+$000003a4]
00A66B8B 890424 mov [esp],eax
GameSprite.pas.4295: end;
00A66B8E 8B0424 mov eax,[esp]
00A66B91 5A pop edx
00A66B92 C3 ret
Hooray!, all that overhead is gone. The problem is that the compliler very often makes a mess and the code explodes as an AV (I haven't found a pattern), so I change the way the calls are done and then it no longer crashes.
For example this crashes (in some places of the code some others not):
GameServices.GetPlayers.Kill(pOne);
changing the code a bit as:
players := GameServices.GetPlayers;
players.Kill(pOne);
Not a big deal but quite cumbersome to do all over the code, so I change it when it crashes.
Still not convinced?
Performance test with classic interfaces:
About 40 FPS for 50000 triangles.
Now with this UNSAFE stuff:
Almost 48 FPS for 50000 triangles.
I believe if worth the effort. It is important to note that interfaces should all be declared as const and never as inline.
So once I'm happy with the results I will publish this version.
Cheers.