1*d86ed7fbStbbdev /*
2*d86ed7fbStbbdev     Copyright (c) 2005-2020 Intel Corporation
3*d86ed7fbStbbdev 
4*d86ed7fbStbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*d86ed7fbStbbdev     you may not use this file except in compliance with the License.
6*d86ed7fbStbbdev     You may obtain a copy of the License at
7*d86ed7fbStbbdev 
8*d86ed7fbStbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*d86ed7fbStbbdev 
10*d86ed7fbStbbdev     Unless required by applicable law or agreed to in writing, software
11*d86ed7fbStbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*d86ed7fbStbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d86ed7fbStbbdev     See the License for the specific language governing permissions and
14*d86ed7fbStbbdev     limitations under the License.
15*d86ed7fbStbbdev */
16*d86ed7fbStbbdev 
17*d86ed7fbStbbdev /*
18*d86ed7fbStbbdev     The original source for this example is
19*d86ed7fbStbbdev     Copyright (c) 1994-2008 John E. Stone
20*d86ed7fbStbbdev     All rights reserved.
21*d86ed7fbStbbdev 
22*d86ed7fbStbbdev     Redistribution and use in source and binary forms, with or without
23*d86ed7fbStbbdev     modification, are permitted provided that the following conditions
24*d86ed7fbStbbdev     are met:
25*d86ed7fbStbbdev     1. Redistributions of source code must retain the above copyright
26*d86ed7fbStbbdev        notice, this list of conditions and the following disclaimer.
27*d86ed7fbStbbdev     2. Redistributions in binary form must reproduce the above copyright
28*d86ed7fbStbbdev        notice, this list of conditions and the following disclaimer in the
29*d86ed7fbStbbdev        documentation and/or other materials provided with the distribution.
30*d86ed7fbStbbdev     3. The name of the author may not be used to endorse or promote products
31*d86ed7fbStbbdev        derived from this software without specific prior written permission.
32*d86ed7fbStbbdev 
33*d86ed7fbStbbdev     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
34*d86ed7fbStbbdev     OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
35*d86ed7fbStbbdev     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36*d86ed7fbStbbdev     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
37*d86ed7fbStbbdev     DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38*d86ed7fbStbbdev     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39*d86ed7fbStbbdev     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40*d86ed7fbStbbdev     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41*d86ed7fbStbbdev     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42*d86ed7fbStbbdev     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43*d86ed7fbStbbdev     SUCH DAMAGE.
44*d86ed7fbStbbdev */
45*d86ed7fbStbbdev 
46*d86ed7fbStbbdev #include "machine.hpp"
47*d86ed7fbStbbdev #include "types.hpp"
48*d86ed7fbStbbdev #include "macros.hpp"
49*d86ed7fbStbbdev #include "vector.hpp"
50*d86ed7fbStbbdev #include "tgafile.hpp"
51*d86ed7fbStbbdev #include "trace.hpp"
52*d86ed7fbStbbdev #include "light.hpp"
53*d86ed7fbStbbdev #include "shade.hpp"
54*d86ed7fbStbbdev #include "camera.hpp"
55*d86ed7fbStbbdev #include "util.hpp"
56*d86ed7fbStbbdev #include "intersect.hpp"
57*d86ed7fbStbbdev #include "global.hpp"
58*d86ed7fbStbbdev #include "ui.hpp"
59*d86ed7fbStbbdev #include "tachyon_video.hpp"
60*d86ed7fbStbbdev 
61*d86ed7fbStbbdev // shared but read-only so could be private too
62*d86ed7fbStbbdev static thr_parms *all_parms;
63*d86ed7fbStbbdev static scenedef scene;
64*d86ed7fbStbbdev static int startx;
65*d86ed7fbStbbdev static int stopx;
66*d86ed7fbStbbdev static int starty;
67*d86ed7fbStbbdev static int stopy;
68*d86ed7fbStbbdev static flt jitterscale;
69*d86ed7fbStbbdev static int totaly;
70*d86ed7fbStbbdev static int nthreads;
71*d86ed7fbStbbdev 
72*d86ed7fbStbbdev static int grain_size = 50;
73*d86ed7fbStbbdev 
74*d86ed7fbStbbdev #ifdef _WIN32
75*d86ed7fbStbbdev #include <windows.h>
76*d86ed7fbStbbdev #include "pthread_w.hpp"
77*d86ed7fbStbbdev #else
78*d86ed7fbStbbdev #include <pthread.h>
79*d86ed7fbStbbdev #endif
80*d86ed7fbStbbdev 
81*d86ed7fbStbbdev static pthread_mutex_t MyMutex, MyMutex2, MyMutex3;
82*d86ed7fbStbbdev 
83*d86ed7fbStbbdev static color_t render_one_pixel(int x,
84*d86ed7fbStbbdev                                 int y,
85*d86ed7fbStbbdev                                 unsigned int *local_mbox,
86*d86ed7fbStbbdev                                 unsigned int &serial,
87*d86ed7fbStbbdev                                 int startx,
88*d86ed7fbStbbdev                                 int stopx,
89*d86ed7fbStbbdev                                 int starty,
90*d86ed7fbStbbdev                                 int stopy) {
91*d86ed7fbStbbdev     /* private vars moved inside loop */
92*d86ed7fbStbbdev     ray primary, sample;
93*d86ed7fbStbbdev     color col, avcol;
94*d86ed7fbStbbdev     int R, G, B;
95*d86ed7fbStbbdev     intersectstruct local_intersections;
96*d86ed7fbStbbdev     int alias;
97*d86ed7fbStbbdev     /* end private */
98*d86ed7fbStbbdev 
99*d86ed7fbStbbdev     primary = camray(&scene, x, y);
100*d86ed7fbStbbdev     primary.intstruct = &local_intersections;
101*d86ed7fbStbbdev     primary.flags = RT_RAY_REGULAR;
102*d86ed7fbStbbdev 
103*d86ed7fbStbbdev     serial++;
104*d86ed7fbStbbdev     primary.serial = serial;
105*d86ed7fbStbbdev     primary.mbox = local_mbox;
106*d86ed7fbStbbdev     primary.maxdist = FHUGE;
107*d86ed7fbStbbdev     primary.scene = &scene;
108*d86ed7fbStbbdev     col = trace(&primary);
109*d86ed7fbStbbdev 
110*d86ed7fbStbbdev     serial = primary.serial;
111*d86ed7fbStbbdev 
112*d86ed7fbStbbdev     /* perform antialiasing if enabled.. */
113*d86ed7fbStbbdev     if (scene.antialiasing > 0) {
114*d86ed7fbStbbdev         for (alias = 0; alias < scene.antialiasing; alias++) {
115*d86ed7fbStbbdev             serial++; /* increment serial number */
116*d86ed7fbStbbdev             sample = primary; /* copy the regular primary ray to start with */
117*d86ed7fbStbbdev             sample.serial = serial;
118*d86ed7fbStbbdev 
119*d86ed7fbStbbdev             {
120*d86ed7fbStbbdev                 pthread_mutex_lock(&MyMutex);
121*d86ed7fbStbbdev                 sample.d.x += ((rand() % 100) - 50) / jitterscale;
122*d86ed7fbStbbdev                 sample.d.y += ((rand() % 100) - 50) / jitterscale;
123*d86ed7fbStbbdev                 sample.d.z += ((rand() % 100) - 50) / jitterscale;
124*d86ed7fbStbbdev                 pthread_mutex_unlock(&MyMutex);
125*d86ed7fbStbbdev             }
126*d86ed7fbStbbdev 
127*d86ed7fbStbbdev             avcol = trace(&sample);
128*d86ed7fbStbbdev 
129*d86ed7fbStbbdev             serial = sample.serial; /* update our overall serial # */
130*d86ed7fbStbbdev 
131*d86ed7fbStbbdev             col.r += avcol.r;
132*d86ed7fbStbbdev             col.g += avcol.g;
133*d86ed7fbStbbdev             col.b += avcol.b;
134*d86ed7fbStbbdev         }
135*d86ed7fbStbbdev 
136*d86ed7fbStbbdev         col.r /= (scene.antialiasing + 1.0);
137*d86ed7fbStbbdev         col.g /= (scene.antialiasing + 1.0);
138*d86ed7fbStbbdev         col.b /= (scene.antialiasing + 1.0);
139*d86ed7fbStbbdev     }
140*d86ed7fbStbbdev 
141*d86ed7fbStbbdev     /* Handle overexposure and underexposure here... */
142*d86ed7fbStbbdev     R = (int)(col.r * 255);
143*d86ed7fbStbbdev     if (R > 255)
144*d86ed7fbStbbdev         R = 255;
145*d86ed7fbStbbdev     else if (R < 0)
146*d86ed7fbStbbdev         R = 0;
147*d86ed7fbStbbdev 
148*d86ed7fbStbbdev     G = (int)(col.g * 255);
149*d86ed7fbStbbdev     if (G > 255)
150*d86ed7fbStbbdev         G = 255;
151*d86ed7fbStbbdev     else if (G < 0)
152*d86ed7fbStbbdev         G = 0;
153*d86ed7fbStbbdev 
154*d86ed7fbStbbdev     B = (int)(col.b * 255);
155*d86ed7fbStbbdev     if (B > 255)
156*d86ed7fbStbbdev         B = 255;
157*d86ed7fbStbbdev     else if (B < 0)
158*d86ed7fbStbbdev         B = 0;
159*d86ed7fbStbbdev 
160*d86ed7fbStbbdev     return video->get_color(R, G, B);
161*d86ed7fbStbbdev }
162*d86ed7fbStbbdev 
163*d86ed7fbStbbdev // need this so threads can self-schedule work; returns true (and bounds of work) if more work to do
164*d86ed7fbStbbdev 
165*d86ed7fbStbbdev #define MIN(a, b) (((a) < (b)) ? (a) : (b))
166*d86ed7fbStbbdev 
167*d86ed7fbStbbdev static int sched_nexty;
168*d86ed7fbStbbdev 
169*d86ed7fbStbbdev static bool schedule_thread_work(int &y1, int &y2) {
170*d86ed7fbStbbdev     pthread_mutex_lock(&MyMutex3);
171*d86ed7fbStbbdev #ifdef STATIC_EVEN_SCHEDULING
172*d86ed7fbStbbdev     // optional static-even scheduling
173*d86ed7fbStbbdev     y1 = sched_nexty;
174*d86ed7fbStbbdev     sched_nexty += ((stopy - starty + 1) / nthreads);
175*d86ed7fbStbbdev     y2 = MIN(sched_nexty, stopy);
176*d86ed7fbStbbdev #else
177*d86ed7fbStbbdev     // dynamic-chunk scheduling with specified grain_size
178*d86ed7fbStbbdev     y1 = sched_nexty;
179*d86ed7fbStbbdev     sched_nexty += grain_size;
180*d86ed7fbStbbdev     y2 = MIN(sched_nexty, stopy);
181*d86ed7fbStbbdev #endif
182*d86ed7fbStbbdev     pthread_mutex_unlock(&MyMutex3);
183*d86ed7fbStbbdev     return (y1 <= stopy);
184*d86ed7fbStbbdev }
185*d86ed7fbStbbdev 
186*d86ed7fbStbbdev static void parallel_thread(void *arg) {
187*d86ed7fbStbbdev     // thread-local storage
188*d86ed7fbStbbdev     unsigned int serial = 1;
189*d86ed7fbStbbdev     unsigned int mboxsize = sizeof(unsigned int) * (max_objectid() + 20);
190*d86ed7fbStbbdev     unsigned int *local_mbox = (unsigned int *)alloca(mboxsize);
191*d86ed7fbStbbdev     memset(local_mbox, 0, mboxsize);
192*d86ed7fbStbbdev 
193*d86ed7fbStbbdev     // int thread_no = (int) arg;
194*d86ed7fbStbbdev     int y1, y2;
195*d86ed7fbStbbdev     while (schedule_thread_work(y1, y2)) {
196*d86ed7fbStbbdev         for (int y = y1; y < y2; y++) {
197*d86ed7fbStbbdev             {
198*d86ed7fbStbbdev                 drawing_area drawing(startx, totaly - y, stopx - startx, 1);
199*d86ed7fbStbbdev                 for (int x = startx; x < stopx; x++) {
200*d86ed7fbStbbdev                     color_t c =
201*d86ed7fbStbbdev                         render_one_pixel(x, y, local_mbox, serial, startx, stopx, starty, stopy);
202*d86ed7fbStbbdev                     drawing.put_pixel(c);
203*d86ed7fbStbbdev                 }
204*d86ed7fbStbbdev             }
205*d86ed7fbStbbdev             if (!video->next_frame())
206*d86ed7fbStbbdev                 pthread_exit(arg);
207*d86ed7fbStbbdev         }
208*d86ed7fbStbbdev     }
209*d86ed7fbStbbdev     pthread_exit(arg);
210*d86ed7fbStbbdev }
211*d86ed7fbStbbdev 
212*d86ed7fbStbbdev // need this (for each platform) so we can create the right number of threads, to work efficiently
213*d86ed7fbStbbdev 
214*d86ed7fbStbbdev #if defined(_WIN32)
215*d86ed7fbStbbdev 
216*d86ed7fbStbbdev static int get_num_cpus(void) {
217*d86ed7fbStbbdev     SYSTEM_INFO si;
218*d86ed7fbStbbdev     GetNativeSystemInfo(&si);
219*d86ed7fbStbbdev     return (int)si.dwNumberOfProcessors;
220*d86ed7fbStbbdev }
221*d86ed7fbStbbdev 
222*d86ed7fbStbbdev #elif defined(__APPLE__)
223*d86ed7fbStbbdev 
224*d86ed7fbStbbdev #include "sys/types.hpp"
225*d86ed7fbStbbdev #include "sys/sysctl.hpp"
226*d86ed7fbStbbdev static int get_num_cpus(void) {
227*d86ed7fbStbbdev     int name[2] = { CTL_HW, HW_NCPU };
228*d86ed7fbStbbdev     int ncpu;
229*d86ed7fbStbbdev     std::size_t size = sizeof(ncpu);
230*d86ed7fbStbbdev     sysctl(name, 2, &ncpu, &size, nullptr, 0);
231*d86ed7fbStbbdev     return ncpu;
232*d86ed7fbStbbdev }
233*d86ed7fbStbbdev 
234*d86ed7fbStbbdev #else /*  Linux  */
235*d86ed7fbStbbdev 
236*d86ed7fbStbbdev #include <sys/sysinfo.h>
237*d86ed7fbStbbdev static int get_num_cpus(void) {
238*d86ed7fbStbbdev     return get_nprocs();
239*d86ed7fbStbbdev }
240*d86ed7fbStbbdev 
241*d86ed7fbStbbdev #endif
242*d86ed7fbStbbdev 
243*d86ed7fbStbbdev void *thread_trace(thr_parms *parms) {
244*d86ed7fbStbbdev     // shared but read-only so could be private too
245*d86ed7fbStbbdev     all_parms = parms;
246*d86ed7fbStbbdev     scene = parms->scene;
247*d86ed7fbStbbdev     startx = parms->startx;
248*d86ed7fbStbbdev     stopx = parms->stopx;
249*d86ed7fbStbbdev     starty = parms->starty;
250*d86ed7fbStbbdev     stopy = parms->stopy;
251*d86ed7fbStbbdev     jitterscale = 40.0 * (scene.hres + scene.vres);
252*d86ed7fbStbbdev     totaly = parms->scene.vres - 1;
253*d86ed7fbStbbdev 
254*d86ed7fbStbbdev     int n;
255*d86ed7fbStbbdev     nthreads = get_num_cpus();
256*d86ed7fbStbbdev     char *nthreads_str = getenv("THR_NUM_THREADS");
257*d86ed7fbStbbdev     if (nthreads_str && (sscanf(nthreads_str, "%d", &n) > 0) && (n > 0))
258*d86ed7fbStbbdev         nthreads = n;
259*d86ed7fbStbbdev     char *grain_str = getenv("THR_GRAINSIZE");
260*d86ed7fbStbbdev     if (grain_str && (sscanf(grain_str, "%d", &n) > 0) && (n > 0))
261*d86ed7fbStbbdev         grain_size = n;
262*d86ed7fbStbbdev     pthread_t *threads = (pthread_t *)alloca(nthreads * sizeof(pthread_t));
263*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex, nullptr);
264*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex2, nullptr);
265*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex3, nullptr);
266*d86ed7fbStbbdev     sched_nexty = starty; // initialize schedule_thread_work() self-scheduler
267*d86ed7fbStbbdev     for (int i = 0; i < nthreads; i++) {
268*d86ed7fbStbbdev         pthread_create(
269*d86ed7fbStbbdev             &threads[i], nullptr, (void *(*)(void *))parallel_thread, (void *)((std::size_t)i));
270*d86ed7fbStbbdev     }
271*d86ed7fbStbbdev     for (int i = 0; i < nthreads; i++) {
272*d86ed7fbStbbdev         void *exit_val;
273*d86ed7fbStbbdev         pthread_join(threads[i], &exit_val);
274*d86ed7fbStbbdev         // expect i = (int) exit_val
275*d86ed7fbStbbdev     }
276*d86ed7fbStbbdev 
277*d86ed7fbStbbdev     return (nullptr);
278*d86ed7fbStbbdev }
279