// Copyright (c) 2016- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Log.h" #include "Common/StringUtils.h" #include "Common/GPU/Vulkan/VulkanContext.h" #include "GPU/Vulkan/VulkanUtil.h" using namespace PPSSPP_VK; const VkComponentMapping VULKAN_4444_SWIZZLE = { VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B }; const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }; const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan) { InitDeviceObjects(); } Vulkan2D::~Vulkan2D() { DestroyDeviceObjects(); } void Vulkan2D::Shutdown() { DestroyDeviceObjects(); } void Vulkan2D::DestroyDeviceObjects() { for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { frameData_[i].descPool.Destroy(); } for (auto it : pipelines_) { vulkan_->Delete().QueueDeletePipeline(it.second); } pipelines_.clear(); for (auto pipeline : keptPipelines_) { vulkan_->Delete().QueueDeletePipeline(pipeline); } keptPipelines_.clear(); if (descriptorSetLayout_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); } if (pipelineLayout_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); } // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. if (pipelineCache_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); } } void Vulkan2D::InitDeviceObjects() { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); _assert_(VK_SUCCESS == res); VkDescriptorSetLayoutBinding bindings[2] = {}; // Texture. bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[0].binding = 0; // In depal, this second texture is used for the palette. bindings[1].descriptorCount = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[1].binding = 1; VkDevice device = vulkan_->GetDevice(); VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dsl.bindingCount = 2; dsl.pBindings = bindings; res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_); _assert_(VK_SUCCESS == res); std::vector dpTypes; dpTypes.resize(1); dpTypes[0].descriptorCount = 3000; dpTypes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later. dp.flags = 0; dp.maxSets = 3000; for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { frameData_[i].descPool.Create(vulkan_, dp, dpTypes); } VkPushConstantRange push = {}; push.offset = 0; push.size = 48; push.stageFlags = VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pl.pPushConstantRanges = &push; pl.pushConstantRangeCount = 1; pl.setLayoutCount = 1; pl.pSetLayouts = &descriptorSetLayout_; pl.flags = 0; res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_); _assert_(VK_SUCCESS == res); } void Vulkan2D::DeviceLost() { DestroyDeviceObjects(); } void Vulkan2D::DeviceRestore(VulkanContext *vulkan) { vulkan_ = vulkan; InitDeviceObjects(); } void Vulkan2D::BeginFrame() { int curFrame = vulkan_->GetCurFrame(); FrameData &frame = frameData_[curFrame]; frame.descPool.Reset(); } void Vulkan2D::EndFrame() { } void Vulkan2D::PurgeVertexShader(VkShaderModule s, bool keepPipeline) { for (auto it = pipelines_.begin(); it != pipelines_.end(); ) { if (it->first.vs == s) { if (keepPipeline) { keptPipelines_.push_back(it->second); } else { vulkan_->Delete().QueueDeletePipeline(it->second); } it = pipelines_.erase(it); } else { ++it; } } } void Vulkan2D::PurgeFragmentShader(VkShaderModule s, bool keepPipeline) { for (auto it = pipelines_.begin(); it != pipelines_.end(); ) { if (it->first.fs == s) { if (keepPipeline) { keptPipelines_.push_back(it->second); } else { vulkan_->Delete().QueueDeletePipeline(it->second); } it = pipelines_.erase(it); } else { ++it; } } } VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2) { DescriptorSetKey key; key.imageView[0] = tex1; key.imageView[1] = tex2; key.sampler[0] = sampler1; key.sampler[1] = sampler2; int curFrame = vulkan_->GetCurFrame(); FrameData *frame = &frameData_[curFrame]; auto iter = frame->descSets.find(key); if (iter != frame->descSets.end()) { return iter->second; } // Didn't find one in the frame descriptor set cache, let's make a new one. // We wipe the cache on every frame. VkDescriptorSet desc = frame->descPool.Allocate(1, &descriptorSetLayout_); _assert_(desc != VK_NULL_HANDLE); // We just don't write to the slots we don't care about. VkWriteDescriptorSet writes[2]{}; // Main and sub textures int n = 0; VkDescriptorImageInfo image1{}; VkDescriptorImageInfo image2{}; if (tex1 && sampler1) { image1.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image1.imageView = tex1; image1.sampler = sampler1; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].dstBinding = 0; writes[n].pImageInfo = &image1; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[n].dstSet = desc; n++; } if (tex2 && sampler2) { // TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them? image2.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image2.imageView = tex2; image2.sampler = sampler2; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].dstBinding = 1; writes[n].pImageInfo = &image2; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[n].dstSet = desc; n++; } vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr); frame->descSets[key] = desc; return desc; } VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices, VK2DDepthStencilMode depthStencilMode) { PipelineKey key; key.vs = vs; key.fs = fs; key.rp = rp; key.depthStencilMode = depthStencilMode; key.readVertices = readVertices; auto iter = pipelines_.find(key); if (iter != pipelines_.end()) { return iter->second; } VkPipelineColorBlendAttachmentState blend0 = {}; blend0.blendEnable = false; blend0.colorWriteMask = depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS ? VK_COLOR_COMPONENT_A_BIT : 0xF; VkPipelineColorBlendStateCreateInfo cbs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cbs.pAttachments = &blend0; cbs.attachmentCount = 1; cbs.logicOpEnable = false; VkPipelineDepthStencilStateCreateInfo dss = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; dss.depthBoundsTestEnable = false; dss.depthTestEnable = false; dss.stencilTestEnable = false; switch (depthStencilMode) { case VK2DDepthStencilMode::NONE: break; case VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS: dss.stencilTestEnable = true; dss.front.reference = 0xFF; dss.front.compareMask = 0xFF; dss.front.compareOp = VK_COMPARE_OP_ALWAYS; dss.front.depthFailOp = VK_STENCIL_OP_REPLACE; dss.front.failOp = VK_STENCIL_OP_REPLACE; dss.front.passOp = VK_STENCIL_OP_REPLACE; dss.back = dss.front; break; } VkDynamicState dynamicStates[5]; int numDyn = 0; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT; if (depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS) { dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; } VkPipelineDynamicStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; ds.pDynamicStates = dynamicStates; ds.dynamicStateCount = numDyn; VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineShaderStageCreateInfo ss[2] = {}; ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT; ss[0].module = vs; ss[0].pName = "main"; ss[0].flags = 0; ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; ss[1].module = fs; ss[1].pName = "main"; ss[1].flags = 0; VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; inputAssembly.flags = 0; inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; inputAssembly.primitiveRestartEnable = false; VkVertexInputAttributeDescription attrs[2]; int attributeCount = 2; attrs[0].binding = 0; attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT; attrs[0].location = 0; attrs[0].offset = 0; attrs[1].binding = 0; attrs[1].format = VK_FORMAT_R32G32_SFLOAT; attrs[1].location = 1; attrs[1].offset = 12; int vertexStride = 12 + 8; VkVertexInputBindingDescription ibd = {}; ibd.binding = 0; ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; ibd.stride = vertexStride; VkPipelineVertexInputStateCreateInfo vis = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; vis.vertexBindingDescriptionCount = readVertices ? 1 : 0; vis.pVertexBindingDescriptions = readVertices ? &ibd : nullptr; vis.vertexAttributeDescriptionCount = readVertices ? attributeCount : 0; vis.pVertexAttributeDescriptions = readVertices ? attrs : nullptr; VkPipelineViewportStateCreateInfo views = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; views.viewportCount = 1; views.scissorCount = 1; views.pViewports = nullptr; // dynamic views.pScissors = nullptr; // dynamic VkGraphicsPipelineCreateInfo pipe = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipe.flags = 0; pipe.stageCount = 2; pipe.pStages = ss; pipe.basePipelineIndex = 0; pipe.pColorBlendState = &cbs; pipe.pDepthStencilState = &dss; pipe.pRasterizationState = &rs; pipe.pVertexInputState = &vis; pipe.pViewportState = &views; pipe.pTessellationState = nullptr; pipe.pDynamicState = &ds; pipe.pInputAssemblyState = &inputAssembly; pipe.pMultisampleState = &ms; pipe.layout = pipelineLayout_; pipe.basePipelineHandle = VK_NULL_HANDLE; pipe.basePipelineIndex = 0; pipe.renderPass = rp; pipe.subpass = 0; VkPipeline pipeline; VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pipe, nullptr, &pipeline); if (result == VK_SUCCESS) { pipelines_[key] = pipeline; return pipeline; } else { return VK_NULL_HANDLE; } } VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) { std::vector spirv; bool success = GLSLtoSPV(stage, code, GLSLVariant::VULKAN, spirv, error); if (!error->empty()) { if (success) { ERROR_LOG(G3D, "Warnings in shader compilation!"); } else { ERROR_LOG(G3D, "Error in shader compilation!"); } ERROR_LOG(G3D, "Messages: %s", error->c_str()); ERROR_LOG(G3D, "Shader source:\n%s", LineNumberString(code).c_str()); OutputDebugStringUTF8("Messages:\n"); OutputDebugStringUTF8(error->c_str()); OutputDebugStringUTF8(LineNumberString(code).c_str()); return VK_NULL_HANDLE; } else { VkShaderModule module; if (vulkan->CreateShaderModule(spirv, &module)) { return module; } else { return VK_NULL_HANDLE; } } } VulkanComputeShaderManager::VulkanComputeShaderManager(VulkanContext *vulkan) : vulkan_(vulkan), pipelines_(8) { } VulkanComputeShaderManager::~VulkanComputeShaderManager() {} void VulkanComputeShaderManager::InitDeviceObjects() { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); _assert_(VK_SUCCESS == res); VkDescriptorSetLayoutBinding bindings[3] = {}; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[0].binding = 0; bindings[1].descriptorCount = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[1].binding = 1; bindings[2].descriptorCount = 1; bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[2].binding = 2; VkDevice device = vulkan_->GetDevice(); VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dsl.bindingCount = ARRAY_SIZE(bindings); dsl.pBindings = bindings; res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_); _assert_(VK_SUCCESS == res); std::vector dpTypes; dpTypes.resize(2); dpTypes[0].descriptorCount = 8192; dpTypes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; dpTypes[1].descriptorCount = 4096; dpTypes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later. dp.maxSets = 4096; // GTA can end up creating more than 1000 textures in the first frame! for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { frameData_[i].descPool.Create(vulkan_, dp, dpTypes); } VkPushConstantRange push = {}; push.offset = 0; push.size = 16; push.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pl.pPushConstantRanges = &push; pl.pushConstantRangeCount = 1; pl.setLayoutCount = 1; pl.pSetLayouts = &descriptorSetLayout_; pl.flags = 0; res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_); _assert_(VK_SUCCESS == res); } void VulkanComputeShaderManager::DestroyDeviceObjects() { for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { frameData_[i].descPool.Destroy(); } if (descriptorSetLayout_) { vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); } pipelines_.Iterate([&](const PipelineKey &key, VkPipeline pipeline) { vulkan_->Delete().QueueDeletePipeline(pipeline); }); pipelines_.Clear(); if (pipelineLayout_) { vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); } if (pipelineCache_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); } } VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2) { int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; VkDescriptorSet desc = frameData.descPool.Allocate(1, &descriptorSetLayout_); _assert_(desc != VK_NULL_HANDLE); VkWriteDescriptorSet writes[2]{}; int n = 0; VkDescriptorImageInfo imageInfo = {}; VkDescriptorBufferInfo bufferInfo[2] = {}; if (image) { imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; imageInfo.imageView = image; imageInfo.sampler = VK_NULL_HANDLE; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].dstBinding = 0; writes[n].pImageInfo = &imageInfo; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; writes[n].dstSet = desc; n++; } bufferInfo[0].buffer = buffer; bufferInfo[0].offset = offset; bufferInfo[0].range = range; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].dstBinding = 1; writes[n].pBufferInfo = &bufferInfo[0]; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writes[n].dstSet = desc; n++; if (buffer2) { bufferInfo[1].buffer = buffer2; bufferInfo[1].offset = offset2; bufferInfo[1].range = range2; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].dstBinding = 2; writes[n].pBufferInfo = &bufferInfo[1]; writes[n].descriptorCount = 1; writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; writes[n].dstSet = desc; n++; } vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr); return desc; } VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { PipelineKey key{ cs }; VkPipeline pipeline = pipelines_.Get(key); if (pipeline) return pipeline; VkComputePipelineCreateInfo pci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; pci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pci.stage.module = cs; pci.stage.pName = "main"; pci.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; pci.layout = pipelineLayout_; pci.flags = 0; VkResult res = vkCreateComputePipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pci, nullptr, &pipeline); _assert_(res == VK_SUCCESS); pipelines_.Insert(key, pipeline); return pipeline; } void VulkanComputeShaderManager::BeginFrame() { int curFrame = vulkan_->GetCurFrame(); FrameData &frame = frameData_[curFrame]; frame.descPool.Reset(); } void VulkanComputeShaderManager::EndFrame() { }