-
Notifications
You must be signed in to change notification settings - Fork 0
/
Mandelbrot.cu
137 lines (126 loc) · 4.63 KB
/
Mandelbrot.cu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include "Mandelbrot.h"
#include <iostream>
#include <iterator>
#define CHECKED_CUDA_INVOKE(call) \
do { \
call; \
auto err = cudaGetLastError(); \
if (err != cudaSuccess) { \
std::cerr << "CUDA ERROR: " << cudaGetErrorString(err) << " in " \
<< #call << " at " << __FILE__ << ':' << __LINE__ \
<< std::endl; \
} \
} while (0)
Mandelbrot::Mandelbrot(std::array<double, 4> origin, std::array<double, 4> size,
std::array<std::size_t, 3> dimensions,
std::array<std::size_t, 3> projectionAxes)
: Origin(origin)
, Size(size)
, Dimensions(dimensions)
, ProjectionAxes(projectionAxes)
{
std::size_t numPoints = dimensions[0] * dimensions[1] * dimensions[2];
CHECKED_CUDA_INVOKE(cudaStreamCreate(&this->IterationStream));
CHECKED_CUDA_INVOKE(cudaMallocAsync(&this->Iterations, numPoints * sizeof(double), this->IterationStream));
// Default 0.01 spacing in each dimension.
std::fill(this->Spacings.begin(), this->Spacings.end(), 0.01);
// For different projection axes, recompute spacings.
if ((this->ProjectionAxes[0] != 0) ||
(this->ProjectionAxes[1] != 1) ||
(this->ProjectionAxes[2] != 2))
{
for (int idx = 0; idx < 3; ++idx)
{
const auto& length = this->Dimensions[idx];
const auto& axis = this->ProjectionAxes[idx];
this->Spacings[axis] = this->Size[axis] / length;
}
}
}
Mandelbrot::~Mandelbrot()
{
if (this->Iterations != nullptr)
{
CHECKED_CUDA_INVOKE(cudaFree(this->Iterations));
this->Iterations= nullptr;
}
CHECKED_CUDA_INVOKE(cudaStreamDestroy(this->IterationStream));
this->IterationStream = nullptr;
}
double *Mandelbrot::GetIterationsArray() const
{
CHECKED_CUDA_INVOKE(cudaStreamSynchronize(this->IterationStream));
return this->Iterations;
}
namespace
{
__global__ void EvaluateMandelbrotSet(std::size_t nx, std::size_t ny, std::size_t nz,
std::size_t a0, std::size_t a1, std::size_t a2,
double o0, double o1, double o2, double o3,
double d0, double d1, double d2, double d3, std::size_t maxIters, double* iters)
{
const auto threadId = blockIdx.x * blockDim.x + threadIdx.x;
if (threadId >= nx * ny * nz)
{
return;
}
// z increases slowest, x increases fastest
const std::size_t z = threadId / (ny * nx);
const std::size_t temp = threadId - (z * ny * nx);
const std::size_t y = temp / nx;
const std::size_t x = temp % nx;
double p[4] = { o0, o1, o2, o3 };
const double o[4] = { o0, o1, o2, o3 };
const double d[4] = { d0, d1, d2, d3 };
const std::size_t axes[3] = { a0, a1, a2};
const std::size_t ax_map[3] = { x, y, z };
for (int dim = 0; dim < 3; ++dim)
{
const std::size_t& axis = axes[dim];
p[axis] = o[axis] + ax_map[dim] * d[axis];
}
std::size_t count = 0;
double v0, v1;
double cReal, cImag, zReal, zImag;
double zReal2, zImag2;
cReal = p[0];
cImag = p[1];
zReal = p[2];
zImag = p[3];
zReal2 = zReal * zReal;
zImag2 = zImag * zImag;
v0 = 0.0;
v1 = (zReal2 + zImag2);
while (v1 < 4.0 && count < maxIters)
{
zImag = 2.0 * zReal * zImag + cImag;
zReal = zReal2 - zImag2 + cReal;
zReal2 = zReal * zReal;
zImag2 = zImag * zImag;
++count;
v0 = v1;
v1 = (zReal2 + zImag2);
}
if (count == maxIters)
{
iters[threadId] = static_cast<double>(count);
}
else
{
iters[threadId] = static_cast<double>(count) + (4.0 - v0) / (v1 - v0);
}
}
}
void Mandelbrot::Compute(std::size_t maxIters)
{
int blockSize = 512;
int numThreads = this->Dimensions[0] * this->Dimensions[1] * this->Dimensions[2];
int numBlocks = (numThreads + blockSize - 1) / blockSize;
::EvaluateMandelbrotSet<<<numBlocks, blockSize, 0, this->IterationStream>>>(
this->Dimensions[0], this->Dimensions[1], this->Dimensions[2],
this->ProjectionAxes[0], this->ProjectionAxes[1], this->ProjectionAxes[2],
this->Origin[0], this->Origin[1], this->Origin[2], this->Origin[3],
this->Spacings[0], this->Spacings[1], this->Spacings[2], this->Spacings[3],
maxIters, this->Iterations);
CHECKED_CUDA_INVOKE(void("After kernel 'EvaluateMandelbrotSet' was launched"));
}