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