Committing TBB 2019 Update 9 source code
[platform/upstream/tbb.git] / examples / task_arena / fractal / fractal.cpp
1 /*
2     Copyright (c) 2005-2019 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 "fractal.h"
18
19 #include "tbb/parallel_for.h"
20 #include "tbb/blocked_range2d.h"
21 #include "tbb/tick_count.h"
22
23 #include <math.h>
24 #include <stdio.h>
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; x = 0; y = 0;
43     mu = 0;
44
45     while (((x*x + y*y) <= 4) && (iter < max_iterations)) {
46         xtemp = x*x - y*y + fx0;
47         y = 2*x*y + fy0;
48         x = xtemp;
49         mu += exp(-sqrt(x*x+y*y));
50         iter++;
51     }
52
53     if (iter == max_iterations) {
54         // point corresponds to the mandelbrot set
55         color = v->get_color(255, 255, 255);
56         return color;
57     }
58
59     int b = (int)(256*mu);
60     int g = (b/8);
61     int r = (g/16);
62
63     b = b>255 ? 255 : b;
64     g = g>255 ? 255 : g;
65     r = r>255 ? 255 : r;
66
67     color = v->get_color(r, g, b);
68     return color;
69 }
70
71 void fractal::clear() {
72     drawing_area area( off_x, off_y, size_x, size_y, dm ) ;
73
74     // fill the rendering area with black color
75     for (int y=0; y<size_y; ++y) {
76         area.set_pos( 0, y );
77         for (int x=0; x<size_x; ++x) {
78             area.put_pixel( v->get_color(0, 0, 0) );
79         }
80     }
81 }
82
83 void fractal::draw_border( bool is_active ) {
84     color_t color = is_active ? v->get_color(0, 255, 0) // green color
85                                 : v->get_color(96, 128, 96); // green-gray color
86
87     // top border
88     drawing_area area0( off_x-1, off_y-1, size_x+2, 1, dm );
89     for (int i=-1; i<size_x+1; ++i)
90         area0.put_pixel(color);
91     // bottom border
92     drawing_area area1( off_x-1, off_y+size_y, size_x+2, 1, dm );
93     for (int i=-1; i<size_x+1; ++i)
94         area1.put_pixel(color);
95     // left border
96     drawing_area area2( off_x-1, off_y, 1, size_y+2, dm );
97     for (int i=0; i<size_y; ++i)
98         area2.set_pixel(0, i, color);
99     // right border
100     drawing_area area3( size_x+off_x, off_y, 1, size_y+2, dm );
101     for (int i=0; i<size_y; ++i)
102         area3.set_pixel(0, i, color);
103 }
104
105 void fractal::render_rect( int x0, int y0, int x1, int y1 ) const {
106     // render the specified rectangle area
107     drawing_area area(off_x+x0, off_y+y0, x1-x0, y1-y0, dm);
108     for ( int y=y0; y<y1; ++y ) {
109         area.set_pos( 0, y-y0 );
110         for ( int x=x0; x<x1; ++x ) {
111             area.put_pixel( calc_one_pixel( x, y ) );
112         }
113     }
114 }
115
116 class fractal_body {
117     fractal &f;
118 public:
119     void operator()( tbb::blocked_range2d<int> &r ) const {
120         if ( v->next_frame() )
121             f.render_rect( r.cols().begin(), r.rows().begin(), r.cols().end(), r.rows().end() );
122     }
123
124     fractal_body( fractal &_f ) : f(_f) {
125     }
126 };
127
128 void fractal::render( tbb::task_group_context &context ) {
129     // Make copy of fractal object and render fractal with parallel_for with
130     // the provided context and partitioner chosen by schedule_auto.
131     // Updates to fractal are not reflected in the render.
132     fractal f = *this;
133     fractal_body body(f);
134
135     if( schedule_auto )
136         tbb::parallel_for( tbb::blocked_range2d<int>(0, size_y, grain_size, 0, size_x, grain_size ),
137                 body, tbb::auto_partitioner(), context);
138     else
139         tbb::parallel_for( tbb::blocked_range2d<int>(0, size_y, grain_size, 0, size_x, grain_size ),
140                 body, tbb::simple_partitioner(), context);
141 }
142
143 void fractal::run( tbb::task_group_context &context ) {
144     clear();
145     context.reset();
146     render( context );
147 }
148
149 bool fractal::check_point( int x, int y ) const {
150     return x >= off_x && x <= off_x+size_x &&
151             y >= off_y && y <= off_y+size_y;
152 }
153
154 void fractal_group::calc_fractal( int num ) {
155     // calculate the fractal
156     fractal &f = num ? f1 : f0;
157
158     tbb::tick_count t0 = tbb::tick_count::now();
159     while ( v->next_frame() && num_frames[num] != 0 ) {
160         f.run( context[num] );
161         if ( num_frames[num]>0 ) num_frames[num] -= 1;
162     }
163     tbb::tick_count t1 = tbb::tick_count::now();
164
165     if ( !silent ) {
166         printf("  %s fractal finished. Time: %g\n", num ? "Second" : "First", (t1-t0).seconds());
167     }
168 }
169
170 void fractal_group::switch_active( int new_active ) {
171     if( new_active!=-1 ) active = new_active;
172     else                 active = 1-active; // assumes 'active' is only 0 or 1
173     draw_borders();
174 }
175
176 void fractal_group::set_num_frames_at_least( int n ) {
177     if ( num_frames[0]<n ) num_frames[0] = n;
178     if ( num_frames[1]<n ) num_frames[1] = n;
179 }
180
181 void fractal_group::run( bool create_second_fractal ) {
182     // First argument of arenas construntor is used to restrict concurrency
183     arenas[0].initialize(num_threads);
184     arenas[1].initialize(num_threads / 2);
185
186     draw_borders();
187
188     // the second fractal is calculating on separated thread
189     if ( create_second_fractal ) {
190         arenas[1].execute( [&] {
191             groups[1].run( [&] { calc_fractal( 1 ); } );
192         } );
193     }
194
195     arenas[0].execute( [&] {
196         groups[0].run( [&] { calc_fractal( 0 ); } );
197     } );
198
199     if ( create_second_fractal ) {
200         arenas[1].execute( [&] { groups[1].wait(); } );
201     }
202
203     arenas[0].execute( [&] { groups[0].wait(); } );
204 }
205
206 void fractal_group::draw_borders() {
207     f0.draw_border( active==0 );
208     f1.draw_border( active==1 );
209 }
210
211 fractal_group::fractal_group( const drawing_memory &_dm, int _num_threads, unsigned int _max_iterations, int _num_frames ) : f0(_dm), f1(_dm), num_threads(_num_threads) {
212     // set rendering areas
213     f0.size_x = f1.size_x = _dm.sizex/2-4;
214     f0.size_y = f1.size_y = _dm.sizey-4;
215     f0.off_x = f0.off_y = f1.off_y = 2;
216     f1.off_x = f0.size_x+4+2;
217
218     // set fractals parameters
219     f0.cx = -0.6f; f0.cy = 0.0f; f0.magn = 200.0f;
220     f1.cx = -0.6f; f1.cy = 0.0f; f1.magn = 200.0f;
221     f0.max_iterations = f1.max_iterations = _max_iterations;
222
223     // initially the first fractal is active
224     active = 0;
225
226     num_frames[0] = num_frames[1] = _num_frames;
227 }
228
229 void fractal_group::mouse_click( int x, int y ) {
230     // assumption that the point is not inside any fractal area
231     int new_active = -1;
232
233     if ( f0.check_point( x, y ) ) {
234         // the point is inside the first fractal area
235         new_active = 0;
236     } else if ( f1.check_point( x, y ) ) {
237         // the point is inside the second fractal area
238         new_active = 1;
239     }
240
241     if ( new_active != -1 && new_active != active ) {
242         switch_active( new_active );
243     }
244 }