From b9b162b377b4b6bcaf9d374d4afd78ad0ed2fdc9 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 20 Apr 2024 17:29:29 -0400 Subject: [PATCH] Add experimental new rendering system I started writing this late last year, and finally uploading parts of it. Currently, doesn't work, but doesn't crash either. Enable it via the NOVUS_USE_NEW_RENDERER environment variable. The goal is to use the game's own shaders instead of creating our own, it's lacking support for buffers with actual data in them and constants so while it "works" nothing is displayed on screen yet. --- extern/libphysis | 2 +- parts/mdl/mdlpart.cpp | 2 +- parts/shpk/shpkpart.cpp | 2 - renderer/CMakeLists.txt | 13 +- renderer/README.md | 8 +- renderer/include/renderer.hpp | 13 +- renderer/include/rendersystem.h | 71 ++++ renderer/src/renderer.cpp | 278 +++++++++------ renderer/src/rendersystem.cpp | 603 ++++++++++++++++++++++++++++++++ 9 files changed, 883 insertions(+), 109 deletions(-) create mode 100644 renderer/include/rendersystem.h create mode 100644 renderer/src/rendersystem.cpp diff --git a/extern/libphysis b/extern/libphysis index d90badd..991146e 160000 --- a/extern/libphysis +++ b/extern/libphysis @@ -1 +1 @@ -Subproject commit d90badda85518997d9e639c49744531d4745d165 +Subproject commit 991146eeff4c154a6206d58a9f17b4ecea517d58 diff --git a/parts/mdl/mdlpart.cpp b/parts/mdl/mdlpart.cpp index 22e9fb8..506b5a4 100644 --- a/parts/mdl/mdlpart.cpp +++ b/parts/mdl/mdlpart.cpp @@ -28,7 +28,7 @@ MDLPart::MDLPart(GameData *data, FileCache &cache, QWidget *parent) pbd = physis_parse_pbd(physis_gamedata_extract_file(data, "chara/xls/bonedeformer/human.pbd")); - renderer = new Renderer(); + renderer = new Renderer(data); auto inst = new QVulkanInstance(); inst->setVkInstance(renderer->instance); diff --git a/parts/shpk/shpkpart.cpp b/parts/shpk/shpkpart.cpp index 879fd76..61cd3dc 100644 --- a/parts/shpk/shpkpart.cpp +++ b/parts/shpk/shpkpart.cpp @@ -9,8 +9,6 @@ #include #include -dxvk::Logger dxvk::Logger::s_instance("dxbc.log"); - SHPKPart::SHPKPart(GameData *data, QWidget *parent) : QWidget(parent) , data(data) diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt index 8831e92..f34f2da 100644 --- a/renderer/CMakeLists.txt +++ b/renderer/CMakeLists.txt @@ -1,6 +1,10 @@ # SPDX-FileCopyrightText: 2023 Joshua Goins # SPDX-License-Identifier: CC0-1.0 +find_package(spirv_cross_core REQUIRED) +find_package(spirv_cross_glsl REQUIRED) +find_package(SPIRV-Headers REQUIRED) + add_library(renderer STATIC) target_sources(renderer PRIVATE @@ -8,7 +12,9 @@ target_sources(renderer src/renderer.cpp src/imguipass.cpp - src/imguipass.h) + src/imguipass.h + src/rendersystem.cpp + include/rendersystem.h) qt_add_resources(renderer "shaders" PREFIX "/" @@ -25,7 +31,10 @@ target_link_libraries(renderer Vulkan::Vulkan Physis::Physis glm::glm - imgui) + imgui + dxbc + spirv-cross-core + spirv-cross-glsl) target_compile_definitions(renderer PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE GLM_ENABLE_EXPERIMENTAL) add_library(Novus::Renderer ALIAS renderer) \ No newline at end of file diff --git a/renderer/README.md b/renderer/README.md index 2f04e5a..59729ec 100644 --- a/renderer/README.md +++ b/renderer/README.md @@ -1,3 +1,9 @@ # Renderer -This handles rendering FFXIV visual data, such as models and materials. It's still basic at the moment. \ No newline at end of file +This handles rendering FFXIV visual data, such as models and materials. + +Currently it has a basic rendering method enabled by default, which is limited to displaying a material's diffuse texture and not much else. + +## Experimental + +There's a more advanced renderer which is still in development, which uses the game's own shaders to render models. Launch any Novus tool with `NOVUS_USE_NEW_RENDERER=1` set to enable it. \ No newline at end of file diff --git a/renderer/include/renderer.hpp b/renderer/include/renderer.hpp index 21bff39..c890d76 100644 --- a/renderer/include/renderer.hpp +++ b/renderer/include/renderer.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include "rendersystem.h" struct RenderPart { size_t numIndices; @@ -62,7 +62,7 @@ struct ImGuiContext; class Renderer { public: - Renderer(); + Renderer(GameData *data); void initPipeline(); void initDescriptors(); @@ -98,6 +98,9 @@ public: VkDeviceMemory depthMemory; VkImageView depthView; + VkBuffer dummyBuffer; + VkDeviceMemory dummyBufferMemory; + VkImage dummyImage; VkDeviceMemory dummyMemory; VkImageView dummyView; @@ -148,6 +151,12 @@ public: private: void createDummyTexture(); + void createDummyBuffer(); ImGuiPass *imGuiPass = nullptr; + + std::unique_ptr m_renderSystem; + + GameData *m_data = nullptr; + bool m_enableNewRenderSystem = false; }; \ No newline at end of file diff --git a/renderer/include/rendersystem.h b/renderer/include/rendersystem.h new file mode 100644 index 0000000..30f6974 --- /dev/null +++ b/renderer/include/rendersystem.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +class Renderer; +struct RenderModel; + +class RenderSystem +{ +public: + RenderSystem(Renderer &renderer, GameData *data); + + void testInit(::RenderModel *m); + + void render(uint32_t imageIndex, VkCommandBuffer commandBuffer); + + void setSize(uint32_t width, uint32_t height); + +private: + void beginPass(uint32_t imageIndex, VkCommandBuffer commandBuffer, std::string_view passName); + void endPass(VkCommandBuffer commandBuffer); + void bindPipeline(VkCommandBuffer commandBuffer, physis_Shader &vertexShader, physis_Shader &pixelShader); + VkShaderModule convertShaderModule(const physis_Shader &shader, spv::ExecutionModel executionModel); + spirv_cross::CompilerGLSL getShaderModuleResources(const physis_Shader &shader); + + struct RenderModel { + physis_SHPK shpk; + + ::RenderModel *internal_model = nullptr; + }; + std::vector m_renderModels; + + struct RequestedBinding { + VkDescriptorType type; + VkShaderStageFlags stageFlags; + bool used = false; + }; + + struct RequestedSet { + bool used = true; + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + std::vector bindings; + }; + + struct CachedPipeline { + VkPipeline pipeline = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + std::vector setLayouts; + std::map cachedDescriptors; + std::vector requestedSets; + }; + + // combined vertex + pixel code length + std::unordered_map m_cachedPipelines; + + Renderer &m_renderer; + GameData *m_data = nullptr; + VkExtent2D m_extent = {640, 480}; + + VkDescriptorSet createDescriptorFor(const RenderModel &model, const CachedPipeline &cachedPipeline, int i); +}; \ No newline at end of file diff --git a/renderer/src/renderer.cpp b/renderer/src/renderer.cpp index 2c5ca21..284e378 100644 --- a/renderer/src/renderer.cpp +++ b/renderer/src/renderer.cpp @@ -45,8 +45,11 @@ VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBits return VK_FALSE; } -Renderer::Renderer() +Renderer::Renderer(GameData *data) + : m_data(data) { + m_enableNewRenderSystem = qgetenv("NOVUS_USE_NEW_RENDERER") == QByteArrayLiteral("1"); + Q_INIT_RESOURCE(shaders); ctx = ImGui::CreateContext(); @@ -80,11 +83,15 @@ Renderer::Renderer() debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; debugCreateInfo.pfnUserCallback = DebugCallback; + VkApplicationInfo applicationInfo = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; + applicationInfo.apiVersion = VK_API_VERSION_1_3; + VkInstanceCreateInfo createInfo = {}; createInfo.pNext = &debugCreateInfo; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.ppEnabledExtensionNames = instanceExtensions.data(); createInfo.enabledExtensionCount = instanceExtensions.size(); + createInfo.pApplicationInfo = &applicationInfo; vkCreateInstance(&createInfo, nullptr, &instance); @@ -184,12 +191,30 @@ Renderer::Renderer() } } + VkPhysicalDeviceFeatures enabledFeatures{}; + enabledFeatures.shaderClipDistance = VK_TRUE; + enabledFeatures.shaderCullDistance = VK_TRUE; + + VkPhysicalDeviceVulkan11Features enabled11Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES}; + enabled11Features.shaderDrawParameters = VK_TRUE; + + VkPhysicalDeviceVulkan12Features enabled12Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES}; + enabled12Features.vulkanMemoryModel = VK_TRUE; + enabled12Features.pNext = &enabled11Features; + + VkPhysicalDeviceVulkan13Features enabled13Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES}; + enabled13Features.shaderDemoteToHelperInvocation = VK_TRUE; + enabled13Features.dynamicRendering = VK_TRUE; + enabled13Features.pNext = &enabled12Features; + VkDeviceCreateInfo deviceCeateInfo = {}; deviceCeateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCeateInfo.pQueueCreateInfos = queueCreateInfos.data(); deviceCeateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); deviceCeateInfo.ppEnabledExtensionNames = deviceExtensions.data(); deviceCeateInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + deviceCeateInfo.pEnabledFeatures = &enabledFeatures; + deviceCeateInfo.pNext = &enabled13Features; vkCreateDevice(physicalDevice, &deviceCeateInfo, nullptr, &device); @@ -206,6 +231,11 @@ Renderer::Renderer() vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); createDummyTexture(); + createDummyBuffer(); + + if (m_enableNewRenderSystem) { + m_renderSystem = std::make_unique(*this, m_data); + } qInfo() << "Initialized renderer!"; } @@ -418,6 +448,10 @@ bool Renderer::initSwapchain(VkSurfaceKHR surface, int width, int height) ImGui::SetCurrentContext(ctx); imGuiPass = new ImGuiPass(*this); + if (m_enableNewRenderSystem) { + m_renderSystem->setSize(width, height); + } + return true; } @@ -454,124 +488,129 @@ void Renderer::render(const std::vector &models) vkBeginCommandBuffer(commandBuffer, &beginInfo); - VkRenderPassBeginInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapchainFramebuffers[imageIndex]; + if (m_enableNewRenderSystem) { + m_renderSystem->render(imageIndex, commandBuffer); + } else { + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapchainFramebuffers[imageIndex]; - std::array clearValues = {}; - clearValues[0].color.float32[0] = 0.24; - clearValues[0].color.float32[1] = 0.24; - clearValues[0].color.float32[2] = 0.24; - clearValues[0].color.float32[3] = 1.0; - clearValues[1].depthStencil = {1.0f, 0}; + std::array clearValues = {}; + clearValues[0].color.float32[0] = 0.24; + clearValues[0].color.float32[1] = 0.24; + clearValues[0].color.float32[2] = 0.24; + clearValues[0].color.float32[3] = 1.0; + clearValues[1].depthStencil = {1.0f, 0}; - renderPassInfo.clearValueCount = clearValues.size(); - renderPassInfo.pClearValues = clearValues.data(); - renderPassInfo.renderArea.extent = swapchainExtent; + renderPassInfo.clearValueCount = clearValues.size(); + renderPassInfo.pClearValues = clearValues.data(); + renderPassInfo.renderArea.extent = swapchainExtent; - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - for (auto model : models) { - if (model.skinned) { - if (wireframe) { - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skinnedPipelineWireframe); - } else { - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skinnedPipeline); - } - } else { - if (wireframe) { - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineWireframe); - } else { - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - } - } - - // copy bone data - { - const size_t bufferSize = sizeof(glm::mat4) * 128; - void *mapped_data = nullptr; - vkMapMemory(device, model.boneInfoMemory, 0, bufferSize, 0, &mapped_data); - - memcpy(mapped_data, model.boneData.data(), bufferSize); - - VkMappedMemoryRange range = {}; - range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range.memory = model.boneInfoMemory; - range.size = bufferSize; - vkFlushMappedMemoryRanges(device, 1, &range); - - vkUnmapMemory(device, model.boneInfoMemory); - } - - for (const auto &part : model.parts) { - RenderMaterial defaultMaterial = {}; - - RenderMaterial *material = nullptr; - - if (static_cast(part.materialIndex) >= model.materials.size()) { - material = &defaultMaterial; - } else { - material = &model.materials[part.materialIndex]; - } - - const auto h = hash(model, *material); - if (!cachedDescriptors.count(h)) { - if (auto descriptor = createDescriptorFor(model, *material); descriptor != VK_NULL_HANDLE) { - cachedDescriptors[h] = descriptor; + for (auto model : models) { + if (model.skinned) { + if (wireframe) { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skinnedPipelineWireframe); } else { - continue; + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, skinnedPipeline); + } + } else { + if (wireframe) { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineWireframe); + } else { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); } } - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &cachedDescriptors[h], 0, nullptr); + // copy bone data + { + const size_t bufferSize = sizeof(glm::mat4) * 128; + void *mapped_data = nullptr; + vkMapMemory(device, model.boneInfoMemory, 0, bufferSize, 0, &mapped_data); - VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); - vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); + memcpy(mapped_data, model.boneData.data(), bufferSize); - glm::mat4 p = glm::perspective(glm::radians(45.0f), swapchainExtent.width / (float)swapchainExtent.height, 0.1f, 1000.0f); - glm::mat4 v = view; - glm::mat4 vp = p * v; + VkMappedMemoryRange range = {}; + range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range.memory = model.boneInfoMemory; + range.size = bufferSize; + vkFlushMappedMemoryRanges(device, 1, &range); - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(glm::mat4), &vp); + vkUnmapMemory(device, model.boneInfoMemory); + } - auto m = glm::mat4(1.0f); - m = glm::translate(m, model.position); + for (const auto &part : model.parts) { + RenderMaterial defaultMaterial = {}; - vkCmdPushConstants(commandBuffer, - pipelineLayout, - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - sizeof(glm::mat4), - sizeof(glm::mat4), - &m); + RenderMaterial *material = nullptr; - int test = 0; - vkCmdPushConstants(commandBuffer, - pipelineLayout, - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - sizeof(glm::mat4) * 2, - sizeof(int), - &test); + if (static_cast(part.materialIndex) >= model.materials.size()) { + material = &defaultMaterial; + } else { + material = &model.materials[part.materialIndex]; + } - int type = (int)material->type; - vkCmdPushConstants(commandBuffer, - pipelineLayout, - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - sizeof(glm::mat4) * 2 + sizeof(int), - sizeof(int), - &type); + const auto h = hash(model, *material); + if (!cachedDescriptors.count(h)) { + if (auto descriptor = createDescriptorFor(model, *material); descriptor != VK_NULL_HANDLE) { + cachedDescriptors[h] = descriptor; + } else { + continue; + } + } - vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &cachedDescriptors[h], 0, nullptr); + + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); + + glm::mat4 p = glm::perspective(glm::radians(45.0f), swapchainExtent.width / (float)swapchainExtent.height, 0.1f, 1000.0f); + glm::mat4 v = view; + glm::mat4 vp = p * v; + + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(glm::mat4), &vp); + + auto m = glm::mat4(1.0f); + m = glm::translate(m, model.position); + + vkCmdPushConstants(commandBuffer, + pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + sizeof(glm::mat4), + sizeof(glm::mat4), + &m); + + int test = 0; + vkCmdPushConstants(commandBuffer, + pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + sizeof(glm::mat4) * 2, + sizeof(int), + &test); + + int type = (int)material->type; + vkCmdPushConstants(commandBuffer, + pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + sizeof(glm::mat4) * 2 + sizeof(int), + sizeof(int), + &type); + + vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); + } } + + if (imGuiPass != nullptr) { + ImGui::SetCurrentContext(ctx); + imGuiPass->render(commandBuffer); + } + + vkCmdEndRenderPass(commandBuffer); } - if (imGuiPass != nullptr) { - ImGui::SetCurrentContext(ctx); - imGuiPass->render(commandBuffer); - } - - vkCmdEndRenderPass(commandBuffer); vkEndCommandBuffer(commandBuffer); VkSubmitInfo submitInfo = {}; @@ -662,6 +701,10 @@ RenderModel Renderer::addModel(const physis_MDL &model, int lod) reloadModel(renderModel, lod); + if (m_enableNewRenderSystem) { + m_renderSystem->testInit(&renderModel); + } + return renderModel; } @@ -1501,3 +1544,38 @@ void Renderer::createDummyTexture() vkCreateSampler(device, &samplerInfo, nullptr, &dummySampler); } + +void Renderer::createDummyBuffer() +{ + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = 655360; + bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateBuffer(device, &bufferInfo, nullptr, &dummyBuffer); + + // allocate staging memory + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, dummyBuffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + vkAllocateMemory(device, &allocInfo, nullptr, &dummyBufferMemory); + + vkBindBufferMemory(device, dummyBuffer, dummyBufferMemory, 0); + + std::vector fakeData(192); + for (int i = 0; i < fakeData.size(); i++) { + fakeData[i] = glm::vec4{1.0f}; + } + + // copy to staging buffer + void *mapped_data; + vkMapMemory(device, dummyBufferMemory, 0, sizeof(glm::vec4) * 192, 0, &mapped_data); + memcpy(mapped_data, fakeData.data(), sizeof(glm::vec4) * 192); + vkUnmapMemory(device, dummyBufferMemory); +} diff --git a/renderer/src/rendersystem.cpp b/renderer/src/rendersystem.cpp new file mode 100644 index 0000000..69a428a --- /dev/null +++ b/renderer/src/rendersystem.cpp @@ -0,0 +1,603 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rendersystem.h" + +#include + +#include + +#include + +#include "dxbc_module.h" +#include "dxbc_reader.h" +#include "renderer.hpp" +#include + +dxvk::Logger dxvk::Logger::s_instance("dxbc.log"); + +const std::array passes = { + // Shadows? + "PASS_0", + "PASS_Z_OPAQUE", + // Z "Prepass", normals + depth (or is this maybe G_OPAQUE? + "PASS_G_OPAQUE", + // g run for each light + // takes view pos, then unknown texture and normal + "PASS_LIGHTING_OPAQUE", + "PASS_G_SEMITRANSPARENCY", + "PASS_COMPOSITE_OPAQUE", + "PASS_7", + "PASS_WATER", + "PASS_WATER_Z", + "PASS_SEMITRANSPARENCY", + "PASS_COMPOSITE_SEMITRANSPARENCY", + "PASS_10", + "PASS_12", + "PASS_14"}; + +/* +// TODO: auto-select node from material shader keys (see main.rs in physis) +auto node = shpk->get_node(12); + +for (int i = 0; i < 16; i++) { + const int passIndex = node->pass_indices[i]; + if (passIndex != -1) { + physis_ShaderPass *pass = node->passes[passIndex]; + + auto vertex_shader = shpk->shaders[pass->vertex_shader_index]; + auto pixel_shader = shpk->shaders[pass->vertex_shader_index]; + + use_shader(vertex_shader); + use_shader(pixel_shader); + + // set parameters from material + g_MaterialParameter = mtrl->constants; + + draw(); + } +}*/ + +RenderSystem::RenderSystem(Renderer &renderer, GameData *data) + : m_renderer(renderer) + , m_data(data) +{ +} + +void RenderSystem::testInit(::RenderModel *m) +{ + qInfo() << "initialzing render system with dummy data..."; + RenderModel model{.internal_model = new ::RenderModel(*m), + .shpk = physis_parse_shpk(physis_gamedata_extract_file(m_data, "shader/sm5/shpk/character.shpk"))}; + m_renderModels.push_back(model); +} + +void RenderSystem::render(uint32_t imageIndex, VkCommandBuffer commandBuffer) +{ + int i = 0; + for (const auto pass : passes) { + beginPass(imageIndex, commandBuffer, pass); + + // hardcoded to the known pass for now + if (std::string_view{"PASS_G_OPAQUE"} == pass) { + for (auto &model : m_renderModels) { + // hardcoded selector for now + const u_int32_t selector = 276147857; + const physis_SHPKNode node = physis_shpk_get_node(&model.shpk, selector); + + // check if invalid + if (node.pass_count == 0) { + continue; + } + + // this is an index into the node's pass array, not to get confused with the global one we always follow. + const int passIndice = node.pass_indices[i]; + if (passIndice != 255) { + const Pass currentPass = node.passes[passIndice]; + + const uint32_t vertexShaderIndice = currentPass.vertex_shader; + const uint32_t pixelShaderIndice = currentPass.vertex_shader; + + physis_Shader vertexShader = model.shpk.vertex_shaders[vertexShaderIndice]; + physis_Shader pixelShader = model.shpk.pixel_shaders[pixelShaderIndice]; + + bindPipeline(commandBuffer, vertexShader, pixelShader); + + for (const auto &part : model.internal_model->parts) { + // if (part.materialIndex == 1) { + const uint32_t hash = vertexShader.len + pixelShader.len; + auto &cachedPipeline = m_cachedPipelines[hash]; + + int i = 0; + for (auto setLayout : cachedPipeline.setLayouts) { + qInfo() << "Trying to cache descriptor set at " << i; + + if (!cachedPipeline.cachedDescriptors.count(i)) { + if (auto descriptor = createDescriptorFor(model, cachedPipeline, i); descriptor != VK_NULL_HANDLE) { + cachedPipeline.cachedDescriptors[i] = descriptor; + } else { + continue; + } + } + + // TODO: we can pass all descriptors in one function call + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + cachedPipeline.pipelineLayout, + i, + 1, + &cachedPipeline.cachedDescriptors[i], + 0, + nullptr); + qInfo() << "Binding descriptor set at " << i; + + i++; + } + + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); + + qInfo() << "Calling index"; + vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); + //} + } + } + } + } + + endPass(commandBuffer); + + i++; + } +} + +void RenderSystem::setSize(uint32_t width, uint32_t height) +{ + m_extent = {width, height}; +} + +void RenderSystem::beginPass(uint32_t imageIndex, VkCommandBuffer commandBuffer, const std::string_view passName) +{ + VkRenderingInfo renderingInfo{VK_STRUCTURE_TYPE_RENDERING_INFO}; + + renderingInfo.renderArea.extent = m_extent; + + std::vector colorAttachments; + VkRenderingAttachmentInfo depthStencilAttachment{}; + + if (passName == "PASS_G_OPAQUE") { + // normals, it seems like + { + VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; + attachmentInfo.imageView = m_renderer.swapchainViews[imageIndex]; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + attachmentInfo.clearValue.color.float32[0] = 0.24; + attachmentInfo.clearValue.color.float32[1] = 0.24; + attachmentInfo.clearValue.color.float32[2] = 0.24; + attachmentInfo.clearValue.color.float32[3] = 1.0; + + colorAttachments.push_back(attachmentInfo); + } + + // unknown, seems to be background? + { + VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; + attachmentInfo.imageView = VK_NULL_HANDLE; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + colorAttachments.push_back(attachmentInfo); + } + + // unknown, seems to be background? + { + VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; + attachmentInfo.imageView = VK_NULL_HANDLE; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + colorAttachments.push_back(attachmentInfo); + } + + // depth + { + VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; + attachmentInfo.imageView = m_renderer.depthView; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentInfo.clearValue.depthStencil.depth = 1.0f; + + depthStencilAttachment = attachmentInfo; + } + } else { + // qWarning() << "Unimplemented pass" << passName; + } + + renderingInfo.layerCount = 1; + renderingInfo.pColorAttachments = colorAttachments.data(); + renderingInfo.colorAttachmentCount = colorAttachments.size(); + + if (depthStencilAttachment.imageView != VK_NULL_HANDLE) { + renderingInfo.pDepthAttachment = &depthStencilAttachment; + // renderingInfo.pStencilAttachment = &depthStencilAttachment; + } + + vkCmdBeginRendering(commandBuffer, &renderingInfo); +} + +void RenderSystem::endPass(VkCommandBuffer commandBuffer) +{ + vkCmdEndRendering(commandBuffer); +} + +void RenderSystem::bindPipeline(VkCommandBuffer commandBuffer, physis_Shader &vertexShader, physis_Shader &pixelShader) +{ + const uint32_t hash = vertexShader.len + pixelShader.len; + if (!m_cachedPipelines.contains(hash)) { + qInfo() << "Creating new pipeline..."; + + auto vertexShaderModule = convertShaderModule(vertexShader, spv::ExecutionModelVertex); + auto fragmentShaderModule = convertShaderModule(pixelShader, spv::ExecutionModelFragment); + + VkPipelineShaderStageCreateInfo vertexShaderStageInfo = {}; + vertexShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexShaderStageInfo.module = vertexShaderModule; + vertexShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragmentShaderStageInfo = {}; + fragmentShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragmentShaderStageInfo.module = fragmentShaderModule; + fragmentShaderStageInfo.pName = "main"; + + std::array shaderStages = {vertexShaderStageInfo, fragmentShaderStageInfo}; + + VkVertexInputBindingDescription binding = {}; + binding.stride = sizeof(Vertex); + + auto vertex_glsl = getShaderModuleResources(vertexShader); + auto vertex_resources = vertex_glsl.get_shader_resources(); + + auto fragment_glsl = getShaderModuleResources(pixelShader); + auto fragment_resources = fragment_glsl.get_shader_resources(); + + std::vector requestedSets; + + const auto &collectResources = [&requestedSets](const spirv_cross::CompilerGLSL &glsl, + const spirv_cross::SmallVector &resources, + const VkShaderStageFlagBits stageFlagBit) { + for (auto resource : resources) { + unsigned set = glsl.get_decoration(resource.id, spv::DecorationDescriptorSet); + unsigned binding = glsl.get_decoration(resource.id, spv::DecorationBinding); + + if (requestedSets.size() <= set) { + requestedSets.resize(set + 1); + } + + auto &requestSet = requestedSets[set]; + requestSet.used = true; + + if (requestSet.bindings.size() <= binding) { + requestSet.bindings.resize(binding + 1); + } + + auto type = glsl.get_type(resource.type_id); + + if (type.basetype == spirv_cross::SPIRType::Image) { + requestSet.bindings[binding].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + } else if (type.basetype == spirv_cross::SPIRType::Struct) { + requestSet.bindings[binding].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + } else if (type.basetype == spirv_cross::SPIRType::Sampler) { + requestSet.bindings[binding].type = VK_DESCRIPTOR_TYPE_SAMPLER; + } + + requestSet.bindings[binding].used = true; + requestSet.bindings[binding].stageFlags |= stageFlagBit; + + qInfo() << "Requesting set" << set << "at" << binding; + } + }; + + collectResources(vertex_glsl, vertex_resources.uniform_buffers, VK_SHADER_STAGE_VERTEX_BIT); + collectResources(vertex_glsl, vertex_resources.separate_images, VK_SHADER_STAGE_VERTEX_BIT); + collectResources(vertex_glsl, vertex_resources.separate_samplers, VK_SHADER_STAGE_VERTEX_BIT); + + collectResources(fragment_glsl, fragment_resources.uniform_buffers, VK_SHADER_STAGE_FRAGMENT_BIT); + collectResources(fragment_glsl, fragment_resources.separate_images, VK_SHADER_STAGE_FRAGMENT_BIT); + collectResources(fragment_glsl, fragment_resources.separate_samplers, VK_SHADER_STAGE_FRAGMENT_BIT); + + for (auto &set : requestedSets) { + if (set.used) { + int j = 0; + std::vector bindings; + for (auto &binding : set.bindings) { + if (binding.used) { + VkDescriptorSetLayoutBinding boneInfoBufferBinding = {}; + boneInfoBufferBinding.descriptorType = binding.type; + boneInfoBufferBinding.descriptorCount = 1; + boneInfoBufferBinding.stageFlags = binding.stageFlags; + boneInfoBufferBinding.binding = j; + + bindings.push_back(boneInfoBufferBinding); + } + j++; + } + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = bindings.size(); + layoutInfo.pBindings = bindings.data(); + + vkCreateDescriptorSetLayout(m_renderer.device, &layoutInfo, nullptr, &set.layout); + } + } + + VkVertexInputAttributeDescription positionAttribute = {}; + positionAttribute.format = VK_FORMAT_R32G32B32_SFLOAT; + positionAttribute.offset = offsetof(Vertex, position); + + std::vector attributeDescs; + + for (auto texture : vertex_resources.stage_inputs) { + unsigned binding = vertex_glsl.get_decoration(texture.id, spv::DecorationLocation); + + VkVertexInputAttributeDescription uv0Attribute = {}; + + auto type = vertex_glsl.get_type(texture.type_id); + if (type.basetype == spirv_cross::SPIRType::Int) { + switch (type.vecsize) { + case 1: + uv0Attribute.format = VK_FORMAT_R32_SINT; + break; + case 2: + uv0Attribute.format = VK_FORMAT_R32G32_SINT; + break; + case 3: + uv0Attribute.format = VK_FORMAT_R32G32B32_SINT; + break; + case 4: + uv0Attribute.format = VK_FORMAT_R32G32B32A32_SINT; + break; + } + } else { + switch (type.vecsize) { + case 1: + uv0Attribute.format = VK_FORMAT_R32_SFLOAT; + break; + case 2: + uv0Attribute.format = VK_FORMAT_R32G32_SFLOAT; + break; + case 3: + uv0Attribute.format = VK_FORMAT_R32G32B32_SFLOAT; + break; + case 4: + uv0Attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; + break; + } + } + + uv0Attribute.location = binding; + uv0Attribute.offset = offsetof(Vertex, position); + + attributeDescs.push_back(uv0Attribute); + } + + VkPipelineVertexInputStateCreateInfo vertexInputState = {}; + vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputState.vertexBindingDescriptionCount = 1; + vertexInputState.pVertexBindingDescriptions = &binding; + vertexInputState.vertexAttributeDescriptionCount = attributeDescs.size(); + vertexInputState.pVertexAttributeDescriptions = attributeDescs.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkViewport viewport = {}; + viewport.width = 640.0; + viewport.height = 480.0; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.extent.width = 640; + scissor.extent.height = 480; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + std::vector colorBlendAttachments = {colorBlendAttachment, colorBlendAttachment, colorBlendAttachment}; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.attachmentCount = colorBlendAttachments.size(); + colorBlending.pAttachments = colorBlendAttachments.data(); + + VkPipelineDynamicStateCreateInfo dynamicState = {}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + // pipelineLayoutInfo.pushConstantRangeCount = 1; + // pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; + + std::vector setLayouts; + for (auto &set : requestedSets) { + if (set.used) { + setLayouts.push_back(set.layout); + } + } + + pipelineLayoutInfo.setLayoutCount = setLayouts.size(); + pipelineLayoutInfo.pSetLayouts = setLayouts.data(); + + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + vkCreatePipelineLayout(m_renderer.device, &pipelineLayoutInfo, nullptr, &pipelineLayout); + + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.maxDepthBounds = 1.0f; + + std::array colorAttachmentFormats = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED}; + + VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo = {}; + pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + pipelineRenderingCreateInfo.colorAttachmentCount = 3; // TODO: hardcoded + pipelineRenderingCreateInfo.pColorAttachmentFormats = colorAttachmentFormats.data(); + pipelineRenderingCreateInfo.depthAttachmentFormat = VK_FORMAT_D32_SFLOAT; // TODO: hardcoded + + VkGraphicsPipelineCreateInfo createInfo = {}; + createInfo.pNext = &pipelineRenderingCreateInfo; + createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + createInfo.stageCount = shaderStages.size(); + createInfo.pStages = shaderStages.data(); + createInfo.pVertexInputState = &vertexInputState; + createInfo.pInputAssemblyState = &inputAssembly; + createInfo.pViewportState = &viewportState; + createInfo.pRasterizationState = &rasterizer; + createInfo.pMultisampleState = &multisampling; + createInfo.pColorBlendState = &colorBlending; + createInfo.pDynamicState = &dynamicState; + createInfo.pDepthStencilState = &depthStencil; + createInfo.layout = pipelineLayout; + // createInfo.renderPass = m_renderer.renderPass; + + VkPipeline pipeline = VK_NULL_HANDLE; + vkCreateGraphicsPipelines(m_renderer.device, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline); + + qInfo() << "Created" << pipeline << "for hash" << hash; + m_cachedPipelines[hash] = + CachedPipeline{.pipeline = pipeline, .pipelineLayout = pipelineLayout, .setLayouts = setLayouts, .requestedSets = requestedSets}; + } + + auto &pipeline = m_cachedPipelines[hash]; + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); // TODO: return CachedPipeline& +} + +VkShaderModule RenderSystem::convertShaderModule(const physis_Shader &shader, spv::ExecutionModel executionModel) +{ + dxvk::DxbcReader reader(reinterpret_cast(shader.bytecode), shader.len); + + dxvk::DxbcModule module(reader); + + dxvk::DxbcModuleInfo info; + auto result = module.compile(info, "test"); + + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = result.code.size(); + createInfo.pCode = reinterpret_cast(result.code.data()); + + VkShaderModule shaderModule; + vkCreateShaderModule(m_renderer.device, &createInfo, nullptr, &shaderModule); + + return shaderModule; +} + +spirv_cross::CompilerGLSL RenderSystem::getShaderModuleResources(const physis_Shader &shader) +{ + dxvk::DxbcReader reader(reinterpret_cast(shader.bytecode), shader.len); + + dxvk::DxbcModule module(reader); + + dxvk::DxbcModuleInfo info; + auto result = module.compile(info, "test"); + ; + + // glsl.build_combined_image_samplers(); + + return spirv_cross::CompilerGLSL(result.code.data(), result.code.dwords()); +} + +VkDescriptorSet RenderSystem::createDescriptorFor(const RenderModel &model, const CachedPipeline &pipeline, int i) +{ + VkDescriptorSet set; + + VkDescriptorSetAllocateInfo allocateInfo = {}; + allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocateInfo.descriptorPool = m_renderer.descriptorPool; + allocateInfo.descriptorSetCount = 1; + allocateInfo.pSetLayouts = &pipeline.setLayouts[i]; + + vkAllocateDescriptorSets(m_renderer.device, &allocateInfo, &set); + if (set == VK_NULL_HANDLE) { + // qFatal("Failed to create descriptor set!"); + return VK_NULL_HANDLE; + } + + // TODO: way too eager + std::vector writes; + std::vector bufferInfo; + std::vector imageInfo; + + writes.reserve(pipeline.requestedSets[i].bindings.size()); + bufferInfo.reserve(pipeline.requestedSets[i].bindings.size()); + imageInfo.reserve(pipeline.requestedSets[i].bindings.size()); + + int j = 0; + for (auto binding : pipeline.requestedSets[i].bindings) { + if (binding.used) { + VkWriteDescriptorSet &descriptorWrite = writes.emplace_back(); + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.descriptorType = binding.type; + descriptorWrite.dstSet = set; + descriptorWrite.descriptorCount = 1; + descriptorWrite.dstBinding = j; + + switch (binding.type) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + auto info = &imageInfo.emplace_back(); + descriptorWrite.pImageInfo = info; + + info->imageView = m_renderer.dummyView; + info->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } break; + case VK_DESCRIPTOR_TYPE_SAMPLER: { + auto info = &imageInfo.emplace_back(); + descriptorWrite.pImageInfo = info; + + info->sampler = m_renderer.dummySampler; + } break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + auto info = &bufferInfo.emplace_back(); + descriptorWrite.pBufferInfo = info; + + info->buffer = m_renderer.dummyBuffer; + info->range = 655360; + } break; + } + } + j++; + } + + vkUpdateDescriptorSets(m_renderer.device, writes.size(), writes.data(), 0, nullptr); + + return set; +} \ No newline at end of file