diff --git a/Source/Base/Base/Container/BitSet.cpp b/Source/Base/Base/Container/BitSet.cpp new file mode 100644 index 00000000..99fd7036 --- /dev/null +++ b/Source/Base/Base/Container/BitSet.cpp @@ -0,0 +1,195 @@ +#include "BitSet.h" +#include + +BitSet::BitSet(Memory::Allocator* allocator, u32 numSets) + : _allocator(allocator) + , _bitSet(allocator, numSets, numSets) +{ + Reset(); +} + +void BitSet::SetEquals(const BitSet& other) +{ + // Guard self assignment + if (this == &other) + return; + + memcpy(&_bitSet[0], &other._bitSet[0], sizeof(u64) * _bitSet.Count()); +} + +void BitSet::Reset() +{ + memset(&_bitSet[0], 0, sizeof(u64) * _bitSet.Count()); +} + +void BitSet::Set(u32 index) +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + + Set(setIndex, bitIndex); +} + +void BitSet::Set(u32 setIndex, u32 bitIndex) +{ + _bitSet[setIndex] |= 1ull << bitIndex; +} + +void BitSet::Unset(u32 index) +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + + Unset(setIndex, bitIndex); +} + +void BitSet::Unset(u32 setIndex, u32 bitIndex) +{ + u64 inverseMask = ~(1ull << bitIndex); + _bitSet[setIndex] &= inverseMask; +} + +bool BitSet::Has(u32 index) const +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + return Has(setIndex, bitIndex); +} + +bool BitSet::Has(u32 setIndex, u32 bitIndex) const +{ + return (_bitSet[setIndex] & (1ull << bitIndex)) != 0; +} + +u64 BitSet::GetBitSet(u32 index) const +{ + return _bitSet[index]; +} + +bool BitSet::IsSubsetOf(const BitSet& other) const +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + // If they are different size, it is not a subset + if (numBitSets != static_cast(other._bitSet.Count())) + return false; + + for (u32 i = 0; i < numBitSets; i++) + { + u64 a = _bitSet[i]; + u64 b = other._bitSet[i]; + + if ((a & b) != a) + return false; + } + + return true; +} + +void BitSet::BitwiseUnset(const BitSet& other) +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + for (u32 i = 0; i < numBitSets; i++) + { + _bitSet[i] &= (~other._bitSet[i]); + } +} + +void BitSet::BitwiseAND(const BitSet& other) +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + for (u32 i = 0; i < numBitSets; i++) + { + _bitSet[i] &= other._bitSet[i]; + } +} + +void BitSet::BitwiseOR(const BitSet& other) +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + for (u32 i = 0; i < numBitSets; i++) + { + _bitSet[i] |= other._bitSet[i]; + } +} + +void BitSet::BitwiseXOR(const BitSet& other) +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + for (u32 i = 0; i < numBitSets; i++) + { + _bitSet[i] ^= other._bitSet[i]; + } +} + +BitSet* BitSet::NewBitwiseUnset(const BitSet& other) const +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); + *result = *this; + + result->BitwiseUnset(other); + + return result; +} + +BitSet* BitSet::NewBitwiseAND(const BitSet& other) const +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); + *result = *this; + + result->BitwiseAND(other); + + return result; +} + +BitSet* BitSet::NewBitwiseOR(const BitSet& other) const +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); + *result = *this; + + result->BitwiseOR(other); + + return result; +} + +BitSet* BitSet::NewBitwiseXOR(const BitSet& other) const +{ + u32 numBitSets = static_cast(_bitSet.Count()); + + BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); + *result = *this; + + result->BitwiseXOR(other); + + return result; +} + +u32 BitSet::NumBitSets() const +{ + return static_cast(_bitSet.Count()); +} + +void BitSet::ForEachSetBit(std::function callback) const +{ + u32 numBitSets = NumBitSets(); + for (u32 i = 0; i < numBitSets; i++) + { + u64 bits = _bitSet[i]; + while (bits != 0) + { + u32 bit = static_cast(std::countr_zero(bits)); + callback(i, bit); + bits &= bits - 1; // Clear lowest set bit + } + } +} diff --git a/Source/Base/Base/Container/BitSet.h b/Source/Base/Base/Container/BitSet.h index 0a3d0ed9..dcef5bef 100644 --- a/Source/Base/Base/Container/BitSet.h +++ b/Source/Base/Base/Container/BitSet.h @@ -5,203 +5,42 @@ class BitSet { public: - BitSet(Memory::Allocator* allocator, u32 numSets) - : _allocator(allocator) - , _bitSet(allocator, numSets, numSets) - { - Reset(); - } + BitSet(Memory::Allocator* allocator, u32 numSets); - void SetEquals(const BitSet& other) - { - // Guard self assignment - if (this == &other) - return; - - memcpy(&_bitSet[0], &other._bitSet[0], sizeof(u64) * _bitSet.Count()); - } + void SetEquals(const BitSet& other); // Resets all bits back to 0 - void Reset() - { - memset(&_bitSet[0], 0, sizeof(u64) * _bitSet.Count()); - } - - void Set(u32 index) - { - u32 setIndex = index / 64; - u32 bitIndex = index % 64; - - Set(setIndex, bitIndex); - } - - void Set(u32 setIndex, u32 bitIndex) - { - _bitSet[setIndex] |= 1ull << bitIndex; - } - - void Unset(u32 index) - { - u32 setIndex = index / 64; - u32 bitIndex = index % 64; + void Reset(); - Unset(setIndex, bitIndex); - } + void Set(u32 index); + void Set(u32 setIndex, u32 bitIndex); - void Unset(u32 setIndex, u32 bitIndex) - { - u64 inverseMask = ~(1ull << bitIndex); - _bitSet[setIndex] &= inverseMask; - } + void Unset(u32 index); + void Unset(u32 setIndex, u32 bitIndex); - bool Has(u32 index) const - { - u32 setIndex = index / 64; - u32 bitIndex = index % 64; - return Has(setIndex, bitIndex); - } + bool Has(u32 index) const; + bool Has(u32 setIndex, u32 bitIndex) const; - bool Has(u32 setIndex, u32 bitIndex) const - { - return (_bitSet[setIndex] & (1ull << bitIndex)) != 0; - } - - u64 GetBitSet(u32 index) - { - return _bitSet[index]; - } + u64 GetBitSet(u32 index) const; // Returns true if this bitset is a subset of the other bitset - bool IsSubsetOf(const BitSet& other) const - { - u32 numBitSets = static_cast(_bitSet.Count()); - - // If they are different size, it is not a subset - if (numBitSets != static_cast(other._bitSet.Count())) - return false; - - for (u32 i = 0; i < numBitSets; i++) - { - u64 a = _bitSet[i]; - u64 b = other._bitSet[i]; - - if ((a & b) != a) - return false; - } - - return true; - } - - void BitwiseUnset(const BitSet& other) - { - u32 numBitSets = static_cast(_bitSet.Count()); - - for (u32 i = 0; i < numBitSets; i++) - { - _bitSet[i] &= (~other._bitSet[i]); - } - } - - void BitwiseAND(const BitSet& other) - { - u32 numBitSets = static_cast(_bitSet.Count()); - - for (u32 i = 0; i < numBitSets; i++) - { - _bitSet[i] &= other._bitSet[i]; - } - } - - void BitwiseOR(const BitSet& other) - { - u32 numBitSets = static_cast(_bitSet.Count()); - - for (u32 i = 0; i < numBitSets; i++) - { - _bitSet[i] |= other._bitSet[i]; - } - } - - void BitwiseXOR(const BitSet& other) - { - u32 numBitSets = static_cast(_bitSet.Count()); - - for (u32 i = 0; i < numBitSets; i++) - { - _bitSet[i] ^= other._bitSet[i]; - } - } - - BitSet* NewBitwiseUnset(const BitSet& other) const - { - u32 numBitSets = static_cast(_bitSet.Count()); - - BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); - *result = *this; - - result->BitwiseUnset(other); - - return result; - } - - BitSet* NewBitwiseAND(const BitSet& other) const - { - u32 numBitSets = static_cast(_bitSet.Count()); - - BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); - *result = *this; - - result->BitwiseAND(other); - - return result; - } - - BitSet* NewBitwiseOR(const BitSet& other) const - { - u32 numBitSets = static_cast(_bitSet.Count()); - - BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); - *result = *this; - - result->BitwiseOR(other); - - return result; - } - - BitSet* NewBitwiseXOR(const BitSet& other) const - { - u32 numBitSets = static_cast(_bitSet.Count()); - - BitSet* result = Memory::Allocator::New(_allocator, _allocator, numBitSets); - *result = *this; - - result->BitwiseXOR(other); - - return result; - } + bool IsSubsetOf(const BitSet& other) const; - u32 NumBitSets() const { return static_cast(_bitSet.Count()); } + void BitwiseUnset(const BitSet& other); + void BitwiseAND(const BitSet& other); + void BitwiseOR(const BitSet& other); + void BitwiseXOR(const BitSet& other); - void ForEachSetBit(std::function callback) const - { - u32 numBitSets = NumBitSets(); - for (u32 i = 0; i < numBitSets; i++) - { - u64 set = _bitSet[i]; + BitSet* NewBitwiseUnset(const BitSet& other) const; + BitSet* NewBitwiseAND(const BitSet& other) const; + BitSet* NewBitwiseOR(const BitSet& other) const; + BitSet* NewBitwiseXOR(const BitSet& other) const; - for (u32 bit = 0; bit < 64; bit++) - { - bool bitIsSet = (set >> bit) & 0x1; + u32 NumBitSets() const; - if (bitIsSet) - { - callback(i, bit); - } - } - } - } + void ForEachSetBit(std::function callback) const; private: Memory::Allocator* _allocator; DynamicArray _bitSet; -}; \ No newline at end of file +}; diff --git a/Source/Base/Base/Container/PersistentBitSet.cpp b/Source/Base/Base/Container/PersistentBitSet.cpp new file mode 100644 index 00000000..1cd15db8 --- /dev/null +++ b/Source/Base/Base/Container/PersistentBitSet.cpp @@ -0,0 +1,174 @@ +#include "PersistentBitSet.h" +#include "BitSet.h" +#include +#include + +void PersistentBitSet::Reset() +{ + std::fill(_bitSet.begin(), _bitSet.end(), 0ull); +} + +void PersistentBitSet::Set(u32 index) +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + EnsureCapacity(setIndex); + _bitSet[setIndex] |= 1ull << bitIndex; +} + +void PersistentBitSet::Set(u32 setIndex, u32 bitIndex) +{ + EnsureCapacity(setIndex); + _bitSet[setIndex] |= 1ull << bitIndex; +} + +void PersistentBitSet::Unset(u32 index) +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + if (setIndex < _bitSet.size()) + _bitSet[setIndex] &= ~(1ull << bitIndex); +} + +void PersistentBitSet::Unset(u32 setIndex, u32 bitIndex) +{ + if (setIndex < _bitSet.size()) + _bitSet[setIndex] &= ~(1ull << bitIndex); +} + +bool PersistentBitSet::Has(u32 index) const +{ + u32 setIndex = index / 64; + u32 bitIndex = index % 64; + if (setIndex >= _bitSet.size()) [[unlikely]] + return false; + return (_bitSet[setIndex] & (1ull << bitIndex)) != 0; +} + +bool PersistentBitSet::Has(u32 setIndex, u32 bitIndex) const +{ + if (setIndex >= _bitSet.size()) [[unlikely]] + return false; + return (_bitSet[setIndex] & (1ull << bitIndex)) != 0; +} + +bool PersistentBitSet::HasAnyBitSet() const +{ + u64 combined = 0; + for (u64 bits : _bitSet) + combined |= bits; + return combined != 0; +} + +u64 PersistentBitSet::GetBitSet(u32 index) const +{ + return (index < _bitSet.size()) ? _bitSet[index] : 0ull; +} + +void PersistentBitSet::SetEquals(const PersistentBitSet& other) +{ + _bitSet = other._bitSet; +} + +void PersistentBitSet::SetEquals(const BitSet& other) +{ + u32 numBitSets = other.NumBitSets(); + _bitSet.resize(numBitSets); + + for (u32 i = 0; i < numBitSets; i++) + { + _bitSet[i] = other.GetBitSet(i); + } +} + +bool PersistentBitSet::IsSubsetOf(const BitSet& other) const +{ + u32 otherNumSets = other.NumBitSets(); + + for (u32 i = 0; i < _bitSet.size(); i++) + { + u64 ourBits = _bitSet[i]; + // If we have bits beyond other's capacity, they can't be subset + u64 otherBits = (i < otherNumSets) ? other.GetBitSet(i) : 0ull; + + if ((ourBits & otherBits) != ourBits) + return false; + } + return true; +} + +bool PersistentBitSet::IsSubsetOf(const PersistentBitSet& other) const +{ + for (u32 i = 0; i < _bitSet.size(); i++) + { + u64 ourBits = _bitSet[i]; + u64 otherBits = other.GetBitSet(i); + if ((ourBits & otherBits) != ourBits) + return false; + } + return true; +} + +void PersistentBitSet::BitwiseUnset(const PersistentBitSet& other) +{ + u32 count = std::min(static_cast(_bitSet.size()), other.NumBitSets()); + for (u32 i = 0; i < count; i++) + _bitSet[i] &= ~other._bitSet[i]; +} + +void PersistentBitSet::BitwiseUnset(const BitSet& other) +{ + u32 otherNumSets = other.NumBitSets(); + u32 count = std::min(static_cast(_bitSet.size()), otherNumSets); + + for (u32 i = 0; i < count; i++) + { + _bitSet[i] &= ~other.GetBitSet(i); + } +} + +void PersistentBitSet::BitwiseAND(const PersistentBitSet& other) +{ + u32 count = std::min(static_cast(_bitSet.size()), other.NumBitSets()); + for (u32 i = 0; i < count; i++) + _bitSet[i] &= other._bitSet[i]; + // Bits beyond other's size become 0 (AND with implicit 0) + for (u32 i = count; i < _bitSet.size(); i++) + _bitSet[i] = 0; +} + +void PersistentBitSet::BitwiseOR(const PersistentBitSet& other) +{ + if (other.NumBitSets() > _bitSet.size()) + _bitSet.resize(other.NumBitSets(), 0ull); + for (u32 i = 0; i < other.NumBitSets(); i++) + _bitSet[i] |= other._bitSet[i]; +} + +void PersistentBitSet::BitwiseXOR(const PersistentBitSet& other) +{ + if (other.NumBitSets() > _bitSet.size()) + _bitSet.resize(other.NumBitSets(), 0ull); + for (u32 i = 0; i < other.NumBitSets(); i++) + _bitSet[i] ^= other._bitSet[i]; +} + +void PersistentBitSet::ForEachSetBit(std::function callback) const +{ + for (u32 i = 0; i < _bitSet.size(); i++) + { + u64 bits = _bitSet[i]; + while (bits != 0) + { + u32 bit = static_cast(std::countr_zero(bits)); + callback(i, bit); + bits &= bits - 1; // Clear lowest set bit + } + } +} + +void PersistentBitSet::EnsureCapacity(u32 setIndex) +{ + if (setIndex >= _bitSet.size()) [[unlikely]] + _bitSet.resize(setIndex + 1, 0ull); +} diff --git a/Source/Base/Base/Container/PersistentBitSet.h b/Source/Base/Base/Container/PersistentBitSet.h new file mode 100644 index 00000000..ba111901 --- /dev/null +++ b/Source/Base/Base/Container/PersistentBitSet.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include + +class BitSet; // Forward declaration + +class PersistentBitSet +{ +public: + PersistentBitSet() = default; + + // Resets all bits to 0 (keeps capacity) + void Reset(); + + // Set bit at linear index (grows if needed) + void Set(u32 index); + + // Set bit at (setIndex, bitIndex) - grows if needed + void Set(u32 setIndex, u32 bitIndex); + + // Unset bit at linear index + void Unset(u32 index); + + // Unset bit at (setIndex, bitIndex) + void Unset(u32 setIndex, u32 bitIndex); + + // Check bit at linear index + [[nodiscard]] bool Has(u32 index) const; + + // Check bit at (setIndex, bitIndex) + [[nodiscard]] bool Has(u32 setIndex, u32 bitIndex) const; + + [[nodiscard]] bool IsEmpty() const { return _bitSet.empty(); } + [[nodiscard]] u32 NumBitSets() const { return static_cast(_bitSet.size()); } + + // Returns true if any bit is set + [[nodiscard]] bool HasAnyBitSet() const; + + // Get raw u64 at index (returns 0 if out of bounds) + [[nodiscard]] u64 GetBitSet(u32 index) const; + + // Copy bits from another PersistentBitSet + void SetEquals(const PersistentBitSet& other); + + // Copy bits from a BitSet (defined in .cpp to avoid circular include) + void SetEquals(const BitSet& other); + + // Check if this is a subset of a BitSet (defined in .cpp to avoid circular include) + [[nodiscard]] bool IsSubsetOf(const BitSet& other) const; + + // Check if this is a subset of another PersistentBitSet + [[nodiscard]] bool IsSubsetOf(const PersistentBitSet& other) const; + + // Bitwise operations (grows to match other's size if needed) + void BitwiseUnset(const PersistentBitSet& other); + + // BitwiseUnset with BitSet (defined in .cpp to avoid circular include) + void BitwiseUnset(const BitSet& other); + + void BitwiseAND(const PersistentBitSet& other); + void BitwiseOR(const PersistentBitSet& other); + void BitwiseXOR(const PersistentBitSet& other); + + // Efficient iteration using countr_zero (only visits set bits) + void ForEachSetBit(std::function callback) const; + +private: + void EnsureCapacity(u32 setIndex); + + std::vector _bitSet; +}; diff --git a/Source/Renderer/Renderer/DescriptorSet.cpp b/Source/Renderer/Renderer/DescriptorSet.cpp index be1c8a1a..75a9ee39 100644 --- a/Source/Renderer/Renderer/DescriptorSet.cpp +++ b/Source/Renderer/Renderer/DescriptorSet.cpp @@ -87,7 +87,7 @@ namespace Renderer } } } - + // Pixel if (pipelineDesc.states.pixelShader != PixelShaderID::Invalid()) { diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.cpp b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.cpp index 4ac8c389..a947ecf8 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.cpp +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.cpp @@ -1,12 +1,18 @@ #include "DescriptorHandlerVK.h" #include "TextureHandlerVK.h" +#include "BufferHandlerVK.h" #include "RenderDeviceVK.h" #include "FormatConverterVK.h" +#include "Renderer/TrackedBufferBitSets.h" #include +#include #include +#include + #include +#include namespace Renderer { @@ -28,6 +34,22 @@ namespace Renderer VkDescriptorSet sets[RenderDeviceVK::FRAME_INDEX_COUNT]; VkDescriptorSetLayout layout; + + PersistentBitSet bufferAccesses; // All accessed buffers (for pipeline stage check) + PersistentBitSet bufferReadAccesses; // Buffers read from + PersistentBitSet bufferWriteAccesses; // Buffers written to + + // Bitset to show which bindings are write access + PersistentBitSet writeBindings; + + // Bitset to track unbound bindings - set on create, unset on bind + PersistentBitSet unboundBindings; + + // Track which buffer is bound to each binding so we can unset the bit when rebound + std::unordered_map bindingToBuffer; + + // Reverse map: buffer -> binding (for fast lookup during validation) + std::unordered_map bufferToBinding; }; struct DescriptorHandlerData : public IDescriptorHandlerData @@ -39,11 +61,33 @@ namespace Renderer std::vector descriptorSets; }; - void DescriptorHandlerVK::Init(RenderDeviceVK* device, TextureHandlerVK* textureHandler) + std::string GetBindingName(const DescriptorSet& descriptorSet, u32 binding) + { + for (const auto& [_, descriptor] : descriptorSet.desc.reflection->descriptors) + { + if (descriptor.binding == binding) + return descriptor.name; + } + return "Unknown"; + } + + std::string BindingSlotNames[] = { + "INVALID", + "DEBUG", + "GLOBAL", + "LIGHT", + "TERRAIN", + "MODEL", + "PER_PASS", + "PER_DRAW" + }; + + void DescriptorHandlerVK::Init(RenderDeviceVK* device, TextureHandlerVK* textureHandler, BufferHandlerVK* bufferHandler) { ZoneScoped; _device = device; _textureHandler = textureHandler; + _bufferHandler = bufferHandler; _data = new DescriptorHandlerData(); CreateDescriptorPool(); @@ -63,6 +107,96 @@ namespace Renderer return id; } + bool DescriptorHandlerVK::ValidatePermissionViolations(u32 slot, const DescriptorSet& descriptorSet, const PersistentBitSet& accesses, const BitSet& permissions, const char* permissionName, const PersistentBitSet* usedBindings) + { + if (accesses.IsEmpty()) + return false; + + if (accesses.IsSubsetOf(permissions)) + return false; + + // Compute violations: accesses & ~permissions + PersistentBitSet violations; + violations.SetEquals(accesses); + violations.BitwiseUnset(permissions); + + bool didError = false; + violations.ForEachSetBit([&](u32 setIndex, u32 bitIndex) + { + u32 bufferIndex = setIndex * 64 + bitIndex; + + auto it = descriptorSet.bufferToBinding.find(bufferIndex); + i32 binding = (it != descriptorSet.bufferToBinding.end()) ? static_cast(it->second) : -1; + + // Skip buffers at bindings the current pipeline doesn't use + if (usedBindings && binding >= 0 && !usedBindings->Has(static_cast(binding))) + return; + + if (!didError) + { + NC_LOG_ERROR("--- {} ACCESS VIOLATIONS ---", permissionName); + didError = true; + } + + BufferID bufferID = BufferID(bufferIndex); + const std::string& bufferName = _bufferHandler->GetBufferName(bufferID); + std::string bindingName = (binding >= 0) ? GetBindingName(descriptorSet, static_cast(binding)) : "Unknown"; + + NC_LOG_ERROR(" ({}) Set {} Buffer {} '{}' at binding {} '{}' needs {} permission", BindingSlotNames[slot], bufferIndex, bufferName, binding, bindingName, permissionName); + }); + + return didError; + } + + void DescriptorHandlerVK::ValidatePermissions(u32 slot, DescriptorSetID descriptorSetID, const TrackedBufferBitSets* bufferPermissions, bool isGraphicsPipeline, const PersistentBitSet* usedBindings) + { + ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + + DescriptorSetID::type id = static_cast(descriptorSetID); + if (id >= data.descriptorSets.size()) + return; + + DescriptorSet& descriptorSet = data.descriptorSets[id]; + bool didError = false; + + // Check for unbound bindings + if (descriptorSet.unboundBindings.HasAnyBitSet()) + { + bool hasUnbound = false; + descriptorSet.unboundBindings.ForEachSetBit([&](u32 setIndex, u32 bitIndex) + { + u32 binding = setIndex * 64 + bitIndex; + if (usedBindings && !usedBindings->Has(binding)) + return; + + if (!hasUnbound) + { + NC_LOG_ERROR("--- UNBOUND BINDINGS ---"); + hasUnbound = true; + } + std::string bindingName = GetBindingName(descriptorSet, binding); + NC_LOG_ERROR(" ({}) Binding {} '{}' was never bound", BindingSlotNames[slot], binding, bindingName); + }); + didError |= hasUnbound; + } + + // Check read accesses + didError |= ValidatePermissionViolations(slot, descriptorSet, descriptorSet.bufferReadAccesses, bufferPermissions->GetReadBitSet(), "READ", usedBindings); + + // Check write accesses + didError |= ValidatePermissionViolations(slot, descriptorSet, descriptorSet.bufferWriteAccesses, bufferPermissions->GetWriteBitSet(), "WRITE", usedBindings); + + // Check pipeline stage permissions + const BitSet& stagePermissions = isGraphicsPipeline ? bufferPermissions->GetGraphicsBitSet() : bufferPermissions->GetComputeBitSet(); + didError |= ValidatePermissionViolations(slot, descriptorSet, descriptorSet.bufferAccesses, stagePermissions, isGraphicsPipeline ? "GRAPHICS" : "COMPUTE", usedBindings); + + if (didError) + { + NC_LOG_CRITICAL("ValidatePermissions failed for DescriptorSet {}", id); + } + } + VkDescriptorSet DescriptorHandlerVK::GetVkDescriptorSet(DescriptorSetID descriptorSetID, u32 frameIndex) { DescriptorHandlerData& data = *static_cast(_data); @@ -189,11 +323,70 @@ namespace Renderer NC_LOG_CRITICAL("DescriptorHandlerVK::CreateDescriptorSet: Failed to allocate descriptor set! You probably need to increase maxDescriptorSets."); } } + + // Store binding info from reflection + for (auto& [_, descriptor] : descriptorSet.desc.reflection->descriptors) + { + // Track which bindings need to be bound + descriptorSet.unboundBindings.Set(descriptor.binding); + + // Track which bindings are write access + if (descriptor.accessType == FileFormat::DescriptorAccessTypeReflection::ReadWrite || + descriptor.accessType == FileFormat::DescriptorAccessTypeReflection::Write) + { + descriptorSet.writeBindings.Set(descriptor.binding); + } + } } - void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, VkBuffer buffer, DescriptorType type, u32 frameIndex) + void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, VkBuffer buffer, DescriptorType type, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + + BufferID::type newBufferIndex = static_cast(bufferID); + + // Check if this binding already has a buffer bound + auto it = descriptorSet.bindingToBuffer.find(binding); + if (it != descriptorSet.bindingToBuffer.end()) + { + BufferID oldBufferID = it->second; + + // Only unset if it's a different buffer + if (oldBufferID != bufferID) + { + BufferID::type oldBufferIndex = static_cast(oldBufferID); + + // Unset the old buffer's bits + descriptorSet.bufferAccesses.Unset(oldBufferIndex); + descriptorSet.bufferReadAccesses.Unset(oldBufferIndex); + if (descriptorSet.writeBindings.Has(binding)) + { + descriptorSet.bufferWriteAccesses.Unset(oldBufferIndex); + } + descriptorSet.bufferToBinding.erase(oldBufferIndex); + } + } + + // Set the new buffer's bits + descriptorSet.bufferAccesses.Set(newBufferIndex); + descriptorSet.bufferReadAccesses.Set(newBufferIndex); + + // Pure bitwise check - no hash lookup + if (descriptorSet.writeBindings.Has(binding)) + { + descriptorSet.bufferWriteAccesses.Set(newBufferIndex); + } + + // Update the binding <-> buffer mappings + descriptorSet.bindingToBuffer[binding] = bufferID; + descriptorSet.bufferToBinding[newBufferIndex] = binding; + + // Mark binding as bound + descriptorSet.unboundBindings.Unset(binding); + + // Vulkan descriptor update VkDescriptorBufferInfo bufferInfo{}; bufferInfo.buffer = buffer; bufferInfo.offset = 0; @@ -212,6 +405,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, VkImageView image, DescriptorType type, bool isRT, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + VkDescriptorImageInfo imageInfo{}; imageInfo.imageView = image; imageInfo.imageLayout = (!isRT && type == DescriptorType::SampledImage) ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; @@ -229,6 +426,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptorArray(DescriptorSetID setID, u32 binding, VkImageView image, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + VkDescriptorImageInfo imageInfo{}; imageInfo.imageView = image; imageInfo.imageLayout = (!isRT && type == DescriptorType::SampledImage) ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; @@ -247,6 +448,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptorArray(DescriptorSetID setID, u32 binding, std::vector& images, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + u32 count = static_cast(images.size()); VkImageLayout layout = (!isRT && type == DescriptorType::SampledImage) @@ -275,6 +480,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, VkSampler sampler, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + VkDescriptorImageInfo samplerInfo{}; samplerInfo.sampler = sampler; @@ -291,6 +500,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptorArray(DescriptorSetID setID, u32 binding, VkSampler sampler, u32 arrayIndex, u32 frameIndex) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + VkDescriptorImageInfo samplerInfo{}; samplerInfo.sampler = sampler; @@ -308,6 +521,10 @@ namespace Renderer void DescriptorHandlerVK::BindDescriptor(DescriptorSetID setID, u32 binding, TextureArrayID textureArrayID) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + // Register this binding so future texture array updates can propagate to this descriptor set _textureHandler->RegisterTextureArrayBinding(textureArrayID, setID, binding); @@ -352,6 +569,10 @@ namespace Renderer void DescriptorHandlerVK::UpdateTextureArrayDescriptors(DescriptorSetID setID, u32 binding, const TextureID* textureIDs, u32 startIndex, u32 count) { ZoneScoped; + DescriptorHandlerData& data = *static_cast(_data); + DescriptorSet& descriptorSet = data.descriptorSets[static_cast(setID)]; + descriptorSet.unboundBindings.Unset(binding); + if (count == 0) { return; diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.h b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.h index bc956580..901a4b83 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.h +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/DescriptorHandlerVK.h @@ -1,4 +1,5 @@ #pragma once +#include "Renderer/Descriptors/BufferDesc.h" #include "Renderer/Descriptors/DescriptorSetDesc.h" #include "Renderer/Descriptors/TextureDesc.h" #include "Renderer/Descriptors/TextureArrayDesc.h" @@ -9,12 +10,18 @@ #include +class PersistentBitSet; +class BitSet; + namespace Renderer { + class TrackedBufferBitSets; + namespace Backend { class RenderDeviceVK; class TextureHandlerVK; + class BufferHandlerVK; struct DescriptorSet; struct IDescriptorHandlerData {}; @@ -22,11 +29,11 @@ namespace Renderer class DescriptorHandlerVK { public: - void Init(RenderDeviceVK* device, TextureHandlerVK* textureHandler); + void Init(RenderDeviceVK* device, TextureHandlerVK* textureHandler, BufferHandlerVK* bufferHandler); DescriptorSetID CreateDescriptorSet(const DescriptorSetDesc& desc); - void BindDescriptor(DescriptorSetID setID, u32 binding, VkBuffer buffer, DescriptorType type, u32 frameIndex); + void BindDescriptor(DescriptorSetID setID, u32 binding, BufferID bufferID, VkBuffer buffer, DescriptorType type, u32 frameIndex); void BindDescriptor(DescriptorSetID setID, u32 binding, VkImageView image, DescriptorType type, bool isRT, u32 frameIndex); void BindDescriptorArray(DescriptorSetID setID, u32 binding, VkImageView image, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex); void BindDescriptorArray(DescriptorSetID setID, u32 binding, std::vector& images, u32 arrayOffset, DescriptorType type, bool isRT, u32 frameIndex); @@ -37,17 +44,19 @@ namespace Renderer // Updates only a range of descriptors in a texture array binding (for incremental updates) void UpdateTextureArrayDescriptors(DescriptorSetID setID, u32 binding, const TextureID* textureIDs, u32 startIndex, u32 count); + void ValidatePermissions(u32 slot, DescriptorSetID descriptorSetID, const TrackedBufferBitSets* bufferPermissions, bool isGraphicsPipeline, const PersistentBitSet* usedBindings = nullptr); VkDescriptorSet GetVkDescriptorSet(DescriptorSetID descriptorSetID, u32 frameIndex); VkDescriptorSetLayout GetVkDescriptorSetLayout(DescriptorSetID descriptorSetID); private: void CreateDescriptorPool(); - void CreateDescriptorSet(DescriptorSet& descriptorSet); + bool ValidatePermissionViolations(u32 slot, const DescriptorSet& descriptorSet, const PersistentBitSet& accesses, const BitSet& permissions, const char* permissionName, const PersistentBitSet* usedBindings = nullptr); private: RenderDeviceVK* _device; TextureHandlerVK* _textureHandler; + BufferHandlerVK* _bufferHandler; IDescriptorHandlerData* _data; }; diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.cpp b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.cpp index 9969cdd3..40f7858d 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.cpp +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.cpp @@ -69,6 +69,11 @@ namespace Renderer { std::vector images; std::vector depthImages; + + std::vector imagesToDestroy; + std::vector allocationsToDestroy; + + std::vector imageViewsToDestroy; }; void ImageHandlerVK::Init(RenderDeviceVK* device, SamplerHandlerVK* samplerHandler) @@ -91,6 +96,25 @@ namespace Renderer _imguiSampler = _samplerHandler->CreateSampler(samplerDesc); } + void ImageHandlerVK::FlipFrame(u32 frameIndex) + { + ImageHandlerVKData& data = static_cast(*_data); + + for (u32 i = 0; i < data.imageViewsToDestroy.size(); i++) + { + vkDestroyImageView(_device->_device, data.imageViewsToDestroy[i], nullptr); + } + + for(u32 i = 0; i < data.imagesToDestroy.size(); i++) + { + vmaDestroyImage(_device->_allocator, data.imagesToDestroy[i], data.allocationsToDestroy[i]); + } + + data.imageViewsToDestroy.clear(); + data.imagesToDestroy.clear(); + data.allocationsToDestroy.clear(); + } + void ImageHandlerVK::OnResize(bool windowResize) { ImageHandlerVKData& data = static_cast(*_data); @@ -110,15 +134,16 @@ namespace Renderer { descriptorsToFree.push_back(image.imguiTextureHandle); - // Destroy old image - vkDestroyImageView(_device->_device, image.colorView, nullptr); - vmaDestroyImage(_device->_allocator, image.image, image.allocation); + // Queue up old image for destruction + data.imagesToDestroy.push_back(image.image); + data.allocationsToDestroy.push_back(image.allocation); + data.imageViewsToDestroy.push_back(image.colorView); for (u32 i = 0; i < image.desc.mipLevels; i++) { - vkDestroyImageView(_device->_device, image.mipViews[i], nullptr); + data.imageViewsToDestroy.push_back(image.mipViews[i]); } - + // Create new VkFormat format; CreateImage(image, format); @@ -134,9 +159,10 @@ namespace Renderer { if (image.desc.dimensionType == resizeType || image.desc.dimensionType == resizePyramidType) { - // Destroy old image - vkDestroyImageView(_device->_device, image.depthView, nullptr); - vmaDestroyImage(_device->_allocator, image.image, image.allocation); + // Queue up old image for destruction + data.imagesToDestroy.push_back(image.image); + data.allocationsToDestroy.push_back(image.allocation); + data.imageViewsToDestroy.push_back(image.depthView); // Create new CreateImage(image); @@ -784,8 +810,6 @@ namespace Renderer void ImageHandlerVK::CreateImageViews(Image& image, VkFormat format) { - // ImageHandlerVKData& data = static_cast(*_data); - // Create Color View for individual mips VkImageViewCreateInfo colorViewInfo = {}; colorViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.h b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.h index e2c696fc..8608f4b2 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.h +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/ImageHandlerVK.h @@ -25,6 +25,7 @@ namespace Renderer void Init(RenderDeviceVK* device, SamplerHandlerVK* samplerHandler); void PostInit(); + void FlipFrame(u32 frameIndex); void OnResize(bool windowResize); ImageID CreateImage(const ImageDesc& desc); diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.cpp b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.cpp index 0fd928e5..826ac781 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.cpp +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.cpp @@ -9,8 +9,10 @@ #include #include #include +#include #include +#include namespace Renderer { @@ -38,6 +40,9 @@ namespace Renderer std::vector descriptorSetLayouts; std::vector pushConstantRanges; + + std::unordered_set setsUsed; + std::unordered_map usedBindingsPerSlot; }; struct ComputePipelineCacheDesc @@ -57,6 +62,9 @@ namespace Renderer std::vector descriptorSetLayouts; std::vector pushConstantRanges; + + std::unordered_set setsUsed; + std::unordered_map usedBindingsPerSlot; }; struct PipelineHandlerVKData : IPipelineHandlerVKData @@ -262,6 +270,34 @@ namespace Renderer return static_cast(data.computePipelines[static_cast(id)].descriptorSetLayoutDatas.size()); } + bool PipelineHandlerVK::UsesDescriptorSet(GraphicsPipelineID id, u32 setNumber) + { + PipelineHandlerVKData& data = static_cast(*_data); + return static_cast(data.graphicsPipelines[static_cast(id)].setsUsed.contains(setNumber)); + } + + bool PipelineHandlerVK::UsesDescriptorSet(ComputePipelineID id, u32 setNumber) + { + PipelineHandlerVKData& data = static_cast(*_data); + return static_cast(data.computePipelines[static_cast(id)].setsUsed.contains(setNumber)); + } + + const PersistentBitSet* PipelineHandlerVK::GetUsedBindings(GraphicsPipelineID id, u32 slot) + { + PipelineHandlerVKData& data = static_cast(*_data); + auto& map = data.graphicsPipelines[static_cast(id)].usedBindingsPerSlot; + auto it = map.find(slot); + return (it != map.end()) ? &it->second : nullptr; + } + + const PersistentBitSet* PipelineHandlerVK::GetUsedBindings(ComputePipelineID id, u32 slot) + { + PipelineHandlerVKData& data = static_cast(*_data); + auto& map = data.computePipelines[static_cast(id)].usedBindingsPerSlot; + auto it = map.find(slot); + return (it != map.end()) ? &it->second : nullptr; + } + DescriptorSetLayoutData& PipelineHandlerVK::GetDescriptorSetLayoutData(GraphicsPipelineID id, u32 index) { PipelineHandlerVKData& data = static_cast(*_data); @@ -376,12 +412,28 @@ namespace Renderer std::vector bindInfoPushConstants; if (desc.states.vertexShader != VertexShaderID::Invalid()) { + // Find which sets are used by the shader + const BindReflection& usedBindReflection = _shaderHandler->GetUsedBindReflection(desc.states.vertexShader); + for (const BindInfo& bindInfo : usedBindReflection.dataBindings) + { + pipeline.setsUsed.insert(bindInfo.set); + pipeline.usedBindingsPerSlot[bindInfo.set].Set(bindInfo.binding); + } + const BindReflection& bindReflection = _shaderHandler->GetFullBindReflection(desc.states.vertexShader); bindInfos.insert(bindInfos.end(), bindReflection.dataBindings.begin(), bindReflection.dataBindings.end()); bindInfoPushConstants.insert(bindInfoPushConstants.end(), bindReflection.pushConstants.begin(), bindReflection.pushConstants.end()); } if (desc.states.pixelShader != PixelShaderID::Invalid()) { + // Find which sets are used by the shader + const BindReflection& usedBindReflection = _shaderHandler->GetUsedBindReflection(desc.states.pixelShader); + for (const BindInfo& bindInfo : usedBindReflection.dataBindings) + { + pipeline.setsUsed.insert(bindInfo.set); + pipeline.usedBindingsPerSlot[bindInfo.set].Set(bindInfo.binding); + } + const BindReflection& bindReflection = _shaderHandler->GetFullBindReflection(desc.states.pixelShader); // Loop over all new databindings @@ -762,11 +814,20 @@ namespace Renderer { u32 numSupportedTextures = _device->HasExtendedTextureSupport() ? 8192 : 4096; + // Find which sets are used by the shader + const BindReflection& usedBindReflection = _shaderHandler->GetUsedBindReflection(desc.computeShader); + for(const BindInfo& bindInfo : usedBindReflection.dataBindings) + { + pipeline.setsUsed.insert(bindInfo.set); + pipeline.usedBindingsPerSlot[bindInfo.set].Set(bindInfo.binding); + } + std::vector bindInfos; std::vector bindInfoPushConstants; const BindReflection& bindReflection = _shaderHandler->GetFullBindReflection(desc.computeShader); bindInfos.insert(bindInfos.end(), bindReflection.dataBindings.begin(), bindReflection.dataBindings.end()); bindInfoPushConstants.insert(bindInfoPushConstants.end(), bindReflection.pushConstants.begin(), bindReflection.pushConstants.end()); + for (BindInfo& bindInfo : bindInfos) { DescriptorSetLayoutData& layout = GetDescriptorSet(bindInfo.set, pipeline.descriptorSetLayoutDatas); @@ -783,6 +844,7 @@ namespace Renderer layout.isTextureArray.push_back(isTextureArray); layout.isVariableBinding.push_back(bindInfo.count == 0); } + for (BindInfoPushConstant& pushConstant : bindInfoPushConstants) { VkPushConstantRange& range = pipeline.pushConstantRanges.emplace_back(); @@ -790,6 +852,7 @@ namespace Renderer range.size = pushConstant.size; range.stageFlags = pushConstant.stageFlags; } + size_t numDescriptorSets = pipeline.descriptorSetLayoutDatas.size(); pipeline.descriptorSetLayouts.resize(numDescriptorSets); for (size_t i = 0; i < numDescriptorSets; i++) @@ -825,12 +888,14 @@ namespace Renderer } DebugMarkerUtilVK::SetObjectName(_device->_device, (uint64_t)pipeline.descriptorSetLayouts[i], VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, desc.debugName.c_str()); } + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = static_cast(pipeline.descriptorSetLayouts.size()); pipelineLayoutInfo.pSetLayouts = pipeline.descriptorSetLayouts.data(); pipelineLayoutInfo.pushConstantRangeCount = static_cast(pipeline.pushConstantRanges.size()); // Optional pipelineLayoutInfo.pPushConstantRanges = pipeline.pushConstantRanges.data(); + if (vkCreatePipelineLayout(_device->_device, &pipelineLayoutInfo, nullptr, &pipeline.pipelineLayout) != VK_SUCCESS) { NC_LOG_CRITICAL("Failed to create pipeline layout!"); diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.h b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.h index 7b29655e..c105786f 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.h +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/PipelineHandlerVK.h @@ -10,6 +10,8 @@ #include +class PersistentBitSet; + namespace Memory { class Allocator; @@ -69,6 +71,12 @@ namespace Renderer u32 GetNumDescriptorSetLayouts(GraphicsPipelineID id); u32 GetNumDescriptorSetLayouts(ComputePipelineID id); + bool UsesDescriptorSet(GraphicsPipelineID id, u32 setNumber); + bool UsesDescriptorSet(ComputePipelineID id, u32 setNumber); + + const PersistentBitSet* GetUsedBindings(GraphicsPipelineID id, u32 slot); + const PersistentBitSet* GetUsedBindings(ComputePipelineID id, u32 slot); + DescriptorSetLayoutData& GetDescriptorSetLayoutData(GraphicsPipelineID id, u32 index); DescriptorSetLayoutData& GetDescriptorSetLayoutData(ComputePipelineID id, u32 index); diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/SpirvReflect.h b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/SpirvReflect.h index 437fb1f8..c6e13ee9 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/Backend/SpirvReflect.h +++ b/Source/Renderer/Renderer/Renderers/Vulkan/Backend/SpirvReflect.h @@ -33,7 +33,7 @@ VERSION HISTORY #define SPIRV_REFLECT_USE_SYSTEM_SPIRV_H #if defined(SPIRV_REFLECT_USE_SYSTEM_SPIRV_H) -#include +#include #else #include "./include/spirv/unified1/spirv.h" #endif diff --git a/Source/Renderer/Renderer/Renderers/Vulkan/RendererVK.cpp b/Source/Renderer/Renderer/Renderers/Vulkan/RendererVK.cpp index 027c9865..9c699450 100644 --- a/Source/Renderer/Renderer/Renderers/Vulkan/RendererVK.cpp +++ b/Source/Renderer/Renderer/Renderers/Vulkan/RendererVK.cpp @@ -51,7 +51,7 @@ namespace Renderer _device->Init(); _bufferHandler->Init(_device); - _descriptorHandler->Init(_device, _textureHandler); + _descriptorHandler->Init(_device, _textureHandler, _bufferHandler); _commandListHandler->Init(_device); _samplerHandler->Init(_device); _semaphoreHandler->Init(_device); @@ -254,7 +254,7 @@ namespace Renderer void RendererVK::BindDescriptor(DescriptorSetID descriptorSetID, u32 bindingIndex, BufferID bufferID, DescriptorType type, u32 frameIndex) { VkBuffer buffer = _bufferHandler->GetBuffer(bufferID); - _descriptorHandler->BindDescriptor(descriptorSetID, bindingIndex, buffer, type, frameIndex); + _descriptorHandler->BindDescriptor(descriptorSetID, bindingIndex, bufferID, buffer, type, frameIndex); } void RendererVK::BindDescriptor(DescriptorSetID descriptorSetID, u32 bindingIndex, ImageID imageID, u32 mipLevel, DescriptorType type, u32 frameIndex) @@ -409,15 +409,13 @@ namespace Renderer timeWaited = timer.GetLifeTime(); } + _imageHandler->FlipFrame(frameIndex); if (_renderSizeChanged) { _device->FlushGPU(); _device->SetRenderSize(_renderSize); - //_pipelineHandler->DiscardPipelines(); - //CreateDummyPipeline(); - _imageHandler->OnResize(false); _renderSizeChanged = false; @@ -1020,19 +1018,19 @@ namespace Renderer } renderPassOpenCount--; _commandListHandler->SetRenderPassOpenCount(commandListID, renderPassOpenCount); - VkCommandBuffer commandBuffer = _commandListHandler->GetCommandBuffer(commandListID); vkCmdEndRendering(commandBuffer); + std::vector imageBarriers; + for (int i = 0; i < MAX_RENDER_TARGETS; i++) { if (desc.renderTargets[i] == TextureID::Invalid()) break; TextureBaseDesc textureDesc = _textureHandler->GetTextureDesc(desc.renderTargets[i]); - - // Manual transition for color attachment back to original layout VkImage image = _textureHandler->GetImage(desc.renderTargets[i]); + VkImageMemoryBarrier barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.pNext = nullptr; @@ -1040,7 +1038,8 @@ namespace Renderer barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL; - + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = 0; @@ -1048,13 +1047,18 @@ namespace Renderer barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; + imageBarriers.push_back(barrier); + } + + if (!imageBarriers.empty()) + { vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, - 1, &barrier); + static_cast(imageBarriers.size()), imageBarriers.data()); } } @@ -1401,12 +1405,16 @@ namespace Renderer if (graphicsPipelineID != GraphicsPipelineID::Invalid()) { - /*if (_pipelineHandler->GetNumDescriptorSetLayouts(graphicsPipelineID) <= slot) + if (!_pipelineHandler->UsesDescriptorSet(graphicsPipelineID, slot)) { - return; - }*/ + if (slot == DescriptorSetSlot::DEBUG) + return; - // TODO: Validate buffer permissions + NC_LOG_CRITICAL("Tried to bind a descriptor set to a graphics pipeline that doesn't use it (slot {})", slot); + return; + } + const PersistentBitSet* usedBindings = _pipelineHandler->GetUsedBindings(graphicsPipelineID, slot); + _descriptorHandler->ValidatePermissions(slot, descriptorSetID, bufferPermissions, true, usedBindings); VkPipelineLayout pipelineLayout = _pipelineHandler->GetPipelineLayout(graphicsPipelineID); @@ -1415,12 +1423,16 @@ namespace Renderer } else if (computePipelineID != ComputePipelineID::Invalid()) { - /*if (_pipelineHandler->GetNumDescriptorSetLayouts(computePipelineID) <= slot) + if (!_pipelineHandler->UsesDescriptorSet(computePipelineID, slot)) { - return; - }*/ + if (slot == DescriptorSetSlot::DEBUG) + return; - // TODO: Validate buffer permissions + NC_LOG_CRITICAL("Tried to bind a descriptor set to a compute pipeline that doesn't use it (slot {})", slot); + return; + } + const PersistentBitSet* usedBindings = _pipelineHandler->GetUsedBindings(computePipelineID, slot); + _descriptorHandler->ValidatePermissions(slot, descriptorSetID, bufferPermissions, false, usedBindings); VkPipelineLayout pipelineLayout = _pipelineHandler->GetPipelineLayout(computePipelineID);