1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // Interface header.
31 #include "samplegeneratorjob.h"
32
33 // appleseed.renderer headers.
34 #include "renderer/global/globallogger.h"
35 #include "renderer/kernel/rendering/isamplegenerator.h"
36 #include "renderer/kernel/rendering/progressive/samplecounter.h"
37 #include "renderer/kernel/rendering/sampleaccumulationbuffer.h"
38
39 // appleseed.foundation headers.
40 #include "foundation/image/canvasproperties.h"
41 #include "foundation/image/image.h"
42 #include "foundation/math/scalar.h"
43 #include "foundation/platform/defaulttimers.h"
44 #include "foundation/utility/stopwatch.h"
45
46 // Standard headers.
47 #include <algorithm>
48 #include <cmath>
49
50 using namespace foundation;
51 using namespace std;
52
53 namespace renderer
54 {
55
56 //#define PRINT_DETAILED_PROGRESS
57
SampleGeneratorJob(SampleAccumulationBuffer & buffer,ISampleGenerator * sample_generator,SampleCounter & sample_counter,const Spectrum::Mode spectrum_mode,JobQueue & job_queue,const size_t job_index,IAbortSwitch & abort_switch)58 SampleGeneratorJob::SampleGeneratorJob(
59 SampleAccumulationBuffer& buffer,
60 ISampleGenerator* sample_generator,
61 SampleCounter& sample_counter,
62 const Spectrum::Mode spectrum_mode,
63 JobQueue& job_queue,
64 const size_t job_index,
65 IAbortSwitch& abort_switch)
66 : m_buffer(buffer)
67 , m_sample_generator(sample_generator)
68 , m_sample_counter(sample_counter)
69 , m_spectrum_mode(spectrum_mode)
70 , m_job_queue(job_queue)
71 , m_job_index(job_index)
72 , m_abort_switch(abort_switch)
73 {
74 }
75
76 namespace
77 {
78 // Duration of the interruptible phase.
79 // The higher this value, the more refined is the image but the lower is the interactivity.
80 const uint64 SamplesInUninterruptiblePhase = 32 * 32 * 2;
81
82 // Duration of the linear phase.
83 const uint64 SamplesInLinearPhase = 500 * 1000;
84
85 // Refinement rates of the image in the different phases.
86 // The higher the refinement rates, the lower the parallelism / CPU efficiency.
87 const uint64 SamplesPerJobInLinearPhase = 250;
88 const uint64 MaxSamplesPerJobInExponentialPhase = 250 * 1000;
89
90 // Shape of the curve in the exponential phase.
91 const double CurveExponentInExponentialPhase = 2.0;
92
93 // Constraints.
94 static_assert(
95 SamplesInUninterruptiblePhase >= SamplesPerJobInLinearPhase,
96 "In renderer::SampleGeneratorJob, the uninterruptible phase must have at least as many samples as the linear phase");
97 }
98
samples_to_samples_per_job(const uint64 samples)99 uint64 SampleGeneratorJob::samples_to_samples_per_job(const uint64 samples)
100 {
101 if (samples < SamplesInLinearPhase)
102 return SamplesPerJobInLinearPhase;
103
104 const double x = (samples - SamplesInLinearPhase) * 0.001;
105 const uint64 y = SamplesPerJobInLinearPhase + truncate<uint64>(pow(x, CurveExponentInExponentialPhase));
106
107 return min(y, MaxSamplesPerJobInExponentialPhase);
108 }
109
execute(const size_t thread_index)110 void SampleGeneratorJob::execute(const size_t thread_index)
111 {
112 // Initialize thread-local variables.
113 Spectrum::set_mode(m_spectrum_mode);
114
115 #ifdef PRINT_DETAILED_PROGRESS
116 Stopwatch<DefaultWallclockTimer> stopwatch(0);
117 stopwatch.measure();
118 const double t1 = stopwatch.get_seconds();
119 #endif
120
121 // We will base the number of samples to be rendered by this job on
122 // the number of samples already reserved (not necessarily rendered).
123 const uint64 current_sample_count = m_sample_counter.read();
124
125 // Reserve a number of samples to be rendered by this job.
126 const uint64 acquired_sample_count =
127 m_sample_counter.reserve(
128 samples_to_samples_per_job(current_sample_count));
129
130 // Terminate this job is there are no more samples to render.
131 if (acquired_sample_count == 0)
132 return;
133
134 // The first phase is uninterruptible in order to always have something to
135 // show during navigation. todo: the renderer freezes if it cannot generate
136 // samples during this phase; fix.
137 const bool abortable = current_sample_count > SamplesInUninterruptiblePhase;
138
139 // Render the samples and store them into the accumulation buffer.
140 if (abortable)
141 {
142 m_sample_generator->generate_samples(
143 static_cast<size_t>(acquired_sample_count),
144 m_buffer,
145 m_abort_switch);
146 }
147 else
148 {
149 AbortSwitch no_abort;
150 m_sample_generator->generate_samples(
151 static_cast<size_t>(acquired_sample_count),
152 m_buffer,
153 no_abort);
154 }
155
156 #ifdef PRINT_DETAILED_PROGRESS
157 stopwatch.measure();
158 const double t2 = stopwatch.get_seconds();
159
160 RENDERER_LOG_DEBUG(
161 "job " FMT_SIZE_T ", rendered " FMT_UINT64 " samples%s, in %s",
162 m_job_index,
163 acquired_sample_count,
164 abortable ? "" : " (non-abortable)",
165 pretty_time(t2 - t1).c_str());
166 #endif
167
168 // Reschedule this job.
169 if (!abortable || !m_abort_switch.is_aborted())
170 m_job_queue.schedule(this, false);
171 }
172
173 } // namespace renderer
174