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