Author Topic: Fractal Zoomer  (Read 879 times)

0 Members and 1 Guest are viewing this topic.

Offline boogop

  • C= 64
  • **
  • Posts: 66
  • Karma: 42
    • View Profile
Fractal Zoomer
« on: November 09, 2016 »
Here's a version of the fractal zoomer from R.I.P. Boogop converted to freeBasic/ASM. I'd love to figure out a way around having to iterate over each pixel 300 times because that's where all your performance goes. It drives me nuts. This seems about as optimized as I know how to make it; if you focus optimizing anywhere else but the iteration loop it's a red herring, that loop is the killer. There are ideas around to split the screen in half and render each half in a separate thread, but that's still working around the central problem of the inability to determine if an equation is going to go to infinity without doing it 300 times.

On my work PC (LoMem Kludge-0-Matic 100) I get to the lose-resolution point in about 22 seconds. If you want to go deeper you'll need to tweak horizPosAdjust and vertPosAdjust to position yourself where you want to be

Code: [Select]
''''''''''''''''''''''''''''''''''''''''''''''''''
' MANDELBROT ASM ZOOMER TEST
' by boogop
' this may be a little faster than the FB version
' but I'm sure I've done lots of stuff wrong or fb
' is just smarter than I am lol
' Where this loses resolution is about the limit
' of a single data type. Converting to double will
' zoom farther in but also take longer
''''''''''''''''''''''''''''''''''''''''''''''''''

#include "tinyptc_ext.bi"

' 350 x 325 to size it the same as the c# effect
dim shared as integer screenWidth = 320
dim shared as integer screenHeight = 200
const SCR_SIZE as integer = 320*200

dim shared as integer ptr p

dim shared buffer( 0 to SCR_SIZE-1 ) as integer
dim shared as uinteger ptr pntr

dim shared as integer w, h, h2

' initial vals
dim shared as single a1 = -2.5
dim shared as single b1 = -1.5
dim shared as single ax1 = -2.5
dim shared as single ax2 = 1.5
dim shared as single bx1 = -1.5
dim shared as single bx2 = 1.6

dim shared as single bailnum = 2.0
dim shared as single bailGT1 = 1.3
dim shared as single bailLT1 = -1.3

dim shared as single z1, z2, z3, dx, dy, crx, yy, cr, ci', dydx
dim shared as integer k = 0   

dim shared as single ww
dim shared as single hh
dim shared as single a1w, b1h
dim shared as single aw
dim shared as single bh

dim shared as single zoom = 1

dim shared as uinteger colSingle(300)

declare sub init
declare sub DrawMandelbrot
declare sub CreateMandelbrot(a1 as single, a2 as single, b1 as single, b2 as single)
declare sub put_pixel(buffer() as integer, byval x as integer, byval y as integer, byval col as integer)



sub init   
   
    w = screenWidth
    h = screenHeight   
    h2 = h / 2     
   
    ' these coords put us in scepter valley
    a1 = 153.41801 
    b1 = h2 - 8.650006   
   
    ' if you change the size of the window you have to figure out where in the
    ' mandelbrot set you are - not easy! >:(
    dim as single horizPosAdjust = 13.24
    dim as single vertPosAdjust = 1.5
   
    a1 = a1 - horizPosAdjust
    b1 = b1 - vertPosAdjust
   
    ww = ax2 - ax1
    hh = bx2 - bx1
   
    a1w = a1 * ww
    b1h = b1 * hh
   
    aw = a1w / w
    bh = b1h / h
   
    dim i as integer = 0
    ' usually the array would be dimensioned to 256 but if we set it to the
    ' number of iterations we can skip a mod operation
    while i < 300     
        dim as single red = 1 + Cos(i * 3.14 / 128)
        dim as single grn = 1 + Cos((i - 85) * 3.14 / 128)
        dim as single blu = 1 + Cos((i + 85) * 3.14 / 128)       
        dim as integer r = int(red * 127) mod 256
        dim as integer g = int(grn * 127) mod 256
        dim as integer b = int(blu * 127) mod 256
       
        colSingle(i) = rgb(r,g,b)     
       
        i = i+1
    wend
   
       
end sub



sub DrawMandelbrot   
   
    zoom = zoom + (zoom * .05)
    dim as single d = zoom + zoom
   
    dim as single wax = ww / d
    dim as single wah = hh / d   
   
    dim as single newleft = aw + ax1 - wax
    dim as single newright = aw + ax1 + wax
    dim as single newtop = bh + bx1 - wah
    dim as single newbottom = bh + bx1 + wah
   
    CreateMandelbrot(newleft, newright, newtop, newbottom)
   
end sub

sub CreateMandelbrot(a1 as single, a2 as single, b1 as single, b2 as single)
   
    p = @buffer(0)
   
    dim as single dr = (a2 - a1) / w
    dim as single di = (b2 - b1) / h
    dim as integer j = 0
    dim as integer i = 0
    dim as single temp, q, a, b
    dim as single dx1, dy1
   
    dim as single ptr t
    'dim as string key
   
    for j = 1 to h
       
        ci = b1 + j * di 
       
        for i = 0 to w-1
           
            cr = a1 + i * dr
           
            dx1 = 0.0
            dy1 = 0.0         
                   
            ' PERIOD CHECKING
            ' if we can tell we're inside the cardiod or period-2 bulb we can skip the iterations.
            ' period checks make the routine blaze until we're past them but the iteration loop is
            ' the big problem
            crx = cr + 1.0
            yy = ci * ci
           
            if ((crx * crx) + yy < .0625) then
               
                ' checks for a point inside the period-2 bulb
                ' but once you're past that it does nothing
                *p = 0
                goto bail
               
            end if
           
            ' check for cardiod bulb
            temp = cr - .25
            t = @temp
           ' q = temp * temp + yy
            q = (*t) * (*t) + yy
            a = q * (q + *t)
            b = .25 * yy
            if (a < b) then
               
                *p = 0
                goto bail
               
            end if
           
            ''''''''''''''''''''''''''''''''''''     
            ''''''''''''''''''''''''''''''''''''
            ' SSE ITERATION LOOP
            ''''''''''''''''''''''''''''''''''''   
            ''''''''''''''''''''''''''''''''''''
            k = 0   
            ' 32 bit singles are only taking up 1/4 of these registers
            asm
                mov cx, 0
                movss xmm2, [ci]
                movss xmm3, [cr]
                 
                @starter:
             
                movss xmm0, [dx1]
                mulss xmm0, xmm0
                movss [z1], xmm0        'z1 =  dx1 * dx1
                               
                movss xmm1, [dy1] 
                mulss xmm1, xmm1   
                movss [z2], xmm1        'z2 =  dy1 * dy1               
                             
                addss xmm0, xmm1        ' z1 + z2     
               
                comiss xmm0,[bailnum]   ' if z1 + z2 > 2.0
                ja @asmdone                 
               
                inc cx                  ' handle the iterations counter
                cmp cx, 300
                je @asmdone
               
                movss xmm0, [dx1]
                movss xmm1, [dy1]
                mulss xmm0, xmm1        ' dy1 * dx1               
                addss xmm0, xmm0        ' (dy1 * dx1) + (dy1 * dx1)               
                addss xmm0, xmm2        ' + ci
                movss [dy1],xmm0        'dy1 = (dy1 * dx1) + (dy1 * dx1) + ci               
               
                movss xmm0, [z1]
                movss xmm1, [z2]
                subps xmm0, xmm1        ' z1 - z2               
                addss xmm0, xmm3        ' + cr
                movss [dx1],xmm0        'dx1 = z1 - z2 + cr   
               
             
                jmp @starter           
               
                @asmdone:
                mov [k],cx
            end asm
            '''''''''''''''''''''''''''''''''''''   
            '''''''''''''''''''''''''''''''''''''
            '''''''''''''''''''''''''''''''''''''
            if k = 300 then
                *p = 0
            else               
                *p = colSingle(k)
            end if
           
            ' print k, z3, dx1, dy1
            ' key = INKEY
            ' WHILE key = "": key = INKEY: WEND
            bail:
           
            p = p + 1
           
        next i
    next j
   
end sub




ptc_setdialog(1,"FullScreen?",0,0)
ptc_open( "tinyPTC Mandelbrot test", screenWidth, screenHeight )


'dim as string key
'print "Press to start"
'WHILE key = "": key = INKEY: WEND

init

dim as single t1,t2

t2 = timer
while inkey() <> chr(27)
   
    t1 = timer
   
    DrawMandelbrot
    ptc_update @buffer(0)
   
    print t1 - t2
   
Wend



Challenge Trophies Won:

Offline Stonemonkey

  • Pentium
  • *****
  • Posts: 1307
  • Karma: 96
    • View Profile
Re: Fractal Zoomer
« Reply #1 on: November 10, 2016 »
You could probably speed the sse asm up a bit, the first part where dx1 and dy1 are squared could be done in parallel and then added horizontally with haddps, keeping z1 and z2 in the registers might help too. There will be other things but ive not looked too closely yet.

That kind of thing's only going to take you so far in speeding things up, for any possible significant gains you have to look at the algorithm itself and it'll be easier to do that with the code in FB rather than asm.

« Last Edit: November 10, 2016 by Stonemonkey »