677 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			677 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#include "iwa/util/shader_meta.hpp"
 | 
						|
 | 
						|
#include "iwa/log.hpp"
 | 
						|
#include "iwa/util/glsl_compiler.hpp"
 | 
						|
#include "iwa/util/vertex_layout.hpp"
 | 
						|
#include "iwa/util/vkutil.hpp"
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
template<typename T>
 | 
						|
inline std::size_t calcCrcSizeAppend(T, std::size_t) noexcept
 | 
						|
{
 | 
						|
    MIJIN_TRAP(); // TODO
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
namespace iwa
 | 
						|
{
 | 
						|
namespace
 | 
						|
{
 | 
						|
vk::ShaderStageFlags typeBitsToVkStages(ShaderTypeBits bits)
 | 
						|
{
 | 
						|
    vk::ShaderStageFlags flags = {};
 | 
						|
    if (bits.compute)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eCompute;
 | 
						|
    }
 | 
						|
    if (bits.vertex)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eVertex;
 | 
						|
    }
 | 
						|
    if (bits.fragment)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eFragment;
 | 
						|
    }
 | 
						|
    if (bits.rayGeneration)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eRaygenKHR;
 | 
						|
    }
 | 
						|
    if (bits.rayClosestHit)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eClosestHitKHR;
 | 
						|
    }
 | 
						|
    if (bits.rayAnyHit)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eAnyHitKHR;
 | 
						|
    }
 | 
						|
    if (bits.rayMiss)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eMissKHR;
 | 
						|
    }
 | 
						|
    if (bits.rayIntersection)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eIntersectionKHR;
 | 
						|
    }
 | 
						|
    if (bits.callable)
 | 
						|
    {
 | 
						|
        flags |= vk::ShaderStageFlagBits::eCallableKHR;
 | 
						|
    }
 | 
						|
    return flags;
 | 
						|
}
 | 
						|
 | 
						|
void addShaderAttribute(std::vector<ShaderAttribute>& attributes, ShaderAttribute&& attribute)
 | 
						|
{
 | 
						|
    bool doInsert = true;
 | 
						|
    for (const ShaderAttribute& myAttribute: attributes)
 | 
						|
    {
 | 
						|
        if (myAttribute.stage == attribute.stage && myAttribute.location == attribute.location && myAttribute.location != UNSPECIFIED_INDEX)
 | 
						|
        {
 | 
						|
            // same location, type must be the same
 | 
						|
            if (myAttribute.type != attribute.type)
 | 
						|
            {
 | 
						|
                logAndDie(
 | 
						|
                    "Attempting to merge incompatible shader metas, attributes {} and {} are incompatible. {} != {}",
 | 
						|
                    myAttribute.name, attribute.name, myAttribute.type, attribute.type);
 | 
						|
            }
 | 
						|
            doInsert = false; // member already exists, don't insert
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!doInsert)
 | 
						|
    {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    auto it = attributes.begin();
 | 
						|
    for (; it != attributes.end(); ++it)
 | 
						|
    {
 | 
						|
        if (static_cast<unsigned>(it->stage) > static_cast<unsigned>(attribute.stage)
 | 
						|
            || (it->stage == attribute.stage && it->location > attribute.location))
 | 
						|
        {
 | 
						|
            break; // insert here
 | 
						|
        }
 | 
						|
    }
 | 
						|
    attributes.insert(it, std::move(attribute));
 | 
						|
}
 | 
						|
}
 | 
						|
ShaderVariableStructType::ShaderVariableStructType() {} // NOLINT(modernize-use-equals-default)
 | 
						|
ShaderVariableStructType::~ShaderVariableStructType() {} // NOLINT(modernize-use-equals-default)
 | 
						|
 | 
						|
void ShaderMeta::extendPushConstant(ShaderPushConstantBlock pushConstantBlock_, ShaderTypeBits pushConstantStages_)
 | 
						|
{
 | 
						|
    if (pushConstantBlock_.type.baseType == ShaderVariableBaseType::NONE) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (pushConstantBlock.type.baseType == ShaderVariableBaseType::NONE)
 | 
						|
    {
 | 
						|
        pushConstantBlock = std::move(pushConstantBlock_);
 | 
						|
        pushConstantStages = pushConstantStages_;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // now comes the actual merging
 | 
						|
    assert(pushConstantBlock.type.baseType == ShaderVariableBaseType::STRUCT);
 | 
						|
    assert(pushConstantBlock_.type.baseType == ShaderVariableBaseType::STRUCT);
 | 
						|
    assert(pushConstantStages_);
 | 
						|
 | 
						|
    for (ShaderVariableStructMember& member : pushConstantBlock_.type.struct_.members)
 | 
						|
    {
 | 
						|
        bool doInsert = true;
 | 
						|
        for (const ShaderVariableStructMember& myMember : pushConstantBlock.type.struct_.members)
 | 
						|
        {
 | 
						|
            if (myMember.offset == member.offset)
 | 
						|
            {
 | 
						|
                // same offset, type must be the same
 | 
						|
                if (myMember.type != member.type)
 | 
						|
                {
 | 
						|
                    logAndDie("Attempting to merge incompatible push constant blocks, members {} and {} are incompatible. {} != {}",
 | 
						|
                              myMember.name, member.name, myMember.type, member.type);
 | 
						|
                }
 | 
						|
                doInsert = false; // member already exists, don't insert
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            // otherwise check for overlaps
 | 
						|
            if ((myMember.offset < member.offset && myMember.offset + calcShaderTypeSize(myMember.type) > member.offset)
 | 
						|
                || (myMember.offset > member.offset && myMember.offset < member.offset + calcShaderTypeSize(member.type)))
 | 
						|
            {
 | 
						|
                logAndDie("Attempting to merge incompatible push constant blocks, members {} and {} are overlapping.",
 | 
						|
                          myMember.name, member.name);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!doInsert) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        auto it = pushConstantBlock.type.struct_.members.begin();
 | 
						|
        for (; it != pushConstantBlock.type.struct_.members.end(); ++it)
 | 
						|
        {
 | 
						|
            if (it->offset > member.offset) {
 | 
						|
                break; // insert here
 | 
						|
            }
 | 
						|
        }
 | 
						|
        pushConstantBlock.type.struct_.members.insert(it, std::move(member));
 | 
						|
    }
 | 
						|
 | 
						|
    pushConstantStages |= pushConstantStages_;
 | 
						|
}
 | 
						|
 | 
						|
void ShaderMeta::addInputAttribute(ShaderAttribute attribute)
 | 
						|
{
 | 
						|
    addShaderAttribute(inputAttributes, std::move(attribute));
 | 
						|
}
 | 
						|
 | 
						|
void ShaderMeta::addOutputAttribute(ShaderAttribute attribute)
 | 
						|
{
 | 
						|
    addShaderAttribute(outputAttributes, std::move(attribute));
 | 
						|
}
 | 
						|
 | 
						|
ObjectPtr<DescriptorSetLayout> DescriptorSetMeta::createDescriptorSetLayout(Device& device) const
 | 
						|
{
 | 
						|
    assert(bindings.size() == bindingFlags.size());
 | 
						|
    return device.createChild<DescriptorSetLayout>(DescriptorSetLayoutCreationArgs{
 | 
						|
        .bindings = bindings,
 | 
						|
        .bindingFlags = bindingFlags,
 | 
						|
        .flags = flags,
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
std::vector<ObjectPtr<DescriptorSet>> PipelineAndDescriptorSetLayouts::createDescriptorSets(DescriptorPool& pool) const
 | 
						|
{
 | 
						|
    std::vector<ObjectPtr<DescriptorSet>> result;
 | 
						|
    result.reserve(descriptorSetLayouts.size());
 | 
						|
 | 
						|
    for (const ObjectPtr<DescriptorSetLayout>& layout : descriptorSetLayouts)
 | 
						|
    {
 | 
						|
        result.push_back(pool.allocateDescriptorSet({
 | 
						|
            .layout = layout
 | 
						|
        }));
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
ObjectPtr<DescriptorSet> PipelineAndDescriptorSetLayouts::createDescriptorSet(DescriptorPool& pool, unsigned setIdx) const
 | 
						|
{
 | 
						|
    MIJIN_ASSERT(setIdx < descriptorSetLayouts.size(), "Invalid set index.");
 | 
						|
    return pool.allocateDescriptorSet({
 | 
						|
        .layout = descriptorSetLayouts[setIdx]
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
PipelineAndDescriptorSetLayouts PipelineLayoutMeta::createPipelineLayout(Device& device) const
 | 
						|
{
 | 
						|
    std::vector<ObjectPtr<DescriptorSetLayout>> descSetLayouts;
 | 
						|
    descSetLayouts.reserve(descriptorSets.size());
 | 
						|
 | 
						|
    for (const DescriptorSetMeta& dslMeta : descriptorSets)
 | 
						|
    {
 | 
						|
        descSetLayouts.push_back(dslMeta.createDescriptorSetLayout(device));
 | 
						|
    }
 | 
						|
 | 
						|
    std::vector<vk::PushConstantRange> pushConstantRanges;
 | 
						|
    if (pushConstantRange.stageFlags)
 | 
						|
    {
 | 
						|
        pushConstantRanges.push_back(pushConstantRange);
 | 
						|
    }
 | 
						|
 | 
						|
    ObjectPtr<PipelineLayout> pipelineLayout = device.createChild<PipelineLayout>(PipelineLayoutCreationArgs{
 | 
						|
        .setLayouts = descSetLayouts,
 | 
						|
        .pushConstantRanges = std::move(pushConstantRanges)
 | 
						|
    });
 | 
						|
    return
 | 
						|
    {
 | 
						|
        .descriptorSetLayouts = std::move(descSetLayouts),
 | 
						|
        .pipelineLayout = std::move(pipelineLayout)
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
void ShaderVariable::verifyCompatible(const ShaderVariable& other) const
 | 
						|
{
 | 
						|
    std::vector<std::string> errors;
 | 
						|
    if (other.binding != binding) {
 | 
						|
        errors.push_back(fmt::format("Variable bindings do not match: {} != {}.", binding, other.binding)); // NOLINT
 | 
						|
    }
 | 
						|
    if (other.descriptorType != descriptorType) {
 | 
						|
        errors.push_back(fmt::format("Descriptor types do not match: {} != {}.",
 | 
						|
                                     magic_enum::enum_name(descriptorType),
 | 
						|
                                     magic_enum::enum_name(other.descriptorType)));
 | 
						|
    }
 | 
						|
    if (other.name != name) {
 | 
						|
        logMsg("Warning: shader variable names do not match, variable will only be referrable to by one of them! ({} != {})",
 | 
						|
               name, other.name);
 | 
						|
    }
 | 
						|
    if (other.type != type) {
 | 
						|
        errors.push_back(fmt::format("Variable types do not match: {} != {}.", type, other.type));
 | 
						|
    }
 | 
						|
 | 
						|
    if (errors.empty()) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    logMsg("Error(s) verifying shader variable compatibility:");
 | 
						|
    for (const std::string& error : errors) {
 | 
						|
        logMsg(error);
 | 
						|
    }
 | 
						|
    std::abort();
 | 
						|
}
 | 
						|
 | 
						|
std::size_t ShaderVariable::calcHash(std::size_t appendTo) const
 | 
						|
{
 | 
						|
    (void) appendTo;
 | 
						|
    MIJIN_TRAP(); // TODO
 | 
						|
    return 0;
 | 
						|
#if 0
 | 
						|
    std::size_t hash = appendTo;
 | 
						|
    hash = type.calcHash(hash);
 | 
						|
    hash = calcCrcSizeAppend(descriptorType, hash);
 | 
						|
    hash = calcCrcSizeAppend(binding, hash);
 | 
						|
    hash = calcCrcSizeAppend(name, hash);
 | 
						|
    return hash;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
ShaderSource ShaderSource::fromFile(std::string fileName, std::string name)
 | 
						|
{
 | 
						|
    (void) fileName;
 | 
						|
    (void) name;
 | 
						|
    MIJIN_TRAP(); // TODO
 | 
						|
    return {};
 | 
						|
    std::string code = readFileText(fileName);
 | 
						|
    return {
 | 
						|
        .code = std::move(code),
 | 
						|
        .fileName = std::move(fileName),
 | 
						|
#if !defined(KAZAN_RELEASE)
 | 
						|
        .name = std::move(name)
 | 
						|
#endif
 | 
						|
    };
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
bool ShaderVariableSet::find(std::string_view varName, ShaderVariableFindResult& outResult) const noexcept
 | 
						|
{
 | 
						|
    for (const ShaderVariable& var : variables)
 | 
						|
    {
 | 
						|
        if (var.name == varName)
 | 
						|
        {
 | 
						|
            outResult.setIndex = setIndex;
 | 
						|
            outResult.bindIndex = var.binding;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ShaderVariableSet::find(unsigned semantic, unsigned semanticIdx, ShaderVariableFindResult& outResult) const noexcept
 | 
						|
{
 | 
						|
    for (const ShaderVariable& var : variables)
 | 
						|
    {
 | 
						|
        if (var.semantic == semantic && var.semanticIndex == semanticIdx)
 | 
						|
        {
 | 
						|
            outResult.setIndex = setIndex;
 | 
						|
            outResult.bindIndex = var.binding;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariable& ShaderVariableSet::getVariableAtBinding(unsigned bindingIdx) const
 | 
						|
{
 | 
						|
    for (const ShaderVariable& var : variables)
 | 
						|
    {
 | 
						|
        if (var.binding == bindingIdx)
 | 
						|
        {
 | 
						|
            return var;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    logAndDie("Could not find shader variable with binding {}!", bindingIdx);
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariable* ShaderVariableSet::getVariableAtBindingOpt(unsigned bindingIdx) const
 | 
						|
{
 | 
						|
    for (const ShaderVariable& var : variables)
 | 
						|
    {
 | 
						|
        if (var.binding == bindingIdx)
 | 
						|
        {
 | 
						|
            return &var;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariable* ShaderVariableSet::getVariableAtSemanticOpt(unsigned semantic, unsigned semanticIdx) const
 | 
						|
{
 | 
						|
    for (const ShaderVariable& var : variables)
 | 
						|
    {
 | 
						|
        if (var.semantic == semantic && var.semanticIndex == semanticIdx)
 | 
						|
        {
 | 
						|
            return &var;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
std::size_t ShaderVariableSet::calcHash(std::size_t appendTo) const
 | 
						|
{
 | 
						|
    std::size_t hash = appendTo;
 | 
						|
    for (const ShaderVariable& var : variables) {
 | 
						|
        hash = var.calcHash(hash);
 | 
						|
    }
 | 
						|
    return hash;
 | 
						|
}
 | 
						|
 | 
						|
void ShaderMeta::extend(ShaderMeta other)
 | 
						|
{
 | 
						|
    for (ShaderVariableSet& set : other.interfaceVariableSets)
 | 
						|
    {
 | 
						|
        ShaderVariableSet& mySet = getOrCreateInterfaceVariableSet(set.setIndex);
 | 
						|
        mySet.usedInStages.bits |= set.usedInStages.bits;
 | 
						|
 | 
						|
        for (ShaderVariable& variable : set.variables)
 | 
						|
        {
 | 
						|
            const ShaderVariable* myVariable = nullptr;
 | 
						|
            if (variable.binding != UNSPECIFIED_INDEX)
 | 
						|
            {
 | 
						|
                myVariable = mySet.getVariableAtBindingOpt(variable.binding);
 | 
						|
            }
 | 
						|
            else if (variable.semantic != UNSPECIFIED_INDEX)
 | 
						|
            {
 | 
						|
                myVariable = mySet.getVariableAtSemanticOpt(variable.semantic, variable.semanticIndex);
 | 
						|
            }
 | 
						|
            if (myVariable)
 | 
						|
            {
 | 
						|
                myVariable->verifyCompatible(variable);
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            mySet.variables.push_back(std::move(variable));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (ShaderAttribute& attribute : other.inputAttributes)
 | 
						|
    {
 | 
						|
        addInputAttribute(std::move(attribute));
 | 
						|
    }
 | 
						|
 | 
						|
    for (ShaderAttribute& attribute : other.outputAttributes)
 | 
						|
    {
 | 
						|
        addOutputAttribute(std::move(attribute));
 | 
						|
    }
 | 
						|
 | 
						|
    extendPushConstant(other.pushConstantBlock, other.pushConstantStages);
 | 
						|
    stages |= other.stages;
 | 
						|
 | 
						|
    if (localSizeX == 0 && localSizeY == 0 && localSizeZ == 0)
 | 
						|
    {
 | 
						|
        localSizeX = other.localSizeX;
 | 
						|
        localSizeY = other.localSizeY;
 | 
						|
        localSizeZ = other.localSizeZ;
 | 
						|
    }
 | 
						|
    else if ((other.localSizeX != 0 || other.localSizeY != 0 || other.localSizeZ != 0) &&
 | 
						|
             (localSizeX != other.localSizeX || localSizeY != other.localSizeY || localSizeZ != other.localSizeZ))
 | 
						|
    {
 | 
						|
        logAndDie("Error merging shader metas, conflicting local size!");
 | 
						|
    }
 | 
						|
 | 
						|
    hash = 0;
 | 
						|
}
 | 
						|
 | 
						|
bool ShaderMeta::findInterfaceVariable(std::string_view varName, ShaderVariableFindResult& outResult) const noexcept
 | 
						|
{
 | 
						|
    for (const ShaderVariableSet& set : interfaceVariableSets)
 | 
						|
    {
 | 
						|
        if (set.find(varName, outResult)) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ShaderMeta::findInterfaceVariable(unsigned semantic, unsigned semanticIdx, ShaderVariableFindResult& outResult) const noexcept
 | 
						|
{
 | 
						|
    for (const ShaderVariableSet& set : interfaceVariableSets)
 | 
						|
    {
 | 
						|
        if (set.find(semantic, semanticIdx, outResult)) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariableSet& ShaderMeta::getInterfaceVariableSet(unsigned setIdx) const
 | 
						|
{
 | 
						|
    const ShaderVariableSet* variableSet = getInterfaceVariableSetOpt(setIdx);
 | 
						|
    MIJIN_ASSERT(variableSet != nullptr, "Could not find interface variable set.");
 | 
						|
    return *variableSet;
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariableSet* ShaderMeta::getInterfaceVariableSetOpt(unsigned setIdx) const
 | 
						|
{
 | 
						|
    for (const ShaderVariableSet& set : interfaceVariableSets)
 | 
						|
    {
 | 
						|
        if (set.setIndex == setIdx) {
 | 
						|
            return &set;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
const ShaderVariableType& ShaderMeta::getInterfaceVariableType(unsigned setIdx, unsigned bindingIdx) const
 | 
						|
{
 | 
						|
    return getInterfaceVariableSet(setIdx).getVariableAtBinding(bindingIdx).type;
 | 
						|
}
 | 
						|
 | 
						|
VertexInput ShaderMeta::generateVertexInput(const NamedVertexInput& namedInput) const noexcept
 | 
						|
{
 | 
						|
    VertexInput result{
 | 
						|
        .bindings = namedInput.bindings
 | 
						|
    };
 | 
						|
 | 
						|
    for (const ShaderAttribute& attribute : inputAttributes)
 | 
						|
    {
 | 
						|
        if (attribute.stage != vk::ShaderStageFlagBits::eVertex) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        MIJIN_ASSERT_FATAL(attribute.type.baseType == ShaderVariableBaseType::SIMPLE, "Vertex shader input must be a simple type.");
 | 
						|
        auto itAttribute = namedInput.attributes.find(attribute.name);
 | 
						|
        MIJIN_ASSERT_FATAL(itAttribute != namedInput.attributes.end(), "Missing attribute in input.");
 | 
						|
        result.attributes.push_back(vk::VertexInputAttributeDescription{
 | 
						|
            .location = attribute.location,
 | 
						|
            .binding = itAttribute->second.binding,
 | 
						|
            .format = attribute.type.simple.format,
 | 
						|
            .offset = itAttribute->second.offset
 | 
						|
        });
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
VertexInput ShaderMeta::generateVertexInputFromLayout(const VertexLayout& layout) const noexcept
 | 
						|
{
 | 
						|
    VertexInput result{
 | 
						|
        .bindings = {
 | 
						|
            vk::VertexInputBindingDescription{
 | 
						|
                .binding = 0,
 | 
						|
                .stride = layout.stride,
 | 
						|
                .inputRate = vk::VertexInputRate::eVertex
 | 
						|
            }
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    for (const ShaderAttribute& attribute : inputAttributes)
 | 
						|
    {
 | 
						|
        if (attribute.stage != vk::ShaderStageFlagBits::eVertex) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        if (attribute.semantic == UNSPECIFIED_INDEX) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        MIJIN_ASSERT_FATAL(attribute.type.baseType == ShaderVariableBaseType::SIMPLE, "Vertex shader input must be a simple type.");
 | 
						|
        auto itAttribute = std::ranges::find_if(layout.attributes, [&attribute](const VertexAttribute& attrib) {
 | 
						|
            return static_cast<unsigned>(attrib.semantic) == attribute.semantic && attrib.semanticIdx == attribute.semanticIndex;
 | 
						|
        });
 | 
						|
        MIJIN_ASSERT_FATAL(itAttribute != layout.attributes.end(), "Missing attribute in vertex layout.");
 | 
						|
        result.attributes.push_back(vk::VertexInputAttributeDescription{
 | 
						|
            .location = attribute.location,
 | 
						|
            .binding = 0,
 | 
						|
            .format = attribute.type.simple.format,
 | 
						|
            .offset = itAttribute->offset
 | 
						|
        });
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
DescriptorSetMeta ShaderMeta::generateDescriptorSetLayout(const ShaderVariableSet& set, const GenerateDescriptorSetLayoutArgs& args) const
 | 
						|
{
 | 
						|
    DescriptorSetMeta setInfo{
 | 
						|
        .flags = args.flags
 | 
						|
    };
 | 
						|
 | 
						|
    for (const ShaderVariable& var : set.variables)
 | 
						|
    {
 | 
						|
        auto itVar = std::ranges::find_if(setInfo.bindings, [&](const vk::DescriptorSetLayoutBinding& binding) {
 | 
						|
            return binding.binding == var.binding;
 | 
						|
        });
 | 
						|
        assert(itVar == setInfo.bindings.end()); // should have been merged!
 | 
						|
        if (itVar != setInfo.bindings.end())
 | 
						|
        {
 | 
						|
            itVar->stageFlags |= typeBitsToVkStages(set.usedInStages);
 | 
						|
            continue; // TODO: verify the bindings are compatible
 | 
						|
        }
 | 
						|
        vk::DescriptorSetLayoutBinding& binding = setInfo.bindings.emplace_back();
 | 
						|
        vk::DescriptorBindingFlags& flags = setInfo.bindingFlags.emplace_back();
 | 
						|
        binding.binding = var.binding;
 | 
						|
        binding.descriptorType = var.descriptorType;
 | 
						|
        binding.descriptorCount = 1;
 | 
						|
        binding.stageFlags = typeBitsToVkStages(set.usedInStages);
 | 
						|
 | 
						|
        // support for dynamically sized descriptors
 | 
						|
        auto itCounts = args.descriptorCounts.find(var.binding);
 | 
						|
        if (itCounts != args.descriptorCounts.end() && itCounts->second > 0)
 | 
						|
        {
 | 
						|
            binding.descriptorCount = itCounts->second;
 | 
						|
            flags |= vk::DescriptorBindingFlagBits::ePartiallyBound;
 | 
						|
        }
 | 
						|
 | 
						|
        if (setInfo.descriptorTypes.size() <= var.binding) {
 | 
						|
            setInfo.descriptorTypes.resize(var.binding + 1);
 | 
						|
        }
 | 
						|
        setInfo.descriptorTypes[var.binding] = var.descriptorType;
 | 
						|
    }
 | 
						|
 | 
						|
    return setInfo;
 | 
						|
}
 | 
						|
 | 
						|
PipelineLayoutMeta ShaderMeta::generatePipelineLayout(const GeneratePipelineLayoutArgs& args) const
 | 
						|
{
 | 
						|
    static const std::vector<std::uint32_t> NO_DESCRIPTOR_COUNTS = {};
 | 
						|
    static const GenerateDescriptorSetLayoutArgs NO_DESCRIPTOR_SET_ARGS = {};
 | 
						|
 | 
						|
    PipelineLayoutMeta result;
 | 
						|
    for (const ShaderVariableSet& set : interfaceVariableSets)
 | 
						|
    {
 | 
						|
        if (set.setIndex >= result.descriptorSets.size()) {
 | 
						|
            result.descriptorSets.resize(set.setIndex + 1);
 | 
						|
        }
 | 
						|
        auto itSet = args.descriptorSets.find(set.setIndex);
 | 
						|
        const GenerateDescriptorSetLayoutArgs setArgs =
 | 
						|
            itSet != args.descriptorSets.end()
 | 
						|
            ? itSet->second
 | 
						|
            : NO_DESCRIPTOR_SET_ARGS;
 | 
						|
        result.descriptorSets[set.setIndex] = generateDescriptorSetLayout(set, setArgs);
 | 
						|
    }
 | 
						|
 | 
						|
    if (pushConstantBlock.type.baseType != ShaderVariableBaseType::NONE)
 | 
						|
    {
 | 
						|
        assert(pushConstantStages);
 | 
						|
        result.pushConstantRange.stageFlags = typeBitsToVkStages(pushConstantStages);
 | 
						|
        result.pushConstantRange.size = pushConstantBlock.offset + calcShaderTypeSize(pushConstantBlock.type);
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
bool ShaderMeta::empty() const
 | 
						|
{
 | 
						|
    static_assert(ShaderMeta::STRUCT_VERSION == 1, "Update me");
 | 
						|
    return interfaceVariableSets.empty()
 | 
						|
           && inputAttributes.empty()
 | 
						|
           && outputAttributes.empty()
 | 
						|
           && pushConstantStages == ShaderTypeBits()
 | 
						|
           && pushConstantBlock.type.baseType == ShaderVariableBaseType::NONE
 | 
						|
           && localSizeX == 0
 | 
						|
           && localSizeY == 0
 | 
						|
           && localSizeZ == 0;
 | 
						|
}
 | 
						|
 | 
						|
std::size_t ShaderMeta::getHash() const
 | 
						|
{
 | 
						|
    if (hash == 0)
 | 
						|
    {
 | 
						|
        hash = 1; // TODO
 | 
						|
        MIJIN_TRAP();
 | 
						|
#if 0
 | 
						|
        for (const ShaderVariableSet& variableSet : interfaceVariableSets) {
 | 
						|
            hash = variableSet.calcHash(hash);
 | 
						|
        }
 | 
						|
        hash = calcCrcSizeAppend(pushConstantStages.bits, hash);
 | 
						|
        hash = pushConstantBlock.type.calcHash(hash);
 | 
						|
        hash = calcCrcSizeAppend(pushConstantBlock.offset, hash);
 | 
						|
        hash = calcCrcSizeAppend(localSizeX, hash);
 | 
						|
        hash = calcCrcSizeAppend(localSizeY, hash);
 | 
						|
        hash = calcCrcSizeAppend(localSizeZ, hash);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    return hash;
 | 
						|
}
 | 
						|
 | 
						|
unsigned calcShaderTypeSize(const ShaderVariableType& type, bool ignoreArraySize) noexcept
 | 
						|
{
 | 
						|
    unsigned size = 0;
 | 
						|
    switch (type.baseType)
 | 
						|
    {
 | 
						|
        case ShaderVariableBaseType::SIMPLE:
 | 
						|
            size = vkFormatSize(type.simple.format);
 | 
						|
            break;
 | 
						|
        case ShaderVariableBaseType::MATRIX:
 | 
						|
            switch (type.matrixType)
 | 
						|
            {
 | 
						|
                case ShaderVariableMatrixType::MAT2:
 | 
						|
                    size = 16;
 | 
						|
                    break;
 | 
						|
                case ShaderVariableMatrixType::MAT3:
 | 
						|
                    size = 36;
 | 
						|
                    break;
 | 
						|
                case ShaderVariableMatrixType::MAT4:
 | 
						|
                    size = 64;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    logAndDie("Lol, what's this?");
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case ShaderVariableBaseType::STRUCT:
 | 
						|
            assert(!type.struct_.members.empty());
 | 
						|
            size = static_cast<unsigned>(type.struct_.members.back().offset + calcShaderTypeSize(type.struct_.members.back().type));
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            logAndDie("How would I know?");
 | 
						|
    }
 | 
						|
    if (!ignoreArraySize) {
 | 
						|
        size *= type.arraySize;
 | 
						|
    }
 | 
						|
    return size;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace iwa
 |