SpirvShader: Implement Uniform and StorageBuffer access

Bug: b/126330097
Change-Id: Ia612ddf785b79b88a59c30b1e436470bb3fba3e8
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/26228
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index ff5d7d6..0e2a6f8 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -15,6 +15,7 @@
 #include <spirv/unified1/spirv.hpp>
 #include "SpirvShader.hpp"
 #include "System/Math.hpp"
+#include "Vulkan/VkBuffer.hpp"
 #include "Vulkan/VkDebug.hpp"
 #include "Vulkan/VkPipelineLayout.hpp"
 #include "Device/Config.hpp"
@@ -154,10 +155,36 @@
 				object.type = typeId;
 				object.pointerBase = insn.word(2);	// base is itself
 
-				// Register builtins
-				if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
+				ASSERT(getType(typeId).storageClass == storageClass);
+
+				switch (storageClass)
 				{
+				case spv::StorageClassInput:
+				case spv::StorageClassOutput:
 					ProcessInterfaceVariable(object);
+					break;
+				case spv::StorageClassUniform:
+				case spv::StorageClassStorageBuffer:
+					object.kind = Object::Kind::PhysicalPointer;
+					break;
+
+				case spv::StorageClassPrivate:
+				case spv::StorageClassFunction:
+					break; // Correctly handled.
+
+				case spv::StorageClassUniformConstant:
+				case spv::StorageClassWorkgroup:
+				case spv::StorageClassCrossWorkgroup:
+				case spv::StorageClassGeneric:
+				case spv::StorageClassPushConstant:
+				case spv::StorageClassAtomicCounter:
+				case spv::StorageClassImage:
+					UNIMPLEMENTED("StorageClass %d not yet implemented", (int)storageClass);
+					break;
+
+				default:
+					UNREACHABLE("Unexpected StorageClass"); // See Appendix A of the Vulkan spec.
+					break;
 				}
 				break;
 			}
@@ -1036,15 +1063,43 @@
 		ObjectID resultId = insn.word(2);
 		auto &object = getObject(resultId);
 		auto &objectTy = getType(object.type);
-		if (object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassInput)
+		switch (objectTy.storageClass)
 		{
-			auto &dst = routine->getValue(resultId);
-			int offset = 0;
-			VisitInterface(resultId,
-							[&](Decorations const &d, AttribType type) {
-								auto scalarSlot = d.Location << 2 | d.Component;
-								dst[offset++] = routine->inputs[scalarSlot];
-							});
+		case spv::StorageClassInput:
+		{
+			if (object.kind == Object::Kind::InterfaceVariable)
+			{
+				auto &dst = routine->getValue(resultId);
+				int offset = 0;
+				VisitInterface(resultId,
+								[&](Decorations const &d, AttribType type) {
+									auto scalarSlot = d.Location << 2 | d.Component;
+									dst[offset++] = routine->inputs[scalarSlot];
+								});
+			}
+			break;
+		}
+		case spv::StorageClassUniform:
+		case spv::StorageClassStorageBuffer:
+		{
+			Decorations d{};
+			ApplyDecorationsForId(&d, resultId);
+			ASSERT(d.DescriptorSet >= 0);
+			ASSERT(d.Binding >= 0);
+
+			size_t bindingOffset = routine->pipelineLayout->getBindingOffset(d.DescriptorSet, d.Binding);
+
+			Pointer<Byte> set = routine->descriptorSets[d.DescriptorSet]; // DescriptorSet*
+			Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset); // VkDescriptorBufferInfo*
+			Pointer<Byte> buffer = *Pointer<Pointer<Byte>>(binding + OFFSET(VkDescriptorBufferInfo, buffer)); // vk::Buffer*
+			Pointer<Byte> data = *Pointer<Pointer<Byte>>(buffer + vk::Buffer::DataOffset); // void*
+			Int offset = *Pointer<Int>(binding + OFFSET(VkDescriptorBufferInfo, offset));
+			Pointer<Byte> address = data + offset;
+			routine->physicalPointers[resultId] = address;
+			break;
+		}
+		default:
+			break;
 		}
 	}
 
@@ -1061,11 +1116,9 @@
 		ASSERT(getType(pointer.type).element == object.type);
 		ASSERT(TypeID(insn.word(1)) == object.type);
 
-		if (pointerBaseTy.storageClass == spv::StorageClassImage ||
-			pointerBaseTy.storageClass == spv::StorageClassUniform ||
-			pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
+		if (pointerBaseTy.storageClass == spv::StorageClassImage)
 		{
-			UNIMPLEMENTED("Descriptor-backed load not yet implemented");
+			UNIMPLEMENTED("StorageClassImage load not yet implemented");
 		}
 
 		Pointer<Float> ptrBase;
@@ -1125,17 +1178,9 @@
 		ObjectID baseId = insn.word(3);
 		auto &object = getObject(objectId);
 		auto &type = getType(typeId);
-		auto &pointerBase = getObject(object.pointerBase);
-		auto &pointerBaseTy = getType(pointerBase.type);
 		ASSERT(type.sizeInComponents == 1);
 		ASSERT(getObject(baseId).pointerBase == object.pointerBase);
 
-		if (pointerBaseTy.storageClass == spv::StorageClassImage ||
-			pointerBaseTy.storageClass == spv::StorageClassUniform ||
-			pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
-		{
-			UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
-		}
 		auto &dst = routine->createIntermediate(objectId, type.sizeInComponents);
 		dst.emplace(0, As<SIMD::Float>(WalkAccessChain(baseId, insn.wordCount() - 4, insn.wordPointer(4), routine)));
 	}
@@ -1151,11 +1196,9 @@
 		auto &pointerBase = getObject(pointer.pointerBase);
 		auto &pointerBaseTy = getType(pointerBase.type);
 
-		if (pointerBaseTy.storageClass == spv::StorageClassImage ||
-			pointerBaseTy.storageClass == spv::StorageClassUniform ||
-			pointerBaseTy.storageClass == spv::StorageClassUniformConstant)
+		if (pointerBaseTy.storageClass == spv::StorageClassImage)
 		{
-			UNIMPLEMENTED("Descriptor-backed store not yet implemented");
+			UNIMPLEMENTED("StorageClassImage store not yet implemented");
 		}
 
 		Pointer<Float> ptrBase;
diff --git a/src/Vulkan/VkBuffer.cpp b/src/Vulkan/VkBuffer.cpp
index 6b12067..421a4a0 100644
--- a/src/Vulkan/VkBuffer.cpp
+++ b/src/Vulkan/VkBuffer.cpp
@@ -21,6 +21,8 @@
 namespace vk
 {
 
+const size_t Buffer::DataOffset = offsetof(Buffer, memory);
+
 Buffer::Buffer(const VkBufferCreateInfo* pCreateInfo, void* mem) :
 	flags(pCreateInfo->flags), size(pCreateInfo->size), usage(pCreateInfo->usage),
 	sharingMode(pCreateInfo->sharingMode), queueFamilyIndexCount(pCreateInfo->queueFamilyIndexCount),
@@ -43,21 +45,21 @@
 const VkMemoryRequirements Buffer::getMemoryRequirements() const
 {
 	VkMemoryRequirements memoryRequirements = {};
-	if(usage & (VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT))

-	{

-		memoryRequirements.alignment = vk::MIN_TEXEL_BUFFER_OFFSET_ALIGNMENT;

-	}

-	else if(usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)

-	{

-		memoryRequirements.alignment = vk::MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT;

-	}

-	else if(usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)

-	{

-		memoryRequirements.alignment = vk::MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT;

-	}

-	else

-	{

-		memoryRequirements.alignment = REQUIRED_MEMORY_ALIGNMENT;

+	if(usage & (VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT))
+	{
+		memoryRequirements.alignment = vk::MIN_TEXEL_BUFFER_OFFSET_ALIGNMENT;
+	}
+	else if(usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
+	{
+		memoryRequirements.alignment = vk::MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT;
+	}
+	else if(usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
+	{
+		memoryRequirements.alignment = vk::MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT;
+	}
+	else
+	{
+		memoryRequirements.alignment = REQUIRED_MEMORY_ALIGNMENT;
 	}
 	memoryRequirements.memoryTypeBits = vk::MEMORY_TYPE_GENERIC_BIT;
 	memoryRequirements.size = size; // TODO: also reserve space for a header containing
diff --git a/src/Vulkan/VkBuffer.hpp b/src/Vulkan/VkBuffer.hpp
index 19dfc55..255b0f1 100644
--- a/src/Vulkan/VkBuffer.hpp
+++ b/src/Vulkan/VkBuffer.hpp
@@ -38,6 +38,10 @@
 	void update(VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 	void* getOffsetPointer(VkDeviceSize offset) const;
 
+	// DataOffset is the offset in bytes from the Buffer to the pointer to the
+	// buffer's data memory.
+	static const size_t DataOffset;
+
 private:
 	void*                 memory = nullptr;
 	VkBufferCreateFlags   flags = 0;