diff --git a/README.md b/README.md index d21e897..8dabb09 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,12 @@ Spatially Hashed Radiance Cache is a technique aimed at improving signal quality ## Distribution SHARC is distributed as a set of shader-only sources along with [integration guide][SharcIntegrationGuide]. -For usage of the SHARC library please check the samples in the [RTXGI v2.0 SDK][RTXGI]. +For usage of the SHARC library please check the samples in the [RTXGI v2.0 SDK][RTXGI2]. + +See the [changelog][Changelog] in RTXGI v2.0 SDK for the latest SHARC changes. + [SharcIntegrationGuide]: ./docs/Integration.md -[RTXGI]: https://github.com/NVIDIAGameWorks/RTXGI +[RTXGI2]: https://github.com/NVIDIAGameWorks/RTXGI/tree/main +[Changelog]: https://github.com/NVIDIAGameWorks/RTXGI/blob/main/CHANGELOG.md + diff --git a/docs/Integration.md b/docs/Integration.md index b15b096..96a7c93 100644 --- a/docs/Integration.md +++ b/docs/Integration.md @@ -34,10 +34,11 @@ At Render-Time `Hash grid` visualization itself doesn’t require any GPU resources to be used. The simplest debug visualization uses world space position derived from the primary ray hit intersection. ```C++ -GridParameters gridParameters; +HashGridParameters gridParameters; gridParameters.cameraPosition = g_Constants.cameraPosition; gridParameters.logarithmBase = SHARC_GRID_LOGARITHM_BASE; gridParameters.sceneScale = g_Constants.sharcSceneScale; +gridParameters.levelBias = SHARC_GRID_LEVEL_BIAS; float3 color = HashGridDebugColoredHash(positionWorld, gridParameters); ``` @@ -48,7 +49,7 @@ float3 color = HashGridDebugColoredHash(positionWorld, gridParameters);
Image 2. SHaRC hash grid vizualization
-Logarithm base controls levels of detail distribution and voxel size ratio change between neighboring levels, it doesn’t make voxel sizes bigger or smaller on average. To control voxel size use ```sceneScale``` parameter instead. HASH_GRID_LEVEL_BIAS should be used to control at which level near the camera the voxel level get's clamped to avoid getting detailed levels if it is not required. +Logarithm base controls levels of detail distribution and voxel size ratio change between neighboring levels, it doesn’t make voxel sizes bigger or smaller on average. To control voxel size use ```sceneScale``` parameter instead. HashGridParameters::levelBias should be used to control at which level near the camera the voxel level get's clamped to avoid getting detailed levels if it is not required. ## Implementation Details diff --git a/include/HashGridCommon.h b/include/HashGridCommon.h index 2e2d432..9921c4d 100644 --- a/include/HashGridCommon.h +++ b/include/HashGridCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. * * NVIDIA CORPORATION and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -8,6 +8,7 @@ * license agreement from NVIDIA CORPORATION is strictly prohibited. */ +// Constants #define HASH_GRID_POSITION_BIT_NUM 17 #define HASH_GRID_POSITION_BIT_MASK ((1u << HASH_GRID_POSITION_BIT_NUM) - 1) #define HASH_GRID_LEVEL_BIT_NUM 10 @@ -16,31 +17,46 @@ #define HASH_GRID_NORMAL_BIT_MASK ((1u << HASH_GRID_NORMAL_BIT_NUM) - 1) #define HASH_GRID_HASH_MAP_BUCKET_SIZE 32 #define HASH_GRID_INVALID_HASH_KEY 0 -#define HASH_GRID_INVALID_CACHE_ENTRY 0xFFFFFFFF -#define HASH_GRID_USE_NORMALS 1 // account for normal data in the hash key +#define HASH_GRID_INVALID_CACHE_INDEX 0xFFFFFFFF + +// Tweakable parameters +#ifndef HASH_GRID_USE_NORMALS +#define HASH_GRID_USE_NORMALS 1 // account for the normal data in the hash key +#endif + +#ifndef HASH_GRID_ALLOW_COMPACTION #define HASH_GRID_ALLOW_COMPACTION (HASH_GRID_HASH_MAP_BUCKET_SIZE == 32) -#define HASH_GRID_LEVEL_BIAS 2 // positive bias adds extra levels with content magnification (can be negative as well) +#endif + +#ifndef HASH_GRID_POSITION_OFFSET #define HASH_GRID_POSITION_OFFSET float3(0.0f, 0.0f, 0.0f) +#endif + +#ifndef HASH_GRID_POSITION_BIAS #define HASH_GRID_POSITION_BIAS 1e-4f // may require adjustment for extreme scene scales +#endif + +#ifndef HASH_GRID_NORMAL_BIAS #define HASH_GRID_NORMAL_BIAS 1e-3f +#endif -#define CacheEntry uint -#define HashKey uint64_t +#define HashGridIndex uint +#define HashGridKey uint64_t -struct GridParameters +struct HashGridParameters { float3 cameraPosition; - float3 cameraPositionPrev; float logarithmBase; float sceneScale; + float levelBias; }; -float LogBase(float x, float base) +float HashGridLogBase(float x, float base) { return log(x) / log(base); } -uint GetBaseSlot(uint slot, uint capacity) +uint HashGridGetBaseSlot(uint slot, uint capacity) { #if HASH_GRID_ALLOW_COMPACTION return (slot / HASH_GRID_HASH_MAP_BUCKET_SIZE) * HASH_GRID_HASH_MAP_BUCKET_SIZE; @@ -50,7 +66,7 @@ uint GetBaseSlot(uint slot, uint capacity) } // http://burtleburtle.net/bob/hash/integer.html -uint HashJenkins32(uint a) +uint HashGridHashJenkins32(uint a) { a = (a + 0x7ed55d16) + (a << 12); a = (a ^ 0xc761c23c) ^ (a >> 19); @@ -61,50 +77,49 @@ uint HashJenkins32(uint a) return a; } -uint Hash32(HashKey hashKey) +uint HashGridHash32(HashGridKey hashKey) { - return HashJenkins32(uint((hashKey >> 0) & 0xffffffff)) - ^ HashJenkins32(uint((hashKey >> 32) & 0xffffffff)); + return HashGridHashJenkins32(uint((hashKey >> 0) & 0xFFFFFFFF)) ^ HashGridHashJenkins32(uint((hashKey >> 32) & 0xFFFFFFFF)); } -uint GetGridLevel(float3 samplePosition, GridParameters gridParameters) +uint HashGridGetLevel(float3 samplePosition, HashGridParameters gridParameters) { const float distance2 = dot(gridParameters.cameraPosition - samplePosition, gridParameters.cameraPosition - samplePosition); - return uint(clamp(0.5f * LogBase(distance2, gridParameters.logarithmBase) + HASH_GRID_LEVEL_BIAS, 1.0f, float(HASH_GRID_LEVEL_BIT_MASK))); + return uint(clamp(0.5f * HashGridLogBase(distance2, gridParameters.logarithmBase) + gridParameters.levelBias, 1.0f, float(HASH_GRID_LEVEL_BIT_MASK))); } -float GetVoxelSize(uint gridLevel, GridParameters gridParameters) +float HashGridGetVoxelSize(uint gridLevel, HashGridParameters gridParameters) { - return pow(gridParameters.logarithmBase, gridLevel) / (gridParameters.sceneScale * pow(gridParameters.logarithmBase, HASH_GRID_LEVEL_BIAS)); + return pow(gridParameters.logarithmBase, gridLevel) / (gridParameters.sceneScale * pow(gridParameters.logarithmBase, gridParameters.levelBias)); } // Based on logarithmic caching by Johannes Jendersie -int4 CalculateGridPositionLog(float3 samplePosition, GridParameters gridParameters) +int4 HashGridCalculatePositionLog(float3 samplePosition, HashGridParameters gridParameters) { samplePosition += float3(HASH_GRID_POSITION_BIAS, HASH_GRID_POSITION_BIAS, HASH_GRID_POSITION_BIAS); - uint gridLevel = GetGridLevel(samplePosition, gridParameters); - float voxelSize = GetVoxelSize(gridLevel, gridParameters); - int3 gridPosition = int3(floor(samplePosition / voxelSize)); + uint gridLevel = HashGridGetLevel(samplePosition, gridParameters); + float voxelSize = HashGridGetVoxelSize(gridLevel, gridParameters); + int3 gridPosition = int3(floor(samplePosition / voxelSize)); return int4(gridPosition.xyz, gridLevel); } -HashKey ComputeSpatialHash(float3 samplePosition, float3 sampleNormal, GridParameters gridParameters) +HashGridKey HashGridComputeSpatialHash(float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters) { - uint4 gridPosition = uint4(CalculateGridPositionLog(samplePosition, gridParameters)); + uint4 gridPosition = uint4(HashGridCalculatePositionLog(samplePosition, gridParameters)); - HashKey hashKey = ((uint64_t(gridPosition.x) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 0)) - | ((uint64_t(gridPosition.y) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 1)) - | ((uint64_t(gridPosition.z) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 2)) - | ((uint64_t(gridPosition.w) & HASH_GRID_LEVEL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3)); + HashGridKey hashKey = ((uint64_t(gridPosition.x) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 0)) | + ((uint64_t(gridPosition.y) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 1)) | + ((uint64_t(gridPosition.z) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 2)) | + ((uint64_t(gridPosition.w) & HASH_GRID_LEVEL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3)); #if HASH_GRID_USE_NORMALS uint normalBits = - (sampleNormal.x + HASH_GRID_NORMAL_BIAS >= 0 ? 1 : 0) + - (sampleNormal.y + HASH_GRID_NORMAL_BIAS >= 0 ? 2 : 0) + - (sampleNormal.z + HASH_GRID_NORMAL_BIAS >= 0 ? 4 : 0); + (sampleNormal.x + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 1) + + (sampleNormal.y + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 2) + + (sampleNormal.z + HASH_GRID_NORMAL_BIAS >= 0 ? 0 : 4); hashKey |= (uint64_t(normalBits) << (HASH_GRID_POSITION_BIT_NUM * 3 + HASH_GRID_LEVEL_BIT_NUM)); #endif // HASH_GRID_USE_NORMALS @@ -112,7 +127,7 @@ HashKey ComputeSpatialHash(float3 samplePosition, float3 sampleNormal, GridParam return hashKey; } -float3 GetPositionFromHashKey(const HashKey hashKey, GridParameters gridParameters) +float3 HashGridGetPositionFromKey(const HashGridKey hashKey, HashGridParameters gridParameters) { const int signBit = 1 << (HASH_GRID_POSITION_BIT_NUM - 1); const int signMask = ~((1 << HASH_GRID_POSITION_BIT_NUM) - 1); @@ -127,9 +142,9 @@ float3 GetPositionFromHashKey(const HashKey hashKey, GridParameters gridParamete gridPosition.y = (gridPosition.y & signBit) != 0 ? gridPosition.y | signMask : gridPosition.y; gridPosition.z = (gridPosition.z & signBit) != 0 ? gridPosition.z | signMask : gridPosition.z; - uint gridLevel = uint((hashKey >> HASH_GRID_POSITION_BIT_NUM * 3) & HASH_GRID_LEVEL_BIT_MASK); - float voxelSize = GetVoxelSize(gridLevel, gridParameters); - float3 samplePosition = (gridPosition + 0.5f) * voxelSize; + uint gridLevel = uint((hashKey >> HASH_GRID_POSITION_BIT_NUM * 3) & HASH_GRID_LEVEL_BIT_MASK); + float voxelSize = HashGridGetVoxelSize(gridLevel, gridParameters); + float3 samplePosition = (gridPosition + 0.5f) * voxelSize; return samplePosition; } @@ -178,42 +193,43 @@ void HashMapAtomicCompareExchange(in HashMapData hashMapData, in uint dstOffset, #endif // !HASH_GRID_ENABLE_64_BIT_ATOMICS } -bool HashMapInsert(in HashMapData hashMapData, const HashKey hashKey, out CacheEntry cacheEntry) +bool HashMapInsert(in HashMapData hashMapData, const HashGridKey hashKey, out HashGridIndex cacheIndex) { - uint hash = Hash32(hashKey); - uint slot = hash % hashMapData.capacity; - uint initSlot = slot; - HashKey prevHashKey = HASH_GRID_INVALID_HASH_KEY; + uint hash = HashGridHash32(hashKey); + uint slot = hash % hashMapData.capacity; + uint initSlot = slot; + HashGridKey prevHashGridKey = HASH_GRID_INVALID_HASH_KEY; - const uint baseSlot = GetBaseSlot(slot, hashMapData.capacity); + const uint baseSlot = HashGridGetBaseSlot(slot, hashMapData.capacity); for (uint bucketOffset = 0; bucketOffset < HASH_GRID_HASH_MAP_BUCKET_SIZE; ++bucketOffset) { - HashMapAtomicCompareExchange(hashMapData, baseSlot + bucketOffset, HASH_GRID_INVALID_HASH_KEY, hashKey, prevHashKey); + HashMapAtomicCompareExchange(hashMapData, baseSlot + bucketOffset, HASH_GRID_INVALID_HASH_KEY, hashKey, prevHashGridKey); - if (prevHashKey == HASH_GRID_INVALID_HASH_KEY || prevHashKey == hashKey) + if (prevHashGridKey == HASH_GRID_INVALID_HASH_KEY || prevHashGridKey == hashKey) { - cacheEntry = baseSlot + bucketOffset; + cacheIndex = baseSlot + bucketOffset; return true; } } - cacheEntry = 0; + cacheIndex = 0; + return false; } -bool HashMapFind(in HashMapData hashMapData, const HashKey hashKey, inout CacheEntry cacheEntry) +bool HashMapFind(in HashMapData hashMapData, const HashGridKey hashKey, inout HashGridIndex cacheIndex) { - uint hash = Hash32(hashKey); - uint slot = hash % hashMapData.capacity; + uint hash = HashGridHash32(hashKey); + uint slot = hash % hashMapData.capacity; - const uint baseSlot = GetBaseSlot(slot, hashMapData.capacity); + const uint baseSlot = HashGridGetBaseSlot(slot, hashMapData.capacity); for (uint bucketOffset = 0; bucketOffset < HASH_GRID_HASH_MAP_BUCKET_SIZE; ++bucketOffset) { - HashKey storedHashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, baseSlot + bucketOffset); + HashGridKey storedHashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, baseSlot + bucketOffset); if (storedHashKey == hashKey) { - cacheEntry = baseSlot + bucketOffset; + cacheIndex = baseSlot + bucketOffset; return true; } #if HASH_GRID_ALLOW_COMPACTION @@ -227,26 +243,26 @@ bool HashMapFind(in HashMapData hashMapData, const HashKey hashKey, inout CacheE return false; } -CacheEntry HashMapInsertEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, GridParameters gridParameters) +HashGridIndex HashMapInsertEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters) { - CacheEntry cacheEntry = HASH_GRID_INVALID_CACHE_ENTRY; - const HashKey hashKey = ComputeSpatialHash(samplePosition, sampleNormal, gridParameters); - bool successful = HashMapInsert(hashMapData, hashKey, cacheEntry); + HashGridIndex cacheIndex = HASH_GRID_INVALID_CACHE_INDEX; + const HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, sampleNormal, gridParameters); + bool successful = HashMapInsert(hashMapData, hashKey, cacheIndex); - return cacheEntry; + return cacheIndex; } -CacheEntry HashMapFindEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, GridParameters gridParameters) +HashGridIndex HashMapFindEntry(in HashMapData hashMapData, float3 samplePosition, float3 sampleNormal, HashGridParameters gridParameters) { - CacheEntry cacheEntry = HASH_GRID_INVALID_CACHE_ENTRY; - const HashKey hashKey = ComputeSpatialHash(samplePosition, sampleNormal, gridParameters); - bool successful = HashMapFind(hashMapData, hashKey, cacheEntry); + HashGridIndex cacheIndex = HASH_GRID_INVALID_CACHE_INDEX; + const HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, sampleNormal, gridParameters); + bool successful = HashMapFind(hashMapData, hashKey, cacheIndex); - return cacheEntry; + return cacheIndex; } // Debug functions -float3 GetColorFromHash32(uint hash) +float3 HashGridGetColorFromHash32(uint hash) { float3 color; color.x = ((hash >> 0) & 0x3ff) / 1023.0f; @@ -257,12 +273,11 @@ float3 GetColorFromHash32(uint hash) } // Debug visualization -float3 HashGridDebugColoredHash(float3 samplePosition, GridParameters gridParameters) +float3 HashGridDebugColoredHash(float3 samplePosition, HashGridParameters gridParameters) { - HashKey hashKey = ComputeSpatialHash(samplePosition, float3(0, 0, 0), gridParameters); - - uint gridLevel = GetGridLevel(samplePosition, gridParameters); - float3 color = GetColorFromHash32(Hash32(hashKey)) * GetColorFromHash32(HashJenkins32(gridLevel)).xyz; + HashGridKey hashKey = HashGridComputeSpatialHash(samplePosition, float3(0, 0, 0), gridParameters); + uint gridLevel = HashGridGetLevel(samplePosition, gridParameters); + float3 color = HashGridGetColorFromHash32(HashGridHash32(hashKey)) * HashGridGetColorFromHash32(HashGridHashJenkins32(gridLevel)).xyz; return color; } @@ -280,9 +295,8 @@ float3 HashGridDebugOccupancy(uint2 pixelPosition, uint2 screenSize, HashMapData if (elementIndex < hashMapData.capacity && ((pixelPosition.x % blockSize) < elementSize && (pixelPosition.y % blockSize) < elementSize)) { - HashKey storedHashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, elementIndex); - - if (storedHashKey != HASH_GRID_INVALID_HASH_KEY) + HashGridKey storedHashGridKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, elementIndex); + if (storedHashGridKey != HASH_GRID_INVALID_HASH_KEY) return float3(0.0f, 1.0f, 0.0f); } diff --git a/include/SharcCommon.h b/include/SharcCommon.h index a49f4e2..ff61c7f 100644 --- a/include/SharcCommon.h +++ b/include/SharcCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. * * NVIDIA CORPORATION and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -8,32 +8,13 @@ * license agreement from NVIDIA CORPORATION is strictly prohibited. */ -#define SHARC_VERSION_MAJOR 1 -#define SHARC_VERSION_MINOR 3 -#define SHARC_VERSION_BUILD 1 -#define SHARC_VERSION_REVISION 0 +// Version +#define SHARC_VERSION_MAJOR 1 +#define SHARC_VERSION_MINOR 4 +#define SHARC_VERSION_BUILD 3 +#define SHARC_VERSION_REVISION 0 -#if (SHARC_UPDATE || SHARC_QUERY) -#if SHARC_UPDATE -#define SHARC_QUERY 0 -#else // !SHARC_UPDATE -#define SHARC_UPDATE 0 -#endif // !SHARC_UPDATE -#else // !(SHARC_UPDATE || SHARC_QUERY) -#define SHARC_QUERY 0 -#define SHARC_UPDATE 0 -#endif // !(SHARC_UPDATE || SHARC_QUERY) - -#define SHARC_SAMPLE_NUM_MULTIPLIER 16 // increase sample count internally to make resolve step with low sample count more robust, power of 2 usage may help compiler with optimizations -#define SHARC_SAMPLE_NUM_THRESHOLD 0 // elements with sample count above this threshold will be used for early-out/resampling -#define SHARC_SEPARATE_EMISSIVE 0 // if set, emissive values should be passed separately on updates and added to the cache query -#define SHARC_PROPOGATION_DEPTH 4 // controls the amount of vertices stored in memory for signal backpropagation -#define SHARC_ENABLE_CACHE_RESAMPLING (SHARC_UPDATE && (SHARC_PROPOGATION_DEPTH > 1)) // resamples the cache during update step -#define SHARC_RESAMPLING_DEPTH_MIN 1 // controls minimum path depth which can be used with cache resampling -#define SHARC_RADIANCE_SCALE 1e3f // scale used for radiance values accumulation. Each component uses 32-bit integer for data storage -#define SHARC_ACCUMULATED_FRAME_NUM_MIN 1 // minimum number of frames to use for data accumulation -#define SHARC_ACCUMULATED_FRAME_NUM_MAX 64 // maximum number of frames to use for data accumulation -#define SHARC_STALE_FRAME_NUM_MIN 32 // minimum number of frames to keep the element in the cache +// Constants #define SHARC_SAMPLE_NUM_BIT_NUM 18 #define SHARC_SAMPLE_NUM_BIT_OFFSET 0 #define SHARC_SAMPLE_NUM_BIT_MASK ((1u << SHARC_SAMPLE_NUM_BIT_NUM) - 1) @@ -44,67 +25,63 @@ #define SHARC_STALE_FRAME_NUM_BIT_OFFSET (SHARC_SAMPLE_NUM_BIT_NUM + SHARC_ACCUMULATED_FRAME_NUM_BIT_NUM) #define SHARC_STALE_FRAME_NUM_BIT_MASK ((1u << SHARC_STALE_FRAME_NUM_BIT_NUM) - 1) #define SHARC_GRID_LOGARITHM_BASE 2.0f +#define SHARC_GRID_LEVEL_BIAS 0 // positive bias adds extra levels with content magnification (can be negative as well) #define SHARC_ENABLE_COMPACTION HASH_GRID_ALLOW_COMPACTION #define SHARC_BLEND_ADJACENT_LEVELS 1 // combine the data from adjacent levels on camera movement #define SHARC_DEFERRED_HASH_COMPACTION (SHARC_ENABLE_COMPACTION && SHARC_BLEND_ADJACENT_LEVELS) #define SHARC_NORMALIZED_SAMPLE_NUM (1u << (SHARC_SAMPLE_NUM_BIT_NUM - 1)) +#define SHARC_ACCUMULATED_FRAME_NUM_MIN 1 // minimum number of frames to use for data accumulation +#define SHARC_ACCUMULATED_FRAME_NUM_MAX SHARC_ACCUMULATED_FRAME_NUM_BIT_MASK // maximum number of frames to use for data accumulation -// Debug -#define SHARC_DEBUG_BITS_OCCUPANCY_THRESHOLD_LOW 0.125 -#define SHARC_DEBUG_BITS_OCCUPANCY_THRESHOLD_MEDIUM 0.5 - -#if SHARC_ENABLE_GLSL - -// Required extensions -// #extension GL_EXT_buffer_reference : require -// #extension GL_EXT_shader_explicit_arithmetic_types_int64 : require -// #extension GL_EXT_shader_atomic_int64 : require -// #extension GL_KHR_shader_subgroup_ballot : require - -// Buffer reference types can be constructed from a 'uint64_t' or a 'uvec2' value. -// The low - order 32 bits of the reference map to and from the 'x' component -// of the 'uvec2'. -#define float2 vec2 -#define float3 vec3 -#define float4 vec4 +// Tweakable parameters +#ifndef SHARC_SAMPLE_NUM_MULTIPLIER +#define SHARC_SAMPLE_NUM_MULTIPLIER 16 // increase sample count internally to make resolve step with low sample count more robust, power of 2 usage may help compiler with optimizations +#endif -#define uint2 uvec2 -#define uint3 uvec3 -#define uint4 uvec4 +#ifndef SHARC_SAMPLE_NUM_THRESHOLD +#define SHARC_SAMPLE_NUM_THRESHOLD 0 // elements with sample count above this threshold will be used for early-out/resampling +#endif -#define int2 ivec2 -#define int3 ivec3 -#define int4 ivec4 +#ifndef SHARC_SEPARATE_EMISSIVE +#define SHARC_SEPARATE_EMISSIVE 0 // if set, emissive values should be passed separately on updates and added to the cache query +#endif -#define lerp mix -#define InterlockedAdd atomicAdd -#define InterlockedCompareExchange atomicCompSwap -#define WaveActiveCountBits(value) subgroupBallotBitCount(uint4(value, 0, 0, 0)) -#define WaveActiveBallot subgroupBallot -#define WavePrefixCountBits(value) subgroupBallotExclusiveBitCount(uint4(value, 0, 0, 0)) +#ifndef SHARC_INCLUDE_DIRECT_LIGHTING +#define SHARC_INCLUDE_DIRECT_LIGHTING 1 // if set cache values include both direct and indirect lighting +#endif -#define RW_STRUCTURED_BUFFER(name, type) RWStructuredBuffer_##type name -#define BUFFER_AT_OFFSET(name, offset) name.data[offset] +#ifndef SHARC_PROPOGATION_DEPTH +#define SHARC_PROPOGATION_DEPTH 4 // controls the amount of vertices stored in memory for signal backpropagation +#endif -layout(buffer_reference, std430, buffer_reference_align = 8) buffer RWStructuredBuffer_uint64_t { - uint64_t data[]; -}; +#ifndef SHARC_ENABLE_CACHE_RESAMPLING +#define SHARC_ENABLE_CACHE_RESAMPLING (SHARC_UPDATE && (SHARC_PROPOGATION_DEPTH > 1)) // resamples the cache during update step +#endif -layout(buffer_reference, std430, buffer_reference_align = 4) buffer RWStructuredBuffer_uint { - uint data[]; -}; +#ifndef SHARC_RESAMPLING_DEPTH_MIN +#define SHARC_RESAMPLING_DEPTH_MIN 1 // controls minimum path depth which can be used with cache resampling +#endif -layout(buffer_reference, std430, buffer_reference_align = 16) buffer RWStructuredBuffer_uint4 { - uvec4 data[]; -}; +#ifndef SHARC_RADIANCE_SCALE +#define SHARC_RADIANCE_SCALE 1e3f // scale used for radiance values accumulation. Each component uses 32-bit integer for data storage +#endif -#else // !SHARC_ENABLE_GLSL +#ifndef SHARC_STALE_FRAME_NUM_MIN +#define SHARC_STALE_FRAME_NUM_MIN 8 // minimum number of frames to keep the element in the cache +#endif +#ifndef RW_STRUCTURED_BUFFER #define RW_STRUCTURED_BUFFER(name, type) RWStructuredBuffer name +#endif + +#ifndef BUFFER_AT_OFFSET #define BUFFER_AT_OFFSET(name, offset) name[offset] +#endif -#endif // !SHARC_ENABLE_GLSL +// Debug +#define SHARC_DEBUG_BITS_OCCUPANCY_THRESHOLD_LOW 0.125 +#define SHARC_DEBUG_BITS_OCCUPANCY_THRESHOLD_MEDIUM 0.5 /* * RTXGI2 DIVERGENCE: @@ -138,6 +115,34 @@ layout(buffer_reference, std430, buffer_reference_align = 16) buffer RWStructure #endif #include "HashGridCommon.h" +struct SharcParameters +{ + HashGridParameters gridParameters; + HashMapData hashMapData; + bool enableAntiFireflyFilter; + + RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4); + RW_STRUCTURED_BUFFER(voxelDataBufferPrev, uint4); +}; + +struct SharcState +{ +#if SHARC_UPDATE + HashGridIndex cacheIndices[SHARC_PROPOGATION_DEPTH]; + float3 sampleWeights[SHARC_PROPOGATION_DEPTH]; + uint pathLength; +#endif // SHARC_UPDATE +}; + +struct SharcHitData +{ + float3 positionWorld; + float3 normalWorld; +#if SHARC_SEPARATE_EMISSIVE + float3 emissive; +#endif // SHARC_SEPARATE_EMISSIVE +}; + struct SharcVoxelData { uint3 accumulatedRadiance; @@ -146,6 +151,14 @@ struct SharcVoxelData uint staleFrameNum; }; +struct SharcResolveParameters +{ + float3 cameraPositionPrev; + uint accumulationFrameNum; + uint staleFrameNumMax; + bool enableAntiFireflyFilter; +}; + uint SharcGetSampleNum(uint packedData) { return (packedData >> SHARC_SAMPLE_NUM_BIT_OFFSET) & SHARC_SAMPLE_NUM_BIT_MASK; @@ -176,7 +189,7 @@ SharcVoxelData SharcUnpackVoxelData(uint4 voxelDataPacked) return voxelData; } -SharcVoxelData SharcGetVoxelData(RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4), CacheEntry cacheEntry) +SharcVoxelData SharcGetVoxelData(RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4), HashGridIndex cacheIndex) { SharcVoxelData voxelData; voxelData.accumulatedRadiance = uint3(0, 0, 0); @@ -184,53 +197,52 @@ SharcVoxelData SharcGetVoxelData(RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4), C voxelData.accumulatedFrameNum = 0; voxelData.staleFrameNum = 0; - if (cacheEntry == HASH_GRID_INVALID_CACHE_ENTRY) + if (cacheIndex == HASH_GRID_INVALID_CACHE_INDEX) return voxelData; - uint4 voxelDataPacked = BUFFER_AT_OFFSET(voxelDataBuffer, cacheEntry); + uint4 voxelDataPacked = BUFFER_AT_OFFSET(voxelDataBuffer, cacheIndex); return SharcUnpackVoxelData(voxelDataPacked); } -void SharcAddVoxelData(RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4), CacheEntry cacheEntry, float3 value, uint sampleData) +void SharcAddVoxelData(in SharcParameters sharcParameters, HashGridIndex cacheIndex, float3 sampleValue, float3 sampleWeight, uint sampleData) { - if (cacheEntry == HASH_GRID_INVALID_CACHE_ENTRY) + if (cacheIndex == HASH_GRID_INVALID_CACHE_INDEX) return; - uint3 scaledRadiance = uint3(value * SHARC_RADIANCE_SCALE); - - if (scaledRadiance.x != 0) InterlockedAdd(BUFFER_AT_OFFSET(voxelDataBuffer, cacheEntry).x, scaledRadiance.x); - if (scaledRadiance.y != 0) InterlockedAdd(BUFFER_AT_OFFSET(voxelDataBuffer, cacheEntry).y, scaledRadiance.y); - if (scaledRadiance.z != 0) InterlockedAdd(BUFFER_AT_OFFSET(voxelDataBuffer, cacheEntry).z, scaledRadiance.z); - if (sampleData != 0) InterlockedAdd(BUFFER_AT_OFFSET(voxelDataBuffer, cacheEntry).w, sampleData); -} - -struct SharcState -{ - GridParameters gridParameters; - HashMapData hashMapData; - -#if SHARC_UPDATE - CacheEntry cacheEntry[SHARC_PROPOGATION_DEPTH]; - float3 sampleWeight[SHARC_PROPOGATION_DEPTH]; - uint pathLength; -#endif // SHARC_UPDATE + if (sharcParameters.enableAntiFireflyFilter) + { + float scalarWeight = dot(sampleWeight, float3(0.213f, 0.715f, 0.072f)); + scalarWeight = max(scalarWeight, 1.0f); - RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4); + const float sampleWeightThreshold = 2.0f; + if (scalarWeight > sampleWeightThreshold) + { + uint4 voxelDataPackedPrev = BUFFER_AT_OFFSET(sharcParameters.voxelDataBufferPrev, cacheIndex); + uint sampleNumPrev = SharcGetSampleNum(voxelDataPackedPrev.w); + const uint sampleConfidenceThreshold = 2; + if (sampleNumPrev > SHARC_SAMPLE_NUM_MULTIPLIER * sampleConfidenceThreshold) + { + float luminancePrev = max(dot(SharcResolveAccumulatedRadiance(voxelDataPackedPrev.xyz, sampleNumPrev), float3(0.213f, 0.715f, 0.072f)), 1.0f); + float luminanceCur = max(dot(sampleValue * sampleWeight, float3(0.213f, 0.715f, 0.072f)), 1.0f); + float confidenceScale = lerp(5.0f, 10.0f, 1.0f / sampleNumPrev); + sampleWeight *= saturate(confidenceScale * luminancePrev / luminanceCur); + } + else + { + scalarWeight = pow(scalarWeight, 0.5f); + sampleWeight /= scalarWeight; + } + } + } -#if SHARC_ENABLE_CACHE_RESAMPLING - RW_STRUCTURED_BUFFER(voxelDataBufferPrev, uint4); -#endif // SHARC_ENABLE_CACHE_RESAMPLING -}; + uint3 scaledRadiance = uint3(sampleValue * sampleWeight * SHARC_RADIANCE_SCALE); -struct SharcHitData -{ - float3 positionWorld; - float3 normalWorld; -#if SHARC_SEPARATE_EMISSIVE - float3 emissive; -#endif // SHARC_SEPARATE_EMISSIVE -}; + if (scaledRadiance.x != 0) InterlockedAdd(BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, cacheIndex).x, scaledRadiance.x); + if (scaledRadiance.y != 0) InterlockedAdd(BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, cacheIndex).y, scaledRadiance.y); + if (scaledRadiance.z != 0) InterlockedAdd(BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, cacheIndex).z, scaledRadiance.z); + if (sampleData != 0) InterlockedAdd(BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, cacheIndex).w, sampleData); +} void SharcInit(inout SharcState sharcState) { @@ -239,40 +251,49 @@ void SharcInit(inout SharcState sharcState) #endif // SHARC_UPDATE } -void SharcUpdateMiss(inout SharcState sharcState, float3 radiance) +void SharcUpdateMiss(in SharcParameters sharcParameters, in SharcState sharcState, float3 radiance) { #if SHARC_UPDATE for (int i = 0; i < sharcState.pathLength; ++i) { - radiance *= sharcState.sampleWeight[i]; - SharcAddVoxelData(sharcState.voxelDataBuffer, sharcState.cacheEntry[i], radiance, 0); + SharcAddVoxelData(sharcParameters, sharcState.cacheIndices[i], radiance, sharcState.sampleWeights[i], 0); + radiance *= sharcState.sampleWeights[i]; } #endif // SHARC_UPDATE } -bool SharcUpdateHit(inout SharcState sharcState, SharcHitData sharcHitData, float3 lighting, float random) +bool SharcUpdateHit(in SharcParameters sharcParameters, inout SharcState sharcState, SharcHitData sharcHitData, float3 directLighting, float random) { bool continueTracing = true; #if SHARC_UPDATE - CacheEntry cacheEntry = HashMapInsertEntry(sharcState.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcState.gridParameters); + HashGridIndex cacheIndex = HashMapInsertEntry(sharcParameters.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcParameters.gridParameters); - float3 sharcRadiance = lighting; + float3 sharcRadiance = directLighting; #if SHARC_ENABLE_CACHE_RESAMPLING uint resamplingDepth = uint(round(lerp(SHARC_RESAMPLING_DEPTH_MIN, SHARC_PROPOGATION_DEPTH - 1, random))); if (resamplingDepth <= sharcState.pathLength) { - SharcVoxelData voxelData = SharcGetVoxelData(sharcState.voxelDataBufferPrev, cacheEntry); + SharcVoxelData voxelData = SharcGetVoxelData(sharcParameters.voxelDataBufferPrev, cacheIndex); if (voxelData.accumulatedSampleNum > SHARC_SAMPLE_NUM_THRESHOLD) { sharcRadiance = SharcResolveAccumulatedRadiance(voxelData.accumulatedRadiance, voxelData.accumulatedSampleNum); +#if !SHARC_INCLUDE_DIRECT_LIGHTING + sharcRadiance += directLighting; +#endif // !SHARC_INCLUDE_DIRECT_LIGHTING continueTracing = false; } } #endif // SHARC_ENABLE_CACHE_RESAMPLING if (continueTracing) - SharcAddVoxelData(sharcState.voxelDataBuffer, cacheEntry, lighting, 1); + { +#if SHARC_INCLUDE_DIRECT_LIGHTING + SharcAddVoxelData(sharcParameters, cacheIndex, directLighting, float3(1.0f, 1.0f, 1.0f), 1); +#else // !SHARC_INCLUDE_DIRECT_LIGHTING + SharcAddVoxelData(sharcParameters, cacheIndex, float3(0.0f, 0.0f, 0.0f), float3(0.0f, 0.0f, 0.0f), 1); +#endif // !SHARC_INCLUDE_DIRECT_LIGHTING + } #if SHARC_SEPARATE_EMISSIVE sharcRadiance += sharcHitData.emissive; @@ -281,17 +302,17 @@ bool SharcUpdateHit(inout SharcState sharcState, SharcHitData sharcHitData, floa uint i; for (i = 0; i < sharcState.pathLength; ++i) { - sharcRadiance *= sharcState.sampleWeight[i]; - SharcAddVoxelData(sharcState.voxelDataBuffer, sharcState.cacheEntry[i], sharcRadiance, 0); + SharcAddVoxelData(sharcParameters, sharcState.cacheIndices[i], sharcRadiance, sharcState.sampleWeights[i], 0); + sharcRadiance *= sharcState.sampleWeights[i]; } for (i = sharcState.pathLength; i > 0; --i) { - sharcState.cacheEntry[i] = sharcState.cacheEntry[i - 1]; - sharcState.sampleWeight[i] = sharcState.sampleWeight[i - 1]; + sharcState.cacheIndices[i] = sharcState.cacheIndices[i - 1]; + sharcState.sampleWeights[i] = sharcState.sampleWeights[i - 1]; } - sharcState.cacheEntry[0] = cacheEntry; + sharcState.cacheIndices[0] = cacheIndex; sharcState.pathLength = min(++sharcState.pathLength, SHARC_PROPOGATION_DEPTH - 1); #endif // SHARC_UPDATE return continueTracing; @@ -300,20 +321,20 @@ bool SharcUpdateHit(inout SharcState sharcState, SharcHitData sharcHitData, floa void SharcSetThroughput(inout SharcState sharcState, float3 throughput) { #if SHARC_UPDATE - sharcState.sampleWeight[0] = throughput; + sharcState.sampleWeights[0] = throughput; #endif // SHARC_UPDATE } -bool SharcGetCachedRadiance(in SharcState sharcState, in SharcHitData sharcHitData, out float3 radiance, bool debug) +bool SharcGetCachedRadiance(in SharcParameters sharcParameters, in SharcHitData sharcHitData, out float3 radiance, bool debug) { if (debug) radiance = float3(0, 0, 0); const uint sampleThreshold = debug ? 0 : SHARC_SAMPLE_NUM_THRESHOLD; - CacheEntry cacheEntry = HashMapFindEntry(sharcState.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcState.gridParameters); - if (cacheEntry == HASH_GRID_INVALID_CACHE_ENTRY) + HashGridIndex cacheIndex = HashMapFindEntry(sharcParameters.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcParameters.gridParameters); + if (cacheIndex == HASH_GRID_INVALID_CACHE_INDEX) return false; - SharcVoxelData voxelData = SharcGetVoxelData(sharcState.voxelDataBuffer, cacheEntry); + SharcVoxelData voxelData = SharcGetVoxelData(sharcParameters.voxelDataBuffer, cacheIndex); if (voxelData.accumulatedSampleNum > sampleThreshold) { radiance = SharcResolveAccumulatedRadiance(voxelData.accumulatedRadiance, voxelData.accumulatedSampleNum); @@ -338,13 +359,13 @@ void SharcCopyHashEntry(uint entryIndex, HashMapData hashMapData, RW_STRUCTURED_ if (copyOffset == 0) return; - if (copyOffset == HASH_GRID_INVALID_CACHE_ENTRY) + if (copyOffset == HASH_GRID_INVALID_CACHE_INDEX) { BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex) = HASH_GRID_INVALID_HASH_KEY; } else if (copyOffset != 0) { - HashKey hashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex); + HashGridKey hashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex); BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex) = HASH_GRID_INVALID_HASH_KEY; BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, copyOffset) = hashKey; } @@ -358,7 +379,7 @@ int SharcGetGridDistance2(int3 position) return position.x * position.x + position.y * position.y + position.z * position.z; } -HashKey SharcGetAdjacentLevelHashKey(HashKey hashKey, GridParameters gridParameters) +HashGridKey SharcGetAdjacentLevelHashKey(HashGridKey hashKey, HashGridParameters gridParameters, float3 cameraPositionPrev) { const int signBit = 1 << (HASH_GRID_POSITION_BIT_NUM - 1); const int signMask = ~((1 << HASH_GRID_POSITION_BIT_NUM) - 1); @@ -375,12 +396,12 @@ HashKey SharcGetAdjacentLevelHashKey(HashKey hashKey, GridParameters gridParamet int level = int((hashKey >> (HASH_GRID_POSITION_BIT_NUM * 3)) & HASH_GRID_LEVEL_BIT_MASK); - float voxelSize = GetVoxelSize(level, gridParameters); + float voxelSize = HashGridGetVoxelSize(level, gridParameters); int3 cameraGridPosition = int3(floor((gridParameters.cameraPosition + HASH_GRID_POSITION_OFFSET) / voxelSize)); int3 cameraVector = cameraGridPosition - gridPosition; int cameraDistance = SharcGetGridDistance2(cameraVector); - int3 cameraGridPositionPrev = int3(floor((gridParameters.cameraPositionPrev + HASH_GRID_POSITION_OFFSET) / voxelSize)); + int3 cameraGridPositionPrev = int3(floor((cameraPositionPrev + HASH_GRID_POSITION_OFFSET) / voxelSize)); int3 cameraVectorPrev = cameraGridPositionPrev - gridPosition; int cameraDistancePrev = SharcGetGridDistance2(cameraVectorPrev); @@ -395,50 +416,56 @@ HashKey SharcGetAdjacentLevelHashKey(HashKey hashKey, GridParameters gridParamet level = max(level - 1, 1); } - HashKey modifiedHashKey = ((uint64_t(gridPosition.x) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 0)) + HashGridKey modifiedHashGridKey = ((uint64_t(gridPosition.x) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 0)) | ((uint64_t(gridPosition.y) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 1)) | ((uint64_t(gridPosition.z) & HASH_GRID_POSITION_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 2)) | ((uint64_t(level) & HASH_GRID_LEVEL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3)); #if HASH_GRID_USE_NORMALS - modifiedHashKey |= hashKey & (uint64_t(HASH_GRID_NORMAL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3 + HASH_GRID_LEVEL_BIT_NUM)); + modifiedHashGridKey |= hashKey & (uint64_t(HASH_GRID_NORMAL_BIT_MASK) << (HASH_GRID_POSITION_BIT_NUM * 3 + HASH_GRID_LEVEL_BIT_NUM)); #endif // HASH_GRID_USE_NORMALS - return modifiedHashKey; + return modifiedHashGridKey; } -void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapData hashMapData, RW_STRUCTURED_BUFFER(copyOffsetBuffer, uint), - RW_STRUCTURED_BUFFER(voxelDataBuffer, uint4), RW_STRUCTURED_BUFFER(voxelDataBufferPrev, uint4), uint accumulationFrameNum, uint staleFrameNumMax) +void SharcResolveEntry(uint entryIndex, SharcParameters sharcParameters, SharcResolveParameters resolveParameters +#if SHARC_DEFERRED_HASH_COMPACTION + , RW_STRUCTURED_BUFFER(copyOffsetBuffer, uint) +#endif // SHARC_DEFERRED_HASH_COMPACTION +) { - if (entryIndex >= hashMapData.capacity) + if (entryIndex >= sharcParameters.hashMapData.capacity) return; - HashKey hashKey = BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex); + HashGridKey hashKey = BUFFER_AT_OFFSET(sharcParameters.hashMapData.hashEntriesBuffer, entryIndex); if (hashKey == HASH_GRID_INVALID_HASH_KEY) return; - uint4 voxelDataPackedPrev = BUFFER_AT_OFFSET(voxelDataBufferPrev, entryIndex); - uint4 voxelDataPacked = BUFFER_AT_OFFSET(voxelDataBuffer, entryIndex); + uint4 voxelDataPackedPrev = BUFFER_AT_OFFSET(sharcParameters.voxelDataBufferPrev, entryIndex); + uint4 voxelDataPacked = BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, entryIndex); uint sampleNum = SharcGetSampleNum(voxelDataPacked.w); uint sampleNumPrev = SharcGetSampleNum(voxelDataPackedPrev.w); - uint accumulatedFrameNum = SharcGetAccumulatedFrameNum(voxelDataPackedPrev.w); + uint accumulatedFrameNum = SharcGetAccumulatedFrameNum(voxelDataPackedPrev.w) + 1; uint staleFrameNum = SharcGetStaleFrameNum(voxelDataPackedPrev.w); - uint3 accumulatedRadiance = voxelDataPacked.xyz * SHARC_SAMPLE_NUM_MULTIPLIER + voxelDataPackedPrev.xyz; - uint accumulatedSampleNum = SharcGetSampleNum(voxelDataPacked.w) * SHARC_SAMPLE_NUM_MULTIPLIER + SharcGetSampleNum(voxelDataPackedPrev.w); + voxelDataPacked.xyz *= SHARC_SAMPLE_NUM_MULTIPLIER; + sampleNum *= SHARC_SAMPLE_NUM_MULTIPLIER; + + uint3 accumulatedRadiance = voxelDataPacked.xyz + voxelDataPackedPrev.xyz; + uint accumulatedSampleNum = sampleNum + sampleNumPrev; #if SHARC_BLEND_ADJACENT_LEVELS // Reproject sample from adjacent level - float3 cameraOffset = gridParameters.cameraPosition.xyz - gridParameters.cameraPositionPrev.xyz; - if ((dot(cameraOffset, cameraOffset) != 0) && (accumulatedFrameNum < accumulationFrameNum)) + float3 cameraOffset = sharcParameters.gridParameters.cameraPosition.xyz - resolveParameters.cameraPositionPrev.xyz; + if ((dot(cameraOffset, cameraOffset) != 0) && (accumulatedFrameNum < resolveParameters.accumulationFrameNum)) { - HashKey adjacentLevelHashKey = SharcGetAdjacentLevelHashKey(hashKey, gridParameters); + HashGridKey adjacentLevelHashKey = SharcGetAdjacentLevelHashKey(hashKey, sharcParameters.gridParameters, resolveParameters.cameraPositionPrev); - CacheEntry cacheEntry = HASH_GRID_INVALID_CACHE_ENTRY; - if (HashMapFind(hashMapData, adjacentLevelHashKey, cacheEntry)) + HashGridIndex cacheIndex = HASH_GRID_INVALID_CACHE_INDEX; + if (HashMapFind(sharcParameters.hashMapData, adjacentLevelHashKey, cacheIndex)) { - uint4 adjacentPackedDataPrev = BUFFER_AT_OFFSET(voxelDataBufferPrev, cacheEntry); + uint4 adjacentPackedDataPrev = BUFFER_AT_OFFSET(sharcParameters.voxelDataBufferPrev, cacheIndex); uint adjacentSampleNum = SharcGetSampleNum(adjacentPackedDataPrev.w); if (adjacentSampleNum > SHARC_SAMPLE_NUM_THRESHOLD) { @@ -457,7 +484,7 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa accumulatedRadiance >>= 1; } - accumulationFrameNum = clamp(accumulationFrameNum, SHARC_ACCUMULATED_FRAME_NUM_MIN, SHARC_ACCUMULATED_FRAME_NUM_MAX); + uint accumulationFrameNum = clamp(resolveParameters.accumulationFrameNum, SHARC_ACCUMULATED_FRAME_NUM_MIN, SHARC_ACCUMULATED_FRAME_NUM_MAX); if (accumulatedFrameNum > accumulationFrameNum) { float normalizedAccumulatedSampleNum = round(accumulatedSampleNum * float(accumulationFrameNum) / accumulatedFrameNum); @@ -468,7 +495,6 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa accumulatedFrameNum = uint(accumulatedFrameNum * normalizationScale); } - ++accumulatedFrameNum; staleFrameNum = (sampleNum != 0) ? 0 : staleFrameNum + 1; uint4 packedData; @@ -478,13 +504,13 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa packedData.w |= (min(accumulatedFrameNum, SHARC_ACCUMULATED_FRAME_NUM_BIT_MASK) << SHARC_ACCUMULATED_FRAME_NUM_BIT_OFFSET); packedData.w |= (min(staleFrameNum, SHARC_STALE_FRAME_NUM_BIT_MASK) << SHARC_STALE_FRAME_NUM_BIT_OFFSET); - bool isValidElement = (staleFrameNum < max(staleFrameNumMax, SHARC_STALE_FRAME_NUM_MIN)) ? true : false; + bool isValidElement = (staleFrameNum < max(resolveParameters.staleFrameNumMax, SHARC_STALE_FRAME_NUM_MIN)) ? true : false; if (!isValidElement) { packedData = uint4(0, 0, 0, 0); #if !SHARC_ENABLE_COMPACTION - BUFFER_AT_OFFSET(hashMapData.hashEntriesBuffer, entryIndex) = HASH_GRID_INVALID_HASH_KEY; + BUFFER_AT_OFFSET(sharcParameters.hashMapData.hashEntriesBuffer, entryIndex) = HASH_GRID_INVALID_HASH_KEY; #endif // !SHARC_ENABLE_COMPACTION } @@ -501,7 +527,7 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa hashMapData.hashEntriesBuffer[entryIndex] = HASH_GRID_INVALID_HASH_KEY; #endif // !SHARC_DEFERRED_HASH_COMPACTION - BUFFER_AT_OFFSET(voxelDataBuffer, entryIndex) = uint4(0, 0, 0, 0); + BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, entryIndex) = uint4(0, 0, 0, 0); if (isValidElement) { @@ -512,12 +538,12 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa { if (emptySlotIndex == movableElementIndex) { - writeOffset += GetBaseSlot(entryIndex, hashMapData.capacity); + writeOffset += HashGridGetBaseSlot(entryIndex, sharcParameters.hashMapData.capacity); #if !SHARC_DEFERRED_HASH_COMPACTION hashMapData.hashEntriesBuffer[writeOffset] = hashKey; #endif // !SHARC_DEFERRED_HASH_COMPACTION - BUFFER_AT_OFFSET(voxelDataBuffer, writeOffset) = packedData; + BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, writeOffset) = packedData; break; } @@ -529,18 +555,18 @@ void SharcResolveEntry(uint entryIndex, GridParameters gridParameters, HashMapDa } #if SHARC_DEFERRED_HASH_COMPACTION - BUFFER_AT_OFFSET(copyOffsetBuffer, entryIndex) = (writeOffset != 0) ? writeOffset : HASH_GRID_INVALID_CACHE_ENTRY; + BUFFER_AT_OFFSET(copyOffsetBuffer, entryIndex) = (writeOffset != 0) ? writeOffset : HASH_GRID_INVALID_CACHE_INDEX; #endif // SHARC_DEFERRED_HASH_COMPACTION } else if (isValidElement) #endif // SHARC_ENABLE_COMPACTION { - BUFFER_AT_OFFSET(voxelDataBuffer, entryIndex) = packedData; + BUFFER_AT_OFFSET(sharcParameters.voxelDataBuffer, entryIndex) = packedData; } #if !SHARC_BLEND_ADJACENT_LEVELS // Clear buffer entry for the next frame - //BUFFER_AT_OFFSET(voxelDataBufferPrev, entryIndex) = uint4(0, 0, 0, 0); + //BUFFER_AT_OFFSET(sharcParameters.voxelDataBufferPrev, entryIndex) = uint4(0, 0, 0, 0); #endif // !SHARC_BLEND_ADJACENT_LEVELS } @@ -556,22 +582,22 @@ float3 SharcDebugGetBitsOccupancyColor(float occupancy) } // Debug visualization -float3 SharcDebugBitsOccupancySampleNum(in SharcState sharcState, in SharcHitData sharcHitData) +float3 SharcDebugBitsOccupancySampleNum(in SharcParameters sharcParameters, in SharcHitData sharcHitData) { - CacheEntry cacheEntry = HashMapFindEntry(sharcState.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcState.gridParameters); - SharcVoxelData voxelData = SharcGetVoxelData(sharcState.voxelDataBuffer, cacheEntry); + HashGridIndex cacheIndex = HashMapFindEntry(sharcParameters.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcParameters.gridParameters); + SharcVoxelData voxelData = SharcGetVoxelData(sharcParameters.voxelDataBuffer, cacheIndex); float occupancy = float(voxelData.accumulatedSampleNum) / SHARC_SAMPLE_NUM_BIT_MASK; return SharcDebugGetBitsOccupancyColor(occupancy); } -float3 SharcDebugBitsOccupancyRadiance(in SharcState sharcState, in SharcHitData sharcHitData) +float3 SharcDebugBitsOccupancyRadiance(in SharcParameters sharcParameters, in SharcHitData sharcHitData) { - CacheEntry cacheEntry = HashMapFindEntry(sharcState.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcState.gridParameters); - SharcVoxelData voxelData = SharcGetVoxelData(sharcState.voxelDataBuffer, cacheEntry); + HashGridIndex cacheIndex = HashMapFindEntry(sharcParameters.hashMapData, sharcHitData.positionWorld, sharcHitData.normalWorld, sharcParameters.gridParameters); + SharcVoxelData voxelData = SharcGetVoxelData(sharcParameters.voxelDataBuffer, cacheIndex); - float occupancy = float(max(voxelData.accumulatedRadiance.x, max(voxelData.accumulatedRadiance.y, voxelData.accumulatedRadiance.z))) / 0xffffffff; + float occupancy = float(max(voxelData.accumulatedRadiance.x, max(voxelData.accumulatedRadiance.y, voxelData.accumulatedRadiance.z))) / 0xFFFFFFFF; return SharcDebugGetBitsOccupancyColor(occupancy); } diff --git a/include/SharcGlsl.h b/include/SharcGlsl.h new file mode 100644 index 0000000..45fa959 --- /dev/null +++ b/include/SharcGlsl.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + */ + +#if SHARC_ENABLE_GLSL + +// Required extensions +// #extension GL_EXT_buffer_reference : require +// #extension GL_EXT_shader_explicit_arithmetic_types_int64 : require +// #extension GL_EXT_shader_atomic_int64 : require +// #extension GL_KHR_shader_subgroup_ballot : require + +// Buffer reference types can be constructed from a 'uint64_t' or a 'uvec2' value. +// The low - order 32 bits of the reference map to and from the 'x' component +// of the 'uvec2'. + +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 + +#define uint2 uvec2 +#define uint3 uvec3 +#define uint4 uvec4 + +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 + +#define lerp mix +#define InterlockedAdd atomicAdd +#define InterlockedCompareExchange atomicCompSwap +#define WaveActiveCountBits(value) subgroupBallotBitCount(uint4(value, 0, 0, 0)) +#define WaveActiveBallot subgroupBallot +#define WavePrefixCountBits(value) subgroupBallotExclusiveBitCount(uint4(value, 0, 0, 0)) + +#define RW_STRUCTURED_BUFFER(name, type) RWStructuredBuffer_##type name +#define BUFFER_AT_OFFSET(name, offset) name.data[offset] + +layout(buffer_reference, std430, buffer_reference_align = 8) buffer RWStructuredBuffer_uint64_t { + uint64_t data[]; +}; + +layout(buffer_reference, std430, buffer_reference_align = 4) buffer RWStructuredBuffer_uint { + uint data[]; +}; + +layout(buffer_reference, std430, buffer_reference_align = 16) buffer RWStructuredBuffer_uint4 { + uvec4 data[]; +}; + +#endif // SHARC_ENABLE_GLSL