diff options
Diffstat (limited to 'intern/cycles/kernel/sample')
-rw-r--r-- | intern/cycles/kernel/sample/sample_jitter.h | 169 | ||||
-rw-r--r-- | intern/cycles/kernel/sample/sample_lcg.h | 51 | ||||
-rw-r--r-- | intern/cycles/kernel/sample/sample_mapping.h | 177 | ||||
-rw-r--r-- | intern/cycles/kernel/sample/sample_mis.h | 64 | ||||
-rw-r--r-- | intern/cycles/kernel/sample/sample_pattern.h | 185 |
5 files changed, 646 insertions, 0 deletions
diff --git a/intern/cycles/kernel/sample/sample_jitter.h b/intern/cycles/kernel/sample/sample_jitter.h new file mode 100644 index 00000000000..b62ec7fda42 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_jitter.h @@ -0,0 +1,169 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +CCL_NAMESPACE_BEGIN + +ccl_device_inline uint32_t laine_karras_permutation(uint32_t x, uint32_t seed) +{ + x += seed; + x ^= (x * 0x6c50b47cu); + x ^= x * 0xb82f1e52u; + x ^= x * 0xc7afe638u; + x ^= x * 0x8d22f6e6u; + + return x; +} + +ccl_device_inline uint32_t nested_uniform_scramble(uint32_t x, uint32_t seed) +{ + x = reverse_integer_bits(x); + x = laine_karras_permutation(x, seed); + x = reverse_integer_bits(x); + + return x; +} + +ccl_device_inline uint cmj_hash(uint i, uint p) +{ + i ^= p; + i ^= i >> 17; + i ^= i >> 10; + i *= 0xb36534e5; + i ^= i >> 12; + i ^= i >> 21; + i *= 0x93fc4795; + i ^= 0xdf6e307f; + i ^= i >> 17; + i *= 1 | p >> 18; + + return i; +} + +ccl_device_inline uint cmj_hash_simple(uint i, uint p) +{ + i = (i ^ 61) ^ p; + i += i << 3; + i ^= i >> 4; + i *= 0x27d4eb2d; + return i; +} + +ccl_device_inline float cmj_randfloat(uint i, uint p) +{ + return cmj_hash(i, p) * (1.0f / 4294967808.0f); +} + +ccl_device_inline float cmj_randfloat_simple(uint i, uint p) +{ + return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension) +{ + /* Perform Owen shuffle of the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, rng_hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, rng_hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); +#endif + + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = (dimension + sample_set); + const uint dim = d % NUM_PMJ_PATTERNS; + + /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D + * the x part is used for even dims and the y for odd. */ + int index = 2 * ((dim >> 1) * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)) + (dim & 1); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, rng_hash); +# else + float dx = cmj_randfloat(d, rng_hash); +# endif + /* Jitter sample locations and map back into [0 1]. */ + fx = fx + dx; + fx = fx - floorf(fx); +#else +# warning "Not using Cranley-Patterson Rotation." +#endif + + return fx; +} + +ccl_device void pmj_sample_2D(KernelGlobals kg, + uint sample, + uint rng_hash, + uint dimension, + ccl_private float *x, + ccl_private float *y) +{ + /* Perform a shuffle on the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, rng_hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, rng_hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); +#endif + + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = (dimension + sample_set); + uint dim = d % NUM_PMJ_PATTERNS; + int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + float fy = kernel_tex_fetch(__sample_pattern_lut, index + 1); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, rng_hash); + float dy = cmj_randfloat_simple(d + 1, rng_hash); +# else + float dx = cmj_randfloat(d, rng_hash); + float dy = cmj_randfloat(d + 1, rng_hash); +# endif + /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */ + float sx = fx + dx; + float sy = fy + dy; + sx = sx - floorf(sx); + sy = sy - floorf(sy); +#else +# warning "Not using Cranley Patterson Rotation." +#endif + + (*x) = sx; + (*y) = sy; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_lcg.h b/intern/cycles/kernel/sample/sample_lcg.h new file mode 100644 index 00000000000..92cfff639b4 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_lcg.h @@ -0,0 +1,51 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Linear Congruential Generator */ + +ccl_device uint lcg_step_uint(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return *rng; +} + +ccl_device float lcg_step_float(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return (float)*rng * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device uint lcg_init(uint seed) +{ + uint rng = seed; + lcg_step_uint(&rng); + return rng; +} + +ccl_device_inline uint lcg_state_init(const uint rng_hash, + const uint rng_offset, + const uint sample, + const uint scramble) +{ + return lcg_init(rng_hash + rng_offset + sample * scramble); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_mapping.h b/intern/cycles/kernel/sample/sample_mapping.h new file mode 100644 index 00000000000..3297aa2a29a --- /dev/null +++ b/intern/cycles/kernel/sample/sample_mapping.h @@ -0,0 +1,177 @@ +/* + * Parts adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* distribute uniform xy on [0,1] over unit disk [-1,1] */ +ccl_device void to_unit_disk(ccl_private float *x, ccl_private float *y) +{ + float phi = M_2PI_F * (*x); + float r = sqrtf(*y); + + *x = r * cosf(phi); + *y = r * sinf(phi); +} + +/* return an orthogonal tangent and bitangent given a normal and tangent that + * may not be exactly orthogonal */ +ccl_device void make_orthonormals_tangent(const float3 N, + const float3 T, + ccl_private float3 *a, + ccl_private float3 *b) +{ + *b = normalize(cross(N, T)); + *a = cross(*b, N); +} + +/* sample direction with cosine weighted distributed in hemisphere */ +ccl_device_inline void sample_cos_hemisphere( + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) +{ + to_unit_disk(&randu, &randv); + float costheta = sqrtf(max(1.0f - randu * randu - randv * randv, 0.0f)); + float3 T, B; + make_orthonormals(N, &T, &B); + *omega_in = randu * T + randv * B + costheta * N; + *pdf = costheta * M_1_PI_F; +} + +/* sample direction uniformly distributed in hemisphere */ +ccl_device_inline void sample_uniform_hemisphere( + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) +{ + float z = randu; + float r = sqrtf(max(0.0f, 1.0f - z * z)); + float phi = M_2PI_F * randv; + float x = r * cosf(phi); + float y = r * sinf(phi); + + float3 T, B; + make_orthonormals(N, &T, &B); + *omega_in = x * T + y * B + z * N; + *pdf = 0.5f * M_1_PI_F; +} + +/* sample direction uniformly distributed in cone */ +ccl_device_inline void sample_uniform_cone(const float3 N, + float angle, + float randu, + float randv, + ccl_private float3 *omega_in, + ccl_private float *pdf) +{ + float zMin = cosf(angle); + float z = zMin - zMin * randu + randu; + float r = safe_sqrtf(1.0f - sqr(z)); + float phi = M_2PI_F * randv; + float x = r * cosf(phi); + float y = r * sinf(phi); + + float3 T, B; + make_orthonormals(N, &T, &B); + *omega_in = x * T + y * B + z * N; + *pdf = M_1_2PI_F / (1.0f - zMin); +} + +ccl_device_inline float pdf_uniform_cone(const float3 N, float3 D, float angle) +{ + float zMin = cosf(angle); + float z = dot(N, D); + if (z > zMin) { + return M_1_2PI_F / (1.0f - zMin); + } + return 0.0f; +} + +/* sample uniform point on the surface of a sphere */ +ccl_device float3 sample_uniform_sphere(float u1, float u2) +{ + float z = 1.0f - 2.0f * u1; + float r = sqrtf(fmaxf(0.0f, 1.0f - z * z)); + float phi = M_2PI_F * u2; + float x = r * cosf(phi); + float y = r * sinf(phi); + + return make_float3(x, y, z); +} + +/* distribute uniform xy on [0,1] over unit disk [-1,1], with concentric mapping + * to better preserve stratification for some RNG sequences */ +ccl_device float2 concentric_sample_disk(float u1, float u2) +{ + float phi, r; + float a = 2.0f * u1 - 1.0f; + float b = 2.0f * u2 - 1.0f; + + if (a == 0.0f && b == 0.0f) { + return zero_float2(); + } + else if (a * a > b * b) { + r = a; + phi = M_PI_4_F * (b / a); + } + else { + r = b; + phi = M_PI_2_F - M_PI_4_F * (a / b); + } + + return make_float2(r * cosf(phi), r * sinf(phi)); +} + +/* sample point in unit polygon with given number of corners and rotation */ +ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, float v) +{ + /* sample corner number and reuse u */ + float corner = floorf(u * corners); + u = u * corners - corner; + + /* uniform sampled triangle weights */ + u = sqrtf(u); + v = v * u; + u = 1.0f - u; + + /* point in triangle */ + float angle = M_PI_F / corners; + float2 p = make_float2((u + v) * cosf(angle), (u - v) * sinf(angle)); + + /* rotate */ + rotation += corner * 2.0f * angle; + + float cr = cosf(rotation); + float sr = sinf(rotation); + + return make_float2(cr * p.x - sr * p.y, sr * p.x + cr * p.y); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_mis.h b/intern/cycles/kernel/sample/sample_mis.h new file mode 100644 index 00000000000..0878b3aac36 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_mis.h @@ -0,0 +1,64 @@ +/* + * Parts adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Multiple importance sampling utilities. */ + +ccl_device float balance_heuristic(float a, float b) +{ + return (a) / (a + b); +} + +ccl_device float balance_heuristic_3(float a, float b, float c) +{ + return (a) / (a + b + c); +} + +ccl_device float power_heuristic(float a, float b) +{ + return (a * a) / (a * a + b * b); +} + +ccl_device float power_heuristic_3(float a, float b, float c) +{ + return (a * a) / (a * a + b * b + c * c); +} + +ccl_device float max_heuristic(float a, float b) +{ + return (a > b) ? 1.0f : 0.0f; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/sample_pattern.h b/intern/cycles/kernel/sample/sample_pattern.h new file mode 100644 index 00000000000..95635c2c855 --- /dev/null +++ b/intern/cycles/kernel/sample/sample_pattern.h @@ -0,0 +1,185 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "kernel/sample/sample_jitter.h" +#include "util/util_hash.h" + +CCL_NAMESPACE_BEGIN + +/* Pseudo random numbers, uncomment this for debugging correlations. Only run + * this single threaded on a CPU for repeatable results. */ +//#define __DEBUG_CORRELATION__ + +/* High Dimensional Sobol. + * + * Multidimensional sobol with generator matrices. Dimension 0 and 1 are equal + * to classic Van der Corput and Sobol sequences. */ + +#ifdef __SOBOL__ + +/* Skip initial numbers that for some dimensions have clear patterns that + * don't cover the entire sample space. Ideally we would have a better + * progressive pattern that doesn't suffer from this problem, because even + * with this offset some dimensions are quite poor. + */ +# define SOBOL_SKIP 64 + +ccl_device uint sobol_dimension(KernelGlobals kg, int index, int dimension) +{ + uint result = 0; + uint i = index + SOBOL_SKIP; + for (int j = 0, x; (x = find_first_set(i)); i >>= x) { + j += x; + result ^= __float_as_uint(kernel_tex_fetch(__sample_pattern_lut, 32 * dimension + j - 1)); + } + return result; +} + +#endif /* __SOBOL__ */ + +ccl_device_forceinline float path_rng_1D(KernelGlobals kg, + uint rng_hash, + int sample, + int dimension) +{ +#ifdef __DEBUG_CORRELATION__ + return (float)drand48(); +#endif + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) +#endif + { + return pmj_sample_1D(kg, sample, rng_hash, dimension); + } + +#ifdef __SOBOL__ + /* Sobol sequence value using direction vectors. */ + uint result = sobol_dimension(kg, sample, dimension); + float r = (float)result * (1.0f / (float)0xFFFFFFFF); + + /* Cranly-Patterson rotation using rng seed */ + float shift; + + /* Hash rng with dimension to solve correlation issues. + * See T38710, T50116. + */ + uint tmp_rng = cmj_hash_simple(dimension, rng_hash); + shift = tmp_rng * (1.0f / (float)0xFFFFFFFF); + + return r + shift - floorf(r + shift); +#endif +} + +ccl_device_forceinline void path_rng_2D(KernelGlobals kg, + uint rng_hash, + int sample, + int dimension, + ccl_private float *fx, + ccl_private float *fy) +{ +#ifdef __DEBUG_CORRELATION__ + *fx = (float)drand48(); + *fy = (float)drand48(); + return; +#endif + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) +#endif + { + pmj_sample_2D(kg, sample, rng_hash, dimension, fx, fy); + + return; + } + +#ifdef __SOBOL__ + /* Sobol. */ + *fx = path_rng_1D(kg, rng_hash, sample, dimension); + *fy = path_rng_1D(kg, rng_hash, sample, dimension + 1); +#endif +} + +/** + * 1D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqint1(uint n) +{ + n = (n << 13U) ^ n; + n = n * (n * n * 15731U + 789221U) + 1376312589U; + + return n; +} + +/** + * 2D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqnt2d(const uint x, const uint y) +{ + const uint qx = 1103515245U * ((x >> 1U) ^ (y)); + const uint qy = 1103515245U * ((y >> 1U) ^ (x)); + const uint n = 1103515245U * ((qx) ^ (qy >> 3U)); + + return n; +} + +ccl_device_inline uint path_rng_hash_init(KernelGlobals kg, + const int sample, + const int x, + const int y) +{ + const uint rng_hash = hash_iqnt2d(x, y) ^ kernel_data.integrator.seed; + +#ifdef __DEBUG_CORRELATION__ + srand48(rng_hash + sample); +#else + (void)sample; +#endif + + return rng_hash; +} + +ccl_device_inline bool sample_is_even(int pattern, int sample) +{ + if (pattern == SAMPLING_PATTERN_PMJ) { + /* See Section 10.2.1, "Progressive Multi-Jittered Sample Sequences", Christensen et al. + * We can use this to get divide sample sequence into two classes for easier variance + * estimation. */ +#if defined(__GNUC__) && !defined(__KERNEL_GPU__) + return __builtin_popcount(sample & 0xaaaaaaaa) & 1; +#elif defined(__NVCC__) + return __popc(sample & 0xaaaaaaaa) & 1; +#else + /* TODO(Stefan): pop-count intrinsic for Windows with fallback for older CPUs. */ + int i = sample & 0xaaaaaaaa; + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + i = (((i + (i >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; + return i & 1; +#endif + } + else { + /* TODO(Stefan): Are there reliable ways of dividing CMJ and Sobol into two classes? */ + return sample & 0x1; + } +} + +CCL_NAMESPACE_END |