integrator.cpp 31.2 KB
Newer Older
M
Matt Pharr 已提交
1 2 3 4
// pbrt is Copyright(c) 1998-2020 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
// The pbrt source code is licensed under the Apache License, Version 2.0.
// SPDX: Apache-2.0

5
#include <pbrt/wavefront/integrator.h>
M
Matt Pharr 已提交
6 7 8 9 10

#include <pbrt/base/medium.h>
#include <pbrt/cameras.h>
#include <pbrt/film.h>
#include <pbrt/filters.h>
11 12 13
#ifdef PBRT_BUILD_GPU_RENDERER
#include <pbrt/gpu/aggregate.h>
#include <pbrt/gpu/memory.h>
14
#endif  // PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
15 16 17 18 19 20 21 22 23 24 25
#include <pbrt/lights.h>
#include <pbrt/lightsamplers.h>
#include <pbrt/util/color.h>
#include <pbrt/util/colorspace.h>
#include <pbrt/util/display.h>
#include <pbrt/util/file.h>
#include <pbrt/util/image.h>
#include <pbrt/util/log.h>
#include <pbrt/util/print.h>
#include <pbrt/util/progressreporter.h>
#include <pbrt/util/pstd.h>
26
#include <pbrt/util/spectrum.h>
M
Matt Pharr 已提交
27
#include <pbrt/util/stats.h>
M
Matt Pharr 已提交
28
#include <pbrt/util/string.h>
M
Matt Pharr 已提交
29
#include <pbrt/util/taggedptr.h>
30
#include <pbrt/wavefront/aggregate.h>
M
Matt Pharr 已提交
31

32
#include <atomic>
M
Matt Pharr 已提交
33 34 35 36
#include <cstring>
#include <iostream>
#include <map>

37
#ifdef PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
38 39
#include <cuda.h>
#include <cuda_runtime.h>
40
#endif  // PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
41 42 43

namespace pbrt {

44
STAT_MEMORY_COUNTER("Memory/Wavefront integrator pixel state", pathIntegratorBytes);
M
Matt Pharr 已提交
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
static void updateMaterialNeeds(
    Material m, pstd::array<bool, Material::NumTags()> *haveBasicEvalMaterial,
    pstd::array<bool, Material::NumTags()> *haveUniversalEvalMaterial,
    bool *haveSubsurface) {
    if (!m)
        return;

    if (MixMaterial *mix = m.CastOrNullptr<MixMaterial>(); mix) {
        // This is a somewhat odd place for this check, but it's convenient...
        if (!m.CanEvaluateTextures(BasicTextureEvaluator()))
            ErrorExit("\"mix\" material has a texture that can't be evaluated with the "
                      "BasicTextureEvaluator, which is all that is currently supported "
                      "int the wavefront renderer--sorry! %s",
                      *mix);

        updateMaterialNeeds(mix->GetMaterial(0), haveBasicEvalMaterial,
                            haveUniversalEvalMaterial, haveSubsurface);
        updateMaterialNeeds(mix->GetMaterial(1), haveBasicEvalMaterial,
                            haveUniversalEvalMaterial, haveSubsurface);
        return;
    }

    *haveSubsurface |= m.HasSubsurfaceScattering();

    FloatTexture displace = m.GetDisplacement();
    if (m.CanEvaluateTextures(BasicTextureEvaluator()) &&
        (!displace || BasicTextureEvaluator().CanEvaluate({displace}, {})))
        (*haveBasicEvalMaterial)[m.Tag()] = true;
    else
        (*haveUniversalEvalMaterial)[m.Tag()] = true;
}
77
WavefrontPathIntegrator::WavefrontPathIntegrator(Allocator alloc, ParsedScene &scene) {
M
Matt Pharr 已提交
78
    // Allocate all of the data structures that represent the scene...
79
    std::map<std::string, Medium> media = scene.CreateMedia(alloc);
M
Matt Pharr 已提交
80 81 82 83 84 85 86 87 88 89

    haveMedia = false;
    // Check the shapes...
    for (const auto &shape : scene.shapes)
        if (!shape.insideMedium.empty() || !shape.outsideMedium.empty())
            haveMedia = true;
    for (const auto &shape : scene.animatedShapes)
        if (!shape.insideMedium.empty() || !shape.outsideMedium.empty())
            haveMedia = true;

90
    auto findMedium = [&](const std::string &s, const FileLoc *loc) -> Medium {
M
Matt Pharr 已提交
91 92 93 94 95 96 97 98 99 100
        if (s.empty())
            return nullptr;

        auto iter = media.find(s);
        if (iter == media.end())
            ErrorExit(loc, "%s: medium not defined", s);
        haveMedia = true;
        return iter->second;
    };

101 102
    filter = Filter::Create(scene.filter.name, scene.filter.parameters, &scene.filter.loc,
                            alloc);
M
Matt Pharr 已提交
103

104 105
    Float exposureTime = scene.camera.parameters.GetOneFloat("shutterclose", 1.f) -
                         scene.camera.parameters.GetOneFloat("shutteropen", 0.f);
106 107 108 109 110
    if (exposureTime <= 0)
        ErrorExit(&scene.camera.loc,
                  "The specified camera shutter times imply that the shutter "
                  "does not open.  A black image will result.");

111 112
    film = Film::Create(scene.film.name, scene.film.parameters, exposureTime, filter,
                        &scene.film.loc, alloc);
M
Matt Pharr 已提交
113 114
    initializeVisibleSurface = film.UsesVisibleSurface();

115 116
    sampler = Sampler::Create(scene.sampler.name, scene.sampler.parameters,
                              film.FullResolution(), &scene.sampler.loc, alloc);
117
    samplesPerPixel = sampler.SamplesPerPixel();
M
Matt Pharr 已提交
118

119 120 121
    Medium cameraMedium = findMedium(scene.camera.medium, &scene.camera.loc);
    camera = Camera::Create(scene.camera.name, scene.camera.parameters, cameraMedium,
                            scene.camera.cameraTransform, film, &scene.camera.loc, alloc);
M
Matt Pharr 已提交
122

123 124
    // Textures
    LOG_VERBOSE("Starting to create textures");
125
    NamedTextures textures = scene.CreateTextures(alloc, Options->useGPU);
126 127
    LOG_VERBOSE("Done creating textures");

128
    pstd::vector<Light> allLights;
M
Matt Pharr 已提交
129

130
    infiniteLights = alloc.new_object<pstd::vector<Light>>(alloc);
M
Matt Pharr 已提交
131
    for (const auto &light : scene.lights) {
132
        Medium outsideMedium = findMedium(light.medium, &light.loc);
M
Matt Pharr 已提交
133 134 135 136
        if (light.renderFromObject.IsAnimated())
            Warning(&light.loc,
                    "Animated lights aren't supported. Using the start transform.");

137
        Light l = Light::Create(
M
Matt Pharr 已提交
138 139 140 141
            light.name, light.parameters, light.renderFromObject.startTransform,
            scene.camera.cameraTransform, outsideMedium, &light.loc, alloc);

        if (l.Is<UniformInfiniteLight>() || l.Is<ImageInfiniteLight>() ||
142
            l.Is<PortalImageInfiniteLight>())
143
            infiniteLights->push_back(l);
M
Matt Pharr 已提交
144 145 146 147 148

        allLights.push_back(l);
    }

    // Area lights...
149
    std::map<int, pstd::vector<Light> *> shapeIndexToAreaLights;
M
Matt Pharr 已提交
150 151 152 153
    for (size_t i = 0; i < scene.shapes.size(); ++i) {
        const auto &shape = scene.shapes[i];
        if (shape.lightIndex == -1)
            continue;
154 155

        auto isInterface = [&]() {
156
            std::string materialName;
157
            if (shape.materialIndex != -1)
158
                materialName = scene.materials[shape.materialIndex].name;
159 160 161 162
            else {
                for (auto iter = scene.namedMaterials.begin();
                     iter != scene.namedMaterials.end(); ++iter)
                    if (iter->first == shape.materialName) {
163
                        materialName = iter->second.parameters.GetOneString("type", "");
164 165 166
                        break;
                    }
            }
167 168
            return (materialName == "interface" || materialName == "none" ||
                    materialName.empty());
169 170 171 172
        };
        if (isInterface())
            continue;

M
Matt Pharr 已提交
173 174 175 176
        CHECK_LT(shape.lightIndex, scene.areaLights.size());
        const auto &areaLightEntity = scene.areaLights[shape.lightIndex];
        AnimatedTransform renderFromLight(*shape.renderFromObject);

177
        pstd::vector<Shape> shapes =
178 179
            Shape::Create(shape.name, shape.renderFromObject, shape.objectFromRender,
                          shape.reverseOrientation, shape.parameters, &shape.loc, alloc);
M
Matt Pharr 已提交
180

181
        if (shapes.empty())
M
Matt Pharr 已提交
182 183
            continue;

184
        Medium outsideMedium = findMedium(shape.outsideMedium, &shape.loc);
M
Matt Pharr 已提交
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
        FloatTexture alphaTex;
        std::string alphaTexName = shape.parameters.GetTexture("alpha");
        if (!alphaTexName.empty()) {
            if (textures.floatTextures.find(alphaTexName) !=
                textures.floatTextures.end()) {
                alphaTex = textures.floatTextures[alphaTexName];
                if (!BasicTextureEvaluator().CanEvaluate({alphaTex}, {}))
                    // A warning will be issued elsewhere...
                    alphaTex = nullptr;
            } else
                ErrorExit(&shape.loc,
                          "%s: couldn't find float texture for \"alpha\" parameter.",
                          alphaTexName);
        } else if (Float alpha = shape.parameters.GetOneFloat("alpha", 1.f); alpha < 1.f)
            alphaTex = alloc.new_object<FloatConstantTexture>(alpha);

202 203
        pstd::vector<Light> *lightsForShape =
            alloc.new_object<pstd::vector<Light>>(alloc);
204
        for (Shape sh : shapes) {
M
Matt Pharr 已提交
205
            if (renderFromLight.IsAnimated())
206
                ErrorExit(&shape.loc, "Animated lights are not supported.");
M
Matt Pharr 已提交
207 208
            DiffuseAreaLight *area = DiffuseAreaLight::Create(
                renderFromLight.startTransform, outsideMedium, areaLightEntity.parameters,
209 210
                areaLightEntity.parameters.ColorSpace(), &areaLightEntity.loc, alloc, sh,
                alphaTex);
M
Matt Pharr 已提交
211 212 213 214 215 216
            allLights.push_back(area);
            lightsForShape->push_back(area);
        }
        shapeIndexToAreaLights[i] = lightsForShape;
    }

217 218 219 220 221
    LOG_VERBOSE("Starting to create materials");
    std::map<std::string, pbrt::Material> namedMaterials;
    std::vector<pbrt::Material> materials;
    scene.CreateMaterials(textures, alloc, &namedMaterials, &materials);

M
Matt Pharr 已提交
222 223 224
    haveBasicEvalMaterial.fill(false);
    haveUniversalEvalMaterial.fill(false);
    haveSubsurface = false;
225 226 227 228 229 230 231 232
    for (Material m : materials)
        updateMaterialNeeds(m, &haveBasicEvalMaterial, &haveUniversalEvalMaterial,
                            &haveSubsurface);
    for (const auto &m : namedMaterials)
        updateMaterialNeeds(m.second, &haveBasicEvalMaterial, &haveUniversalEvalMaterial,
                            &haveSubsurface);
    LOG_VERBOSE("Finished creating materials");

233 234
    if (Options->useGPU) {
#ifdef PBRT_BUILD_GPU_RENDERER
235
        aggregate = new OptiXAggregate(scene, alloc, textures, shapeIndexToAreaLights,
236
                                       media, namedMaterials, materials);
237 238 239 240
#else
        LOG_FATAL("Options->useGPU was set without PBRT_BUILD_GPU_RENDERER enabled");
#endif
    } else
241
        aggregate = new CPUAggregate(scene, alloc, textures, shapeIndexToAreaLights,
242
                                     media, namedMaterials, materials);
M
Matt Pharr 已提交
243 244

    // Preprocess the light sources
245
    for (Light light : allLights)
246
        light.Preprocess(aggregate->Bounds());
M
Matt Pharr 已提交
247 248 249 250 251 252 253 254 255 256 257

    bool haveLights = !allLights.empty();
    for (const auto &m : media)
        haveLights |= m.second.IsEmissive();
    if (!haveLights)
        ErrorExit("No light sources specified");

    std::string lightSamplerName =
        scene.integrator.parameters.GetOneString("lightsampler", "bvh");
    if (allLights.size() == 1)
        lightSamplerName = "uniform";
258
    lightSampler = LightSampler::Create(lightSamplerName, allLights, alloc);
M
Matt Pharr 已提交
259

260
    if (scene.integrator.name != "path" && scene.integrator.name != "volpath")
261
        Warning(&scene.integrator.loc,
262
                "Ignoring specified integrator \"%s\": the wavefront integrator "
263 264
                "always uses a \"volpath\" integrator.",
                scene.integrator.name);
265

M
Matt Pharr 已提交
266 267 268 269
    // Integrator parameters
    regularize = scene.integrator.parameters.GetOneBool("regularize", false);
    maxDepth = scene.integrator.parameters.GetOneInt("maxdepth", 5);

270 271
    // Warn about unsupported stuff...
    if (Options->forceDiffuse)
272
        Warning("The wavefront integrator does not support --force-diffuse.");
273
    if (Options->writePartialImages)
274
        Warning("The wavefront integrator does not support --write-partial-images.");
275
    if (Options->recordPixelStatistics)
276
        Warning("The wavefront integrator does not support --pixelstats.");
277
    if (!Options->mseReferenceImage.empty())
278
        Warning("The wavefront integrator does not support --mse-reference-image.");
279
    if (!Options->mseReferenceOutput.empty())
280
        Warning("The wavefront integrator does not support --mse-reference-out.");
281

282 283
        ///////////////////////////////////////////////////////////////////////////
        // Allocate storage for all of the queues/buffers...
M
Matt Pharr 已提交
284

285
#ifdef PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
286 287
    CUDATrackedMemoryResource *mr =
        dynamic_cast<CUDATrackedMemoryResource *>(gpuMemoryAllocator.resource());
M
Matt Pharr 已提交
288
    CHECK(mr);
M
Matt Pharr 已提交
289
    size_t startSize = mr->BytesAllocated();
290
#endif  // PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
291

292
    // Compute number of scanlines to render per pass
M
Matt Pharr 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    Vector2i resolution = film.PixelBounds().Diagonal();
    // TODO: make this configurable. Base it on the amount of GPU memory?
    int maxSamples = 1024 * 1024;
    scanlinesPerPass = std::max(1, maxSamples / resolution.x);
    int nPasses = (resolution.y + scanlinesPerPass - 1) / scanlinesPerPass;
    scanlinesPerPass = (resolution.y + nPasses - 1) / nPasses;
    maxQueueSize = resolution.x * scanlinesPerPass;
    LOG_VERBOSE("Will render in %d passes %d scanlines per pass\n", nPasses,
                scanlinesPerPass);

    pixelSampleState = SOA<PixelSampleState>(maxQueueSize, alloc);

    rayQueues[0] = alloc.new_object<RayQueue>(maxQueueSize, alloc);
    rayQueues[1] = alloc.new_object<RayQueue>(maxQueueSize, alloc);

    shadowRayQueue = alloc.new_object<ShadowRayQueue>(maxQueueSize, alloc);

    if (haveSubsurface) {
        bssrdfEvalQueue =
            alloc.new_object<GetBSSRDFAndProbeRayQueue>(maxQueueSize, alloc);
        subsurfaceScatterQueue =
            alloc.new_object<SubsurfaceScatterQueue>(maxQueueSize, alloc);
    }

317
    if (infiniteLights->size())
M
Matt Pharr 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330
        escapedRayQueue = alloc.new_object<EscapedRayQueue>(maxQueueSize, alloc);
    hitAreaLightQueue = alloc.new_object<HitAreaLightQueue>(maxQueueSize, alloc);

    basicEvalMaterialQueue = alloc.new_object<MaterialEvalQueue>(
        maxQueueSize, alloc,
        pstd::MakeConstSpan(&haveBasicEvalMaterial[1], haveBasicEvalMaterial.size() - 1));
    universalEvalMaterialQueue = alloc.new_object<MaterialEvalQueue>(
        maxQueueSize, alloc,
        pstd::MakeConstSpan(&haveUniversalEvalMaterial[1],
                            haveUniversalEvalMaterial.size() - 1));

    if (haveMedia) {
        mediumSampleQueue = alloc.new_object<MediumSampleQueue>(maxQueueSize, alloc);
331 332 333 334 335 336 337 338

        // TODO: in the presence of multiple PhaseFunction implementations,
        // it could be worthwhile to see which are present in the scene and
        // then initialize havePhase accordingly...
        pstd::array<bool, PhaseFunction::NumTags()> havePhase;
        havePhase.fill(true);
        mediumScatterQueue =
            alloc.new_object<MediumScatterQueue>(maxQueueSize, alloc, havePhase);
M
Matt Pharr 已提交
339 340 341 342
    }

    stats = alloc.new_object<Stats>(maxDepth, alloc);

343
#ifdef PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
344 345
    size_t endSize = mr->BytesAllocated();
    pathIntegratorBytes += endSize - startSize;
346
#endif  // PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
347 348
}

349 350
// WavefrontPathIntegrator Method Definitions
Float WavefrontPathIntegrator::Render() {
351 352 353
    Bounds2i pixelBounds = film.PixelBounds();
    Vector2i resolution = pixelBounds.Diagonal();
    Timer timer;
354
    // Prefetch allocations to GPU memory
355 356 357 358 359 360
#ifdef PBRT_BUILD_GPU_RENDERER
    if (Options->useGPU) {
        int deviceIndex;
        CUDA_CHECK(cudaGetDevice(&deviceIndex));
        int hasConcurrentManagedAccess;
        CUDA_CHECK(cudaDeviceGetAttribute(&hasConcurrentManagedAccess,
361 362
                                          cudaDevAttrConcurrentManagedAccess,
                                          deviceIndex));
363 364 365 366 367 368 369 370 371 372

        // Copy all of the scene data structures over to GPU memory.  This
        // ensures that there isn't a big performance hitch for the first batch
        // of rays as that stuff is copied over on demand.
        if (hasConcurrentManagedAccess) {
            // Set things up so that we can still have read from the
            // WavefrontPathIntegrator struct on the CPU without hurting
            // performance. (This makes it possible to use the values of things
            // like WavefrontPathIntegrator::haveSubsurface to conditionally launch
            // kernels according to what's in the scene...)
373 374
            CUDA_CHECK(cudaMemAdvise(this, sizeof(*this), cudaMemAdviseSetReadMostly,
                                     /* ignored argument */ 0));
375 376 377 378 379 380 381 382
            CUDA_CHECK(cudaMemAdvise(this, sizeof(*this),
                                     cudaMemAdviseSetPreferredLocation, deviceIndex));

            // Copy all of the scene data structures over to GPU memory.  This
            // ensures that there isn't a big performance hitch for the first batch
            // of rays as that stuff is copied over on demand.
            CUDATrackedMemoryResource *mr =
                dynamic_cast<CUDATrackedMemoryResource *>(gpuMemoryAllocator.resource());
M
Matt Pharr 已提交
383
            CHECK(mr);
384 385 386 387 388 389 390
            mr->PrefetchToGPU();
        } else {
            // TODO: on systems with basic unified memory, just launching a
            // kernel should cause everything to be copied over. Is an empty
            // kernel sufficient?
        }
    }
391
#endif  // PBRT_BUILD_GPU_RENDERER
392

393
    // Launch thread to copy image for display server, if enabled
M
Matt Pharr 已提交
394 395 396 397 398
    RGB *displayRGB = nullptr, *displayRGBHost = nullptr;
    std::atomic<bool> exitCopyThread{false};
    std::thread copyThread;

    if (!Options->displayServer.empty()) {
399 400 401 402
#ifdef PBRT_BUILD_GPU_RENDERER
        if (Options->useGPU) {
            // Allocate staging memory on the GPU to store the current WIP
            // image.
403 404 405 406
            CUDA_CHECK(
                cudaMalloc(&displayRGB, resolution.x * resolution.y * sizeof(RGB)));
            CUDA_CHECK(
                cudaMemset(displayRGB, 0, resolution.x * resolution.y * sizeof(RGB)));
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

            // Host-side memory for the WIP Image.  We'll just let this leak so
            // that the lambda passed to DisplayDynamic below doesn't access
            // freed memory after Render() returns...
            displayRGBHost = new RGB[resolution.x * resolution.y];

            copyThread = std::thread([&]() {
                GPURegisterThread("DISPLAY_SERVER_COPY_THREAD");

                // Copy back to the CPU using a separate stream so that we can
                // periodically but asynchronously pick up the latest results
                // from the GPU.
                cudaStream_t memcpyStream;
                CUDA_CHECK(cudaStreamCreate(&memcpyStream));
                GPUNameStream(memcpyStream, "DISPLAY_SERVER_COPY_STREAM");

                // Copy back to the host from the GPU buffer, without any
                // synthronization.
                while (!exitCopyThread) {
                    CUDA_CHECK(cudaMemcpyAsync(displayRGBHost, displayRGB,
                                               resolution.x * resolution.y * sizeof(RGB),
                                               cudaMemcpyDeviceToHost, memcpyStream));
                    std::this_thread::sleep_for(std::chrono::milliseconds(50));

                    CUDA_CHECK(cudaStreamSynchronize(memcpyStream));
                }
M
Matt Pharr 已提交
433

434 435 436 437 438 439 440 441 442 443
                // Copy one more time to get the final image before exiting.
                CUDA_CHECK(cudaMemcpy(displayRGBHost, displayRGB,
                                      resolution.x * resolution.y * sizeof(RGB),
                                      cudaMemcpyDeviceToHost));
                CUDA_CHECK(cudaDeviceSynchronize());
            });

            // Now on the CPU side, give the display system a lambda that
            // copies values from |displayRGBHost| into its buffers used for
            // sending messages to the display program (i.e., tev).
444 445
            DisplayDynamic(film.GetFilename(), {resolution.x, resolution.y},
                           {"R", "G", "B"},
446
                           [resolution, displayRGBHost](
447
                               Bounds2i b, pstd::span<pstd::span<Float>> displayValue) {
448 449 450 451 452 453 454 455 456 457
                               int index = 0;
                               for (Point2i p : b) {
                                   RGB rgb = displayRGBHost[p.x + p.y * resolution.x];
                                   displayValue[0][index] = rgb.r;
                                   displayValue[1][index] = rgb.g;
                                   displayValue[2][index] = rgb.b;
                                   ++index;
                               }
                           });
        } else
458 459 460 461 462 463 464 465 466 467 468 469 470 471
#endif  // PBRT_BUILD_GPU_RENDERER
            DisplayDynamic(
                film.GetFilename(), Point2i(pixelBounds.Diagonal()), {"R", "G", "B"},
                [pixelBounds, this](Bounds2i b,
                                    pstd::span<pstd::span<Float>> displayValue) {
                    int index = 0;
                    for (Point2i p : b) {
                        RGB rgb =
                            film.GetPixelRGB(pixelBounds.pMin + p, 1.f /* splat scale */);
                        for (int c = 0; c < 3; ++c)
                            displayValue[c][index] = rgb[c];
                        ++index;
                    }
                });
M
Matt Pharr 已提交
472 473
    }

474
    // Loop over sample indices and evaluate pixel samples
475
    int firstSampleIndex = 0, lastSampleIndex = samplesPerPixel;
476
    // Update sample index range based on debug start, if provided
M
Matt Pharr 已提交
477
    if (!Options->debugStart.empty()) {
478
        std::vector<int> values = SplitStringToInts(Options->debugStart, ',');
479 480
        if (values.size() != 1 && values.size() != 2)
            ErrorExit("Expected either one or two integer values for --debugstart.");
481

482
        firstSampleIndex = values[0];
483 484 485 486
        if (values.size() == 2)
            lastSampleIndex = firstSampleIndex + values[1];
        else
            lastSampleIndex = firstSampleIndex + 1;
M
Matt Pharr 已提交
487 488
    }

489
    ProgressReporter progress(lastSampleIndex - firstSampleIndex, "Rendering",
490
                              Options->quiet, Options->useGPU);
491 492
    for (int sampleIndex = firstSampleIndex; sampleIndex < lastSampleIndex;
         ++sampleIndex) {
493
        CheckCallbackScope _([&]() {
494
            return StringPrintf("Wavefront rendering failed at sample %d. Debug with "
495 496 497 498
                                "\"--debugstart %d\"\n",
                                sampleIndex, sampleIndex);
        });

499
        // Render image for sample _sampleIndex_
M
Matt Pharr 已提交
500
        LOG_VERBOSE("Starting to submit work for sample %d", sampleIndex);
M
Matt Pharr 已提交
501 502
        for (int y0 = pixelBounds.pMin.y; y0 < pixelBounds.pMax.y;
             y0 += scanlinesPerPass) {
503
            // Generate camera rays for current scanline range
504
            RayQueue *cameraRayQueue = CurrentRayQueue(0);
505 506
            Do(
                "Reset ray queue", PBRT_CPU_GPU_LAMBDA() {
507
                    PBRT_DBG("Starting scanlines at y0 = %d, sample %d / %d\n", y0,
508
                             sampleIndex, samplesPerPixel);
509 510
                    cameraRayQueue->Reset();
                });
M
Matt Pharr 已提交
511
            GenerateCameraRays(y0, sampleIndex);
512
            Do(
513
                "Update camera ray stats",
514
                PBRT_CPU_GPU_LAMBDA() { stats->cameraRays += cameraRayQueue->Size(); });
M
Matt Pharr 已提交
515

M
Matt Pharr 已提交
516
            // Trace rays and estimate radiance up to maximum ray depth
517
            for (int wavefrontDepth = 0; true; ++wavefrontDepth) {
518
                // Reset queues before tracing rays
519
                RayQueue *nextQueue = NextRayQueue(wavefrontDepth);
520 521
                Do(
                    "Reset queues before tracing rays", PBRT_CPU_GPU_LAMBDA() {
M
Matt Pharr 已提交
522
                        nextQueue->Reset();
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
                        // Reset queues before tracing next batch of rays
                        if (mediumSampleQueue)
                            mediumSampleQueue->Reset();
                        if (mediumScatterQueue)
                            mediumScatterQueue->Reset();

                        if (escapedRayQueue)
                            escapedRayQueue->Reset();
                        hitAreaLightQueue->Reset();

                        basicEvalMaterialQueue->Reset();
                        universalEvalMaterialQueue->Reset();

                        if (bssrdfEvalQueue)
                            bssrdfEvalQueue->Reset();
                        if (subsurfaceScatterQueue)
                            subsurfaceScatterQueue->Reset();
                    });

                // Follow active ray paths and accumulate radiance estimates
543
                GenerateRaySamples(wavefrontDepth, sampleIndex);
544

545
                // Find closest intersections along active rays
546
                aggregate->IntersectClosest(
547 548 549
                    maxQueueSize, CurrentRayQueue(wavefrontDepth), escapedRayQueue,
                    hitAreaLightQueue, basicEvalMaterialQueue, universalEvalMaterialQueue,
                    mediumSampleQueue, NextRayQueue(wavefrontDepth));
M
Matt Pharr 已提交
550

551
                if (wavefrontDepth > 0) {
552
                    // As above, with the indexing...
553
                    RayQueue *statsQueue = CurrentRayQueue(wavefrontDepth);
554 555
                    Do(
                        "Update indirect ray stats", PBRT_CPU_GPU_LAMBDA() {
556
                            stats->indirectRays[wavefrontDepth] += statsQueue->Size();
557
                        });
558
                }
559 560 561 562 563 564 565

                SampleMediumInteraction(wavefrontDepth);

                HandleEscapedRays();

                HandleEmissiveIntersection();

566
                if (wavefrontDepth == maxDepth)
M
Matt Pharr 已提交
567
                    break;
568

569
                EvaluateMaterialsAndBSDFs(wavefrontDepth);
570

571
                // Do immediately so that we have space for shadow rays for subsurface..
572
                TraceShadowRays(wavefrontDepth);
573 574

                SampleSubsurface(wavefrontDepth);
M
Matt Pharr 已提交
575
            }
M
Matt Pharr 已提交
576

M
Matt Pharr 已提交
577
            UpdateFilm();
578
            // Copy updated film pixels to buffer for display
579
#ifdef PBRT_BUILD_GPU_RENDERER
580
            if (Options->useGPU && !Options->displayServer.empty())
581 582
                GPUParallelFor(
                    "Update Display RGB Buffer", maxQueueSize,
583
                    PBRT_CPU_GPU_LAMBDA(int pixelIndex) {
584 585 586 587 588 589 590
                        Point2i pPixel = pixelSampleState.pPixel[pixelIndex];
                        if (!InsideExclusive(pPixel, film.PixelBounds()))
                            return;

                        Point2i p(pPixel - film.PixelBounds().pMin);
                        displayRGB[p.x + p.y * resolution.x] = film.GetPixelRGB(pPixel);
                    });
591
#endif  //  PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
592 593 594 595 596
        }

        progress.Update();
    }
    progress.Done();
597

598 599 600
#ifdef PBRT_BUILD_GPU_RENDERER
    if (Options->useGPU)
        GPUWait();
601
#endif  // PBRT_BUILD_GPU_RENDERER
602
    Float seconds = timer.ElapsedSeconds();
603
    // Shut down display server thread, if active
604 605 606 607 608 609 610 611
#ifdef PBRT_BUILD_GPU_RENDERER
    if (Options->useGPU) {
        // Wait until rendering is all done before we start to shut down the
        // display stuff..
        if (!Options->displayServer.empty()) {
            exitCopyThread = true;
            copyThread.join();
        }
612

613 614 615
        // Another synchronization to make sure no kernels are running on the
        // GPU so that we can safely access unified memory from the CPU.
        GPUWait();
M
Matt Pharr 已提交
616
    }
617
#endif  // PBRT_BUILD_GPU_RENDERER
M
Matt Pharr 已提交
618

619
    return seconds;
M
Matt Pharr 已提交
620 621
}

622
void WavefrontPathIntegrator::HandleEscapedRays() {
623 624
    if (!escapedRayQueue)
        return;
625 626
    ForAllQueued(
        "Handle escaped rays", escapedRayQueue, maxQueueSize,
627
        PBRT_CPU_GPU_LAMBDA(const EscapedRayWorkItem w) {
628
            // Compute weighted radiance for escaped ray
629
            SampledSpectrum L(0.f);
630
            for (const auto &light : *infiniteLights) {
631 632
                if (SampledSpectrum Le = light.Le(Ray(w.rayo, w.rayd), w.lambda); Le) {
                    // Compute path radiance contribution from infinite light
633 634 635
                    PBRT_DBG("L %f %f %f %f T_hat %f %f %f %f Le %f %f %f %f", L[0], L[1],
                             L[2], L[3], w.T_hat[0], w.T_hat[1], w.T_hat[2], w.T_hat[3],
                             Le[0], Le[1], Le[2], Le[3]);
636
                    PBRT_DBG("pdf uni %f %f %f %f pdf nee %f %f %f %f", w.uniPathPDF[0],
637 638 639
                             w.uniPathPDF[1], w.uniPathPDF[2], w.uniPathPDF[3],
                             w.lightPathPDF[0], w.lightPathPDF[1], w.lightPathPDF[2],
                             w.lightPathPDF[3]);
640

641
                    if (w.depth == 0 || w.specularBounce) {
642 643 644 645 646 647 648 649 650 651 652
                        L += w.T_hat * Le / w.uniPathPDF.Average();
                    } else {
                        // Compute MIS-weighted radiance contribution from infinite light
                        LightSampleContext ctx = w.prevIntrCtx;
                        Float lightChoicePDF = lightSampler.PDF(ctx, light);
                        SampledSpectrum lightPathPDF =
                            w.lightPathPDF * lightChoicePDF *
                            light.PDF_Li(ctx, w.rayd, LightSamplingMode::WithMIS);
                        L += w.T_hat * Le / (w.uniPathPDF + lightPathPDF).Average();
                    }
                }
653
            }
654 655

            // Update pixel radiance if ray's radiance is non-zero
656
            if (L) {
657 658
                PBRT_DBG("Added L %f %f %f %f for escaped ray pixel index %d\n", L[0],
                         L[1], L[2], L[3], w.pixelIndex);
659

660 661 662
                L += pixelSampleState.L[w.pixelIndex];
                pixelSampleState.L[w.pixelIndex] = L;
            }
663
        });
M
Matt Pharr 已提交
664 665
}

666
void WavefrontPathIntegrator::HandleEmissiveIntersection() {
M
Matt Pharr 已提交
667 668
    ForAllQueued(
        "Handle emitters hit by indirect rays", hitAreaLightQueue, maxQueueSize,
669
        PBRT_CPU_GPU_LAMBDA(const HitAreaLightWorkItem w) {
670
            // Find emitted radiance from surface that ray hit
M
Matt Pharr 已提交
671
            SampledSpectrum Le = w.areaLight.L(w.p, w.n, w.uv, w.wo, w.lambda);
M
Matt Pharr 已提交
672 673
            if (!Le)
                return;
674
            PBRT_DBG("Got Le %f %f %f %f from hit area light at depth %d\n", Le[0], Le[1],
675
                     Le[2], Le[3], w.depth);
M
Matt Pharr 已提交
676

677
            // Compute area light's weighted radiance contribution to the path
678
            SampledSpectrum L(0.f);
679
            if (w.depth == 0 || w.isSpecularBounce) {
680
                L = w.T_hat * Le / w.uniPathPDF.Average();
M
Matt Pharr 已提交
681
            } else {
682
                // Compute MIS-weighted radiance contribution from area light
683 684
                Vector3f wi = -w.wo;
                LightSampleContext ctx = w.prevIntrCtx;
685
                Float lightChoicePDF = lightSampler.PDF(ctx, w.areaLight);
M
Matt Pharr 已提交
686
                Float lightPDF = lightChoicePDF *
687
                                 w.areaLight.PDF_Li(ctx, wi, LightSamplingMode::WithMIS);
M
Matt Pharr 已提交
688

689 690 691
                SampledSpectrum uniPathPDF = w.uniPathPDF;
                SampledSpectrum lightPathPDF = w.lightPathPDF * lightPDF;
                L = w.T_hat * Le / (uniPathPDF + lightPathPDF).Average();
M
Matt Pharr 已提交
692 693
            }

694
            PBRT_DBG("Added L %f %f %f %f for pixel index %d\n", L[0], L[1], L[2], L[3],
695
                     w.pixelIndex);
696

697
            // Update _L_ in _PixelSampleState_ for area light's radiance
698 699
            L += pixelSampleState.L[w.pixelIndex];
            pixelSampleState.L[w.pixelIndex] = L;
M
Matt Pharr 已提交
700 701 702
        });
}

703
void WavefrontPathIntegrator::TraceShadowRays(int wavefrontDepth) {
704
    if (haveMedia)
705
        aggregate->IntersectShadowTr(maxQueueSize, shadowRayQueue, &pixelSampleState);
706
    else
707
        aggregate->IntersectShadow(maxQueueSize, shadowRayQueue, &pixelSampleState);
708
    // Reset shadow ray queue
709 710
    Do(
        "Reset shadowRayQueue", PBRT_CPU_GPU_LAMBDA() {
711
            stats->shadowRays[wavefrontDepth] += shadowRayQueue->Size();
712 713 714 715
            shadowRayQueue->Reset();
        });
}

716
WavefrontPathIntegrator::Stats::Stats(int maxDepth, Allocator alloc)
M
Matt Pharr 已提交
717 718
    : indirectRays(maxDepth + 1, alloc), shadowRays(maxDepth, alloc) {}

719
std::string WavefrontPathIntegrator::Stats::Print() const {
M
Matt Pharr 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732
    std::string s;
    s += StringPrintf("    %-42s               %12" PRIu64 "\n", "Camera rays",
                      cameraRays);
    for (int i = 1; i < indirectRays.size(); ++i)
        s += StringPrintf("    %-42s               %12" PRIu64 "\n",
                          StringPrintf("Indirect rays, depth %-3d", i), indirectRays[i]);
    for (int i = 0; i < shadowRays.size(); ++i)
        s += StringPrintf("    %-42s               %12" PRIu64 "\n",
                          StringPrintf("Shadow rays, depth %-3d", i), shadowRays[i]);
    return s;
}

}  // namespace pbrt