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