|
|
@@ -5,7 +5,6 @@
|
|
|
|
|
|
#include <AnKi/Gr/Vulkan/VkShader.h>
|
|
|
#include <AnKi/Gr/Vulkan/VkGrManager.h>
|
|
|
-#include <SpirvCross/spirv_cross.hpp>
|
|
|
|
|
|
#define ANKI_DUMP_SHADERS 0
|
|
|
|
|
|
@@ -28,48 +27,12 @@ Shader* Shader::newInstance(const ShaderInitInfo& init)
|
|
|
return impl;
|
|
|
}
|
|
|
|
|
|
-template<typename TFunc>
|
|
|
-static void visitSpirv(ConstWeakArray<U32> spv, TFunc func)
|
|
|
-{
|
|
|
- ANKI_ASSERT(spv.getSize() > 5);
|
|
|
-
|
|
|
- const U32* it = &spv[5];
|
|
|
- do
|
|
|
- {
|
|
|
- const U32 instructionCount = *it >> 16u;
|
|
|
- const U32 opcode = *it & 0xFFFFu;
|
|
|
-
|
|
|
- func(opcode);
|
|
|
-
|
|
|
- it += instructionCount;
|
|
|
- } while(it < spv.getEnd());
|
|
|
-
|
|
|
- ANKI_ASSERT(it == spv.getEnd());
|
|
|
-}
|
|
|
-
|
|
|
-class ShaderImpl::SpecConstsVector
|
|
|
-{
|
|
|
-public:
|
|
|
- spirv_cross::SmallVector<spirv_cross::SpecializationConstant> m_vec;
|
|
|
-};
|
|
|
-
|
|
|
ShaderImpl::~ShaderImpl()
|
|
|
{
|
|
|
if(m_handle)
|
|
|
{
|
|
|
vkDestroyShaderModule(getVkDevice(), m_handle, nullptr);
|
|
|
}
|
|
|
-
|
|
|
- if(m_specConstInfo.pMapEntries)
|
|
|
- {
|
|
|
- deleteArray(GrMemoryPool::getSingleton(), const_cast<VkSpecializationMapEntry*>(m_specConstInfo.pMapEntries), m_specConstInfo.mapEntryCount);
|
|
|
- }
|
|
|
-
|
|
|
- if(m_specConstInfo.pData)
|
|
|
- {
|
|
|
- deleteArray(GrMemoryPool::getSingleton(), static_cast<I32*>(const_cast<void*>(m_specConstInfo.pData)),
|
|
|
- m_specConstInfo.dataSize / sizeof(I32));
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
Error ShaderImpl::init(const ShaderInitInfo& inf)
|
|
|
@@ -78,6 +41,8 @@ Error ShaderImpl::init(const ShaderInitInfo& inf)
|
|
|
ANKI_ASSERT(m_handle == VK_NULL_HANDLE);
|
|
|
m_shaderType = inf.m_shaderType;
|
|
|
m_shaderBinarySize = U32(inf.m_binary.getSizeInBytes());
|
|
|
+ m_reflection = inf.m_reflection;
|
|
|
+ m_reflection.validate();
|
|
|
|
|
|
#if ANKI_DUMP_SHADERS
|
|
|
{
|
|
|
@@ -95,189 +60,7 @@ Error ShaderImpl::init(const ShaderInitInfo& inf)
|
|
|
|
|
|
ANKI_VK_CHECK(vkCreateShaderModule(getVkDevice(), &ci, nullptr, &m_handle));
|
|
|
|
|
|
- // Get reflection info
|
|
|
- SpecConstsVector specConstIds;
|
|
|
- doReflection(inf.m_binary, specConstIds);
|
|
|
-
|
|
|
- // Set spec info
|
|
|
- if(specConstIds.m_vec.size())
|
|
|
- {
|
|
|
- const U32 constCount = U32(specConstIds.m_vec.size());
|
|
|
-
|
|
|
- m_specConstInfo.mapEntryCount = constCount;
|
|
|
- m_specConstInfo.pMapEntries = newArray<VkSpecializationMapEntry>(GrMemoryPool::getSingleton(), constCount);
|
|
|
- m_specConstInfo.dataSize = constCount * sizeof(U32);
|
|
|
- m_specConstInfo.pData = newArray<U32>(GrMemoryPool::getSingleton(), constCount);
|
|
|
-
|
|
|
- U32 count = 0;
|
|
|
- for(const spirv_cross::SpecializationConstant& sconst : specConstIds.m_vec)
|
|
|
- {
|
|
|
- // Set the entry
|
|
|
- VkSpecializationMapEntry& entry = const_cast<VkSpecializationMapEntry&>(m_specConstInfo.pMapEntries[count]);
|
|
|
- entry.constantID = sconst.constant_id;
|
|
|
- entry.offset = count * sizeof(U32);
|
|
|
- entry.size = sizeof(U32);
|
|
|
-
|
|
|
- // Find the value
|
|
|
- const ShaderSpecializationConstValue* val = nullptr;
|
|
|
- for(const ShaderSpecializationConstValue& v : inf.m_constValues)
|
|
|
- {
|
|
|
- if(v.m_constantId == entry.constantID)
|
|
|
- {
|
|
|
- val = &v;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- ANKI_ASSERT(val && "Contant ID wasn't found in the init info");
|
|
|
-
|
|
|
- // Copy the data
|
|
|
- U8* data = static_cast<U8*>(const_cast<void*>(m_specConstInfo.pData));
|
|
|
- data += entry.offset;
|
|
|
- *reinterpret_cast<U32*>(data) = val->m_uint;
|
|
|
-
|
|
|
- ++count;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
return Error::kNone;
|
|
|
}
|
|
|
|
|
|
-void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specConstIds)
|
|
|
-{
|
|
|
- spirv_cross::Compiler spvc(reinterpret_cast<const uint32_t*>(&spirv[0]), spirv.getSize() / sizeof(unsigned int));
|
|
|
- spirv_cross::ShaderResources rsrc = spvc.get_shader_resources();
|
|
|
- spirv_cross::ShaderResources rsrcActive = spvc.get_shader_resources(spvc.get_active_interface_variables());
|
|
|
-
|
|
|
- Array<U32, kMaxDescriptorSets> counts = {};
|
|
|
- Array2d<DSBinding, kMaxDescriptorSets, kMaxBindingsPerDescriptorSet> descriptors;
|
|
|
-
|
|
|
- auto func = [&](const spirv_cross::SmallVector<spirv_cross::Resource>& resources, const DescriptorType origType) -> void {
|
|
|
- for(const spirv_cross::Resource& r : resources)
|
|
|
- {
|
|
|
- const U32 id = r.id;
|
|
|
- const U32 set = spvc.get_decoration(id, spv::Decoration::DecorationDescriptorSet);
|
|
|
- ANKI_ASSERT(set < kMaxDescriptorSets);
|
|
|
- const U32 binding = spvc.get_decoration(id, spv::Decoration::DecorationBinding);
|
|
|
- ANKI_ASSERT(binding < kMaxBindingsPerDescriptorSet);
|
|
|
-
|
|
|
- const spirv_cross::SPIRType& typeInfo = spvc.get_type(r.type_id);
|
|
|
- U32 arraySize = 1;
|
|
|
- if(typeInfo.array.size() != 0)
|
|
|
- {
|
|
|
- ANKI_ASSERT(typeInfo.array.size() == 1 && "Only 1D arrays are supported");
|
|
|
- arraySize = typeInfo.array[0];
|
|
|
- ANKI_ASSERT(arraySize > 0);
|
|
|
- }
|
|
|
-
|
|
|
- m_descriptorSetMask.set(set);
|
|
|
- m_activeBindingMask[set].set(set);
|
|
|
-
|
|
|
- // Images are special, they might be texel buffers
|
|
|
- DescriptorType type = origType;
|
|
|
- if(type == DescriptorType::kTexture)
|
|
|
- {
|
|
|
- if(typeInfo.image.dim == spv::DimBuffer && typeInfo.image.sampled == 1)
|
|
|
- {
|
|
|
- type = DescriptorType::kReadTextureBuffer;
|
|
|
- }
|
|
|
- else if(typeInfo.image.dim == spv::DimBuffer && typeInfo.image.sampled == 2)
|
|
|
- {
|
|
|
- type = DescriptorType::kReadWriteTextureBuffer;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Check that there are no other descriptors with the same binding
|
|
|
- U32 foundIdx = kMaxU32;
|
|
|
- for(U32 i = 0; i < counts[set]; ++i)
|
|
|
- {
|
|
|
- if(descriptors[set][i].m_binding == binding)
|
|
|
- {
|
|
|
- foundIdx = i;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(foundIdx == kMaxU32)
|
|
|
- {
|
|
|
- // New binding, init it
|
|
|
- DSBinding& descriptor = descriptors[set][counts[set]++];
|
|
|
- descriptor.m_binding = U8(binding);
|
|
|
- descriptor.m_type = type;
|
|
|
- descriptor.m_stageMask = ShaderTypeBit(1 << m_shaderType);
|
|
|
- descriptor.m_arraySize = arraySize;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Same binding, make sure the type is compatible
|
|
|
- ANKI_ASSERT(type == descriptors[set][foundIdx].m_type && "Same binding different type");
|
|
|
- ANKI_ASSERT(arraySize == descriptors[set][foundIdx].m_arraySize && "Same binding different array size");
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- func(rsrc.uniform_buffers, DescriptorType::kUniformBuffer);
|
|
|
- func(rsrc.separate_images, DescriptorType::kTexture); // This also handles texture buffers
|
|
|
- func(rsrc.separate_samplers, DescriptorType::kSampler);
|
|
|
- func(rsrc.storage_buffers, DescriptorType::kStorageBuffer);
|
|
|
- func(rsrc.storage_images, DescriptorType::kImage);
|
|
|
- func(rsrc.acceleration_structures, DescriptorType::kAccelerationStructure);
|
|
|
-
|
|
|
- for(U32 set = 0; set < kMaxDescriptorSets; ++set)
|
|
|
- {
|
|
|
- if(counts[set])
|
|
|
- {
|
|
|
- m_bindings[set].resize(counts[set]);
|
|
|
- memcpy(&m_bindings[set][0], &descriptors[set][0], counts[set] * sizeof(DSBinding));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Color attachments
|
|
|
- if(m_shaderType == ShaderType::kFragment)
|
|
|
- {
|
|
|
- for(const spirv_cross::Resource& r : rsrc.stage_outputs)
|
|
|
- {
|
|
|
- const U32 id = r.id;
|
|
|
- const U32 location = spvc.get_decoration(id, spv::Decoration::DecorationLocation);
|
|
|
-
|
|
|
- m_colorAttachmentWritemask.set(location);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Attribs
|
|
|
- if(m_shaderType == ShaderType::kVertex)
|
|
|
- {
|
|
|
- for(const spirv_cross::Resource& r : rsrcActive.stage_inputs)
|
|
|
- {
|
|
|
- const U32 id = r.id;
|
|
|
- const U32 location = spvc.get_decoration(id, spv::Decoration::DecorationLocation);
|
|
|
-
|
|
|
- m_attributeMask.set(location);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Spec consts
|
|
|
- specConstIds.m_vec = spvc.get_specialization_constants();
|
|
|
-
|
|
|
- // Push consts
|
|
|
- if(rsrc.push_constant_buffers.size() == 1)
|
|
|
- {
|
|
|
- const U32 blockSize = U32(spvc.get_declared_struct_size(spvc.get_type(rsrc.push_constant_buffers[0].base_type_id)));
|
|
|
- ANKI_ASSERT(blockSize > 0);
|
|
|
- ANKI_ASSERT(blockSize % 16 == 0 && "Should be aligned");
|
|
|
- ANKI_ASSERT(blockSize <= getGrManagerImpl().getDeviceCapabilities().m_pushConstantsSize);
|
|
|
- m_pushConstantsSize = blockSize;
|
|
|
- }
|
|
|
-
|
|
|
- // Discards?
|
|
|
- if(m_shaderType == ShaderType::kFragment)
|
|
|
- {
|
|
|
- visitSpirv(ConstWeakArray<U32>(reinterpret_cast<const U32*>(&spirv[0]), spirv.getSize() / sizeof(U32)), [this](U32 cmd) {
|
|
|
- if(cmd == spv::OpKill)
|
|
|
- {
|
|
|
- m_hasDiscard = true;
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
} // end namespace anki
|