Renderer: Add support for storage buffers

This commit is contained in:
SirLynix 2022-06-17 20:15:16 +02:00
parent 0978feafbc
commit 093d9d344e
16 changed files with 160 additions and 18 deletions

View File

@ -41,17 +41,26 @@ namespace Nz
private: private:
struct DescriptorPool; struct DescriptorPool;
struct StorageBufferDescriptor;
struct TextureDescriptor; struct TextureDescriptor;
struct UniformBufferDescriptor; struct UniformBufferDescriptor;
DescriptorPool& AllocatePool(); DescriptorPool& AllocatePool();
ShaderBindingPtr AllocateFromPool(std::size_t poolIndex, UInt32 setIndex); ShaderBindingPtr AllocateFromPool(std::size_t poolIndex, UInt32 setIndex);
template<typename F> void ForEachDescriptor(std::size_t poolIndex, std::size_t bindingIndex, F&& functor); template<typename F> void ForEachDescriptor(std::size_t poolIndex, std::size_t bindingIndex, F&& functor);
StorageBufferDescriptor& GetStorageBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex);
TextureDescriptor& GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); TextureDescriptor& GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex);
UniformBufferDescriptor& GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); UniformBufferDescriptor& GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex);
void Release(ShaderBinding& binding); void Release(ShaderBinding& binding);
inline void TryToShrink(); inline void TryToShrink();
struct StorageBufferDescriptor
{
GLuint buffer;
GLintptr offset;
GLsizeiptr size;
};
struct TextureDescriptor struct TextureDescriptor
{ {
GLuint texture; GLuint texture;
@ -66,7 +75,7 @@ namespace Nz
GLsizeiptr size; GLsizeiptr size;
}; };
using Descriptor = std::variant<std::monostate, TextureDescriptor, UniformBufferDescriptor>; using Descriptor = std::variant<std::monostate, StorageBufferDescriptor, TextureDescriptor, UniformBufferDescriptor>;
struct DescriptorPool struct DescriptorPool
{ {

View File

@ -265,6 +265,7 @@ namespace Nz
case GL::BufferTarget::ElementArray: return GL_ELEMENT_ARRAY_BUFFER; case GL::BufferTarget::ElementArray: return GL_ELEMENT_ARRAY_BUFFER;
case GL::BufferTarget::PixelPack: return GL_PIXEL_PACK_BUFFER; case GL::BufferTarget::PixelPack: return GL_PIXEL_PACK_BUFFER;
case GL::BufferTarget::PixelUnpack: return GL_PIXEL_UNPACK_BUFFER; case GL::BufferTarget::PixelUnpack: return GL_PIXEL_UNPACK_BUFFER;
case GL::BufferTarget::Storage: return GL_SHADER_STORAGE_BUFFER;
case GL::BufferTarget::TransformFeedback: return GL_TRANSFORM_FEEDBACK_BUFFER; case GL::BufferTarget::TransformFeedback: return GL_TRANSFORM_FEEDBACK_BUFFER;
case GL::BufferTarget::Uniform: return GL_UNIFORM_BUFFER; case GL::BufferTarget::Uniform: return GL_UNIFORM_BUFFER;
} }

View File

@ -36,6 +36,7 @@ namespace Nz::GL
ElementArray, ElementArray,
PixelPack, PixelPack,
PixelUnpack, PixelUnpack,
Storage,
TransformFeedback, TransformFeedback,
Uniform, Uniform,
@ -52,6 +53,7 @@ namespace Nz::GL
{ {
DepthClamp, DepthClamp,
SpirV, SpirV,
StorageBuffers,
TextureCompressionS3tc, TextureCompressionS3tc,
TextureFilterAnisotropic, TextureFilterAnisotropic,
@ -120,6 +122,7 @@ namespace Nz::GL
void BindFramebuffer(FramebufferTarget target, GLuint fbo) const; void BindFramebuffer(FramebufferTarget target, GLuint fbo) const;
void BindProgram(GLuint program) const; void BindProgram(GLuint program) const;
void BindSampler(UInt32 textureUnit, GLuint sampler) const; void BindSampler(UInt32 textureUnit, GLuint sampler) const;
void BindStorageBuffer(UInt32 storageUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const;
void BindTexture(TextureTarget target, GLuint texture) const; void BindTexture(TextureTarget target, GLuint texture) const;
void BindTexture(UInt32 textureUnit, TextureTarget target, GLuint texture) const; void BindTexture(UInt32 textureUnit, TextureTarget target, GLuint texture) const;
void BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const; void BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const;
@ -212,22 +215,23 @@ namespace Nz::GL
GLsizei width, height; GLsizei width, height;
}; };
struct TextureUnit struct BufferBinding
{
GLuint sampler = 0;
std::array<GLuint, UnderlyingCast(TextureTarget::Max) + 1> textureTargets = { 0 };
};
struct UniformBufferUnit
{ {
GLuint buffer = 0; GLuint buffer = 0;
GLintptr offset = 0; GLintptr offset = 0;
GLsizeiptr size = 0; GLsizeiptr size = 0;
}; };
struct TextureUnit
{
GLuint sampler = 0;
std::array<GLuint, UnderlyingCast(TextureTarget::Max) + 1> textureTargets = { 0 };
};
std::array<GLuint, UnderlyingCast(BufferTarget::Max) + 1> bufferTargets = { 0 }; std::array<GLuint, UnderlyingCast(BufferTarget::Max) + 1> bufferTargets = { 0 };
std::vector<TextureUnit> textureUnits; std::vector<TextureUnit> textureUnits;
std::vector<UniformBufferUnit> uboUnits; std::vector<BufferBinding> storageUnits;
std::vector<BufferBinding> uboUnits;
Box scissorBox; Box scissorBox;
Box viewport; Box viewport;
GLuint boundProgram = 0; GLuint boundProgram = 0;

View File

@ -136,6 +136,7 @@ namespace Nz
enum class ShaderBindingType enum class ShaderBindingType
{ {
StorageBuffer,
Texture, Texture,
UniformBuffer, UniformBuffer,

View File

@ -18,11 +18,14 @@ namespace Nz
bool anisotropicFiltering = false; bool anisotropicFiltering = false;
bool depthClamping = false; bool depthClamping = false;
bool nonSolidFaceFilling = false; bool nonSolidFaceFilling = false;
bool storageBuffers = false;
}; };
struct RenderDeviceLimits struct RenderDeviceLimits
{ {
UInt64 minUniformBufferOffsetAlignment; UInt64 minUniformBufferOffsetAlignment;
UInt64 maxStorageBufferSize;
UInt64 maxUniformBufferSize;
}; };
struct RenderDeviceInfo struct RenderDeviceInfo

View File

@ -40,6 +40,13 @@ namespace Nz
ShaderBinding& operator=(const ShaderBinding&) = delete; ShaderBinding& operator=(const ShaderBinding&) = delete;
ShaderBinding& operator=(ShaderBinding&&) = delete; ShaderBinding& operator=(ShaderBinding&&) = delete;
struct StorageBufferBinding
{
RenderBuffer* buffer;
UInt64 offset;
UInt64 range;
};
struct TextureBinding struct TextureBinding
{ {
const Texture* texture; const Texture* texture;
@ -56,7 +63,7 @@ namespace Nz
struct Binding struct Binding
{ {
std::size_t bindingIndex; std::size_t bindingIndex;
std::variant<TextureBinding, UniformBufferBinding> content; std::variant<StorageBufferBinding, TextureBinding, UniformBufferBinding> content;
}; };
protected: protected:

View File

@ -60,6 +60,7 @@ namespace Nz
{ {
Index, Index,
Vertex, Vertex,
Storage,
Uniform, Uniform,
Max = Uniform Max = Uniform

View File

@ -98,6 +98,7 @@ namespace Nz
switch (bufferType) switch (bufferType)
{ {
case BufferType::Index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; case BufferType::Index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BufferType::Storage: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
case BufferType::Vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; case BufferType::Vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BufferType::Uniform: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; case BufferType::Uniform: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
} }
@ -381,6 +382,7 @@ namespace Nz
{ {
switch (bindingType) switch (bindingType)
{ {
case ShaderBindingType::StorageBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
case ShaderBindingType::Texture: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; case ShaderBindingType::Texture: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case ShaderBindingType::UniformBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; case ShaderBindingType::UniformBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
} }

View File

@ -19,6 +19,7 @@ namespace Nz
switch (type) switch (type)
{ {
case BufferType::Index: target = GL::BufferTarget::ElementArray; break; case BufferType::Index: target = GL::BufferTarget::ElementArray; break;
case BufferType::Storage: target = GL::BufferTarget::Storage; break;
case BufferType::Uniform: target = GL::BufferTarget::Uniform; break; case BufferType::Uniform: target = GL::BufferTarget::Uniform; break;
case BufferType::Vertex: target = GL::BufferTarget::Array; break; case BufferType::Vertex: target = GL::BufferTarget::Array; break;

View File

@ -64,6 +64,9 @@ namespace Nz
if (m_referenceContext->IsExtensionSupported(GL::Extension::DepthClamp)) if (m_referenceContext->IsExtensionSupported(GL::Extension::DepthClamp))
m_deviceInfo.features.depthClamping = true; m_deviceInfo.features.depthClamping = true;
if (m_referenceContext->IsExtensionSupported(GL::Extension::StorageBuffers))
m_deviceInfo.features.storageBuffers = true;
if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension
m_deviceInfo.features.nonSolidFaceFilling = true; m_deviceInfo.features.nonSolidFaceFilling = true;
@ -74,6 +77,23 @@ namespace Nz
assert(minUboOffsetAlignment >= 1); assert(minUboOffsetAlignment >= 1);
m_deviceInfo.limits.minUniformBufferOffsetAlignment = static_cast<UInt64>(minUboOffsetAlignment); m_deviceInfo.limits.minUniformBufferOffsetAlignment = static_cast<UInt64>(minUboOffsetAlignment);
GLint maxUboBlockSize;
m_referenceContext->glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUboBlockSize);
assert(maxUboBlockSize >= 1);
m_deviceInfo.limits.maxUniformBufferSize = static_cast<UInt64>(maxUboBlockSize);
if (m_deviceInfo.features.storageBuffers)
{
GLint maxStorageBlockSize;
m_referenceContext->glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxStorageBlockSize);
assert(maxStorageBlockSize >= 1);
m_deviceInfo.limits.maxStorageBufferSize = static_cast<UInt64>(maxStorageBlockSize);
}
else
m_deviceInfo.limits.maxStorageBufferSize = 0;
m_contexts.insert(m_referenceContext.get()); m_contexts.insert(m_referenceContext.get());
} }

View File

@ -87,6 +87,16 @@ namespace Nz
return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId)); return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId));
} }
auto OpenGLRenderPipelineLayout::GetStorageBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> StorageBufferDescriptor&
{
assert(poolIndex < m_descriptorPools.size());
auto& pool = m_descriptorPools[poolIndex];
assert(!pool.freeBindings.Test(bindingIndex));
assert(descriptorIndex < m_maxDescriptorCount);
return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace<StorageBufferDescriptor>();
}
auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> TextureDescriptor& auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> TextureDescriptor&
{ {
assert(poolIndex < m_descriptorPools.size()); assert(poolIndex < m_descriptorPools.size());

View File

@ -34,7 +34,14 @@ namespace Nz
UInt32 bindingPoint = bindingMappingIt->second; UInt32 bindingPoint = bindingMappingIt->second;
if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::TextureDescriptor>) if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::StorageBufferDescriptor>)
{
if (bindingInfo.type != ShaderBindingType::StorageBuffer)
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a storage buffer");
context.BindStorageBuffer(bindingPoint, descriptor.buffer, descriptor.offset, descriptor.size);
}
else if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::TextureDescriptor>)
{ {
if (bindingInfo.type != ShaderBindingType::Texture) if (bindingInfo.type != ShaderBindingType::Texture)
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture"); throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture");
@ -63,8 +70,24 @@ namespace Nz
std::visit([&](auto&& arg) std::visit([&](auto&& arg)
{ {
using T = std::decay_t<decltype(arg)>; using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, StorageBufferBinding>)
{
auto& storageDescriptor = m_owner.GetStorageBufferDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
storageDescriptor.offset = arg.offset;
storageDescriptor.size = arg.range;
if constexpr (std::is_same_v<T, TextureBinding>) if (OpenGLBuffer* glBuffer = static_cast<OpenGLBuffer*>(arg.buffer))
{
if (glBuffer->GetType() != BufferType::Storage)
throw std::runtime_error("expected storage buffer");
storageDescriptor.buffer = glBuffer->GetBuffer().GetObjectId();
}
else
storageDescriptor.buffer = 0;
}
else if constexpr (std::is_same_v<T, TextureBinding>)
{ {
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
@ -103,7 +126,7 @@ namespace Nz
uboDescriptor.buffer = 0; uboDescriptor.buffer = 0;
} }
else else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor"); static_assert(AlwaysFalse<T>(), "non-exhaustive visitor");
}, binding.content); }, binding.content);
} }

View File

@ -191,6 +191,28 @@ namespace Nz::GL
} }
} }
void Context::BindStorageBuffer(UInt32 storageUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const
{
if (storageUnit >= m_state.storageUnits.size())
throw std::runtime_error("unsupported storage buffer unit #" + std::to_string(storageUnit));
auto& unit = m_state.storageUnits[storageUnit];
if (unit.buffer != buffer || unit.offset != offset || unit.size != size)
{
if (!SetCurrentContext(this))
throw std::runtime_error("failed to activate context");
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, storageUnit, buffer, offset, size);
unit.buffer = buffer;
unit.offset = offset;
unit.size = size;
// glBindBufferRange does replace the currently bound buffer
m_state.bufferTargets[UnderlyingCast(BufferTarget::Storage)] = buffer;
}
}
void Context::BindTexture(TextureTarget target, GLuint texture) const void Context::BindTexture(TextureTarget target, GLuint texture) const
{ {
BindTexture(m_state.currentTextureUnit, target, texture); BindTexture(m_state.currentTextureUnit, target, texture);
@ -375,6 +397,12 @@ namespace Nz::GL
else if (m_supportedExtensions.count("GL_ARB_gl_spirv")) else if (m_supportedExtensions.count("GL_ARB_gl_spirv"))
m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB; m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::ARB;
// Storage buffers (SSBO)
if ((m_params.type == ContextType::OpenGL && glVersion >= 430) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 310))
m_extensionStatus[UnderlyingCast(Extension::StorageBuffers)] = ExtensionStatus::Core;
else if (m_supportedExtensions.count("GL_ARB_shader_storage_buffer_object"))
m_extensionStatus[UnderlyingCast(Extension::StorageBuffers)] = ExtensionStatus::ARB;
// Texture compression (S3tc) // Texture compression (S3tc)
if (m_supportedExtensions.count("GL_EXT_texture_compression_s3tc")) if (m_supportedExtensions.count("GL_EXT_texture_compression_s3tc"))
m_extensionStatus[UnderlyingCast(Extension::TextureCompressionS3tc)] = ExtensionStatus::EXT; m_extensionStatus[UnderlyingCast(Extension::TextureCompressionS3tc)] = ExtensionStatus::EXT;
@ -449,7 +477,7 @@ namespace Nz::GL
GLint maxTextureUnits = -1; GLint maxTextureUnits = -1;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units
NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", >= 32 expected"); NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", expected >= 32");
assert(maxTextureUnits > 0); assert(maxTextureUnits > 0);
m_state.textureUnits.resize(maxTextureUnits); m_state.textureUnits.resize(maxTextureUnits);
@ -457,11 +485,22 @@ namespace Nz::GL
GLint maxUniformBufferUnits = -1; GLint maxUniformBufferUnits = -1;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferUnits); glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferUnits);
if (maxUniformBufferUnits < 24) //< OpenGL ES 3.0 requires at least 24 uniform buffers units if (maxUniformBufferUnits < 24) //< OpenGL ES 3.0 requires at least 24 uniform buffers units
NazaraWarning("GL_MAX_UNIFORM_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", >= 24 expected"); NazaraWarning("GL_MAX_UNIFORM_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 24");
assert(maxUniformBufferUnits > 0); assert(maxUniformBufferUnits > 0);
m_state.uboUnits.resize(maxUniformBufferUnits); m_state.uboUnits.resize(maxUniformBufferUnits);
if (IsExtensionSupported(Extension::StorageBuffers))
{
GLint maxStorageBufferUnits = -1;
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxStorageBufferUnits);
if (maxStorageBufferUnits < 24) //< OpenGL ES 3.1 requires at least 8 storage buffers units
NazaraWarning("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 8");
assert(maxStorageBufferUnits > 0);
m_state.storageUnits.resize(maxStorageBufferUnits);
}
std::array<GLint, 4> res; std::array<GLint, 4> res;
glGetIntegerv(GL_SCISSOR_BOX, res.data()); glGetIntegerv(GL_SCISSOR_BOX, res.data());

View File

@ -51,5 +51,11 @@ namespace Nz
NazaraWarning("non-solid face filling was enabled but device doesn't support it, disabling..."); NazaraWarning("non-solid face filling was enabled but device doesn't support it, disabling...");
enabledFeatures.nonSolidFaceFilling = false; enabledFeatures.nonSolidFaceFilling = false;
} }
if (enabledFeatures.storageBuffers && !supportedFeatures.storageBuffers)
{
NazaraWarning("storage buffers support was enabled but device doesn't support it, disabling...");
enabledFeatures.storageBuffers = false;
}
} }
} }

View File

@ -56,8 +56,10 @@ namespace Nz
deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy; deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy;
deviceInfo.features.depthClamping = physDevice.features.depthClamp; deviceInfo.features.depthClamping = physDevice.features.depthClamp;
deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid; deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid;
deviceInfo.features.storageBuffers = true;
deviceInfo.limits.minUniformBufferOffsetAlignment = physDevice.properties.limits.minUniformBufferOffsetAlignment; deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange;
deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange;
switch (physDevice.properties.deviceType) switch (physDevice.properties.deviceType)
{ {

View File

@ -32,7 +32,20 @@ namespace Nz
{ {
using T = std::decay_t<decltype(arg)>; using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, TextureBinding>) if constexpr (std::is_same_v<T, StorageBufferBinding>)
{
VulkanBuffer* vkBuffer = static_cast<VulkanBuffer*>(arg.buffer);
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
bufferInfo.offset = arg.offset;
bufferInfo.range = arg.range;
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeOp.pBufferInfo = &bufferInfo;
}
else if constexpr (std::is_same_v<T, TextureBinding>)
{ {
const VulkanTexture* vkTexture = static_cast<const VulkanTexture*>(arg.texture); const VulkanTexture* vkTexture = static_cast<const VulkanTexture*>(arg.texture);
const VulkanTextureSampler* vkSampler = static_cast<const VulkanTextureSampler*>(arg.sampler); const VulkanTextureSampler* vkSampler = static_cast<const VulkanTextureSampler*>(arg.sampler);
@ -60,7 +73,7 @@ namespace Nz
writeOp.pBufferInfo = &bufferInfo; writeOp.pBufferInfo = &bufferInfo;
} }
else else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor"); static_assert(AlwaysFalse<T>(), "non-exhaustive visitor");
}, binding.content); }, binding.content);
} }