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 31 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 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 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 107 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: 122 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 127 fractal_body(fractal &_f) : f(_f) {} 128 }; 129 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 151 void fractal::run(oneapi::tbb::task_group_context &context) { 152 clear(); 153 context.reset(); 154 render(context); 155 } 156 157 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 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 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 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 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 226 void fractal_group::draw_borders() { 227 f0.draw_border(active == 0); 228 f1.draw_border(active == 1); 229 } 230 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 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