1 /*
2 Copyright (c) 2005-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #include <cmath>
18 #include <cstdio>
19
20 #include "oneapi/tbb/parallel_for.h"
21 #include "oneapi/tbb/blocked_range2d.h"
22 #include "oneapi/tbb/tick_count.h"
23
24 #include "fractal.hpp"
25
26 video *v;
27 extern bool silent;
28 extern bool schedule_auto;
29 extern int grain_size;
30
calc_one_pixel(int x0,int y0) const31 color_t fractal::calc_one_pixel(int x0, int y0) const {
32 unsigned int iter;
33 double fx0, fy0, xtemp, x, y, mu;
34
35 color_t color;
36
37 fx0 = (double)x0 - (double)size_x / 2.0;
38 fy0 = (double)y0 - (double)size_y / 2.0;
39 fx0 = fx0 / magn + cx;
40 fy0 = fy0 / magn + cy;
41
42 iter = 0;
43 x = 0;
44 y = 0;
45 mu = 0;
46
47 while (((x * x + y * y) <= 4) && (iter < max_iterations)) {
48 xtemp = x * x - y * y + fx0;
49 y = 2 * x * y + fy0;
50 x = xtemp;
51 mu += exp(-sqrt(x * x + y * y));
52 iter++;
53 }
54
55 if (iter == max_iterations) {
56 // point corresponds to the mandelbrot set
57 color = v->get_color(255, 255, 255);
58 return color;
59 }
60
61 int b = (int)(256 * mu);
62 int g = (b / 8);
63 int r = (g / 16);
64
65 b = b > 255 ? 255 : b;
66 g = g > 255 ? 255 : g;
67 r = r > 255 ? 255 : r;
68
69 color = v->get_color(r, g, b);
70 return color;
71 }
72
clear()73 void fractal::clear() {
74 drawing_area area(off_x, off_y, size_x, size_y, dm);
75
76 // fill the rendering area with black color
77 for (int y = 0; y < size_y; ++y) {
78 area.set_pos(0, y);
79 for (int x = 0; x < size_x; ++x) {
80 area.put_pixel(v->get_color(0, 0, 0));
81 }
82 }
83 }
84
draw_border(bool is_active)85 void fractal::draw_border(bool is_active) {
86 color_t color = is_active ? v->get_color(0, 255, 0) // green color
87 : v->get_color(96, 128, 96); // green-gray color
88
89 // top border
90 drawing_area area0(off_x - 1, off_y - 1, size_x + 2, 1, dm);
91 for (int i = -1; i < size_x + 1; ++i)
92 area0.put_pixel(color);
93 // bottom border
94 drawing_area area1(off_x - 1, off_y + size_y, size_x + 2, 1, dm);
95 for (int i = -1; i < size_x + 1; ++i)
96 area1.put_pixel(color);
97 // left border
98 drawing_area area2(off_x - 1, off_y, 1, size_y + 2, dm);
99 for (int i = 0; i < size_y; ++i)
100 area2.set_pixel(0, i, color);
101 // right border
102 drawing_area area3(size_x + off_x, off_y, 1, size_y + 2, dm);
103 for (int i = 0; i < size_y; ++i)
104 area3.set_pixel(0, i, color);
105 }
106
render_rect(int x0,int y0,int x1,int y1) const107 void fractal::render_rect(int x0, int y0, int x1, int y1) const {
108 // render the specified rectangle area
109 drawing_area area(off_x + x0, off_y + y0, x1 - x0, y1 - y0, dm);
110 for (int y = y0; y < y1; ++y) {
111 area.set_pos(0, y - y0);
112 for (int x = x0; x < x1; ++x) {
113 area.put_pixel(calc_one_pixel(x, y));
114 }
115 }
116 }
117
118 class fractal_body {
119 fractal &f;
120
121 public:
operator ()(oneapi::tbb::blocked_range2d<int> & r) const122 void operator()(oneapi::tbb::blocked_range2d<int> &r) const {
123 if (v->next_frame())
124 f.render_rect(r.cols().begin(), r.rows().begin(), r.cols().end(), r.rows().end());
125 }
126
fractal_body(fractal & _f)127 fractal_body(fractal &_f) : f(_f) {}
128 };
129
render(oneapi::tbb::task_group_context & context)130 void fractal::render(oneapi::tbb::task_group_context &context) {
131 // Make copy of fractal object and render fractal with parallel_for with
132 // the provided context and partitioner chosen by schedule_auto.
133 // Updates to fractal are not reflected in the render.
134 fractal f = *this;
135 fractal_body body(f);
136
137 if (schedule_auto)
138 oneapi::tbb::parallel_for(
139 oneapi::tbb::blocked_range2d<int>(0, size_y, grain_size, 0, size_x, grain_size),
140 body,
141 oneapi::tbb::auto_partitioner(),
142 context);
143 else
144 oneapi::tbb::parallel_for(
145 oneapi::tbb::blocked_range2d<int>(0, size_y, grain_size, 0, size_x, grain_size),
146 body,
147 oneapi::tbb::simple_partitioner(),
148 context);
149 }
150
run(oneapi::tbb::task_group_context & context)151 void fractal::run(oneapi::tbb::task_group_context &context) {
152 clear();
153 context.reset();
154 render(context);
155 }
156
check_point(int x,int y) const157 bool fractal::check_point(int x, int y) const {
158 return x >= off_x && x <= off_x + size_x && y >= off_y && y <= off_y + size_y;
159 }
160
calc_fractal(int num)161 void fractal_group::calc_fractal(int num) {
162 // calculate the fractal
163 fractal &f = num ? f1 : f0;
164
165 oneapi::tbb::tick_count t0 = oneapi::tbb::tick_count::now();
166 while (v->next_frame() && num_frames[num] != 0) {
167 f.run(context[num]);
168 if (num_frames[num] > 0)
169 num_frames[num] -= 1;
170 }
171 oneapi::tbb::tick_count t1 = oneapi::tbb::tick_count::now();
172
173 if (!silent) {
174 printf(" %s fractal finished. Time: %g\n", num ? "Second" : "First", (t1 - t0).seconds());
175 }
176 }
177
switch_active(int new_active)178 void fractal_group::switch_active(int new_active) {
179 if (new_active != -1)
180 active = new_active;
181 else
182 active = 1 - active; // assumes 'active' is only 0 or 1
183 draw_borders();
184 }
185
set_num_frames_at_least(int n)186 void fractal_group::set_num_frames_at_least(int n) {
187 if (num_frames[0] < n)
188 num_frames[0] = n;
189 if (num_frames[1] < n)
190 num_frames[1] = n;
191 }
192
run(bool create_second_fractal)193 void fractal_group::run(bool create_second_fractal) {
194 // First argument of arenas construntor is used to restrict concurrency
195 arenas[0].initialize(num_threads);
196 arenas[1].initialize(num_threads / 2);
197
198 draw_borders();
199
200 // the second fractal is calculating on separated thread
201 if (create_second_fractal) {
202 arenas[1].execute([&] {
203 groups[1].run([&] {
204 calc_fractal(1);
205 });
206 });
207 }
208
209 arenas[0].execute([&] {
210 groups[0].run([&] {
211 calc_fractal(0);
212 });
213 });
214
215 if (create_second_fractal) {
216 arenas[1].execute([&] {
217 groups[1].wait();
218 });
219 }
220
221 arenas[0].execute([&] {
222 groups[0].wait();
223 });
224 }
225
draw_borders()226 void fractal_group::draw_borders() {
227 f0.draw_border(active == 0);
228 f1.draw_border(active == 1);
229 }
230
fractal_group(const drawing_memory & _dm,int _num_threads,unsigned int _max_iterations,int _num_frames)231 fractal_group::fractal_group(const drawing_memory &_dm,
232 int _num_threads,
233 unsigned int _max_iterations,
234 int _num_frames)
235 : f0(_dm),
236 f1(_dm),
237 num_threads(_num_threads) {
238 // set rendering areas
239 f0.size_x = f1.size_x = _dm.sizex / 2 - 4;
240 f0.size_y = f1.size_y = _dm.sizey - 4;
241 f0.off_x = f0.off_y = f1.off_y = 2;
242 f1.off_x = f0.size_x + 4 + 2;
243
244 // set fractals parameters
245 f0.cx = -0.6f;
246 f0.cy = 0.0f;
247 f0.magn = 200.0f;
248 f1.cx = -0.6f;
249 f1.cy = 0.0f;
250 f1.magn = 200.0f;
251 f0.max_iterations = f1.max_iterations = _max_iterations;
252
253 // initially the first fractal is active
254 active = 0;
255
256 num_frames[0] = num_frames[1] = _num_frames;
257 }
258
mouse_click(int x,int y)259 void fractal_group::mouse_click(int x, int y) {
260 // assumption that the point is not inside any fractal area
261 int new_active = -1;
262
263 if (f0.check_point(x, y)) {
264 // the point is inside the first fractal area
265 new_active = 0;
266 }
267 else if (f1.check_point(x, y)) {
268 // the point is inside the second fractal area
269 new_active = 1;
270 }
271
272 if (new_active != -1 && new_active != active) {
273 switch_active(new_active);
274 }
275 }
276