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