About Monkey 2 › Forums › Monkey 2 Programming Help › Julia set fractal generator optimisation (drawpoint)
This topic contains 8 replies, has 5 voices, and was last updated by
taumel
2 years, 8 months ago.
-
AuthorPosts
-
July 16, 2016 at 9:42 pm #2203
I made a little Julia set fractal generator.
My main issue is that drawpoint is actualy very slow for real time plot by plot generation. Using a separate canvas then render to image is a bit more slow and it looks like using pixmap is a bad idea for that kind of thing (have not tried).
Can I draw points to the screen faster? (I remember when writing the same on a 486 with C. Drawing points directly to the VRAM was way faster than calculating them!)
In mx2, writing the color index to an array is reasonably fast, but then drawing it to the screen takes more or less then same time than calculating Julia’s iterations![/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134[crayon-5cba16a9c225b369345343 inline="true" ]''' Julia Set example by abakobo'' a monkey 2 example-learning exercise' next steps/points of interest is to optimise: -the drawpoint speed' -the choice for globals, fields, locals'' Of course the use of the imagearray should finally be skipped for better perfomances'#Import "<std>"#Import "<mojo>"Using std..Using mojo..Global w_width:=800 'window sizeGlobal w_height:=600Global MaxIt:=32 'Julia's calculation precisionGlobal Threshold:=2 'Julia's escape ThresholdGlobal zoom:=2.7 'inverted zoom factorGlobal Palette:Color[] 'indexed colors arrayClass Julia Extends WindowField imageArray:= New int[w_width,w_height] 'array to store image as indexed colors (to check process times)Method New(title:String,width:Int,height:Int,flags:WindowFlags=WindowFlags.Resizable )'Silly call to Super.new (don't understand why I can't do it directly from main, maybe something wrong with Enum)Super.New( title,width,height,WindowFlags.Center )EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()'' local vars declaration'Local Cr:Float, Ci:Float, Zr:Float, Zi:Float ' the complex number parts (should probably use struct...)Local ta:Int,tb:Int,tj:Int 'the timer varsLocal x:Int,y:Int 'coord iterators'' get the mouse coord and transform it to the complex constant used in Julia set'Cr=1+(App.MouseLocation.x-w_width)/(w_width/2.0)Ci=1+(App.MouseLocation.y-w_height)/(w_height/2.0)'' Calculates Julia in indexed colors (iterations up to MaxIt-1 due to array starting at 0)' and copy it to the imagearray array'ta=Millisecs()For x=0 Until w_widthFor y=0 Until w_heightZr=(x-(w_width/2.0))*(zoom/w_width)Zi=(y-(w_height/2.0))*(zoom/w_height)imageArray[x,y]=NbIter(Zr,Zi,MaxIt,Threshold,Cr,Ci)NextNexttb=Millisecs()tj=tb-ta 'Records the time it took to calculate Julia and copy it to the array'' Draw the colors to the canvas using the precalculated palette (indexed-->rgb)'ta=Millisecs()For x=0 Until w_widthFor y=0 Until w_heightcanvas.Color=Palette[imageArray[x,y]]canvas.DrawPoint( x,y )NextNexttb=Millisecs()tb=tb-ta 'records the time it took to draw indexed array to canvas'' Prints timers and FPS'canvas.Color=Palette[MaxIt-1]canvas.DrawText("Move mouse arroud to see various Julia sets",0,0)canvas.DrawText("Julia to array time: "+tj,0,15)canvas.DrawText("array to canvas time: "+tb,0,30)canvas.DrawText("FPS:"+App.FPS,0,45)EndEnd'' squared complex+const series (Julia)'Function NbIter:Int(Zr:Float,Zi:Float,max_iter:Int,exit_radius:Float,Cr:Float,Ci:Float)Local i:=0Local Zr2:Float, Zi2:Float, er2:Floater2=exit_radius*exit_radiusZr2=Zr*ZrZi2=Zi*ZiWhile i<max_iter-1 And Zr2+Zi2<er2Zi=2*Zr*Zi + CiZr=Zr2-Zi2 +CrZr2=Zr*ZrZi2=Zi*Zii+=1EndReturn iEnd'' Create the indexed color Palette'Function CreateGlobalPalette:Void()Palette=New Color[MaxIt] 'Palette is Global, declared at topFor Local i:=0 Until MaxIt-1Palette[i]=New Color(Abs(Sin(2.0*Pi*i/MaxIt)),Abs(Sin(1.2*Pi*i/MaxIt)),0.6-(i/(MaxIt*1.0))*0.3)NextPalette[MaxIt-1]=New Color(0,0,0)EndFunction Main()CreateGlobalPalette()New AppInstanceNew Julia ("Julia",w_width,w_height)App.Run()EndEdit: .monkey2 files are blocked for upload for security reasons…
July 17, 2016 at 12:56 am #2214Had a quick go at speeding this via render-to-pixmap and it’s not bad – was going 22FPS-ish, now does 60 if the mouse is at the edges!
A bit of hackery involved – it draws directly to a pixel ‘ptr’ instead of SetPixel( x,y ) so it’s not recalculating pixel address all the time.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141''' Julia Set example by abakobo'' a monkey 2 example-learning exercise' next steps/points of interest is to optimise: -the drawpoint speed' -the choice for globals, fields, locals'' Of course the use of the imagearray should finally be skipped for better perfomances'#Import "<std>"#Import "<mojo>"Using std..Using mojo..Global w_width:=800 'window sizeGlobal w_height:=600Global MaxIt:=32 'Julia's calculation precisionGlobal Threshold:=2 'Julia's escape ThresholdGlobal zoom:=2.7 'inverted zoom factorGlobal Palette:UInt[]Class Julia Extends WindowField pixmap:=New Pixmap( w_width,w_height )Field image:=New Image( w_width,w_height,TextureFlags.Dynamic )Method New( title:String,width:Int,height:Int,flags:WindowFlags=WindowFlags.Resizable )Super.New( title,width,height,WindowFlags.Center )EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()'' local vars declaration'Local Cr:Float, Ci:Float, Zr:Float, Zi:Float ' the complex number parts (should probably use struct...)Local ta:Int,tb:Int,tj:Int 'the timer varsLocal x:Int,y:Int 'coord iterators'' get the mouse coord and transform it to the complex constant used in Julia set'Cr=1+(App.MouseLocation.x-w_width)/(w_width/2.0)Ci=1+(App.MouseLocation.y-w_height)/(w_height/2.0)'' Calculates Julia in indexed colors (iterations up to MaxIt-1 due to array starting at 0)' and copy it to the imagearray array'ta=Millisecs()For y=0 Until w_heightLocal p:=Cast<UInt Ptr>( pixmap.PixelPtr( 0,y ) )Zi=(y-(w_height/2.0))*(zoom/w_height)For x=0 Until w_widthZr=(x-(w_width/2.0))*(zoom/w_width)Local t:=NbIter( Zr,Zi,MaxIt,Threshold,Cr,Ci )p[x]=Palette[t]NextNexttb=Millisecs()tj=tb-ta 'Records the time it took to calculate Julia and copy it to the arrayimage.Texture.PastePixmap( pixmap,0,0 )canvas.DrawImage( image,0,0 )' Prints timers and FPS'canvas.Color=Color.Blackcanvas.DrawText("Move mouse arroud to see various Julia sets",0,0)canvas.DrawText("Julia to array time: "+tj,0,15)canvas.DrawText("array to canvas time: "+tb,0,30)canvas.DrawText("FPS:"+App.FPS,0,45)EndEnd'' squared complex+const series (Julia)'Function NbIter:Int(Zr:Float,Zi:Float,max_iter:Int,exit_radius:Float,Cr:Float,Ci:Float)Local i:=0Local Zr2:Float, Zi2:Float, er2:Floater2=exit_radius*exit_radiusZr2=Zr*ZrZi2=Zi*ZiWhile i<max_iter-1 And Zr2+Zi2<er2Zi=2*Zr*Zi + CiZr=Zr2-Zi2 +CrZr2=Zr*ZrZi2=Zi*Zii+=1WendReturn iEndFunction ColorToBGRA:UInt( color:Color )Return UInt(color.a*255) Shl 24 | UInt(color.b*255) Shl 16 | UInt(color.g*255) Shl 8 | UInt(color.r*255)End'' Create the indexed color Palette'Function CreateGlobalPalette:Void()Palette=New UInt[MaxIt] 'Palette is Global, declared at topFor Local i:=0 Until MaxIt-1Palette[i]=ColorToBGRA( New Color(Abs(Sin(2.0*Pi*i/MaxIt)),Abs(Sin(1.2*Pi*i/MaxIt)),0.6-(i/(MaxIt*1.0))*0.3) )NextPalette[MaxIt-1]=ColorToBGRA( Color.Black )EndFunction Main()CreateGlobalPalette()New AppInstanceNew Julia( "Julia",w_width,w_height )App.Run()EndJuly 17, 2016 at 3:56 pm #2228wow! now running at 30fps with FullHD on the edges!
Thanks for that. It shows how pointers are powerfull and is a great lesson.
I will need some time to understand all of that well. Especially the Cast operator that let you use p as an array. Is that some kind of pointer arithmetics?I will have to try some simple examples to get that part.
July 17, 2016 at 8:59 pm #2236I was interested to see if there was any difference between float and double. I replaced all “Float” with “F” and tried both Alias F:Float and Alias F:Double but could not discern any difference. I suppose under the hood this only affects storage times not calculation times.
July 17, 2016 at 9:27 pm #2237> Especially the Cast operator that let you use p as an array. Is that some kind of pointer arithmetics?
Since pixels can be stored in a number of different formats, PixelPtr just returns a general purpose ‘UByte Ptr’ and leaves it up to you to cast it to the correct type (if you need to).
Our pixels are UInts, so the cast converts this to a ‘UInt Ptr’. It’s still just a pointer though, and it will have have the same value. It’s just that when you do math with it (eg: index it with the [] operator, or add/subtract to/from it) the fact the pointer is a ‘UInt Ptr’ will be taken into account. For example, p+=1 where p is a ‘UInt Ptr’ will actually add 4 to p, because UInts are 4 bytes long.
July 22, 2016 at 5:13 pm #2345I continued a bit on this exercise and had the flowing conclusions:
-as Simon said Float or Double doesn’t change anything (due to 64bit CPU?)
-Struct is making things slower (made a complex struct based on Vec2)
One thing about that is that setting a Field directly is (much) faster than setting via Properties.
So why would we use such properties if it is only to get and set the field the same way it would be done directly?I made a github repo for my further exercices and posted the two julia versions on it…
https://github.com/abakobo/learn_monkey2July 23, 2016 at 5:18 am #2358So why would we use such properties if it is only to get and set the field the same way it would be done directly?
If you just want to set a field, without the need to get
the class/object noticed about the change, you can just
use fields directly.
But, such fields are always read+write, so for a read-only field
you need to use a property.You would do a field like ‘Window.Title’ (String) as Property,
because just setting the string is often not enough.
The property calls an OS/API function like SetWindowTitle()
in the background if you change win.Title – so that’s where
you want to use Properties.
Or setting win.X really moves a window on desktop, because of
property-internal OS-calls. Just setting an Int value would not
magically move the window.For structs that just hold plain/simple public data (like Matrix),
you could just use plain fields, most of the time.July 26, 2016 at 4:39 pm #2467Here’s the most optimized I could find, if anyone see something to be done for optimization welcome!
I tried a bit to use RGB24 for the pixmap but don’t understand how to get a pointer of that length. But I suppose it won’t change anything with speed or may be worse.
It can zoom and move and save the picture now. Can we set the png quality for pixmap saving? It does not have the exact same look when saved.[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251[crayon-5cba16a9d768b701939845 inline="true" ]''' Julia Set example - resizable window'' a monkey 2 example-learning exercise' points of interest is to optimise: -the drawpoint speed' (done?, optimised by marksibly, using pixmap and pointer,' pointer is faster local compared to field,' should try using smaller pixel formats for pixmap (not done, need to understand how to point to 24bits))' -the choice for globals, fields, locals, struct:' using struct for iterations is slower, see julia_with_struct(non_optim).monkey2 (github)' Double or Float doesn't change anything (on 64bit computers at least?)' Local is faster for basic variables, fields and globals seems to have same speed'' -???''#Import "<std>"#Import "<mojo>"Using std..Using mojo..Global w_width:=1000 'initial window sizeGlobal w_height:=700Global MaxIt:=16 'Julia's calculation precisionGlobal Threshold:Double=2.5 'Julia's escape ThresholdGlobal pxSize:Double=0.006 'Initial size of a pixel on the complex plane (inverted zoom factor)Global viewCenter_r:Double=0.0, viewCenter_i:Double=0.0 'Initial center point for viewing (on complex plane)Global move_spd:Double=0.06Global Palette:UInt[]Class Julia Extends WindowField image:=New Image( w_width,w_height,TextureFlags.Dynamic )Field Cr:Double=0.0, Ci:Double=0.0Field modif_const:=True 'Blocks the anim or not (true=animated) (bad name)Field count:=-1 'count for the auto animField modif_const_mouse:=False 'flag/selector for auto or mouse anim (bad name)Field pixmap:=New Pixmap( w_width,w_height,PixelFormat.RGBA32 )Field saveIt:=FalseMethod New( title:String,width:Int,height:Int,flags:WindowFlags=WindowFlags.Resizable )Super.New( title,width,height,flags )End''These are equivalent to Keyboard.KeyPressed(Key.XXX) But with better memory of things'Sometimes in OnRender with low FPS it skips some Keyboard.Keyxxxxx here not!'Method OnKeyEvent( event:KeyEvent ) OverrideSelect event.TypeCase EventType.KeyDownSelect event.KeyCase Key.Spacemodif_const=Not modif_constCase Key.PsaveIt=TrueCase Key.Cmodif_const_mouse=Not modif_const_mouseEnd SelectEnd SelectEnd MethodMethod OnRender( canvas:Canvas ) Override'' local vars declaration'Local Zr:Double, Zi:Double ' the complex number parts (should probably use struct...)Local ta:Int,tb:Int,tj:Int 'the timer varsLocal x:Int,y:Int 'coord iteratorsLocal max_wh:Int 'to get the maximal value of the window height or widthLocal p:UInt Ptr' mandatory for continuous renderApp.RequestRender()'' adapt to window size (resizable window)'If w_width<>Self.Rect.Size.X or w_height<>Self.Rect.Size.YLocal maxs:=Max(w_width,w_height)w_width=Self.Rect.Size.Xw_height=Self.Rect.Size.YLocal maxs2:=Max(w_width,w_height)pxSize=pxSize*(maxs/(maxs2*1.0)) 'adapt zoom to new window size (because zoom factor is pixel's lenght)image.Discard() 'Images are not Garbage Collected so it's always better to Discard before changing it... (here it's a Field so probably not usefull)image=New Image( w_width,w_height,TextureFlags.Dynamic )pixmap=New Pixmap( w_width,w_height )End If''' Keyboard controls''If Keyboard.KeyDown(Key.Up) Then viewCenter_i-=move_spd*(pxSize/0.006)If Keyboard.KeyDown(Key.Down) Then viewCenter_i+=move_spd*(pxSize/0.006)If Keyboard.KeyDown(Key.Left) Then viewCenter_r-=move_spd*(pxSize/0.006)If Keyboard.KeyDown(Key.Right) Then viewCenter_r+=move_spd*(pxSize/0.006)If Keyboard.KeyDown(Key.W|Key.Raw) Then pxSize=pxSize/1.05If Keyboard.KeyDown(Key.S|Key.Raw) Then pxSize=pxSize*1.05If Keyboard.KeyDown(Key.A|Key.Raw) Then Threshold/=1.01If Keyboard.KeyDown(Key.D|Key.Raw) Then Threshold*=1.01If Keyboard.KeyDown(Key.I|Key.Raw)MaxIt+=1CreateGlobalPalette()EndifIf Keyboard.KeyDown(Key.K|Key.Raw)MaxIt=Max(2,MaxIt-1)CreateGlobalPalette()Endif'' auto or mouse anim'If modif_const_mouse=True'' get the mouse coord and transform it to the complex constant used in Julia set'If modif_const=TrueCr=(App.MouseLocation.x-(w_width/2.0))*pxSize+viewCenter_rCi=(App.MouseLocation.y-(w_height/2.0))*pxSize+viewCenter_iEndifElse'' create an anim while modifying the julia's const'If modif_const=Truecount+=1Cr=1.65*Cos(count/29.0)Ci=1.4*Sin(count/30.0)EndifEndif'' Calculates Julia in indexed colors (iterations up to MaxIt-1 due to array starting at 0)' and copy it to the pixmap with pointer for faster copy (faster than stepixel because the adress only calculated #height times)'For y=0 Until w_heightp=Cast<UInt Ptr>( pixmap.PixelPtr( 0,y ) )Zi=(y-(w_height/2.0))*pxSize+viewCenter_iFor x=0 Until w_widthZr=(x-(w_width/2.0))*pxSize+viewCenter_rLocal t:=NbIter( Zr,Zi,MaxIt,Threshold,Cr,Ci )p[x]=Palette[t]NextNext'' copy the pixmap to an image and put the image to the main canvas'image.Texture.PastePixmap( pixmap,0,0 )canvas.DrawImage( image,0,0 )'' Save the pixmap (if asked) before drawing text on it'If saveIt=True 'this is on the eventlistener for better response while on low FPSsaveIt=FalseLocal filename:="Nothing"filename = RequestFile("Save the file","png",True,"Julia")print ("youy"+filename+"youy")If filename<>""pixmap.Save(filename)ElseNotify("Blempro","No files where selected")EndifEndif' Prints'canvas.Color=Color.Whitecanvas.DrawText("Space: Pause/Play --- C to switch to auto-anim or mouse-anim --- P:Save as PNG",0,0)canvas.DrawText("W/S(Raw):Zoom --- A/D(Raw):Thershold --- I/K:Iterations --- Cursor:Move",0,20)canvas.DrawText("Cr:"+Cr,0,40)canvas.DrawText(" Ci:"+Ci,175,40)canvas.DrawText(" Iter:"+MaxIt,350,40)canvas.DrawText("FPS:"+App.FPS,0,55)EndEnd'' squared complex+const series (Julia)'Function NbIter:Int(Zr:Double,Zi:Double,max_iter:Int,exit_radius:Double,Cr:Double,Ci:Double)Local i:=0Local Zr2:Double, Zi2:Double, er2:Doubleer2=exit_radius*exit_radiusZr2=Zr*ZrZi2=Zi*ZiWhile i<max_iter-1 And Zr2+Zi2<er2Zi=2*Zr*Zi + CiZr=Zr2-Zi2 +CrZr2=Zr*ZrZi2=Zi*Zii+=1WendReturn iEndFunction ColorToBGRA:UInt( color:Color )Return UInt(color.a*255) Shl 24 | UInt(color.b*255) Shl 16 | UInt(color.g*255) Shl 8 | UInt(color.r*255)End'' Create the indexed color Palette'Function CreateGlobalPalette:Void()Palette=New UInt[MaxIt] 'Palette is Global, Max It is global. declared at topFor Local i:=0 Until MaxIt-1Palette[i]=ColorToBGRA( New Color(0.9*Abs(Sin(1.0*Pi*i/MaxIt)),0.9*Abs(Sin(1.0*Pi*i/MaxIt)),0.35-(i/(MaxIt*1.0))*0.34) )NextPalette[MaxIt-1]=ColorToBGRA( Color.Black )EndFunction Main()CreateGlobalPalette()New AppInstanceNew Julia( "Julia",w_width,w_height )App.Run()EndJuly 27, 2016 at 2:23 pm #2483When i was into Julia, i followed this route: a) find a fast formula, b) optimise the code, c) take advantage of symmetry, d) utalise multithreading, e) go with a pixel shader.
And i always liked them with colour cycling. :O)
-
AuthorPosts
You must be logged in to reply to this topic.