// Render the fractal on a single thread using the ComplexFloatVec data type // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs public void RenderSingleThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step) { Vector <float> vmax_iters = new Vector <float>((float)max_iters); Vector <float> vlimit = new Vector <float>(limit); Vector <float> vstep = new Vector <float>(step); Vector <float> vxmax = new Vector <float>(xmax); Vector <float> vinc = new Vector <float>((float)Vector <float> .Count * step); Vector <float> vxmin = VectorHelper.Create(i => xmin + step * i); float y = ymin; int yp = 0; for (Vector <float> vy = new Vector <float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++) { int xp = 0; for (Vector <float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector <float> .Count) { ComplexVecFloat num = new ComplexVecFloat(vx, vy); ComplexVecFloat accum = num; Vector <float> viters = Vector <float> .Zero; Vector <float> increment = Vector <float> .One; do { accum = accum.square() + num; viters += increment; Vector <float> vCond = Vector.LessThanOrEqual <float>(accum.sqabs(), vlimit) & Vector.LessThanOrEqual <float>(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <float> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } } }
// 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 void RenderMultiThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf) { double xmin = (double)xminf; double xmax = (double)xmaxf; double ymin = (double)yminf; double ymax = (double)ymaxf; double step = (double)stepf; Vector <long> vmax_iters = new Vector <long>(max_iters); Vector <double> vlimit = new Vector <double>(limit); Vector <double> vinc = new Vector <double>((double)Vector <double> .Count * step); Vector <double> vxmax = new Vector <double>(xmax); Vector <double> vstep = new Vector <double>(step); Vector <double> vxmin = VectorHelper.Create(i => xmin + step * i); Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) => { if (Abort) { return; } Vector <double> vy = new Vector <double>(ymin + step * yp); int xp = 0; for (Vector <double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector <long> .Count) { Vector <double> accumx = vx; Vector <double> accumy = vy; Vector <long> viters = Vector <long> .Zero; Vector <long> increment = Vector <long> .One; do { Vector <double> naccumx = accumx * accumx - accumy * accumy; Vector <double> naccumy = accumx * accumy + accumx * accumy; accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Vector <double> sqabs = accumx * accumx + accumy * accumy; Vector <long> vCond = Vector.LessThanOrEqual(sqabs, vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <long> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } }); }
// 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 void RenderSingleThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf) { double xmin = (double)xminf; double xmax = (double)xmaxf; double ymin = (double)yminf; double ymax = (double)ymaxf; double step = (double)stepf; Vector <double> vlimit = new Vector <double>(limit); Vector <double> vinc = new Vector <double>((double)Vector <double> .Count * step); Vector <double> vstep = new Vector <double>(step); Vector <long> vmax_iters = new Vector <long>(max_iters); Vector <double> vxmin = VectorHelper.Create(i => xmin + step * i); double y = ymin; int yp = 0; for (Vector <double> vy = new Vector <double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++) { int xp = 0; Vector <double> vxmaxd = new Vector <double>(xmax); for (Vector <double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmaxd); vx += vinc, xp += Vector <long> .Count) { Vector <double> accumx = vx; Vector <double> accumy = vy; Vector <long> viters = Vector <long> .Zero; Vector <long> increment = Vector <long> .One; do { Vector <double> naccumx = accumx * accumx - accumy * accumy; Vector <double> naccumy = accumx * accumy + accumx * accumy; accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Vector <double> sqabs = accumx * accumx + accumy * accumy; Vector <long> vCond = Vector.LessThanOrEqual(sqabs, vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <long> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } } }
// Render the fractal on multiple threads using the ComplexVecDouble data type // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs public void RenderMultiThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf) { double xmin = (double)xminf; double xmax = (double)xmaxf; double ymin = (double)yminf; double ymax = (double)ymaxf; double step = (double)stepf; Vector <long> vmax_iters = new Vector <long>(max_iters); Vector <double> vlimit = new Vector <double>(limit); Vector <double> vinc = new Vector <double>((double)Vector <double> .Length * step); Vector <double> vxmax = new Vector <double>(xmax); Vector <double> vstep = new Vector <double>(step); Vector <double> vxmin = VectorHelper.Create(i => xmin + step * i); Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) => { if (Abort) { return; } Vector <double> vy = new Vector <double>(ymin + step * yp); int xp = 0; for (Vector <double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector <long> .Length) { ComplexVecDouble num = new ComplexVecDouble(vx, vy); ComplexVecDouble accum = num; Vector <long> viters = Vector <long> .Zero; Vector <long> increment = Vector <long> .One; do { accum = accum.square() + num; viters += increment; Vector <long> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <long> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } }); }
// Render the fractal on multiple threads using raw Vector<float> data types // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT public void RenderMultiThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step) { Vector <int> vmax_iters = new Vector <int>(max_iters); Vector <float> vlimit = new Vector <float>(limit); Vector <float> vstep = new Vector <float>(step); Vector <float> vinc = new Vector <float>((float)Vector <float> .Length * step); Vector <float> vxmax = new Vector <float>(xmax); Vector <float> vxmin = VectorHelper.Create(i => xmin + step * i); Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) => { if (Abort) { return; } Vector <float> vy = new Vector <float>(ymin + step * yp); int xp = 0; for (Vector <float> vx = vxmin; Vector.LessThanOrEqualAll(vx, vxmax); vx += vinc, xp += Vector <int> .Length) { Vector <float> accumx = vx; Vector <float> accumy = vy; Vector <int> viters = Vector <int> .Zero; Vector <int> increment = Vector <int> .One; do { Vector <float> naccumx = accumx * accumx - accumy * accumy; Vector <float> XtimesY = accumx * accumy; Vector <float> naccumy = XtimesY + XtimesY; accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Vector <float> sqabs = accumx * accumx + accumy * accumy; Vector <int> vCond = Vector.LessThanOrEqual(sqabs, vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <int> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } }); }
// Render the fractal on a single thread using the ComplexVecDouble data type // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs public void RenderSingleThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf) { double xmin = (double)xminf; double xmax = (double)xmaxf; double ymin = (double)yminf; double ymax = (double)ymaxf; double step = (double)stepf; Vector <double> vmax_iters = new Vector <double>((double)max_iters); Vector <double> vlimit = new Vector <double>(limit); Vector <double> vstep = new Vector <double>(step); Vector <double> vinc = new Vector <double>((double)Vector <double> .Length * step); Vector <double> vxmax = new Vector <double>(xmax); Vector <double> vxmin = VectorHelper.Create(i => xmin + step * i); double y = ymin; int yp = 0; for (Vector <double> vy = new Vector <double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++) { int xp = 0; for (Vector <double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector <double> .Length) { ComplexVecDouble num = new ComplexVecDouble(vx, vy); ComplexVecDouble accum = num; Vector <double> viters = Vector <double> .Zero; Vector <double> increment = Vector <double> .One; do { accum = accum.square() + num; viters += increment; Vector <double> vCond = Vector.LessThanOrEqual <double>(accum.sqabs(), vlimit) & Vector.LessThanOrEqual <double>(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <double> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } } }
// Render the fractal on a single thread using raw Vector<float> data types // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT public void RenderSingleThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step) { Vector <int> vmax_iters = new Vector <int>(max_iters); Vector <float> vlimit = new Vector <float>(limit); Vector <float> vstep = new Vector <float>(step); Vector <float> vxmax = new Vector <float>(xmax); Vector <float> vinc = new Vector <float>((float)Vector <float> .Length * step); Vector <float> vxmin = VectorHelper.Create(i => xmin + step * i); float y = ymin; int yp = 0; for (Vector <float> vy = new Vector <float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++) { int xp = 0; for (Vector <float> vx = vxmin; Vector.LessThanOrEqualAll(vx, vxmax); vx += vinc, xp += Vector <int> .Length) { Vector <float> accumx = vx; Vector <float> accumy = vy; Vector <int> viters = Vector <int> .Zero; Vector <int> increment = Vector <int> .One; do { Vector <float> naccumx = accumx * accumx - accumy * accumy; Vector <float> naccumy = accumx * accumy + accumx * accumy; accumx = naccumx + vx; accumy = naccumy + vy; viters += increment; Vector <float> sqabs = accumx * accumx + accumy * accumy; Vector <int> vCond = Vector.LessThanOrEqual(sqabs, vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <int> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter)); } } }
// Render the fractal on multiple threads using the ComplexFloatVec data type // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT public void RenderMultiThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step) { Vector <int> vmax_iters = new Vector <int>(max_iters); Vector <float> vlimit = new Vector <float>(limit); Vector <float> vstep = new Vector <float>(step); Vector <float> vinc = new Vector <float>((float)Vector <float> .Count * step); Vector <float> vxmax = new Vector <float>(xmax); Vector <float> vxmin = VectorHelper.Create(i => xmin + step * i); Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) => { if (Abort) { return; } Vector <float> vy = new Vector <float>(ymin + step * yp); int xp = 0; for (Vector <float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector <int> .Count) { ComplexVecFloat num = new ComplexVecFloat(vx, vy); ComplexVecFloat accum = num; Vector <int> viters = Vector <int> .Zero; Vector <int> increment = Vector <int> .One; do { accum = accum.square() + num; viters += increment; Vector <int> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); increment = increment & vCond; } while (increment != Vector <int> .Zero); viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, iter)); } }); }
// Render the fractal on a single thread using the ComplexFloatVec data type // This is the implementation that has the best comments. public void RenderSingleThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step) { // Initialize a pile of method constant vectors Vector <int> vmax_iters = new Vector <int>(max_iters); Vector <float> vlimit = new Vector <float>(limit); Vector <float> vstep = new Vector <float>(step); Vector <float> vxmax = new Vector <float>(xmax); Vector <float> vinc = new Vector <float>((float)Vector <float> .Count * step); // Use my little helper routine: it's kind of slow, but I find it pleasantly readable. // The alternative would be this: // float[] xmins = new float[Vector<float>.Count]; // for (int i = 0; i < xmins.Count; i++) // xmins[i] = xmin + step * i; // Vector<float> vxmin = new Vector<float>(xmins); // Both allocate some memory, this one just does it in a separate routine :-) Vector <float> vxmin = VectorHelper.Create(i => xmin + step * i); float y = ymin; int yp = 0; for (Vector <float> vy = new Vector <float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++) { int xp = 0; for (Vector <float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); // Vector.{comparision}Any|All return bools, not masks vx += vinc, xp += Vector <int> .Count) { ComplexVecFloat num = new ComplexVecFloat(vx, vy); ComplexVecFloat accum = num; Vector <int> viters = Vector <int> .Zero; // Iteration counts start at all zeros Vector <int> increment = Vector <int> .One; // Increment starts out as all ones do { // This is work that can be vectorized accum = accum.square() + num; // Increment the iteration count Only pixels that haven't already hit the // limit will be incremented because the increment variable gets masked below viters += increment; // Create a mask that correspons to the element-wise logical operation // "accum <= limit && iters <= max_iters" Note that the bitwise and is used, // because the Vector.{comparision} operations return masks, not boolean values Vector <int> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) & Vector.LessThanOrEqual(viters, vmax_iters); // increment becomes zero for the elems that have hit the limit because // vCond is a mask of all zeros or ones, based on the results of the // Vector.{comparison} operations increment = increment & vCond; // Keep going until we have no elements that haven't either hit the value // limit or the iteration count } while (increment != Vector <int> .Zero); // This is another little helper I created. It's definitely kind of slow but I // find it pleasantly succinct. It could also be written like this: // // for (int eNum = 0; eNum < Vector<int>.Count; eNum++) // DrawPixel(xp + eNum, yp, viters[eNum]); // // Neither implementation is particularly fast, because pulling individual elements // is a slow operation for vector types. viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, iter)); } } }