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 const int DIVFACTOR = 2;
74*d86ed7fbStbbdev 
75*d86ed7fbStbbdev #define MIN(a, b) ((a) < (b) ? (a) : (b))
76*d86ed7fbStbbdev 
77*d86ed7fbStbbdev #ifdef _WIN32
78*d86ed7fbStbbdev #include <windows.h>
79*d86ed7fbStbbdev #include "pthread_w.hpp"
80*d86ed7fbStbbdev #else
81*d86ed7fbStbbdev #include <pthread.h>
82*d86ed7fbStbbdev #endif
83*d86ed7fbStbbdev 
84*d86ed7fbStbbdev static pthread_mutex_t MyMutex, MyMutex2, MyMutex3;
85*d86ed7fbStbbdev 
86*d86ed7fbStbbdev static color_t render_one_pixel(int x,
87*d86ed7fbStbbdev                                 int y,
88*d86ed7fbStbbdev                                 unsigned int *local_mbox,
89*d86ed7fbStbbdev                                 unsigned int &serial,
90*d86ed7fbStbbdev                                 int startx,
91*d86ed7fbStbbdev                                 int stopx,
92*d86ed7fbStbbdev                                 int starty,
93*d86ed7fbStbbdev                                 int stopy) {
94*d86ed7fbStbbdev     /* private vars moved inside loop */
95*d86ed7fbStbbdev     ray primary, sample;
96*d86ed7fbStbbdev     color col, avcol;
97*d86ed7fbStbbdev     int R, G, B;
98*d86ed7fbStbbdev     intersectstruct local_intersections;
99*d86ed7fbStbbdev     int alias;
100*d86ed7fbStbbdev     /* end private */
101*d86ed7fbStbbdev 
102*d86ed7fbStbbdev     primary = camray(&scene, x, y);
103*d86ed7fbStbbdev     primary.intstruct = &local_intersections;
104*d86ed7fbStbbdev     primary.flags = RT_RAY_REGULAR;
105*d86ed7fbStbbdev 
106*d86ed7fbStbbdev     serial++;
107*d86ed7fbStbbdev     primary.serial = serial;
108*d86ed7fbStbbdev     primary.mbox = local_mbox;
109*d86ed7fbStbbdev     primary.maxdist = FHUGE;
110*d86ed7fbStbbdev     primary.scene = &scene;
111*d86ed7fbStbbdev     col = trace(&primary);
112*d86ed7fbStbbdev 
113*d86ed7fbStbbdev     serial = primary.serial;
114*d86ed7fbStbbdev 
115*d86ed7fbStbbdev     /* perform antialiasing if enabled.. */
116*d86ed7fbStbbdev     if (scene.antialiasing > 0) {
117*d86ed7fbStbbdev         for (alias = 0; alias < scene.antialiasing; alias++) {
118*d86ed7fbStbbdev             serial++; /* increment serial number */
119*d86ed7fbStbbdev             sample = primary; /* copy the regular primary ray to start with */
120*d86ed7fbStbbdev             sample.serial = serial;
121*d86ed7fbStbbdev 
122*d86ed7fbStbbdev             {
123*d86ed7fbStbbdev                 pthread_mutex_lock(&MyMutex);
124*d86ed7fbStbbdev                 sample.d.x += ((rand() % 100) - 50) / jitterscale;
125*d86ed7fbStbbdev                 sample.d.y += ((rand() % 100) - 50) / jitterscale;
126*d86ed7fbStbbdev                 sample.d.z += ((rand() % 100) - 50) / jitterscale;
127*d86ed7fbStbbdev                 pthread_mutex_unlock(&MyMutex);
128*d86ed7fbStbbdev             }
129*d86ed7fbStbbdev 
130*d86ed7fbStbbdev             avcol = trace(&sample);
131*d86ed7fbStbbdev 
132*d86ed7fbStbbdev             serial = sample.serial; /* update our overall serial # */
133*d86ed7fbStbbdev 
134*d86ed7fbStbbdev             col.r += avcol.r;
135*d86ed7fbStbbdev             col.g += avcol.g;
136*d86ed7fbStbbdev             col.b += avcol.b;
137*d86ed7fbStbbdev         }
138*d86ed7fbStbbdev 
139*d86ed7fbStbbdev         col.r /= (scene.antialiasing + 1.0);
140*d86ed7fbStbbdev         col.g /= (scene.antialiasing + 1.0);
141*d86ed7fbStbbdev         col.b /= (scene.antialiasing + 1.0);
142*d86ed7fbStbbdev     }
143*d86ed7fbStbbdev 
144*d86ed7fbStbbdev     /* Handle overexposure and underexposure here... */
145*d86ed7fbStbbdev     R = (int)(col.r * 255);
146*d86ed7fbStbbdev     if (R > 255)
147*d86ed7fbStbbdev         R = 255;
148*d86ed7fbStbbdev     else if (R < 0)
149*d86ed7fbStbbdev         R = 0;
150*d86ed7fbStbbdev 
151*d86ed7fbStbbdev     G = (int)(col.g * 255);
152*d86ed7fbStbbdev     if (G > 255)
153*d86ed7fbStbbdev         G = 255;
154*d86ed7fbStbbdev     else if (G < 0)
155*d86ed7fbStbbdev         G = 0;
156*d86ed7fbStbbdev 
157*d86ed7fbStbbdev     B = (int)(col.b * 255);
158*d86ed7fbStbbdev     if (B > 255)
159*d86ed7fbStbbdev         B = 255;
160*d86ed7fbStbbdev     else if (B < 0)
161*d86ed7fbStbbdev         B = 0;
162*d86ed7fbStbbdev 
163*d86ed7fbStbbdev     return video->get_color(R, G, B);
164*d86ed7fbStbbdev }
165*d86ed7fbStbbdev 
166*d86ed7fbStbbdev // need this so threads can self-schedule work; returns true (and bounds of work) if more work to do
167*d86ed7fbStbbdev 
168*d86ed7fbStbbdev typedef struct work_queue_entry_s {
169*d86ed7fbStbbdev     patch pch;
170*d86ed7fbStbbdev     struct work_queue_entry_s *next;
171*d86ed7fbStbbdev } work_queue_entry_t;
172*d86ed7fbStbbdev static work_queue_entry_t *work_queue_head = nullptr;
173*d86ed7fbStbbdev static work_queue_entry_t *work_queue_tail = nullptr;
174*d86ed7fbStbbdev 
175*d86ed7fbStbbdev static void generate_work(patch *pchin) {
176*d86ed7fbStbbdev     int startx, stopx, starty, stopy;
177*d86ed7fbStbbdev     int xs, ys;
178*d86ed7fbStbbdev 
179*d86ed7fbStbbdev     startx = pchin->startx;
180*d86ed7fbStbbdev     stopx = pchin->stopx;
181*d86ed7fbStbbdev     starty = pchin->starty;
182*d86ed7fbStbbdev     stopy = pchin->stopy;
183*d86ed7fbStbbdev 
184*d86ed7fbStbbdev     if (((stopx - startx) >= grain_size) || ((stopy - starty) >= grain_size)) {
185*d86ed7fbStbbdev         int xpatchsize = (stopx - startx) / DIVFACTOR + 1;
186*d86ed7fbStbbdev         int ypatchsize = (stopy - starty) / DIVFACTOR + 1;
187*d86ed7fbStbbdev         for (ys = starty; ys <= stopy; ys += ypatchsize)
188*d86ed7fbStbbdev             for (xs = startx; xs <= stopx; xs += xpatchsize) {
189*d86ed7fbStbbdev                 patch pch;
190*d86ed7fbStbbdev                 pch.startx = xs;
191*d86ed7fbStbbdev                 pch.starty = ys;
192*d86ed7fbStbbdev                 pch.stopx = MIN(xs + xpatchsize, stopx);
193*d86ed7fbStbbdev                 pch.stopy = MIN(ys + ypatchsize, stopy);
194*d86ed7fbStbbdev 
195*d86ed7fbStbbdev                 generate_work(&pch);
196*d86ed7fbStbbdev             }
197*d86ed7fbStbbdev     }
198*d86ed7fbStbbdev     else {
199*d86ed7fbStbbdev         /* just trace this patch */
200*d86ed7fbStbbdev         work_queue_entry_t *q = (work_queue_entry_t *)malloc(sizeof(work_queue_entry_t));
201*d86ed7fbStbbdev         q->pch.starty = starty;
202*d86ed7fbStbbdev         q->pch.stopy = stopy;
203*d86ed7fbStbbdev         q->pch.startx = startx;
204*d86ed7fbStbbdev         q->pch.stopx = stopx;
205*d86ed7fbStbbdev         q->next = nullptr;
206*d86ed7fbStbbdev         if (work_queue_head == nullptr) {
207*d86ed7fbStbbdev             work_queue_head = q;
208*d86ed7fbStbbdev         }
209*d86ed7fbStbbdev         else {
210*d86ed7fbStbbdev             work_queue_tail->next = q;
211*d86ed7fbStbbdev         }
212*d86ed7fbStbbdev         work_queue_tail = q;
213*d86ed7fbStbbdev     }
214*d86ed7fbStbbdev }
215*d86ed7fbStbbdev 
216*d86ed7fbStbbdev static void generate_worklist(void) {
217*d86ed7fbStbbdev     patch pch;
218*d86ed7fbStbbdev     pch.startx = startx;
219*d86ed7fbStbbdev     pch.stopx = stopx;
220*d86ed7fbStbbdev     pch.starty = starty;
221*d86ed7fbStbbdev     pch.stopy = stopy;
222*d86ed7fbStbbdev     generate_work(&pch);
223*d86ed7fbStbbdev }
224*d86ed7fbStbbdev 
225*d86ed7fbStbbdev static bool schedule_thread_work(patch &pch) {
226*d86ed7fbStbbdev     pthread_mutex_lock(&MyMutex3);
227*d86ed7fbStbbdev     work_queue_entry_t *q = work_queue_head;
228*d86ed7fbStbbdev     if (q != nullptr) {
229*d86ed7fbStbbdev         pch = q->pch;
230*d86ed7fbStbbdev         work_queue_head = work_queue_head->next;
231*d86ed7fbStbbdev     }
232*d86ed7fbStbbdev     pthread_mutex_unlock(&MyMutex3);
233*d86ed7fbStbbdev     return (q != nullptr);
234*d86ed7fbStbbdev }
235*d86ed7fbStbbdev 
236*d86ed7fbStbbdev static void parallel_thread(void *arg) {
237*d86ed7fbStbbdev     // thread-local storage
238*d86ed7fbStbbdev     unsigned int serial = 1;
239*d86ed7fbStbbdev     unsigned int mboxsize = sizeof(unsigned int) * (max_objectid() + 20);
240*d86ed7fbStbbdev     unsigned int *local_mbox = (unsigned int *)alloca(mboxsize);
241*d86ed7fbStbbdev     memset(local_mbox, 0, mboxsize);
242*d86ed7fbStbbdev 
243*d86ed7fbStbbdev     // int thread_no = (int) arg;
244*d86ed7fbStbbdev     patch pch;
245*d86ed7fbStbbdev     while (schedule_thread_work(pch)) {
246*d86ed7fbStbbdev         {
247*d86ed7fbStbbdev             drawing_area drawing(
248*d86ed7fbStbbdev                 pch.startx, totaly - pch.stopy, pch.stopx - pch.startx, pch.stopy - pch.starty);
249*d86ed7fbStbbdev             for (int i = 1, y = pch.starty; y < pch.stopy; ++y, i++) {
250*d86ed7fbStbbdev                 drawing.set_pos(0, drawing.size_y - i);
251*d86ed7fbStbbdev                 for (int x = pch.startx; x < pch.stopx; x++) {
252*d86ed7fbStbbdev                     color_t c =
253*d86ed7fbStbbdev                         render_one_pixel(x, y, local_mbox, serial, startx, stopx, starty, stopy);
254*d86ed7fbStbbdev                     drawing.put_pixel(c);
255*d86ed7fbStbbdev                 }
256*d86ed7fbStbbdev             }
257*d86ed7fbStbbdev         }
258*d86ed7fbStbbdev         if (!video->next_frame())
259*d86ed7fbStbbdev             pthread_exit(arg);
260*d86ed7fbStbbdev     }
261*d86ed7fbStbbdev     pthread_exit(arg);
262*d86ed7fbStbbdev }
263*d86ed7fbStbbdev 
264*d86ed7fbStbbdev // need this (for each platform) so we can create the right number of threads, to work efficiently
265*d86ed7fbStbbdev 
266*d86ed7fbStbbdev #if defined(_WIN32)
267*d86ed7fbStbbdev 
268*d86ed7fbStbbdev static int get_num_cpus(void) {
269*d86ed7fbStbbdev     SYSTEM_INFO si;
270*d86ed7fbStbbdev     GetNativeSystemInfo(&si);
271*d86ed7fbStbbdev     return (int)si.dwNumberOfProcessors;
272*d86ed7fbStbbdev }
273*d86ed7fbStbbdev 
274*d86ed7fbStbbdev #elif defined(__APPLE__)
275*d86ed7fbStbbdev 
276*d86ed7fbStbbdev #include "sys/types.hpp"
277*d86ed7fbStbbdev #include "sys/sysctl.hpp"
278*d86ed7fbStbbdev static int get_num_cpus(void) {
279*d86ed7fbStbbdev     int name[2] = { CTL_HW, HW_NCPU };
280*d86ed7fbStbbdev     int ncpu;
281*d86ed7fbStbbdev     std::size_t size = sizeof(ncpu);
282*d86ed7fbStbbdev     sysctl(name, 2, &ncpu, &size, nullptr, 0);
283*d86ed7fbStbbdev     return ncpu;
284*d86ed7fbStbbdev }
285*d86ed7fbStbbdev 
286*d86ed7fbStbbdev #else /*  Linux  */
287*d86ed7fbStbbdev 
288*d86ed7fbStbbdev #include <sys/sysinfo.h>
289*d86ed7fbStbbdev static int get_num_cpus(void) {
290*d86ed7fbStbbdev     return get_nprocs();
291*d86ed7fbStbbdev }
292*d86ed7fbStbbdev 
293*d86ed7fbStbbdev #endif
294*d86ed7fbStbbdev 
295*d86ed7fbStbbdev void *thread_trace(thr_parms *parms) {
296*d86ed7fbStbbdev     // shared but read-only so could be private too
297*d86ed7fbStbbdev     all_parms = parms;
298*d86ed7fbStbbdev     scene = parms->scene;
299*d86ed7fbStbbdev     startx = parms->startx;
300*d86ed7fbStbbdev     stopx = parms->stopx;
301*d86ed7fbStbbdev     starty = parms->starty;
302*d86ed7fbStbbdev     stopy = parms->stopy;
303*d86ed7fbStbbdev     jitterscale = 40.0 * (scene.hres + scene.vres);
304*d86ed7fbStbbdev     totaly = parms->scene.vres;
305*d86ed7fbStbbdev 
306*d86ed7fbStbbdev     int n;
307*d86ed7fbStbbdev     nthreads = get_num_cpus();
308*d86ed7fbStbbdev     char *nthreads_str = getenv("THR_NUM_THREADS");
309*d86ed7fbStbbdev     if (nthreads_str && (sscanf(nthreads_str, "%d", &n) > 0) && (n > 0))
310*d86ed7fbStbbdev         nthreads = n;
311*d86ed7fbStbbdev     char *grain_str = getenv("THR_GRAINSIZE");
312*d86ed7fbStbbdev     if (grain_str && (sscanf(grain_str, "%d", &n) > 0) && (n > 0))
313*d86ed7fbStbbdev         grain_size = n;
314*d86ed7fbStbbdev     pthread_t *threads = (pthread_t *)alloca(nthreads * sizeof(pthread_t));
315*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex, nullptr);
316*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex2, nullptr);
317*d86ed7fbStbbdev     pthread_mutex_init(&MyMutex3, nullptr);
318*d86ed7fbStbbdev     generate_worklist(); // initialize schedule_thread_work() self-scheduler
319*d86ed7fbStbbdev     for (int i = 0; i < nthreads; i++) {
320*d86ed7fbStbbdev         pthread_create(
321*d86ed7fbStbbdev             &threads[i], nullptr, (void *(*)(void *))parallel_thread, (void *)((std::size_t)i));
322*d86ed7fbStbbdev     }
323*d86ed7fbStbbdev     for (int i = 0; i < nthreads; i++) {
324*d86ed7fbStbbdev         void *exit_val;
325*d86ed7fbStbbdev         pthread_join(threads[i], &exit_val);
326*d86ed7fbStbbdev         // expect i = (int) exit_val
327*d86ed7fbStbbdev     }
328*d86ed7fbStbbdev 
329*d86ed7fbStbbdev     return (nullptr);
330*d86ed7fbStbbdev }
331