Files
test/intern/cycles/kernel/sample/sobol_burley.h
Nathan Vegdahl d9192aaa6d Cycles: limit the internal sample index of Sobol-Burley for performance
This is done based on the render sample count so that it doesn't impact
sampling quality. It's similar in spirit to the adaptive table size in D16561,
but in this case for performance rather than memory usage.

Differential Revision: https://developer.blender.org/D16726
2022-12-14 17:39:13 +01:00

202 lines
7.4 KiB
C

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
/*
* A shuffled, Owen-scrambled Sobol sampler, implemented with the
* techniques from the paper "Practical Hash-based Owen Scrambling"
* by Brent Burley, 2020, Journal of Computer Graphics Techniques.
*
* Note that unlike a standard high-dimensional Sobol sequence, this
* Sobol sampler uses padding to achieve higher dimensions, as described
* in Burley's paper.
*/
#pragma once
#include "kernel/sample/util.h"
#include "util/hash.h"
#include "util/math.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
/*
* Computes a single dimension of a sample from an Owen-scrambled
* Sobol sequence. This is used in the main sampling functions,
* sobol_burley_sample_#D(), below.
*
* - rev_bit_index: the sample index, with reversed order bits.
* - dimension: the sample dimension.
* - scramble_seed: the Owen scrambling seed.
*
* Note that the seed must be well randomized before being
* passed to this function.
*/
ccl_device_forceinline float sobol_burley(uint rev_bit_index,
const uint dimension,
const uint scramble_seed)
{
uint result = 0;
if (dimension == 0) {
/* Fast-path for dimension 0, which is just Van der corput.
* This makes a notable difference in performance since we reuse
* dimensions for padding, and dimension 0 is reused the most. */
result = reverse_integer_bits(rev_bit_index);
}
else {
uint i = 0;
while (rev_bit_index != 0) {
uint j = count_leading_zeros(rev_bit_index);
result ^= sobol_burley_table[dimension][i + j];
i += j + 1;
/* We can't do "<<= j + 1" because that can overflow the shift
* operator, which doesn't do what we need on at least x86. */
rev_bit_index <<= j;
rev_bit_index <<= 1;
}
}
/* Apply Owen scrambling. */
result = reverse_integer_bits(reversed_bit_owen(result, scramble_seed));
return uint_to_float_excl(result);
}
/*
* NOTE: the functions below intentionally produce samples that are
* uncorrelated between functions. For example, a 1D sample and 2D
* sample produced with the same index, dimension, and seed are
* uncorrelated with each other. This allows more care-free usage
* of the functions together, without having to worry about
* e.g. 1D and 2D samples being accidentally correlated with each
* other.
*/
/*
* Computes a 1D Owen-scrambled and shuffled Sobol sample.
*
* `index` is the index of the sample in the sequence.
*
* `dimension` is which dimensions of the sample you want to fetch. Note
* that different 1D dimensions are uncorrelated. For samples with > 1D
* stratification, use the multi-dimensional sampling methods below.
*
* `seed`: different seeds produce statistically independent,
* uncorrelated sequences.
*
* `shuffled_index_mask` limits the sample sequence length, improving
* performance. It must be a string of binary 1 bits followed by a
* string of binary 0 bits (e.g. 0xffff0000) for the sampler to operate
* correctly. In general, `reverse_integer_bits(shuffled_index_mask)`
* should be >= the maximum number of samples expected to be taken. A safe
* default (but least performant) is 0xffffffff, for maximum sequence
* length.
*/
ccl_device float sobol_burley_sample_1D(uint index,
uint const dimension,
uint seed,
uint shuffled_index_mask)
{
/* Include the dimension in the seed, so we get decorrelated
* sequences for different dimensions via shuffling. */
seed ^= hash_hp_uint(dimension);
/* Shuffle and mask. The masking is just for better
* performance at low sample counts. */
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xbff95bfe);
index &= shuffled_index_mask;
return sobol_burley(index, 0, seed ^ 0x635c77bd);
}
/*
* Computes a 2D Owen-scrambled and shuffled Sobol sample.
*
* `dimension_set` is which two dimensions of the sample you want to
* fetch. For example, 0 is the first two, 1 is the second two, etc.
* The dimensions within a single set are stratified, but different sets
* are uncorrelated.
*
* See sobol_burley_sample_1D for further usage details.
*/
ccl_device float2 sobol_burley_sample_2D(uint index,
const uint dimension_set,
uint seed,
uint shuffled_index_mask)
{
/* Include the dimension set in the seed, so we get decorrelated
* sequences for different dimension sets via shuffling. */
seed ^= hash_hp_uint(dimension_set);
/* Shuffle and mask. The masking is just for better
* performance at low sample counts. */
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xf8ade99a);
index &= shuffled_index_mask;
return make_float2(sobol_burley(index, 0, seed ^ 0xe0aaaf76),
sobol_burley(index, 1, seed ^ 0x94964d4e));
}
/*
* Computes a 3D Owen-scrambled and shuffled Sobol sample.
*
* `dimension_set` is which three dimensions of the sample you want to
* fetch. For example, 0 is the first three, 1 is the second three, etc.
* The dimensions within a single set are stratified, but different sets
* are uncorrelated.
*
* See sobol_burley_sample_1D for further usage details.
*/
ccl_device float3 sobol_burley_sample_3D(uint index,
const uint dimension_set,
uint seed,
uint shuffled_index_mask)
{
/* Include the dimension set in the seed, so we get decorrelated
* sequences for different dimension sets via shuffling. */
seed ^= hash_hp_uint(dimension_set);
/* Shuffle and mask. The masking is just for better
* performance at low sample counts. */
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xcaa726ac);
index &= shuffled_index_mask;
return make_float3(sobol_burley(index, 0, seed ^ 0x9e78e391),
sobol_burley(index, 1, seed ^ 0x67c33241),
sobol_burley(index, 2, seed ^ 0x78c395c5));
}
/*
* Computes a 4D Owen-scrambled and shuffled Sobol sample.
*
* `dimension_set` is which four dimensions of the sample you want to
* fetch. For example, 0 is the first four, 1 is the second four, etc.
* The dimensions within a single set are stratified, but different sets
* are uncorrelated.
*
* See sobol_burley_sample_1D for further usage details.
*/
ccl_device float4 sobol_burley_sample_4D(uint index,
const uint dimension_set,
uint seed,
uint shuffled_index_mask)
{
/* Include the dimension set in the seed, so we get decorrelated
* sequences for different dimension sets via shuffling. */
seed ^= hash_hp_uint(dimension_set);
/* Shuffle and mask. The masking is just for better
* performance at low sample counts. */
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xc2c1a055);
index &= shuffled_index_mask;
return make_float4(sobol_burley(index, 0, seed ^ 0x39468210),
sobol_burley(index, 1, seed ^ 0xe9d8a845),
sobol_burley(index, 2, seed ^ 0x5f32b482),
sobol_burley(index, 3, seed ^ 0x1524cc56));
}
CCL_NAMESPACE_END