diff --git a/CMakeLists.txt b/CMakeLists.txt index be5315b04..4e6a6c949 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1671,6 +1671,43 @@ endif() add_library(love_3p_physfs ${LOVE_SRC_3P_PHYSFS}) +# +# spirv_cross +# + +set(LOVE_SRC_3P_SPIRV_CROSS + src/libraries/spirv_cross/GLSL.std.450.h + src/libraries/spirv_cross/spirv_cfg.cpp + src/libraries/spirv_cross/spirv_cfg.hpp + src/libraries/spirv_cross/spirv_common.hpp + src/libraries/spirv_cross/spirv_cpp.cpp + src/libraries/spirv_cross/spirv_cpp.hpp + src/libraries/spirv_cross/spirv_cross_c.cpp + src/libraries/spirv_cross/spirv_cross_c.h + src/libraries/spirv_cross/spirv_cross_containers.hpp + src/libraries/spirv_cross/spirv_cross_error_handling.hpp + src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp + src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp + src/libraries/spirv_cross/spirv_cross_util.cpp + src/libraries/spirv_cross/spirv_cross_util.hpp + src/libraries/spirv_cross/spirv_cross.cpp + src/libraries/spirv_cross/spirv_cross.hpp + src/libraries/spirv_cross/spirv_glsl.cpp + src/libraries/spirv_cross/spirv_glsl.hpp + src/libraries/spirv_cross/spirv_hlsl.cpp + src/libraries/spirv_cross/spirv_hlsl.hpp + src/libraries/spirv_cross/spirv_msl.cpp + src/libraries/spirv_cross/spirv_msl.hpp + src/libraries/spirv_cross/spirv_parser.cpp + src/libraries/spirv_cross/spirv_parser.hpp + src/libraries/spirv_cross/spirv_reflect.cpp + src/libraries/spirv_cross/spirv_reflect.hpp + src/libraries/spirv_cross/spirv.h + src/libraries/spirv_cross/spirv.hpp +) + +add_library(love_3p_spirv_cross ${LOVE_SRC_3P_SPIRV_CROSS}) + # # stb_image # @@ -1752,6 +1789,7 @@ set(LOVE_3P love_3p_lz4 love_3p_noise1234 love_3p_physfs + love_3p_spirv_cross love_3p_wuff love_3p_xxhash ) diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index db42763fc..35209cd7b 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -3,7 +3,7 @@ #include "libraries/glslang/glslang/Public/ShaderLang.h" #include "libraries/glslang/SPIRV/GlslangToSpv.h" -#include "libraries/spirv_cross/spirv_cross.hpp" + #include @@ -150,12 +150,12 @@ Shader::Shader(StrongRef stages[]) } bool Shader::loadVolatile() { - calculateUniformBufferSizeAligned(); compileShaders(); + calculateUniformBufferSizeAligned(); createDescriptorSetLayout(); createPipelineLayout(); createStreamBuffers(); - currentImage = 0; + currentFrame = 0; count = 0; return true; @@ -201,42 +201,42 @@ static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* textur return imageInfo; } -void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) { +void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t frameIndex) { // detect wether a new frame has begun - if (currentImage != imageIndex) { - currentImage = imageIndex; + if (currentFrame != frameIndex) { + currentFrame = frameIndex; count = 0; // we needed more memory last frame, let's collapse all buffers into a single one. - if (streamBuffers.at(currentImage).size() > 1) { + if (streamBuffers.at(currentFrame).size() > 1) { size_t newSize = 0; - for (auto streamBuffer : streamBuffers.at(currentImage)) { + for (auto streamBuffer : streamBuffers.at(currentFrame)) { newSize += streamBuffer->getSize(); delete streamBuffer; } - streamBuffers.at(currentImage).clear(); - streamBuffers.at(currentImage).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, newSize)); + streamBuffers.at(currentFrame).clear(); + streamBuffers.at(currentFrame).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, newSize)); } // no collapse necessary, can just call nextFrame to reset the current (only) streambuffer else { - streamBuffers.at(currentImage).at(0)->nextFrame(); + streamBuffers.at(currentFrame).at(0)->nextFrame(); } } // still the same frame else { auto usedStreamBufferMemory = count * uniformBufferSizeAligned; - if (usedStreamBufferMemory >= streamBuffers.at(currentImage).back()->getSize()) { + if (usedStreamBufferMemory >= streamBuffers.at(currentFrame).back()->getSize()) { // we ran out of memory in the current frame, need to allocate more. - streamBuffers.at(currentImage).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned)); + streamBuffers.at(currentFrame).push_back(new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned)); count = 0; } } // additional data is always added onto the last stream buffer in the current frame - auto currentStreamBuffer = streamBuffers.at(currentImage).back(); + auto currentStreamBuffer = streamBuffers.at(currentFrame).back(); auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned); - memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned); + memcpy(mapInfo.data, localUniformStagingData.data(), uniformBufferSizeAligned); currentStreamBuffer->unmap(uniformBufferSizeAligned); currentStreamBuffer->markUsed(uniformBufferSizeAligned); @@ -245,53 +245,42 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t image bufferInfo.offset = count * uniformBufferSizeAligned; bufferInfo.range = sizeof(BuiltinUniformData); - auto mainTexImageInfo = createDescriptorImageInfo(mainTex); - auto ytextureImageInfo = createDescriptorImageInfo(ytexture); - auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture); - auto crtextureImageInfo = createDescriptorImageInfo(crtexture); + std::vector descriptorWrite{}; - std::array descriptorWrite{}; - descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[0].dstSet = 0; - descriptorWrite[0].dstBinding = 0; - descriptorWrite[0].dstArrayElement = 0; - descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrite[0].descriptorCount = 1; - descriptorWrite[0].pBufferInfo = &bufferInfo; + // uniform buffer update always happens. + VkWriteDescriptorSet uniformWrite{}; + uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + uniformWrite.dstSet = 0; + uniformWrite.dstBinding = builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->location; + uniformWrite.dstArrayElement = 0; + uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniformWrite.descriptorCount = 1; + uniformWrite.pBufferInfo = &bufferInfo; - descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[1].dstSet = 0; - descriptorWrite[1].dstBinding = 1; - descriptorWrite[1].dstArrayElement = 0; - descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[1].descriptorCount = 1; - descriptorWrite[1].pImageInfo = &mainTexImageInfo; + descriptorWrite.push_back(uniformWrite); - descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[2].dstSet = 0; - descriptorWrite[2].dstBinding = 2; - descriptorWrite[2].dstArrayElement = 0; - descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[2].descriptorCount = 1; - descriptorWrite[2].pImageInfo = &ytextureImageInfo; + // update everything other than uniform buffers (since that's already taken care of. + for (const auto& [key, val] : uniformInfos) { + // fixme: other types. + if (val.baseType == UNIFORM_SAMPLER) { + VkWriteDescriptorSet write{}; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.dstSet = 0; + write.dstBinding = val.location; + write.dstArrayElement = 0; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.descriptorCount = 1; + const auto imageInfo = createDescriptorImageInfo(val.textures[0]); // fixme: arrays + write.pImageInfo = &imageInfo; - descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[3].dstSet = 0; - descriptorWrite[3].dstBinding = 3; - descriptorWrite[3].dstArrayElement = 0; - descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[3].descriptorCount = 1; - descriptorWrite[3].pImageInfo = &cbtextureImageInfo; + descriptorWrite.push_back(write); + } + } - descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[4].dstSet = 0; - descriptorWrite[4].dstBinding = 4; - descriptorWrite[4].dstArrayElement = 0; - descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[4].descriptorCount = 1; - descriptorWrite[4].pImageInfo = &crtextureImageInfo; - - vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast(descriptorWrite.size()), descriptorWrite.data()); + vkCmdPushDescriptorSet( + commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, 0, + static_cast(descriptorWrite.size()), descriptorWrite.data()); count++; } @@ -309,22 +298,89 @@ void Shader::attach() { } int Shader::getVertexAttributeIndex(const std::string& name) { - return vertexAttributeIndices.at(name); + return 0; } void Shader::calculateUniformBufferSizeAligned() { gfx = Module::getInstance(Module::ModuleType::M_GRAPHICS); auto vgfx = (Graphics*)gfx; auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment(); + size_t size = localUniformStagingData.size(); uniformBufferSizeAligned = static_cast( std::ceil( - static_cast(sizeof(BuiltinUniformData)) / static_cast(minAlignment) + static_cast(size) / static_cast(minAlignment) ) ) * minAlignment; } +void Shader::buildLocalUniforms(spirv_cross::Compiler& comp, const spirv_cross::SPIRType& type, size_t baseoff, const std::string& basename) { + using namespace spirv_cross; + + const auto& membertypes = type.member_types; + + for (size_t uindex = 0; uindex < membertypes.size(); uindex) { + const auto& memberType = comp.get_type(membertypes[uindex]); + size_t memberSize = comp.get_declared_struct_member_size(type, uindex); + size_t offset = baseoff + comp.type_struct_member_offset(type, uindex); + + std::string name = basename + comp.get_member_name(type.self, uindex); + + switch (memberType.basetype) { + case SPIRType::Struct: + name += "."; + buildLocalUniforms(comp, memberType, offset, name); + continue; + case SPIRType::Int: + case SPIRType::UInt: + case SPIRType::Float: + break; + default: + continue; + } + + UniformInfo u = {}; + u.name = name; + u.dataSize = memberSize; + u.count = memberType.array.empty() ? 1 : memberType.array[0]; + u.components = 1; + u.data = localUniformStagingData.data() + offset; + + if (memberType.columns == 1) { + if (memberType.basetype == SPIRType::Int) { + u.baseType = UNIFORM_INT; + } + else if (memberType.basetype == SPIRType::UInt) { + u.baseType = UNIFORM_UINT; + } + else { + u.baseType = UNIFORM_FLOAT; + } + u.components = memberType.vecsize; + } + else { + u.baseType = UNIFORM_MATRIX; + u.matrix.rows = memberType.vecsize; + u.matrix.columns = memberType.columns; + } + + // fixme: initializer values + + uniformInfos[u.name] = u; + + BuiltinUniform builtin = BUILTIN_MAX_ENUM; + if (getConstant(u.name.c_str(), builtin)) { + if (builtin == BUILTIN_UNIFORMS_PER_DRAW) { + builtinUniformDataOffset = offset; + } + builtinUniformInfo[builtin] = &uniformInfos[u.name]; + } + + // update uniform. + } +} + void Shader::compileShaders() { using namespace glslang; using namespace spirv_cross; @@ -335,7 +391,6 @@ void Shader::compileShaders() { auto vgfx = (Graphics*)gfx; device = vgfx->getDevice(); - mainTex = vgfx->getDefaultTexture(); ytexture = vgfx->getDefaultTexture(); crtexture = vgfx->getDefaultTexture(); cbtexture = vgfx->getDefaultTexture(); @@ -385,6 +440,8 @@ void Shader::compileShaders() { throw love::Exception("mapIO failed"); } + uniformInfos.clear(); + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { auto glslangStage = getGlslShaderType((ShaderStageType)i); auto intermediate = program->getIntermediate(glslangStage); @@ -424,49 +481,158 @@ void Shader::compileShaders() { shaderStageInfo.pName = "main"; shaderStages.push_back(shaderStageInfo); + + spirv_cross::CompilerGLSL comp(spirv); + + // we only care about variables that are actually getting used. + auto active = comp.get_active_interface_variables(); + auto shaderResources = comp.get_shader_resources(active); + comp.set_enabled_interface_variables(std::move(active)); + + std::string builtinUniformName = "love_UniformsPerDraw"; + + for (const auto& resource : shaderResources.uniform_buffers) { + size_t uniformBufferObjectSize = comp.get_declared_struct_size(comp.get_type(resource.base_type_id)); + + const auto& resourceType = comp.get_type(resource.type_id); + unsigned memberCount = resourceType.member_types.size(); + for (unsigned i = 0; i < memberCount; i++) { + auto& type = comp.get_type(resourceType.member_types[i]); + auto baseType = type.basetype; + const std::string& name = comp.get_member_name(resourceType.self, i); + + if (name == "gl_DefaultUniformBlock") { + auto defaultUniformBlockSize = comp.get_declared_struct_size(type); + localUniformStagingData.resize(defaultUniformBlockSize); + + std::string basename(""); + buildLocalUniforms(comp, type, 0, basename); + } + else if (name == builtinUniformName) { + UniformInfo u{}; + u.name = name; + u.dataSize = sizeof(BuiltinUniformData); + + localUniformStagingData.resize(u.dataSize); + builtinUniformDataOffset = 0; + + u.count = type.array.empty() ? 1 : type.array[0]; + u.components = 1; + u.data = localUniformStagingData.data(); + + if (type.columns == 1) { + if (type.basetype == SPIRType::Int) { + u.baseType = UNIFORM_INT; + } + else if (type.basetype == SPIRType::UInt) { + u.baseType = UNIFORM_UINT; + } + else { + u.baseType = UNIFORM_FLOAT; + } + u.components = type.vecsize; + } + else { + u.baseType = UNIFORM_MATRIX; + u.matrix.rows = type.vecsize; + u.matrix.columns = type.columns; + } + + uniformInfos[u.name] = u; + + builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW] = &uniformInfos[u.name]; + } + else { + throw love::Exception("unimplemented: non default uniform blocks."); + } + } + + for (const auto& r : shaderResources.sampled_images) { + const SPIRType& basetype = comp.get_type(r.base_type_id); + const SPIRType& type = comp.get_type(r.type_id); + const SPIRType& imagetype = comp.get_type(basetype.image.type); + + graphics::Shader::UniformInfo info; + info.location = comp.get_decoration(r.id, spv::DecorationBinding); + info.baseType = UNIFORM_SAMPLER; + info.name = r.name; + info.count = type.array.empty() ? 1 : type.array[0]; + info.isDepthSampler = type.image.depth; + info.components = 1; + + switch (imagetype.basetype) { + case SPIRType::Float: + info.dataBaseType = DATA_BASETYPE_FLOAT; + break; + case SPIRType::Int: + info.dataBaseType = DATA_BASETYPE_INT; + break; + case SPIRType::UInt: + info.dataBaseType = DATA_BASETYPE_UINT; + break; + default: + break; + } + + switch (basetype.image.dim) { + case spv::Dim2D: + info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D; + info.textures = new love::graphics::Texture * [info.count]; + break; + case spv::Dim3D: + info.textureType = TEXTURE_VOLUME; + info.textures = new love::graphics::Texture * [info.count]; + break; + case spv::DimCube: + if (basetype.image.arrayed) { + throw love::Exception("cubemap arrays are not currently supported"); + } + info.textureType = TEXTURE_CUBE; + info.textures = new love::graphics::Texture * [info.count]; + break; + case spv::DimBuffer: + throw love::Exception("dim buffers not implemented yet"); + default: + throw love::Exception("unknown dim"); + } + + if (info.baseType == UNIFORM_SAMPLER) { + auto tex = vgfx->getDefaultTexture(); + for (int i = 0; i < info.count; i++) { + info.textures[i] = tex; + } + } + // fixme + else if (info.baseType == UNIFORM_TEXELBUFFER) { + throw love::Exception("texel buffers not supported yet"); + } + + uniformInfos[r.name] = info; + BuiltinUniform builtin; + if (getConstant(r.name.c_str(), builtin)) { + builtinUniformInfo[builtin] = &uniformInfos[info.name]; + } + } + } } } -// fixme: should generate this dynamically. void Shader::createDescriptorSetLayout() { auto vgfx = (Graphics*)gfx; vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer(); - VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + std::vector bindings; - VkDescriptorSetLayoutBinding samplerLayoutBinding{}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.pImmutableSamplers = nullptr; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + for (auto const& [key, val] : uniformInfos) { + VkDescriptorSetLayoutBinding layoutBinding{}; + layoutBinding.binding = val.location; + layoutBinding.descriptorType = val.baseType == UNIFORM_SAMPLER ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layoutBinding.descriptorCount = 1; // is this correct? + layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; // fixme: can we determine in what shader it got used? - VkDescriptorSetLayoutBinding videoYBinding{}; - videoYBinding.binding = 2; - videoYBinding.descriptorCount = 1; - videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoYBinding.pImmutableSamplers = nullptr; - videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + bindings.push_back(layoutBinding); + } - VkDescriptorSetLayoutBinding videoCBBinding{}; - videoCBBinding.binding = 3; - videoCBBinding.descriptorCount = 1; - videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoCBBinding.pImmutableSamplers = nullptr; - videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding videoCRinding{}; - videoCRinding.binding = 4; - videoCRinding.descriptorCount = 1; - videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoCRinding.pImmutableSamplers = nullptr; - videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - std::array bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding }; VkDescriptorSetLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; @@ -506,11 +672,12 @@ void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cb } void Shader::setUniformData(BuiltinUniformData& data) { - uniformData = data; + char* ptr = (char*) builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->data + builtinUniformDataOffset; + memcpy(ptr, &data, sizeof(BuiltinUniformData)); } void Shader::setMainTex(graphics::Texture* texture) { - mainTex = texture; + builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture; } } // vulkan } // graphics diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 13976a114..a23da784d 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -6,9 +6,12 @@ #include "Vulkan.h" #include +#include "libraries/spirv_cross/spirv_reflect.hpp" #include +#include #include +#include namespace love { @@ -26,7 +29,7 @@ public: const VkPipelineLayout getGraphicsPipelineLayout() const; - void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage); + void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentFrame); void attach() override; @@ -58,6 +61,11 @@ private: void createDescriptorSetLayout(); void createPipelineLayout(); void createStreamBuffers(); + void buildLocalUniforms( + spirv_cross::Compiler& comp, + const spirv_cross::SPIRType& type, + size_t baseoff, + const std::string& basename); VkDeviceSize uniformBufferSizeAligned; PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; @@ -75,27 +83,18 @@ private: Graphics* gfx; VkDevice device; - std::map vertexAttributeIndices = { - { "VertexPosition", 0 }, - { "VertexTexCoord", 1 }, - { "VertexColor", 2 } - }; + std::unordered_map uniformInfos; + UniformInfo* builtinUniformInfo[BUILTIN_MAX_ENUM]; - std::map uniformBindings = { - { "love_UniformsPerDraw", 0 }, - { "love_VideoYChannel", 1 }, - { "love_VideoCbChannel", 2 }, - { "love_VideoCrChannel", 3 }, - { "MainTex", 4 } - }; + std::unique_ptr uniformBufferObjectBuffer; + std::vector localUniformStagingData; + size_t builtinUniformDataOffset; - BuiltinUniformData uniformData; - graphics::Texture* mainTex; graphics::Texture* ytexture; graphics::Texture* cbtexture; graphics::Texture* crtexture; - uint32_t currentImage; + uint32_t currentFrame; // todo: give this variable a better name uint32_t count; };