madflame991 Posted August 22, 2009 Share Posted August 22, 2009 (edited) ResearchWhat I'm trying to achieve: Refresh a tile-based map fast.I'm in the process of making a game, one that has it's environment composed out of tiles, like Zelda or any SNES game like it. When the player reaches the edges of the screen the next screen appears and he is cast on the opposite margin - logic, right? The Problem is that refreshing a 12x22 tiles takes some time and the process should be as fast as possible. That's why I've researched a bit into GDI+ (no, I don't want to use prospeed, sdl, or any other external thingie since it takes some time to learn them and I don't want to rewrite the whole thing again)First example: Straight forward but a bit slow since it reads all the tiles from the hard drive$win = GUICreate('',100,100) $pic = GUICtrlCreatePic('1.bmp',30,30,48,48) GUISetState() $t = TimerInit() For $i = 1 To 3000 GUICtrlSetImage($pic,Mod($i,4)&'.bmp') Next MsgBox(0,'',TimerDiff($t))Second example: Faster, is the anything else even faster?#include <GUIConstantsEx.au3> #include <GDIPlus.au3> #include <WinAPI.au3> Dim $hImage[4], $hbmp[4], $aBmp $hGui = GUICreate('',100,100) $Pic = GUICtrlCreatePic('',20,20,48,48) GUICtrlSetState(-1, $GUI_DISABLE) GUISetState(@SW_SHOW, $hGui) _GDIPlus_Startup() $hWnd = GUICtrlGetHandle($pic) For $i = 0 To 3 $hImage[$i] = _GDIPlus_BitmapCreateFromFile($i&'.bmp') $hbmp[$i] = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage[$i]) Next $dll = DllOpen("user32.dll") $t = TimerInit() For $i = 1 To 3000 $aBmp = DllCall($dll, "hwnd", "SendMessage", "hwnd", $hWnd, "int", 0x0172, "int", 0, "int", $hbmp[Mod($i,4)]) If $aBmp[0] <> 0 Then _WinAPI_DeleteObject($aBmp[0]) Next MsgBox(0,'',TimerDiff($t)) DllClose($dll) For $i = 0 To 3 _GDIPlus_ImageDispose($hImage[$i]) _WinAPI_DeleteObject($hbmp[$i]) Next _GDIPlus_Shutdown()Third attempt:#include <GuiConstantsEx.au3> #include <GDIPlus.au3> Dim $hGUI, $hWnd, $hGraphic, $hBrush, $hFormat, $hFamily, $hFont, $tLayout $hGUI = GUICreate("GDI+", 400, 300) $hWnd = WinGetHandle("GDI+") WinSetTrans($hgui, "", 255) ;two times faster without this GUISetState() _GDIPlus_Startup () Dim $hImage[2] $hImage[0] = _GDIPlus_BitmapCreateFromFile('0.bmp') $hImage[1] = _GDIPlus_BitmapCreateFromFile('1.bmp') $hGraphic = _GDIPlus_GraphicsCreateFromHWND ($hWnd) $t = TimerInit() GUISetState(@SW_DISABLE,$hgui) For $i = 1 To 3000 _GDIPlus_GraphicsDrawImageRect($hGraphic, $hImage[Mod($i,2)], 0, 0, 48, 48) Next GUISetState(@SW_ENABLE,$hgui) MsgBox(0,'',TimerDiff($t)) _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_Shutdown()Images are HERE - their names shoud be: 0.bmp; 1.bmp; 2.bmp; 3.bmpAnd now I'm asking you: What should be the fastest way to do this?Initial request:I read from the Example Scripts forum (trancexx's topics) how much magic you can do with some assembly in tight loops. I even understood part of the code, but not enough to make something actually work and that's why I'm asking for help.I need this bit of code to be transformed into asm and, if you can, please insert some explanations. I have the SwapEndian function; you don't have to add it.For $i = 1 To $tabdim[0] For $j = 1 To $tabdim[1] DllCall($dll, "hwnd", "SendMessage", "hwnd", $hWnd[$i][$j], "int", 0x0172, "int", 0, "int", $hbmp[$map[$i][$j]-1000]) Next Nextthx!EDIT: 10 < $tabdim[0],$tabdim[1] < 40 ... so it only occupies one byte (dunno if that's important) Edited September 4, 2009 by madflame991 Game Game Gadget! - read about indie games, gadgets, chiptunes and demoscenesAssembly-like language interpreter and custom machine emulatorSuper Mario Screen Mate - official website or autoit forum pageCogut - Puzzle Game + Editor like sokoban and others Link to comment Share on other sites More sharing options...
monoceres Posted August 22, 2009 Share Posted August 22, 2009 I don't feel like writing the machine code for you (as it's quite tedious) however I will give you some tips. 1. First off, translate those arrays into binary equals, if you want to do fast stuff directly in memory they need to represented as an array of the right type (int, char, short etc.). 2. Do you really need the code be human generated machine code? If not, you will still get very good results (almost as good, VC++9 generates some amazing optimized code) with a dll compiled from C/C++ code. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
madflame991 Posted September 4, 2009 Author Share Posted September 4, 2009 1. First off, translate those arrays into binary equals, if you want to do fast stuff directly in memory they need to represented as an array of the right type (int, char, short etc.). 2. Do you really need the code be human generated machine code? If not, you will still get very good results (almost as good, VC++9 generates some amazing optimized code) with a dll compiled from C/C++ code. 1. Even if I would translate the values inside the arrays... how would I get the value of a specific $map[$x][$y]? I did not see any examples of that 2. It would be easier/more useful for me to learn how to write 2 loops and a dll call than to use C (I never actually programmed under C before) It took me so much to reply because I tried to optimize it another way (I'm in the process of learning GDI+ as well) The loops were for refreshing a map (from a game I'm making); I think this should be useful for others who want to redraw a tile-based map so I'll post what I've managed to do, rename the topic and ask for help again Cheers! Game Game Gadget! - read about indie games, gadgets, chiptunes and demoscenesAssembly-like language interpreter and custom machine emulatorSuper Mario Screen Mate - official website or autoit forum pageCogut - Puzzle Game + Editor like sokoban and others Link to comment Share on other sites More sharing options...
Zedna Posted September 4, 2009 Share Posted September 4, 2009 (edited) I think learning how to use Prospeed DLL will be much more simple than learning how to translate your code to ASM. I know what I'm talking about because I used Prospeed in one of my projects successfuly several months ago and it was quite easy to lern how to use it. Edited September 4, 2009 by Zedna Resources UDF ResourcesEx UDF AutoIt Forum Search Link to comment Share on other sites More sharing options...
trancexx Posted September 7, 2009 Share Posted September 7, 2009 (edited) It's been brought to my attention that my expertize is needed here (not my wording).This is the translation of the first three scripts to what you want madflame991:expandcollapse popupOpt("MustDeclareVars", 1) #include <GDIPlus.au3> #include <Memory.au3> Global $hGui = GUICreate("", 100, 100) Global $hPic = GUICtrlCreatePic('', 20, 20, 48, 48) GUISetState(@SW_SHOW, $hGui) ; Create structure that will hold hbitmaps so that they could be accessed from the generated code Global $tImages = DllStructCreate("ptr[4]") ; Get handles Global $hImage _GDIPlus_Startup() For $i = 0 To 3 $hImage = _GDIPlus_BitmapCreateFromFile($i & ".png") ; ".bmp" - whatever ; Fill structure with handles DllStructSetData($tImages, 1, _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage), $i + 1) _GDIPlus_ImageDispose($hImage) Next _GDIPlus_Shutdown() ; get pointer to structure Global $pImages = DllStructGetPtr($tImages) ; Allocate enough memory (e.g. 63 bytes, but can be more) with $PAGE_EXECUTE_READWRITE Global $pRemoteCode = _MemVirtualAlloc(0, 63, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) ; Standard allocation in reserved address (63 bytes again) Global $tCodeBuffer = DllStructCreate("byte[63]", $pRemoteCode) ; Get address of SendMessageW function Global $aSendMessageW = DllCall("kernel32.dll", "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW") Global $pSendMessageW = $aSendMessageW[0] ; Get address of DeleteObject function Global $aDeleteObject = DllCall("kernel32.dll", "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("gdi32.dll"), "str", "DeleteObject") Global $pDeleteObject = $aDeleteObject[0] ; Going to loop 3000 times (like in your examples) Global $iRounds = 3000 ; Write 'assembly on the fly' DllStructSetData($tCodeBuffer, 1, _ "0x" & _ "33F6" & _ ; xor esi, esi ;<- esi = 0 "33DB" & _ ; xor ebx, ebx ;<- ebx = 0 "8B83" & SwapEndian($pImages) & _ ; mov eax, [ebx + $pImages] ;<- assigning eax to first bitmap handle "50" & _ ; push eax ;<- hBitmap "68" & SwapEndian(0) & _ ; push IMAGE_BITMAP "68" & SwapEndian(370) & _ ; push STM_SETIMAGE "68" & SwapEndian(GUICtrlGetHandle($hPic)) & _ ; push $hPicHandle "B8" & SwapEndian($pSendMessageW) & _ ; mov eax, SendMessageW ;<- prepare the call "FFD0" & _ ; call eax ;<- call SendMessageW function "50" & _ ; push eax ;<- pushing the return value of SendMessageW function to delete it "B8" & SwapEndian($pDeleteObject) & _ ; mov eax, DeleteObject ;<- prepare the call "FFD0" & _ ; call eax ;<- call DeleteObject function "83C3" & Hex(4, 2) & _ ; add ebx, 4d ;<- add 4 to ebx (4 is the size of ptr) "83FB" & Hex(12, 2) & _ ; cmp ebx, 12d ;<- compare ebx with 12 "76" & Hex(2, 2) & _ ; jna 2d ;<-jump on not above 2 bytes forward "33DB" & _ ; xor ebx, ebx ;<- ebx = 0 "46" & _ ; inc esi ;<- increment esi by value of 1 "81FE" & SwapEndian($iRounds) & _ ; cmp esi, $iRounds ;<- compare esi with $iRounds "77" & Hex(2, 2) & _ ; ja 2d ;<- if above jump forward two bytes "EB" & Hex(-58, 2) & _ ; jump -58d ;<- jump back 58 bytes "C3" _ ; ret ;<- all done. Return. ) ; See generated code. Just out of curiosity. ConsoleWrite("!Generated code:" & @CRLF & " " & DllStructGetData($tCodeBuffer, 1) & @CRLF) ; Execute it and measure time Global $hTimer = TimerInit() DllCall("user32.dll", "int", "CallWindowProcW", _ "ptr", $pRemoteCode, _ "int", 0, _ "int", 0, _ "int", 0, _ "int", 0) ; Write the result ConsoleWrite("!Execution time:" & @CRLF & " " & TimerDiff($hTimer) & @CRLF) ; Wait for the end While 1 Switch GUIGetMsg() Case - 3 Exit EndSwitch WEnd ; Used function Func SwapEndian($iValue) Return Hex(BinaryMid($iValue, 1, 4)) EndFunc ;==>SwapEndian Draw conclusions. Make modifications.edit: added a thing Edited September 8, 2009 by trancexx ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now