Skip to content

Commit 007919c

Browse files
authored
feat: Add support for single and double precision for Mandelbrot (#1992)
* The sample failed on devices that do not support double precision * Modify MandelbrotParameters class to template to support float and double * Add two kernels, one for single precision and one for double precision
1 parent a33598e commit 007919c

File tree

2 files changed

+161
-92
lines changed

2 files changed

+161
-92
lines changed

DirectProgramming/C++SYCL/VisualizedSamples/VisualMandlebrot/Mandel.hpp

Lines changed: 147 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,171 @@
55
using namespace sycl;
66
using namespace std;
77

8-
std::complex<double> complex_square(std::complex<double> c)
8+
template <typename T> std::complex<T> complex_square(std::complex<T> c)
99
{
10-
return std::complex<double>(c.real() * c.real() - c.imag() * c.imag(), c.real() * c.imag() * 2);
10+
return std::complex<T>(c.real() * c.real() - c.imag() * c.imag(), c.real() * c.imag() * 2);
1111
}
1212

13-
struct MandelParameters {
14-
int width;
15-
int height;
16-
int maxIterations;
17-
18-
double xmin;
19-
double xmax;
20-
double ymin;
21-
double ymax;
22-
23-
using ComplexF = std::complex<double>;
24-
25-
MandelParameters(int width, int height, int maxIterations, double xmin, double xmax, double ymin, double ymax) :
26-
width(width),
27-
height(height),
28-
maxIterations(maxIterations),
29-
xmin(xmin),
30-
xmax(xmax),
31-
ymin(ymin),
32-
ymax(ymax) {}
33-
34-
// Scale from 0..width to xmin..xmax
35-
double ScaleRow(int i) const { return xmin + (i * (xmax - xmin) / width); }
36-
37-
// Scale from 0..height to ymin..ymax
38-
double ScaleCol(int i) const { return -(ymin + (i * (ymax - ymin) / height)); }
39-
40-
// Mandelbrot set are points that do not diverge within max_iterations.
41-
int Point(const ComplexF& c) const {
42-
int count = 0;
43-
ComplexF z = 0;
44-
45-
for (int i = 0; i < maxIterations; ++i) {
46-
auto r = z.real();
47-
auto im = z.imag();
48-
49-
// Leave loop if diverging.
50-
if (((r * r) + (im * im)) >= 4.0f) {
51-
break;
13+
template <typename T> class MandelParameters {
14+
using ComplexF = std::complex<T>;
15+
16+
public:
17+
MandelParameters() {}
18+
MandelParameters(int width, int height, int maxIterations, T xmin, T xmax, T ymin, T ymax):
19+
width(width),
20+
height(height),
21+
maxIterations(maxIterations),
22+
xmin(xmin),
23+
xmax(xmax),
24+
ymin(ymin),
25+
ymax(ymax) {}
26+
27+
T ScaleRow(int i) const { return xmin + (i * (xmax - xmin) / width); }
28+
29+
// Scale from 0..height to ymin..ymax
30+
T ScaleCol(int i) const { return -(ymin + (i * (ymax - ymin) / height)); }
31+
32+
// Mandelbrot set are points that do not diverge within max_iterations.
33+
int Point(const ComplexF& c) const {
34+
int count = 0;
35+
ComplexF z = 0;
36+
37+
for (int i = 0; i < maxIterations; ++i) {
38+
auto r = z.real();
39+
auto im = z.imag();
40+
41+
// Leave loop if diverging.
42+
if (((r * r) + (im * im)) >= 4.0f) {
43+
break;
44+
}
45+
46+
// z = z * z + c;
47+
z = complex_square<T>(z) + c;
48+
count++;
5249
}
5350

54-
// z = z * z + c;
55-
z = complex_square(z) + c;
56-
count++;
51+
return count;
5752
}
5853

59-
return count;
60-
}
54+
void scale(T xoffset, T yoffset, T scale) {
55+
// calculate cursour position in the mandelbrot space
56+
T x = xmin + (xmax - xmin) * xoffset;
57+
T y = ymin + (ymax - ymin) * yoffset;
58+
59+
// scale the space
60+
xmin *= 1 + 0.02 * scale;
61+
xmax *= 1 + 0.02 * scale;
62+
ymin *= 1 + 0.02 * scale;
63+
ymax *= 1 + 0.02 * scale;
64+
65+
// calculate coursor position in the scaled mandelbrot space
66+
T x2 = xmin + (xmax - xmin) * xoffset;
67+
T y2 = ymin + (ymax - ymin) * yoffset;
68+
69+
// calculate the offset between position before and after scaling
70+
T offset_x = x2 - x;
71+
T offset_y = y2 - y;
72+
73+
// move the space by offset
74+
xmin -= offset_x;
75+
xmax -= offset_x;
76+
ymin -= offset_y;
77+
ymax -= offset_y;
78+
}
79+
80+
void pan(T xoffset, T yoffset) {
81+
// convert the camera movment from 0 - 1.0 range to distance in mandelbrot space.
82+
T w = (xmax - xmin) * xoffset;
83+
T h = (ymax - ymin) * yoffset;
84+
85+
// move the space by offset
86+
xmin -= w;
87+
xmax -= w;
88+
ymin -= h;
89+
ymax -= h;
90+
}
91+
92+
int width;
93+
int height;
94+
int maxIterations;
95+
T xmin;
96+
T xmax;
97+
T ymin;
98+
T ymax;
6199
};
62100

63101

64102
class Mandelbrot {
65103
public:
66104
Mandelbrot(int width, int height, int maxIterations, double xmin, double xmax, double ymin, double ymax, queue& q);
105+
Mandelbrot(int width, int height, int maxIterations, float xmin, float xmax, float ymin, float ymax, queue& q);
67106
void Calculate(uint32_t* pixels);
68-
MandelParameters getParameters() const { return parameters; }
69107
void scale(double xoffset, double yoffset, double scale);
70108
void pan(double xoffset, double yoffset);
109+
MandelParameters<float> getSPParameters() const { return sp_parameters; }
110+
MandelParameters<double> getDPParameters() const { return dp_parameters; }
71111

72112
private:
113+
void CalculateSP(uint32_t* pixels);
114+
void CalculateDP(uint32_t* pixels);
73115
queue& q;
74-
MandelParameters parameters;
116+
MandelParameters<float> sp_parameters;
117+
MandelParameters<double> dp_parameters;
118+
bool singlePrecision;
75119
};
76120

77121
Mandelbrot::Mandelbrot(int width, int height, int maxIterations, double xmin, double xmax, double ymin, double ymax, queue& q) :
78122
q(q),
79-
parameters(width, height, maxIterations, xmin, xmax, ymin, ymax)
123+
dp_parameters(width, height, maxIterations, xmin, xmax, ymin, ymax),
124+
singlePrecision(false)
125+
{
126+
}
127+
128+
Mandelbrot::Mandelbrot(int width, int height, int maxIterations, float xmin, float xmax, float ymin, float ymax, queue& q) :
129+
q(q),
130+
sp_parameters(width, height, maxIterations, xmin, xmax, ymin, ymax),
131+
singlePrecision(true)
80132
{
81133
}
82134

83135
void Mandelbrot::Calculate(uint32_t* pixels) {
84136

85-
MandelParameters p = getParameters();
86-
const int width = p.width;
87-
const int height = p.height;
88-
const int maxIterations = p.maxIterations;
137+
if (singlePrecision)
138+
CalculateSP(pixels);
139+
else
140+
Calculate(pixels);
141+
}
142+
143+
void Mandelbrot::CalculateSP(uint32_t* pixels) {
144+
MandelParameters<float> parameters = getSPParameters();
145+
const int width = parameters.width;
146+
const int height = parameters.height;
147+
const int maxIterations = parameters.maxIterations;
148+
buffer pixelsBuf(pixels, range(width * height));
149+
150+
// We submit a command group to the queue.
151+
q.submit([&](handler& h) {
152+
153+
accessor ldata(pixelsBuf, h, write_only, no_init);
154+
// Iterate over image and compute mandel for each point.
155+
h.parallel_for(range<1>(height * width), [=](auto index) {
156+
int y = index / height;
157+
int x = index % height;
158+
auto c = std::complex<float>(parameters.ScaleRow(x), parameters.ScaleCol(y));
159+
int value = parameters.Point(c);
160+
float normalized = (1.0f * value) / maxIterations;
161+
ldata[index] = uint32_t(normalized * 0xFFFFFF);
162+
ldata[index] <<= 8;
163+
ldata[index] |= 0xFF;
164+
});
165+
}).wait();
166+
}
167+
168+
void Mandelbrot::CalculateDP(uint32_t* pixels) {
169+
MandelParameters<double> parameters = getDPParameters();
170+
const int width = parameters.width;
171+
const int height = parameters.height;
172+
const int maxIterations = parameters.maxIterations;
89173
buffer pixelsBuf(pixels, range(width * height));
90174

91175
// We submit a command group to the queue.
@@ -96,8 +180,8 @@ void Mandelbrot::Calculate(uint32_t* pixels) {
96180
h.parallel_for(range<1>(height * width), [=](auto index) {
97181
int y = index / height;
98182
int x = index % height;
99-
auto c = std::complex<double>(p.ScaleRow(x), p.ScaleCol(y));
100-
int value = p.Point(c);
183+
auto c = std::complex<double>(parameters.ScaleRow(x), parameters.ScaleCol(y));
184+
int value = parameters.Point(c);
101185
double normalized = (1.0 * value) / maxIterations;
102186
ldata[index] = uint32_t(normalized * 0xFFFFFF);
103187
ldata[index] <<= 8;
@@ -108,41 +192,17 @@ void Mandelbrot::Calculate(uint32_t* pixels) {
108192

109193
void Mandelbrot::scale(double xoffset, double yoffset, double scale)
110194
{
111-
// calculate cursour position in the mandelbrot space
112-
double x = parameters.xmin + (parameters.xmax - parameters.xmin) * xoffset;
113-
double y = parameters.ymin + (parameters.ymax - parameters.ymin) * yoffset;
114-
115-
// scale the space
116-
parameters.xmin *= 1 + 0.02 * scale;
117-
parameters.xmax *= 1 + 0.02 * scale;
118-
parameters.ymin *= 1 + 0.02 * scale;
119-
parameters.ymax *= 1 + 0.02 * scale;
120-
121-
// calculate coursor position in the scaled mandelbrot space
122-
double x2 = parameters.xmin + (parameters.xmax - parameters.xmin) * xoffset;
123-
double y2 = parameters.ymin + (parameters.ymax - parameters.ymin) * yoffset;
124-
125-
// calculate the offset between position before and after scaling
126-
double offset_x = x2 - x;
127-
double offset_y = y2 - y;
128-
129-
// move the space by offset
130-
parameters.xmin -= offset_x;
131-
parameters.xmax -= offset_x;
132-
parameters.ymin -= offset_y;
133-
parameters.ymax -= offset_y;
195+
if (singlePrecision)
196+
sp_parameters.scale((float) xoffset, (float) yoffset, (float) scale);
197+
else
198+
dp_parameters.scale(xoffset, yoffset, scale);
134199
}
135200

136201
void Mandelbrot::pan(double xoffset, double yoffset)
137202
{
138-
// convert the camera movment from 0 - 1.0 range to distance in mandelbrot space.
139-
double w = (parameters.xmax - parameters.xmin) * xoffset;
140-
double h = (parameters.ymax - parameters.ymin) * yoffset;
141-
142-
// move the space by offset
143-
parameters.xmin -= w;
144-
parameters.xmax -= w;
145-
parameters.ymin -= h;
146-
parameters.ymax -= h;
203+
if (singlePrecision)
204+
sp_parameters.pan((float) xoffset, (float) yoffset);
205+
else
206+
dp_parameters.pan(xoffset, yoffset);
147207
}
148208

DirectProgramming/C++SYCL/VisualizedSamples/VisualMandlebrot/MandelbrotSDL.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,18 @@ int main()
8383
queue q(default_selector_v);
8484
ShowDevice(q);
8585

86-
8786
// Create mandelbrot class with width x height texture size, maxIterations, and rendered range from -2 to 2 in X and Y direction
88-
Mandelbrot mandelbrot(width, height, maxIterations, -2, 2, -2, 2, q);
87+
Mandelbrot *mandelbrot;
88+
auto device = q.get_device();
89+
// If the device does not support fp64, we fallback to single precision (float)
90+
if (!device.has(aspect::fp64)) {
91+
std::cout << "Device " << device.get_info<info::device::name>() << " does not support double precision!"
92+
<< " Single precision will be used instead." << std::endl;
93+
// Create mandelbrot class with width x height texture size, maxIterations, and rendered range from -2 to 2 in X and Y direction
94+
mandelbrot = new Mandelbrot(width, height, maxIterations, -2.0f, 2.0f, -2.0f, 2.0f, q);
95+
} else
96+
mandelbrot = new Mandelbrot(width, height, maxIterations, -2.0, 2.0, -2.0, 2.0, q);
97+
8998
uint32_t* pixels;
9099
bool quit = false;
91100
SDL_Event event;
@@ -94,12 +103,12 @@ int main()
94103
{
95104
// Wait for SDL event.
96105
SDL_WaitEvent(&event);
97-
quit = handleEvents(event, mandelbrot);
106+
quit = handleEvents(event, *mandelbrot);
98107

99108
// Lock texture and update the pixels pointer from where SDL will read data.
100109
SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch);
101110
// Calculate mandelbrot and write pixels to pixel pointer
102-
mandelbrot.Calculate(pixels);
111+
mandelbrot->Calculate(pixels);
103112
// Unlock the texture.
104113
SDL_UnlockTexture(texture);
105114
// Copy texture to renderer.
@@ -111,6 +120,6 @@ int main()
111120
// Destroy window and clean the SDL.
112121
SDL_DestroyWindow(window);
113122
SDL_Quit();
114-
123+
delete mandelbrot;
115124
return 0;
116125
}

0 commit comments

Comments
 (0)