Browse Source

Add texture buffer support

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
9656719fdd

+ 2 - 2
AnKi/Gr/CommandBuffer.h

@@ -279,8 +279,8 @@ public:
 	///              of the buffer.
 	/// @param fmt The format of the buffer.
 	/// @param arrayIdx The array index if the binding is an array.
-	void bindTextureBuffer(U32 set, U32 binding, const BufferPtr& buff, PtrSize offset, PtrSize range, Format fmt,
-						   U32 arrayIdx = 0);
+	void bindReadOnlyTextureBuffer(U32 set, U32 binding, const BufferPtr& buff, PtrSize offset, PtrSize range,
+								   Format fmt, U32 arrayIdx = 0);
 
 	/// Bind an acceleration structure.
 	/// @param set The set to bind to.

+ 76 - 0
AnKi/Gr/Vulkan/BufferImpl.cpp

@@ -362,4 +362,80 @@ void BufferImpl::computeBarrierInfo(BufferUsageBit before, BufferUsageBit after,
 	dstAccesses = computeAccessMask(after);
 }
 
+VkBufferView BufferImpl::getOrCreateBufferView(Format fmt, PtrSize offset, PtrSize range) const
+{
+	if(range == MAX_PTR_SIZE)
+	{
+		ANKI_ASSERT(m_size >= offset);
+		range = m_size - offset;
+	}
+
+	// Checks
+	ANKI_ASSERT(!!(m_usage & BufferUsageBit::ALL_TEXTURE));
+	ANKI_ASSERT(offset + range <= m_size);
+
+	ANKI_ASSERT(isAligned(getGrManagerImpl().getDeviceCapabilities().m_textureBufferBindOffsetAlignment,
+						  m_memHandle.m_offset + offset)
+				&& "Offset not aligned");
+
+	ANKI_ASSERT((range % getFormatInfo(fmt).m_texelSize) == 0
+				&& "Range doesn't align with the number of texel elements");
+
+	const PtrSize elementCount = range / getFormatInfo(fmt).m_texelSize;
+	(void)elementCount;
+	ANKI_ASSERT(elementCount <= getGrManagerImpl().getPhysicalDeviceProperties().limits.maxTexelBufferElements);
+
+	// Hash
+	ANKI_BEGIN_PACKED_STRUCT
+	class HashData
+	{
+	public:
+		PtrSize m_offset;
+		PtrSize m_range;
+		Format m_fmt;
+	} toHash;
+	ANKI_END_PACKED_STRUCT
+
+	toHash.m_fmt = fmt;
+	toHash.m_offset = offset;
+	toHash.m_range = range;
+
+	const U64 hash = computeHash(&toHash, sizeof(toHash));
+
+	// Check if exists
+	{
+		RLockGuard<RWMutex> lock(m_viewsMtx);
+
+		auto it = m_views.find(hash);
+		if(it != m_views.getEnd())
+		{
+			return *it;
+		}
+	}
+
+	WLockGuard<RWMutex> lock(m_viewsMtx);
+
+	// Check again
+	auto it = m_views.find(hash);
+	if(it != m_views.getEnd())
+	{
+		return *it;
+	}
+
+	// Doesn't exist, need to create it
+	VkBufferViewCreateInfo viewCreateInfo = {};
+	viewCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
+	viewCreateInfo.buffer = m_handle;
+	viewCreateInfo.format = convertFormat(fmt);
+	viewCreateInfo.offset = offset;
+	viewCreateInfo.range = range;
+
+	VkBufferView view;
+	ANKI_VK_CHECKF(vkCreateBufferView(getDevice(), &viewCreateInfo, nullptr, &view));
+
+	m_views.emplace(getAllocator(), hash, view);
+
+	return view;
+}
+
 } // end namespace anki

+ 7 - 0
AnKi/Gr/Vulkan/BufferImpl.h

@@ -8,6 +8,7 @@
 #include <AnKi/Gr/Buffer.h>
 #include <AnKi/Gr/Vulkan/VulkanObject.h>
 #include <AnKi/Gr/Vulkan/GpuMemoryManager.h>
+#include <AnKi/Util/HashMap.h>
 
 namespace anki {
 
@@ -88,6 +89,9 @@ public:
 		}
 	}
 
+	/// Only for texture buffers.
+	VkBufferView getOrCreateBufferView(Format fmt, PtrSize offset, PtrSize range) const;
+
 private:
 	VkBuffer m_handle = VK_NULL_HANDLE;
 	GpuMemoryHandle m_memHandle;
@@ -97,6 +101,9 @@ private:
 	Bool m_needsFlush : 1;
 	Bool m_needsInvalidate : 1;
 
+	mutable HashMap<U64, VkBufferView> m_views; ///< Only for texture buffers.
+	mutable RWMutex m_viewsMtx;
+
 #if ANKI_EXTRA_CHECKS
 	Bool m_mapped = false;
 	mutable Atomic<U32> m_flushCount = {0};

+ 4 - 3
AnKi/Gr/Vulkan/CommandBuffer.cpp

@@ -226,10 +226,11 @@ void CommandBuffer::bindAccelerationStructure(U32 set, U32 binding, const Accele
 	self.bindAccelerationStructureInternal(set, binding, as, arrayIdx);
 }
 
-void CommandBuffer::bindTextureBuffer(U32 set, U32 binding, const BufferPtr& buff, PtrSize offset, PtrSize range,
-									  Format fmt, U32 arrayIdx)
+void CommandBuffer::bindReadOnlyTextureBuffer(U32 set, U32 binding, const BufferPtr& buff, PtrSize offset,
+											  PtrSize range, Format fmt, U32 arrayIdx)
 {
-	ANKI_ASSERT(!"TODO");
+	ANKI_VK_SELF(CommandBufferImpl);
+	self.bindReadOnlyTextureBufferInternal(set, binding, buff, offset, range, fmt, arrayIdx);
 }
 
 void CommandBuffer::bindAllBindless(U32 set)

+ 8 - 0
AnKi/Gr/Vulkan/CommandBufferImpl.h

@@ -377,6 +377,14 @@ public:
 		m_microCmdb->pushObjectRef(buff);
 	}
 
+	void bindReadOnlyTextureBufferInternal(U32 set, U32 binding, const BufferPtr& buff, PtrSize offset, PtrSize range,
+										   Format fmt, U32 arrayIdx)
+	{
+		commandCommon();
+		m_dsetState[set].bindReadOnlyTextureBuffer(binding, arrayIdx, buff.get(), offset, range, fmt);
+		m_microCmdb->pushObjectRef(buff);
+	}
+
 	void copyBufferToTextureViewInternal(const BufferPtr& buff, PtrSize offset, PtrSize range,
 										 const TextureViewPtr& texView);
 

+ 4 - 4
AnKi/Gr/Vulkan/Common.cpp

@@ -298,14 +298,14 @@ VkBufferUsageFlags convertBufferUsageBit(BufferUsageBit usageMask)
 		out |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
 	}
 
-	if(!!(usageMask & (BufferUsageBit::ALL_TEXTURE & BufferUsageBit::ALL_WRITE)))
+	if(!!(usageMask & (BufferUsageBit::ALL_TEXTURE & BufferUsageBit::ALL_READ)))
 	{
-		out |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+		out |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
 	}
 
-	if(!!(usageMask & (BufferUsageBit::ALL_TEXTURE & BufferUsageBit::ALL_READ)))
+	if(!!(usageMask & (BufferUsageBit::ALL_TEXTURE & BufferUsageBit::ALL_WRITE)))
 	{
-		out |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
+		out |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
 	}
 
 	if(!!(usageMask & BufferUsageBit::ACCELERATION_STRUCTURE_BUILD))

+ 8 - 1
AnKi/Gr/Vulkan/Common.h

@@ -53,7 +53,8 @@ enum class DescriptorType : U8
 	UNIFORM_BUFFER,
 	STORAGE_BUFFER,
 	IMAGE,
-	TEXTURE_BUFFER,
+	READ_TEXTURE_BUFFER,
+	READ_WRITE_TEXTURE_BUFFER,
 	ACCELERATION_STRUCTURE,
 
 	COUNT
@@ -273,6 +274,12 @@ ANKI_USE_RESULT inline VkDescriptorType convertDescriptorType(DescriptorType ak)
 	case DescriptorType::UNIFORM_BUFFER:
 		out = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
 		break;
+	case DescriptorType::READ_TEXTURE_BUFFER:
+		out = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+		break;
+	case DescriptorType::READ_WRITE_TEXTURE_BUFFER:
+		out = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+		break;
 	case DescriptorType::STORAGE_BUFFER:
 		out = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
 		break;

+ 24 - 1
AnKi/Gr/Vulkan/DescriptorSet.cpp

@@ -525,6 +525,7 @@ void DSThreadAllocator::writeSet(const Array<AnyBindingExtended, MAX_BINDINGS_PE
 	DynamicArrayAuto<VkDescriptorImageInfo> texInfos(tmpAlloc);
 	DynamicArrayAuto<VkDescriptorBufferInfo> buffInfos(tmpAlloc);
 	DynamicArrayAuto<VkWriteDescriptorSetAccelerationStructureKHR> asInfos(tmpAlloc);
+	DynamicArrayAuto<VkBufferView> bufferViews(tmpAlloc);
 
 	// First pass: Populate the VkDescriptorImageInfo and VkDescriptorBufferInfo
 	for(U bindingIdx = m_layoutEntry->m_minBinding; bindingIdx <= m_layoutEntry->m_maxBinding; ++bindingIdx)
@@ -572,6 +573,13 @@ void DSThreadAllocator::writeSet(const Array<AnyBindingExtended, MAX_BINDINGS_PE
 					info.range = (b.m_buff.m_range == MAX_PTR_SIZE) ? VK_WHOLE_SIZE : b.m_buff.m_range;
 					break;
 				}
+				case DescriptorType::READ_TEXTURE_BUFFER:
+				case DescriptorType::READ_WRITE_TEXTURE_BUFFER:
+				{
+					VkBufferView& view = *bufferViews.emplaceBack();
+					view = b.m_textureBuffer.m_buffView;
+					break;
+				}
 				case DescriptorType::IMAGE:
 				{
 					VkDescriptorImageInfo& info = *texInfos.emplaceBack();
@@ -600,8 +608,9 @@ void DSThreadAllocator::writeSet(const Array<AnyBindingExtended, MAX_BINDINGS_PE
 	U32 texCounter = 0;
 	U32 buffCounter = 0;
 	U32 asCounter = 0;
+	U32 buffViewsCounter = 0;
 
-	VkWriteDescriptorSet writeTemplate{};
+	VkWriteDescriptorSet writeTemplate = {};
 	writeTemplate.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 	writeTemplate.pNext = nullptr;
 	writeTemplate.dstSet = set.m_handle;
@@ -633,6 +642,10 @@ void DSThreadAllocator::writeSet(const Array<AnyBindingExtended, MAX_BINDINGS_PE
 				case DescriptorType::STORAGE_BUFFER:
 					writeInfo.pBufferInfo = &buffInfos[buffCounter++];
 					break;
+				case DescriptorType::READ_TEXTURE_BUFFER:
+				case DescriptorType::READ_WRITE_TEXTURE_BUFFER:
+					writeInfo.pTexelBufferView = &bufferViews[buffViewsCounter++];
+					break;
 				case DescriptorType::ACCELERATION_STRUCTURE:
 					writeInfo.pNext = &asInfos[asCounter++];
 					break;
@@ -885,6 +898,16 @@ void DescriptorSetState::flush(U64& hash, Array<PtrSize, MAX_BINDINGS_PER_DESCRI
 						dynamicOffsets[dynamicOffsetCount++] = anyBinding.m_buff.m_offset;
 						dynamicOffsetsDirty = dynamicOffsetsDirty || crntBindingDirty;
 						break;
+					case DescriptorType::READ_TEXTURE_BUFFER:
+						ANKI_ASSERT(anyBinding.m_type == DescriptorType::READ_TEXTURE_BUFFER
+									&& "Have bound the wrong type");
+						toHash[toHashCount++] = anyBinding.m_uuids[1];
+						break;
+					case DescriptorType::READ_WRITE_TEXTURE_BUFFER:
+						ANKI_ASSERT(anyBinding.m_type == DescriptorType::READ_WRITE_TEXTURE_BUFFER
+									&& "Have bound the wrong type");
+						toHash[toHashCount++] = anyBinding.m_uuids[1];
+						break;
 					case DescriptorType::IMAGE:
 						ANKI_ASSERT(anyBinding.m_type == DescriptorType::IMAGE && "Have bound the wrong type");
 						break;

+ 23 - 0
AnKi/Gr/Vulkan/DescriptorSet.h

@@ -112,6 +112,12 @@ public:
 	VkAccelerationStructureKHR m_accelerationStructureHandle;
 };
 
+class TextureBufferBinding
+{
+public:
+	VkBufferView m_buffView;
+};
+
 class AnyBinding
 {
 public:
@@ -125,6 +131,7 @@ public:
 		BufferBinding m_buff;
 		ImageBinding m_image;
 		AsBinding m_accelerationStructure;
+		TextureBufferBinding m_textureBuffer;
 	};
 
 	DescriptorType m_type;
@@ -266,6 +273,22 @@ public:
 		unbindBindlessDSet();
 	}
 
+	void bindReadOnlyTextureBuffer(U32 binding, U32 arrayIdx, const Buffer* buff, PtrSize offset, PtrSize range,
+								   Format fmt)
+	{
+		const VkBufferView view = static_cast<const BufferImpl*>(buff)->getOrCreateBufferView(fmt, offset, range);
+		AnyBinding& b = getBindingToPopulate(binding, arrayIdx);
+		b = {};
+		b.m_type = DescriptorType::READ_TEXTURE_BUFFER;
+		b.m_uuids[0] = ptrToNumber(view);
+		b.m_uuids[1] = buff->getUuid();
+
+		b.m_textureBuffer.m_buffView = view;
+
+		m_dirtyBindings.set(binding);
+		unbindBindlessDSet();
+	}
+
 	void bindImage(U32 binding, U32 arrayIdx, const TextureView* texView)
 	{
 		ANKI_ASSERT(texView);

+ 14 - 1
AnKi/Gr/Vulkan/ShaderImpl.cpp

@@ -149,6 +149,19 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 			m_descriptorSetMask.set(set);
 			m_activeBindingMask[set].set(set);
 
+			// Images are special, they might be texel buffers
+			if(type == DescriptorType::TEXTURE)
+			{
+				if(typeInfo.image.dim == spv::DimBuffer && typeInfo.image.sampled == 1)
+				{
+					type = DescriptorType::READ_TEXTURE_BUFFER;
+				}
+				else if(typeInfo.image.dim == spv::DimBuffer && typeInfo.image.sampled == 2)
+				{
+					type = DescriptorType::READ_WRITE_TEXTURE_BUFFER;
+				}
+			}
+
 			// Check that there are no other descriptors with the same binding
 			U32 foundIdx = MAX_U32;
 			for(U32 i = 0; i < counts[set]; ++i)
@@ -180,7 +193,7 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 
 	func(rsrc.uniform_buffers, DescriptorType::UNIFORM_BUFFER);
 	func(rsrc.sampled_images, DescriptorType::COMBINED_TEXTURE_SAMPLER);
-	func(rsrc.separate_images, DescriptorType::TEXTURE);
+	func(rsrc.separate_images, DescriptorType::TEXTURE); // This also handles texture buffers
 	func(rsrc.separate_samplers, DescriptorType::SAMPLER);
 	func(rsrc.storage_buffers, DescriptorType::STORAGE_BUFFER);
 	func(rsrc.storage_images, DescriptorType::IMAGE);

+ 1 - 17
Tests/Gr/Gr.cpp

@@ -5,6 +5,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <Tests/Framework/Framework.h>
+#include <Tests/Gr/GrCommon.h>
 #include <AnKi/Gr.h>
 #include <AnKi/Core/NativeWindow.h>
 #include <AnKi/Input/Input.h>
@@ -326,23 +327,6 @@ static void* setStorage(PtrSize size, CommandBufferPtr& cmdb, U32 set, U32 bindi
 
 const Format DS_FORMAT = Format::D24_UNORM_S8_UINT;
 
-static ShaderPtr createShader(CString src, ShaderType type, GrManager& gr,
-							  ConstWeakArray<ShaderSpecializationConstValue> specVals = {})
-{
-	HeapAllocator<U8> alloc(allocAligned, nullptr);
-	StringAuto header(alloc);
-	ShaderCompilerOptions compilerOptions;
-	ShaderProgramParser::generateAnkiShaderHeader(type, compilerOptions, header);
-	header.append(src);
-	DynamicArrayAuto<U8> spirv(alloc);
-	ANKI_TEST_EXPECT_NO_ERR(compilerGlslToSpirv(header, type, alloc, spirv));
-
-	ShaderInitInfo initInf(type, spirv);
-	initInf.m_constValues = specVals;
-
-	return gr.newShader(initInf);
-}
-
 static ShaderProgramPtr createProgram(CString vertSrc, CString fragSrc, GrManager& gr)
 {
 	ShaderPtr vert = createShader(vertSrc, ShaderType::VERTEX, gr);

+ 31 - 0
Tests/Gr/GrCommon.h

@@ -0,0 +1,31 @@
+// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Gr.h>
+#include <AnKi/ShaderCompiler.h>
+#include <AnKi/ShaderCompiler/ShaderProgramParser.h>
+#include <AnKi/ShaderCompiler/Glslang.h>
+#include <Tests/Framework/Framework.h>
+
+namespace anki {
+
+inline ShaderPtr createShader(CString src, ShaderType type, GrManager& gr,
+							  ConstWeakArray<ShaderSpecializationConstValue> specVals = {})
+{
+	HeapAllocator<U8> alloc(allocAligned, nullptr);
+	StringAuto header(alloc);
+	ShaderCompilerOptions compilerOptions;
+	ShaderProgramParser::generateAnkiShaderHeader(type, compilerOptions, header);
+	header.append(src);
+	DynamicArrayAuto<U8> spirv(alloc);
+	ANKI_TEST_EXPECT_NO_ERR(compilerGlslToSpirv(header, type, alloc, spirv));
+
+	ShaderInitInfo initInf(type, spirv);
+	initInf.m_constValues = specVals;
+
+	return gr.newShader(initInf);
+}
+
+} // end namespace anki

+ 38 - 0
Tests/Gr/GrTextureBuffer.cpp

@@ -0,0 +1,38 @@
+// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <Tests/Framework/Framework.h>
+#include <Tests/Gr/GrCommon.h>
+#include <AnKi/Gr.h>
+
+namespace anki {
+
+ANKI_TEST(Gr, TextureBuffer)
+{
+	ConfigSet cfg(allocAligned, nullptr);
+	cfg.setGrValidation(true);
+
+	NativeWindow* win = createWindow(cfg);
+	GrManager* gr = createGrManager(&cfg, win);
+
+	{
+		const CString shaderSrc = R"(
+layout(binding = 0) uniform textureBuffer u_tbuff;
+layout(binding = 1) uniform image2D u_img;
+
+void main()
+{
+	imageStore(u_img, IVec2(0), texelFetch(u_tbuff, I32(gl_GlobalInvocationID.x)));
+}
+	)";
+
+		ShaderPtr shader = createShader(shaderSrc, ShaderType::COMPUTE, *gr);
+	}
+
+	GrManager::deleteInstance(gr);
+	NativeWindow::deleteInstance(win);
+}
+
+} // end namespace anki