Once per image calculations in formula functions
Somewhere in the fractint documentation I came across explanation to the effect that calculations done in fractint fell into one of three categories, once per image, once per pixel, and every iteration. I assume an example of the first is assignment of p# values on the z-screen, stuff before the ':' encompasses the second, and stuff after the ':' is the third category. But what about expressions that will be constant over an entire image, but need to be specified in the body of the formula before the ':' ? Is there a way to explicitly tell the parser to do that so that it doesn't redo the unnecessary calculations every pixel? I think it is too much to ask to expect the parser to make this optimization implicitly, and my benchmarking indicates that such optimization is not done. Maybe I missed something in the documentation for using the formula editor on this count, but if so then so did others; I notice that Jim M's formula for slicing Julibrots in R4 space, "SliceJulibrot2.frm" could very profitably take advantage of once per image evaluation. In it he converts input angles from a user friendly degree form into the machine friendly radian form, and generates some expression coefficients from the sines and cosines of those angle which are constant over the duration of the image. Once-per-image evaluation of those terms would let that .frm run much faster, yet I noticed that the very accomplished and knowledgeable Jim does not do anything to explicitly force such an evaluation scheme for those terms, which leads me to suspect that there isn't a "nice" syntactic mechanism for doing so. And yet I do recall the categorization somewhere in the documentation that implied it was possible to do once per image calculations. So if anyone knows _how_ to do this "the right way", please reply. I raise the question of syntactic orthodoxy only because I did find a hack, which follows, that seems to accomplish it. It really doesn't feel right though. The hack is to put the once per image initialization code inside an IF..ENDIF block that (hopefully) executes only on the first pixel of the image. It worked when I applied the technique to "SliceJulibrot2.frm", speeding up the rendering of an "easy" image severalfold. However, the code just doesn't feel right. It relies on an assumption that the drawing technique will render pixel (0,0) first, which I tried to ensure by setting the PASSES=1 option, but I really don't think that can be assumed. So I really hope someone will tell us the orthodox way to accomplish once per image initialization. In the meantime, Jim's original .frm was: SliceJulibrot2 {; draws most slices of Julibrot pix=pixel, u=real(pix), v=imag(pix), a=pi*real(p1*0.0055555555555556), b=pi*imag(p1*0.0055555555555556), g=pi*real(p2*0.0055555555555556), d=pi*imag(p2*0.0055555555555556), ca=cos(a), cb=cos(b), sb=sin(b), cg=cos(g), sg=sin(g), cd=cos(d), sd=sin(d), p=u*cg*cd-v*(ca*sb*sg*cd+ca*cb*sd), q=u*cg*sd+v*(ca*cb*cd-ca*sb*sg*sd), r=u*sg+v*ca*sb*cg, s=v*sin(a), c=p+flip(q)+p3, z=r+flip(s)+p4: z=sqr(z)+c |z|<=9 } , while my hack to speed it up (which it does substantially in most cases tried) with o.p.i.i. is: SliceJulibrot2a {; SliceJulibrot2 with per screen initialization IF (scrnpix==(0,0)) a=pi*real(p1*0.0055555555555556), b=pi*imag(p1*0.0055555555555556), g=pi*real(p2*0.0055555555555556), d=pi*imag(p2*0.0055555555555556), ca=cos(a), cb=cos(b), sb=sin(b), cg=cos(g), sg=sin(g), cd=cos(d), sd=sin(d), term1=cg*cd, term2=ca*sb*sg*cd+ca*cb*sd, term3=cg*sd, term4=ca*cb*cd-ca*sb*sg*sd, term5=ca*sb*cg ENDIF pix=pixel, u=real(pix), v=imag(pix), p=u*term1-v*term2, q=u*term3+v*term4, r=u*sg+v*term5, s=v*sin(a), c=p+flip(q)+p3, z=r+flip(s)+p4: z=sqr(z)+c |z|<=9 } Again, the drawing method should be set to single pass, but if anyone has a better solution or can shed more light on this problem, PLEASE reply. Sincerely, Hiram Berry
On Friday 05 March 2004 3:46 pm, Hiram Berry wrote:
Somewhere in the fractint documentation I came across explanation to the effect that calculations done in fractint fell into one of three categories, once per image, once per pixel, and every iteration. I assume an example of the first is assignment of p# values on the z-screen, stuff before the ':' encompasses the second, and stuff after the ':' is the third category. But what about expressions that will be constant over an entire image, but need to be specified in the body of the formula before the ':' ? Is there a way to explicitly tell the parser to do that so that it doesn't redo the unnecessary calculations every pixel? I think it is too much to ask to expect the parser to make this optimization implicitly, and my benchmarking indicates that such optimization is not done.
The parser does not make this optimization. The formula is read in and put into one executable string of op codes and then called as the once per pixel routine for the formula type. Currently there is no way to split out part of the formula and only execute it once per image.
I raise the question of syntactic orthodoxy only because I did find a hack, which follows, that seems to accomplish it. It really doesn't feel right though. The hack is to put the once per image initialization code inside an IF..ENDIF block that (hopefully) executes only on the first pixel of the image. It worked when I applied the technique to "SliceJulibrot2.frm", speeding up the rendering of an "easy" image severalfold. However, the code just doesn't feel right. It relies on an assumption that the drawing technique will render pixel (0,0) first, which I tried to ensure by setting the PASSES=1 option, but I really don't think that can be assumed. So I really hope someone will tell us the orthodox way to accomplish once per image initialization.
Your optimization should work with most passes types except possibly diffusion. The major problem is that if you save a partially completed image, it will not resume correctly because then the first pixel calculated will no longer be (0,0). Certain key strokes may have the same effect if the formula gets read into memory again (like <z> followed by <esc>). Jonathan
Jonathan, Many thanks for your concise yet thorough explanation. Now I don't have to worry about this issue any more :) "Jonathan Osuch" <osuchj@avalon.net> wrote: [...]
The parser does not make this optimization. The formula is read in and put into one executable string of op codes and then called as the once per pixel routine for the formula type. Currently there is no way to split out part of the formula and only execute it once per image.
Perhaps a "const" or "perimage" keyword that .frm writers could apply to assignment expressions in the per_pixel section?
Your optimization should work with most passes types except possibly diffusion. The major problem is that if you save a partially completed image, it will not resume correctly because then the first pixel calculated will no longer be (0,0). Certain key strokes may have the same effect if the formula gets read into memory again (like <z> followed by <esc>).
Hmm. That makes sense. I tried using some other points as triggers in the formula to change the image parameters... and they sometimes happened at places other than where one would expect assuming either column-major or row-major rendering, even with PASSES=1. Thanks again for answering, -- Hiram Berry
Hiram Berry wrote:
SliceJulibrot2a {; SliceJulibrot2 with per screen initialization IF (scrnpix==(0,0)) a=pi*real(p1*0.0055555555555556), b=pi*imag(p1*0.0055555555555556), g=pi*real(p2*0.0055555555555556), d=pi*imag(p2*0.0055555555555556), ca=cos(a), cb=cos(b), sb=sin(b), cg=cos(g), sg=sin(g), cd=cos(d), sd=sin(d), term1=cg*cd, term2=ca*sb*sg*cd+ca*cb*sd, term3=cg*sd, term4=ca*cb*cd-ca*sb*sg*sd, term5=ca*sb*cg ENDIF pix=pixel, u=real(pix), v=imag(pix), p=u*term1-v*term2, q=u*term3+v*term4, r=u*sg+v*term5, s=v*sin(a), c=p+flip(q)+p3, z=r+flip(s)+p4: z=sqr(z)+c |z|<=9 }
Again, the drawing method should be set to single pass, but if anyone has a better solution or can shed more light on this problem, PLEASE reply.
I'm stealing this from Morgan L. Owens, who discussed this in May 2002 (you can look it up in the Fractint Mailing List Archive): Since variables don't have to be declared explicitely in Fractint formulas before using them, the parser creates variables automatically (and sets them to zero) as soon it encounters a variable name not encountered before. So this should (hopefully) work without dependence on screen coordinates: IF (notfirst==0) notfirst = 1 a=pi*real(p1*0.0055555555555556), b=pi*imag(p1*0.0055555555555556), g=pi*real(p2*0.0055555555555556), d=pi*imag(p2*0.0055555555555556), ca=cos(a), cb=cos(b), sb=sin(b), cg=cos(g), sg=sin(g), cd=cos(d), sd=sin(d), term1=cg*cd, term2=ca*sb*sg*cd+ca*cb*sd, term3=cg*sd, term4=ca*cb*cd-ca*sb*sg*sd, term5=ca*sb*cg ENDIF [etc] And watch out for those logical expressions! Fractint does only compare the number's *real parts* in those: IF (screenpix==(0,0)) is the same as IF (screenpix==0) is the same as IF (real(screenpix)==0) To check for pixel (0,0) you need: IF (screenpix==0 && imag(screenpix)==0) Regards, Gerald
Gerald, Thanks for pointing out Morgan Owen's better way of doing per-image initializations. I've found that it works flawlessly without regard to which drawing algorithm is used; that's much more reliable than my attempt to initialize on the first pixel rendered. The idea of using implicitly declared variables within a test condition was strange to me, but happily it works just fine. "Gerald K. Dobiasovsky" <gerald.dob@aon.at> wrote: [...]
And watch out for those logical expressions! Fractint does only compare the number's *real parts* in those:
IF (screenpix==(0,0)) is the same as IF (screenpix==0) is the same as IF (real(screenpix)==0)
To check for pixel (0,0) you need:
IF (screenpix==0 && imag(screenpix)==0)
Argh! You are absolutely right, I was using equality comparison incorrectly by fractint's definition. The "truncate the imaginary part" rule makes sense for {<,>} comparisons since Complex can't be usefully strictly ordered, and now that you bring this up I remember reading it in the fractint documentation. For some reason I must have assumed the equality comparison was exempted from the rule, which it obviously isn't. Also, that incorrect usage completely explains the mystifying drawing behavior I got when doing mid-screen variable mutation like "IF (scrnpix == (300,400) ...{change some p# values}...ENDIF" . Something that occurred to me about doing this technique properly, though: if the pixel rendering order is known, a possibly useful calculation technique is suggested, to save some results of a per-pixel iteration in variables (maybe even p#s or builtins like maxit) and then to use them in some way in the next or succeedingly rendered pixels. Offhand nothing particularly interesting comes to mind, but a pixel sequential changing state seems like it could have uses for generating interesting fractals. Sincerely, Hiram
participants (3)
-
Gerald K. Dobiasovsky -
Hiram Berry -
Jonathan Osuch