public override bool RenderMultiThreaded(Float128 xmin, Float128 xmax, Float128 ymin, Float128 ymax, Float128 step, int maxIterations) { Float128 HALF = new Float128(0.5); //Parallel.For(0, (int)(((ymax - ymin) / step) + .5M), (yp) => Parallel.For(0, ymax.SubFast(ymin).Div(step).AddFast(HALF).IntValue(), (yp) => { if (Abort) { return; } Float128 y = ymin.AddFast(step.Mul(yp)); int xp = 0; for (Float128 x = xmin; x.SubFast(xmax).Hi < 0; x = x.AddFast(step), xp++) { Float128 accumx = x; Float128 accumy = y; int iters = 0; Float128 sqabs = new Float128(0); do { Float128 naccumx = accumx.Sqr().SubFast(accumy.Sqr()); Float128 naccumy = accumx.Mul(accumy).MulPwrOf2(2); accumx = naccumx.AddFast(x); accumy = naccumy.AddFast(y); iters++; sqabs = accumx.Sqr().AddFast(accumy.Sqr()); } while (sqabs.SubFast(limit).Hi < 0 && iters < maxIterations); DrawPixel(xp, yp, iters); } }); return(!Abort); }
public override bool RenderSingleThreaded(Float128 xmin, Float128 xmax, Float128 ymin, Float128 ymax, Float128 step, int maxIterations) { int yp = 0; for (Float128 y = ymin; y.SubFast(ymax).Hi < 0 && !Abort; y = y.AddFast(step), yp++) { if (Abort) { return(false); } int xp = 0; for (Float128 x = xmin; x.SubFast(xmax).Hi < 0; x = x.AddFast(step), xp++) { Float128 accumx = x; Float128 accumy = y; int iters = 0; Float128 sqabs = new Float128(0); do { Float128 naccumx = accumx.Sqr().SubFast(accumy.Sqr()); Float128 naccumy = accumx.Mul(accumx).MulPwrOf2(2); accumx = naccumx.AddFast(x); accumy = naccumy.AddFast(y); iters++; sqabs = accumx.Sqr().AddFast(accumy.Sqr()); } while (sqabs.SubFast(limit).Hi < 0 && iters < maxIterations); DrawPixel(xp, yp, iters); } } return(true); }
// Helper to construct a vector from a lambda that takes an index. It's not efficient, but I // think it's prettier and more succint than the corresponding for loop. // Don't use it on a hot code path (i.e. inside a loop) public static Float128FastVector Create(Func <int, Float128> creator) { double[] dataHi = new double[Vector <double> .Count]; double[] dataLo = new double[Vector <double> .Count]; for (int i = 0; i < Vector <double> .Count; i++) { Float128 float128 = creator(i); dataHi[i] = float128.Hi; dataLo[i] = float128.Lo; } return(new Float128FastVector(dataHi, dataLo)); }
// Render the fractal on a single thread using raw Vector<double> data types // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs public override bool RenderSingleThreaded(Float128 xmin, Float128 xmax, Float128 ymin, Float128 ymax, Float128 step, int maxIterations) { Vector <double> vmax_iters = new Vector <double>((double)maxIterations); Float128FastVector vlimit = new Float128FastVector(limit); Float128FastVector vstep = new Float128FastVector(step); Float128FastVector vinc = new Float128FastVector(new Float128((double)Vector <double> .Count) * step); Float128FastVector vxmax = new Float128FastVector(xmax); Float128FastVector vxmin = Create(i => xmin + step * new Float128((double)i)); Float128 y = ymin; int yp = 0; for (Float128FastVector vy = new Float128FastVector(ymin); (y - ymax).Hi < 0 && !Abort; vy += vstep, y += step, yp++) { if (Abort) { return(false); } int xp = 0; for (Float128FastVector vx = vxmin; Float128FastVector.LessThanOrEqualAll(vx, vxmax); vx += vinc, xp += Vector <double> .Count) { Float128FastVector accumx = vx; Float128FastVector accumy = vy; Vector <double> viters = Vector <double> .Zero; Vector <double> increment = Vector <double> .One; do { Float128FastVector naccumx = accumx.Sqr() - accumy.Sqr(); Float128FastVector naccumy = (accumx * accumy).MulPwrOf2(2); accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Float128FastVector sqabs = accumx.Sqr() + accumy.Sqr(); Vector <double> vCond = Vector.LessThanOrEqual <double>(sqabs.Hi, vlimit.Hi) & Vector.LessThanOrEqual <double>(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <double> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } } return(true); }
// Render the fractal on multiple threads using raw Vector<double> data types // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs public override bool RenderMultiThreaded(Float128 xmin, Float128 xmax, Float128 ymin, Float128 ymax, Float128 step, int maxIterations) { Vector <double> vmax_iters = new Vector <double>((double)maxIterations); Float128FastVector vlimit = new Float128FastVector(limit); Float128FastVector vstep = new Float128FastVector(step); Float128FastVector vinc = new Float128FastVector(new Float128((double)Vector <double> .Count) * step); Float128FastVector vxmax = new Float128FastVector(xmax); Float128FastVector vxmin = Create(i => xmin + step * new Float128((double)i)); Parallel.For(0, (((ymax - ymin) / step) + HALF).IntValue(), (yp) => { if (Abort) { return; } Float128FastVector vy = new Float128FastVector(ymin + step * new Float128((double)yp)); int xp = 0; for (Float128FastVector vx = vxmin; Float128FastVector.LessThanOrEqualAll(vx, vxmax); vx += vinc, xp += Vector <double> .Count) { Float128FastVector accumx = vx; Float128FastVector accumy = vy; Vector <double> viters = Vector <double> .Zero; Vector <double> increment = Vector <double> .One; do { Float128FastVector naccumx = accumx.Sqr() - accumy.Sqr(); Float128FastVector naccumy = (accumx * accumy).MulPwrOf2(2); accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Float128FastVector sqabs = accumx.Sqr() + accumy.Sqr(); Vector <double> vCond = Vector.LessThanOrEqual <double>(sqabs.Hi, vlimit.Hi) & Vector.LessThanOrEqual <double>(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <double> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } }); return(!Abort); }
public void Zoom(int zDelta, Point location) { (Float128 X, Float128 Y)oldDistance = (new Float128(location.X - _bufferWidth / 2.0) * _zoomLevel, new Float128(location.Y - _bufferHeight / 2.0) * _zoomLevel); Float128 factor = new Float128(1.2); Float128 offset = new Float128(0); if (zDelta > 0) { _zoomLevel /= factor; offset = (new Float128(1.0) - new Float128(1.0) / factor); } else { _zoomLevel *= factor; offset = new Float128(1.0) - factor; } // Correct for the position of the mouse _viewR += oldDistance.X * offset; _viewI += oldDistance.Y * offset; OnParametersChanged(); }
public Float128FastVector(Float128 initial) { Hi = new Vector <double>(initial.Hi); Lo = new Vector <double>(initial.Lo); }
public abstract bool RenderSingleThreaded(Float128 xmin, Float128 xmax, Float128 ymin, Float128 ymax, Float128 step, int maxIterations);
private static int to_digits(this Float128 dd, char[] s, int precision, int intBase) { int halfBase = (intBase + 1) >> 1; if (dd.Hi == 0.0) { // Assume dd.lo == 0. for (int i = 0; i < s.Length; ++i) { s[i] = '0'; } return(0); } // First determine the (approximate) exponent. Float128 temp = dd.Abs(); int exp = (int)Math.Floor(Math.Log(temp.Hi) / Math.Log(intBase)); Float128 p = new Float128(intBase); if (exp < -300) { temp.MulSelf(p.Pow(150)); p.PowSelf(-exp - 150); temp.MulSelf(p); } else { p.PowSelf(-exp); temp.MulSelf(p); } // Fix roundoff errors. (eg. floor(log10(1e9))=floor(8.9999~)=8) if (temp.Hi >= intBase) { exp++; temp.Hi /= intBase; temp.Lo /= intBase; } else if (temp.Hi < 1) { exp--; temp.Hi *= intBase; temp.Lo *= intBase; } if (temp.Hi >= intBase || temp.Hi < 1) { throw new Exception("Can't compute exponent."); } // Handle one digit more. Used afterwards for rounding. int numDigits = precision + 1; // Extract the digits. for (int i = 0; i < numDigits; i++) { int val = (int)temp.Hi; temp = temp.Sub(val); temp = temp.Mul(intBase); s[i] = (char)val; } if (s[0] <= 0) { throw new Exception("Negative leading digit."); } // Fix negative digits due to roundoff error in exponent. for (int i = numDigits - 1; i > 0; i--) { if (s[i] >= 32768) { s[i - 1]--; s[i] += (char)intBase; } } // Round, handle carry. if (s[precision] >= halfBase) { s[precision - 1]++; int i = precision - 1; while (i > 0 && s[i] >= intBase) { s[i] -= (char)intBase; s[--i]++; } } s[precision] = (char)0; // If first digit became too high, shift right. if (s[0] >= intBase) { exp++; for (int i = precision; i >= 1;) { s[i] = s[--i]; } } // Convert to ASCII. for (int i = 0; i < precision; i++) { s[i] = Float128.BASE_36_TABLE[s[i]]; } // If first digit became zero, and exp > 0, shift left. if (s[0] == '0' && exp < 32768) { exp--; for (int i = 0; i < precision;) { s[i] = s[++i]; } } return(exp); }
/** * Format a string in an easily readable format. The number is represented * as scientific form on the following conditions: <br> * <ol> * <li>(for big numbers) When the first digit right of the decimal point * would not be within the first minPrecision positions of the string, <br> * <li>(for small numbers) When the most significant digit would not be * within the first minPrecision positions of the string * </ol> * <br> * Where: <code>minPrecision = floor(105 / log2(intBase) + 1)</code> */ public static string ToString(this Float128 dd, int intBase) { double digitsPerBit = Math.Log(2) / Math.Log(intBase); int minPrecision = (int)Math.Floor(105.0 * digitsPerBit + 2); // Get the precision. (The minimum number of significant digits required // for an accurate representation of this number) int expHi = (int)((BitConverter.DoubleToInt64Bits(dd.Hi) & 0x7FF0000000000000L) >> 52); int expLo = dd.Lo == 0 ? expHi - 53 : (int)((BitConverter.DoubleToInt64Bits(dd.Lo) & 0x7FF0000000000000L) >> 52); int precision = (int)Math.Ceiling((expHi - expLo + 53) * digitsPerBit); precision = Math.Max(minPrecision, precision); // Get the raw digit representation. char[] chars = new char[precision + 1]; int exp = dd.to_digits(chars, precision, intBase) + 1; // Get some properties. int left = Math.Max(0, -exp); int right = Math.Max(0, exp); if (chars[precision - 1] == 0) { precision--; } bool sci = -exp >= minPrecision || exp >= minPrecision; // Allocate exactly the right size string. StringBuilder outString = new StringBuilder(precision + (sci ? 3 : left) + (exp > 0 ? 1 : 2)); // Build the string. if (dd.Hi < 0) { outString.Append('-'); } if (sci) { outString.Append(chars, 0, 1); outString.Append('.'); outString.Append(chars, 1, precision - 1); outString.Append('e'); outString.Append(exp - 1); } else { if (exp <= 0) { outString.Append('0'); } if (right > 0) { outString.Append(chars, 0, right); } outString.Append('.'); if (left > 0) { if (Float128.ZEROES.Length < left) { //System.err.println(left); } else { outString.Append(Float128.ZEROES, 0, left); } } outString.Append(chars, right, precision - right); } return(outString.ToString()); }
public void Move(Vector distance) { _viewR += new Float128(distance.X) * _zoomLevel; _viewI += new Float128(distance.Y) * _zoomLevel; OnParametersChanged(); }
public unsafe void DoRender() { if (Closing) { return; } int width = _bufferWidth; int height = _bufferHeight; // Try and get a spare buffer if (!_spareBuffers.TryPop(out int[,] buffer)) { buffer = new int[height, width]; } else if (buffer.GetLength(0) != height || buffer.GetLength(1) != width) { buffer = new int[height, width]; _spareBuffers.Clear(); } int maxiter = (int)(-512 * Math.Log10(_zoomLevel.Hi)); Func <int, (byte R, byte G, byte B)> itersToColor = FractalRendererBase.GetColorProviderV2(maxiter + 1); Action <int, int, int> addPixel = (x, y, iters) => { if (y >= height || x >= width) { return; } var color = itersToColor(iters); buffer[y, x] = (color.R << 16) | (color.G << 8) | color.B; }; int halfHeight = (int)(Math.Floor(height / 2.0)); int halfWidth = (int)(Math.Floor(width / 2.0)); Float128 xMin = new Float128(-halfWidth) * _zoomLevel + _viewR; Float128 xMax = new Float128(halfWidth) * _zoomLevel + _viewR; Float128 yMin = new Float128(-halfHeight) * _zoomLevel + _viewI; Float128 yMax = new Float128(halfHeight) * _zoomLevel + _viewI; Float128 step = _zoomLevel; Func <bool> DoRender = null; Action AbortRender = null; if (Options.LanguageCs) { var cs = Options.Cs; if (!cs.PrecisionFloat128) { (var render, var abort) = FractalRenderer64.SelectRender(addPixel, () => false, cs.MethodCpuSimd, cs.PrecisionFloat64, cs.ThreadModelMulti); DoRender = () => render(xMin.Hi, xMax.Hi, yMin.Hi, yMax.Hi, step.Hi, maxiter); AbortRender = () => abort(); } else { (var render, var abort) = FractalRenderer128.SelectRender128(addPixel, () => false, cs.MethodCpuSimd, cs.ThreadModelMulti); DoRender = () => render(xMin, xMax, yMin, yMax, step, maxiter); AbortRender = () => abort(); } } else if (Options.LanguageCpp) { var cpp = Options.Cpp; DoRender = () => { fixed(int *fixedBuffer = buffer) { RenderMandelbrotCpp(cpp.MethodGpu, cpp.PrecisionFloat64, cpp.ThreadModelMulti, _zoomLevel.Hi, _viewR.Hi, _viewI.Hi, fixedBuffer, width, height); } return(true); }; } _throttledAction.InvokeAction(() => { _stopwatch.Reset(); _stopwatch.Start(); bool success = DoRender(); _stopwatch.Stop(); if (!success) { return; } double fullSetPixels = 4 / _zoomLevel.Hi; double meters = _funDistances.PixelsToMeters(fullSetPixels); Title = $"Mandelbrot Rendering Took {_stopwatch.ElapsedMilliseconds}ms ({width}x{height}) Whole Set Size = {Math.Round(meters)} meters (> {_funDistances.PixelsToFunDistance(fullSetPixels)}) Iterations = {maxiter} Zoom Level = {Math.Round(0.002 / _zoomLevel.Hi, 1)}"; var temp = (MandelbrotImage as ArrayBitmapSource)?.Buffer; var image = new ArrayBitmapSource(buffer); image.Freeze(); MandelbrotImage = image; if (temp != null && temp.GetLength(0) == height && temp.GetLength(1) == width) { _spareBuffers.Push(temp); } PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MandelbrotImage))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title))); }); }