// 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 #include // TODO: maybe need UV? const std::vector planeVertices = { {-1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, {-1.0f, 1.0f, 0.0f, 1.0f}, {-1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, }; dxvk::Logger dxvk::Logger::s_instance("dxbc.log"); const std::array passes = { // Shadows? "PASS_0", // Z "prepass" "PASS_Z_OPAQUE", // computes and stores normals (TODO: denote how these normals are special) "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"}; const int INVALID_PASS = 255; RenderSystem::RenderSystem(Renderer &renderer, GameData *data) : m_renderer(renderer) , m_data(data) { normalGBuffer = createImage(640, 480, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); viewPositionBuffer = createImage(640, 480, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); size_t vertexSize = planeVertices.size() * sizeof(glm::vec4); auto [vertexBuffer, vertexMemory] = m_renderer.createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); m_planeVertexBuffer = vertexBuffer; m_planeVertexMemory = vertexMemory; // copy vertex data { void *mapped_data = nullptr; vkMapMemory(m_renderer.device, vertexMemory, 0, vertexSize, 0, &mapped_data); memcpy(mapped_data, planeVertices.data(), vertexSize); VkMappedMemoryRange range = {}; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.memory = vertexMemory; range.size = vertexSize; vkFlushMappedMemoryRanges(m_renderer.device, 1, &range); vkUnmapMemory(m_renderer.device, vertexMemory); } directionalLightningShpk = physis_parse_shpk(physis_gamedata_extract_file(m_data, "shader/sm5/shpk/directionallighting.shpk")); createViewPositionShpk = physis_parse_shpk(physis_gamedata_extract_file(m_data, "shader/sm5/shpk/createviewposition.shpk")); // camera data { g_CameraParameter = createUniformBuffer(sizeof(CameraParameter)); } // joint matrix data { g_JointMatrixArray = createUniformBuffer(sizeof(JointMatrixArray)); JointMatrixArray jointMatrixArray{}; for (int i = 0; i < 64; i++) { jointMatrixArray.g_JointMatrixArray[i] = glm::mat3x4(1.0f); } copyDataToUniform(g_JointMatrixArray, &jointMatrixArray, sizeof(JointMatrixArray)); } // instance data { g_InstanceParameter = createUniformBuffer(sizeof(InstanceParameter)); InstanceParameter instanceParameter{}; copyDataToUniform(g_InstanceParameter, &instanceParameter, sizeof(InstanceParameter)); } // model data { g_ModelParameter = createUniformBuffer(sizeof(ModelParameter)); ModelParameter modelParameter{}; copyDataToUniform(g_ModelParameter, &modelParameter, sizeof(ModelParameter)); } // material data { g_MaterialParameter = createUniformBuffer(sizeof(MaterialParameter)); MaterialParameter materialParameter{}; materialParameter.g_AlphaThreshold = 0.0f; materialParameter.g_DiffuseColor = glm::vec3(1.0f); copyDataToUniform(g_MaterialParameter, &materialParameter, sizeof(MaterialParameter)); } // light data { g_LightParam = createUniformBuffer(sizeof(LightParam)); LightParam lightParam{}; lightParam.m_Position = glm::vec4(-5); lightParam.m_Direction = glm::normalize(glm::vec4(0) - lightParam.m_Position); lightParam.m_DiffuseColor = glm::vec4(1); lightParam.m_SpecularColor = glm::vec4(1); lightParam.m_Attenuation = glm::vec4(5.0f); /*lightParam.m_ClipMin = glm::vec4(0.0f); lightParam.m_ClipMax = glm::vec4(5.0f);*/ copyDataToUniform(g_LightParam, &lightParam, sizeof(LightParam)); } // common data { g_CommonParameter = createUniformBuffer(sizeof(CommonParameter)); CommonParameter commonParam{}; /*commonParam.m_RenderTarget = {1640.0f / 2.0f, 480.0f / 2.0f, 640.0f / 2.0, 480.0f / 2.0}; // used to convert screen-space coordinates back into 0.0-1.0 */ commonParam.m_RenderTarget = {1.0f / 640.0f, 1.0f / 480.0f, 0.0f, 0.0f}; // used to convert screen-space coordinates back into 0.0-1.0 copyDataToUniform(g_CommonParameter, &commonParam, sizeof(CommonParameter)); } } void RenderSystem::testInit(::RenderModel *m) { RenderModel model{.shpk = physis_parse_shpk(physis_gamedata_extract_file(m_data, "shader/sm5/shpk/character.shpk")), .internal_model = new ::RenderModel(*m)}; m_renderModels.push_back(model); } void RenderSystem::render(uint32_t imageIndex, VkCommandBuffer commandBuffer) { // TODO: this shouldn't be here CameraParameter cameraParameter{}; glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), 640.0f / 480.0f, 0.1f, 1000.0f); glm::mat4 viewMatrix = m_renderer.view; glm::mat4 viewProjectionMatrix = projectionMatrix * viewMatrix; cameraParameter.m_ViewMatrix = glm::transpose(viewMatrix); cameraParameter.m_InverseViewMatrix = glm::transpose(glm::inverse(viewMatrix)); cameraParameter.m_ViewProjectionMatrix = glm::transpose(viewProjectionMatrix); cameraParameter.m_InverseViewProjectionMatrix = glm::transpose(glm::inverse(viewProjectionMatrix)); // known params cameraParameter.m_InverseProjectionMatrix = glm::transpose(glm::inverse(viewProjectionMatrix)); cameraParameter.m_ProjectionMatrix = glm::transpose(viewProjectionMatrix); cameraParameter.m_MainViewToProjectionMatrix = glm::transpose(glm::inverse(projectionMatrix)); cameraParameter.m_EyePosition = glm::vec3(5.0f); // placeholder cameraParameter.m_LookAtVector = glm::vec3(0.0f); // placeholder copyDataToUniform(g_CameraParameter, &cameraParameter, sizeof(CameraParameter)); int i = 0; for (const auto pass : passes) { // hardcoded to the known pass for now if (pass == "PASS_G_OPAQUE" || pass == "PASS_Z_OPAQUE") { beginPass(imageIndex, commandBuffer, pass); for (auto &model : m_renderModels) { std::vector systemKeys; std::vector sceneKeys = { physis_shpk_crc("TransformViewSkin"), physis_shpk_crc("GetAmbientLight_SH"), physis_shpk_crc("GetReflectColor_Texture"), physis_shpk_crc("GetAmbientOcclusion_None"), physis_shpk_crc("ApplyDitherClipOff"), }; std::vector materialKeys; for (int j = 0; j < model.shpk.num_material_keys; j++) { auto value = model.shpk.material_keys[j].default_value; // Replace MODE_DEFAULT with MODE_SIMPLE for now if (value != 0x5CC605B5) { materialKeys.push_back(model.shpk.material_keys[j].default_value); } else { materialKeys.push_back(0x22A4AABF); } } std::vector subviewKeys = {physis_shpk_crc("Default"), physis_shpk_crc("SUB_VIEW_MAIN")}; const u_int32_t selector = physis_shpk_build_selector_from_all_keys(systemKeys.data(), systemKeys.size(), sceneKeys.data(), sceneKeys.size(), materialKeys.data(), materialKeys.size(), subviewKeys.data(), subviewKeys.size()); 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 != INVALID_PASS) { const Pass currentPass = node.passes[passIndice]; const uint32_t vertexShaderIndice = currentPass.vertex_shader; const uint32_t pixelShaderIndice = currentPass.pixel_shader; physis_Shader vertexShader = model.shpk.vertex_shaders[vertexShaderIndice]; physis_Shader pixelShader = model.shpk.pixel_shaders[pixelShaderIndice]; bindPipeline(commandBuffer, pass, vertexShader, pixelShader); for (const auto &part : model.internal_model->parts) { VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); } } } endPass(commandBuffer, pass); } else if (pass == "PASS_LIGHTING_OPAQUE") { // first we need to generate the view positions with createviewpositions beginPass(imageIndex, commandBuffer, "PASS_LIGHTING_OPAQUE_VIEWPOSITION"); { std::vector systemKeys = { physis_shpk_crc("DecodeDepthBuffer_RAWZ"), }; std::vector subviewKeys = { physis_shpk_crc("Default"), physis_shpk_crc("SUB_VIEW_MAIN"), }; const u_int32_t selector = physis_shpk_build_selector_from_all_keys(systemKeys.data(), systemKeys.size(), nullptr, 0, nullptr, 0, subviewKeys.data(), subviewKeys.size()); const physis_SHPKNode node = physis_shpk_get_node(&createViewPositionShpk, selector); // check if invalid if (node.pass_count == 0) { continue; } const int passIndice = node.pass_indices[i]; if (passIndice != INVALID_PASS) { const Pass currentPass = node.passes[passIndice]; const uint32_t vertexShaderIndice = currentPass.vertex_shader; const uint32_t pixelShaderIndice = currentPass.pixel_shader; physis_Shader vertexShader = createViewPositionShpk.vertex_shaders[vertexShaderIndice]; physis_Shader pixelShader = createViewPositionShpk.pixel_shaders[pixelShaderIndice]; bindPipeline(commandBuffer, "PASS_LIGHTING_OPAQUE_VIEWPOSITION", vertexShader, pixelShader); VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_planeVertexBuffer, offsets); vkCmdDraw(commandBuffer, 6, 1, 0, 0); } } endPass(commandBuffer, pass); beginPass(imageIndex, commandBuffer, pass); // then run the directionallighting shader { std::vector systemKeys = { physis_shpk_crc("DecodeDepthBuffer_RAWZ"), }; std::vector sceneKeys = { physis_shpk_crc("GetDirectionalLight_Enable"), physis_shpk_crc("GetFakeSpecular_Disable"), physis_shpk_crc("GetUnderWaterLighting_Disable"), }; std::vector subviewKeys = { physis_shpk_crc("Default"), physis_shpk_crc("SUB_VIEW_MAIN"), }; const u_int32_t selector = physis_shpk_build_selector_from_all_keys(systemKeys.data(), systemKeys.size(), sceneKeys.data(), sceneKeys.size(), nullptr, 0, subviewKeys.data(), subviewKeys.size()); const physis_SHPKNode node = physis_shpk_get_node(&directionalLightningShpk, selector); // check if invalid if (node.pass_count == 0) { continue; } const int passIndice = node.pass_indices[i]; if (passIndice != INVALID_PASS) { const Pass currentPass = node.passes[passIndice]; const uint32_t vertexShaderIndice = currentPass.vertex_shader; const uint32_t pixelShaderIndice = currentPass.pixel_shader; physis_Shader vertexShader = directionalLightningShpk.vertex_shaders[vertexShaderIndice]; physis_Shader pixelShader = directionalLightningShpk.pixel_shaders[pixelShaderIndice]; bindPipeline(commandBuffer, pass, vertexShader, pixelShader); VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_planeVertexBuffer, offsets); vkCmdDraw(commandBuffer, 6, 1, 0, 0); } } endPass(commandBuffer, pass); } i++; } } void RenderSystem::setSize(uint32_t width, uint32_t height) { m_extent = {width, height}; // TODO: this is because of our terrible resource handling. an image referenced in these may be gone due to resizing, for example for (auto &[hash, cachedPipeline] : m_cachedPipelines) { cachedPipeline.cachedDescriptors.clear(); } } 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 = normalGBuffer.imageView; attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 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 if (passName == "PASS_LIGHTING_OPAQUE") { // normals, it seems like { VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; attachmentInfo.imageView = m_renderer.swapchainViews[imageIndex]; attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 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 { 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); } } else if (passName == "PASS_LIGHTING_OPAQUE_VIEWPOSITION") { // TODO: Hack we should not be using a special pass for this, we should just design our API better { VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; attachmentInfo.imageView = viewPositionBuffer.imageView; attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 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); } } else if (passName == "PASS_Z_OPAQUE") { // normals, it seems like { VkRenderingAttachmentInfo attachmentInfo{VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO}; attachmentInfo.imageView = m_renderer.swapchainViews[imageIndex]; attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 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 { 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); } } renderingInfo.layerCount = 1; renderingInfo.pColorAttachments = colorAttachments.data(); renderingInfo.colorAttachmentCount = colorAttachments.size(); if (depthStencilAttachment.imageView != VK_NULL_HANDLE) { renderingInfo.pDepthAttachment = &depthStencilAttachment; } vkCmdBeginRendering(commandBuffer, &renderingInfo); } void RenderSystem::endPass(VkCommandBuffer commandBuffer, std::string_view passName) { vkCmdEndRendering(commandBuffer); } void RenderSystem::bindPipeline(VkCommandBuffer commandBuffer, std::string_view passName, physis_Shader &vertexShader, physis_Shader &pixelShader) { const uint32_t hash = vertexShader.len + pixelShader.len + physis_shpk_crc(passName.data()); if (!m_cachedPipelines.contains(hash)) { 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; // m_renderer.loadShaderFromDisk(":/shaders/dummy.frag.spv"); fragmentShaderStageInfo.pName = "main"; std::array shaderStages = {vertexShaderStageInfo, fragmentShaderStageInfo}; VkVertexInputBindingDescription binding = {}; // TODO: temporary if (passName == "PASS_G_OPAQUE" || passName == "PASS_Z_OPAQUE") { binding.stride = sizeof(Vertex); } else if (passName == "PASS_LIGHTING_OPAQUE" || passName == "PASS_LIGHTING_OPAQUE_VIEWPOSITION") { binding.stride = sizeof(glm::vec4); } 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); } } std::vector attributeDescs; for (auto texture : vertex_resources.stage_inputs) { unsigned binding = vertex_glsl.get_decoration(texture.id, spv::DecorationLocation); auto name = vertex_glsl.get_name(texture.id); 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_R8G8B8A8_UINT; // supposed to be VK_FORMAT_R32G32B32A32_SINT, but our bone_id is uint8_t currently 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; // TODO: temporary if (name == "v0") { uv0Attribute.offset = offsetof(Vertex, position); } else if (name == "v1") { uv0Attribute.offset = offsetof(Vertex, color); } else if (name == "v2") { uv0Attribute.offset = offsetof(Vertex, normal); } else if (name == "v3") { uv0Attribute.offset = offsetof(Vertex, uv0); } else if (name == "v4") { uv0Attribute.offset = offsetof(Vertex, bitangent); // FIXME: should be tangent } else if (name == "v5") { uv0Attribute.offset = offsetof(Vertex, bitangent); } else if (name == "v6") { uv0Attribute.offset = offsetof(Vertex, bone_weight); } else if (name == "v7") { uv0Attribute.offset = offsetof(Vertex, bone_id); } 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_NONE; // TODO: implement cull mode 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; int colorAttachmentCount = 1; // TODO: hardcoded, should be a reusable function to get the color attachments if (passName == "PASS_G_OPAQUE") { colorAttachmentCount = 3; } else if (passName == "PASS_LIGHTING_OPAQUE") { colorAttachmentCount = 2; } for (int i = 0; i < colorAttachmentCount; i++) { colorBlendAttachments.push_back(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, .vertexShader = vertexShader, .pixelShader = pixelShader}; } auto &pipeline = m_cachedPipelines[hash]; vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline); // TODO: return CachedPipeline& int i = 0; for (auto setLayout : pipeline.setLayouts) { if (!pipeline.cachedDescriptors.count(i)) { if (auto descriptor = createDescriptorFor(pipeline, i); descriptor != VK_NULL_HANDLE) { pipeline.cachedDescriptors[i] = descriptor; } else { continue; } } // TODO: we can pass all descriptors in one function call vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, i, 1, &pipeline.cachedDescriptors[i], 0, nullptr); i++; } } 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); // TODO: for debug only spirv_cross::CompilerGLSL glsl(result.code.data(), result.code.dwords()); auto resources = glsl.get_shader_resources(); int i = 0; for (auto texture : resources.stage_inputs) { // glsl.set_name(texture.id, shader.) // qInfo() << shader.resource_parameters[i].name << texture.id; // qInfo() << "stage input" << i << texture.name << glsl.get_type(texture.type_id).width; i++; // glsl.set_name(remap.combined_id, "SPIRV_Cross_Combined"); } // Here you can also set up decorations if you want (binding = #N). i = 0; for (auto texture : resources.separate_images) { glsl.set_name(texture.id, shader.resource_parameters[i].name); i++; } i = 0; for (auto buffer : resources.uniform_buffers) { glsl.set_name(buffer.id, shader.scalar_parameters[i].name); i++; } spirv_cross::CompilerGLSL::Options options; options.vulkan_semantics = true; options.enable_420pack_extension = false; glsl.set_common_options(options); glsl.set_entry_point("main", executionModel); qInfo() << "Compiled GLSL:" << glsl.compile().c_str(); 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 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; int z = 0; int p = 0; VkShaderStageFlags currentStageFlags; for (auto binding : pipeline.requestedSets[i].bindings) { if (binding.used) { // a giant hack if (currentStageFlags != binding.stageFlags) { z = 0; p = 0; currentStageFlags = binding.stageFlags; } 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; if (binding.stageFlags == VK_SHADER_STAGE_FRAGMENT_BIT && p < 4) { auto name = pipeline.pixelShader.resource_parameters[p].name; qInfo() << "Requesting image" << name << "at" << j; if (strcmp(name, "g_SamplerGBuffer") == 0) { info->imageView = normalGBuffer.imageView; } else if (strcmp(name, "g_SamplerViewPosition") == 0) { info->imageView = viewPositionBuffer.imageView; } else if (strcmp(name, "g_SamplerDepth") == 0) { info->imageView = m_renderer.depthView; } else { info->imageView = m_renderer.dummyView; } p++; } else { 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; auto useUniformBuffer = [&info](UniformBuffer &buffer) { info->buffer = buffer.buffer; info->range = buffer.size; }; auto bindBuffer = [this, &useUniformBuffer, &info, j](const char *name) { qInfo() << "Requesting" << name << "at" << j; if (strcmp(name, "g_CameraParameter") == 0) { useUniformBuffer(g_CameraParameter); } else if (strcmp(name, "g_JointMatrixArray") == 0) { useUniformBuffer(g_JointMatrixArray); } else if (strcmp(name, "g_InstanceParameter") == 0) { useUniformBuffer(g_InstanceParameter); } else if (strcmp(name, "g_ModelParameter") == 0) { useUniformBuffer(g_ModelParameter); } else if (strcmp(name, "g_MaterialParameter") == 0) { useUniformBuffer(g_MaterialParameter); } else if (strcmp(name, "g_LightParam") == 0) { useUniformBuffer(g_LightParam); } else if (strcmp(name, "g_CommonParameter") == 0) { useUniformBuffer(g_CommonParameter); } else { qInfo() << "Unknown resource:" << name; info->buffer = m_renderer.dummyBuffer; info->range = 655360; } }; if (binding.stageFlags == VK_SHADER_STAGE_VERTEX_BIT) { auto name = pipeline.vertexShader.scalar_parameters[z].name; bindBuffer(name); z++; } else if (binding.stageFlags == VK_SHADER_STAGE_FRAGMENT_BIT) { auto name = pipeline.pixelShader.scalar_parameters[z].name; bindBuffer(name); z++; } else { // placeholder buffer so it at least doesn't crash info->buffer = m_renderer.dummyBuffer; info->range = 655360; } } break; } } j++; } vkUpdateDescriptorSets(m_renderer.device, writes.size(), writes.data(), 0, nullptr); return set; } RenderSystem::UniformBuffer RenderSystem::createUniformBuffer(size_t size) { UniformBuffer uniformBuffer{}; uniformBuffer.size = size; VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = size; bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkCreateBuffer(m_renderer.device, &bufferInfo, nullptr, &uniformBuffer.buffer); // allocate staging memory VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(m_renderer.device, uniformBuffer.buffer, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = m_renderer.findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); vkAllocateMemory(m_renderer.device, &allocInfo, nullptr, &uniformBuffer.memory); vkBindBufferMemory(m_renderer.device, uniformBuffer.buffer, uniformBuffer.memory, 0); return uniformBuffer; } void RenderSystem::copyDataToUniform(RenderSystem::UniformBuffer &uniformBuffer, void *data, size_t size) { // copy to staging buffer void *mapped_data; vkMapMemory(m_renderer.device, uniformBuffer.memory, 0, size, 0, &mapped_data); memcpy(mapped_data, data, size); vkUnmapMemory(m_renderer.device, uniformBuffer.memory); } RenderSystem::VulkanImage RenderSystem::createImage(int width, int height, VkFormat format, VkImageUsageFlags usage) { VkImage image; VkImageView imageView; VkDeviceMemory imageMemory; VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.extent.width = width; imageCreateInfo.extent.height = height; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.format = format; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.usage = usage; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkCreateImage(m_renderer.device, &imageCreateInfo, nullptr, &image); VkMemoryRequirements memRequirements; vkGetImageMemoryRequirements(m_renderer.device, image, &memRequirements); VkMemoryAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocateInfo.allocationSize = memRequirements.size; allocateInfo.memoryTypeIndex = m_renderer.findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(m_renderer.device, &allocateInfo, nullptr, &imageMemory); vkBindImageMemory(m_renderer.device, image, imageMemory, 0); VkImageViewCreateInfo viewCreateInfo = {}; viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewCreateInfo.image = image; viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewCreateInfo.format = format; viewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // TODO: hardcoded viewCreateInfo.subresourceRange.levelCount = 1; viewCreateInfo.subresourceRange.layerCount = 1; vkCreateImageView(m_renderer.device, &viewCreateInfo, nullptr, &imageView); return {image, imageView, imageMemory}; }