1 // Copyright (c) 2016- PPSSPP Project. 2 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, version 2.0 or later versions. 6 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License 2.0 for more details. 11 12 // A copy of the GPL 2.0 should have been included with the program. 13 // If not, see http://www.gnu.org/licenses/ 14 15 // Official git repository and contact information can be found at 16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. 17 18 #pragma once 19 20 #include <tuple> 21 #include <map> 22 23 #include "Common/Data/Collections/Hashmaps.h" 24 #include "Common/GPU/Vulkan/VulkanContext.h" 25 #include "Common/GPU/Vulkan/VulkanLoader.h" 26 #include "Common/GPU/Vulkan/VulkanImage.h" 27 28 extern const VkComponentMapping VULKAN_4444_SWIZZLE; 29 extern const VkComponentMapping VULKAN_1555_SWIZZLE; 30 extern const VkComponentMapping VULKAN_565_SWIZZLE; 31 extern const VkComponentMapping VULKAN_8888_SWIZZLE; 32 33 // Note: some drivers prefer B4G4R4A4_UNORM_PACK16 over R4G4B4A4_UNORM_PACK16. 34 #define VULKAN_4444_FORMAT VK_FORMAT_B4G4R4A4_UNORM_PACK16 35 #define VULKAN_1555_FORMAT VK_FORMAT_A1R5G5B5_UNORM_PACK16 36 #define VULKAN_565_FORMAT VK_FORMAT_B5G6R5_UNORM_PACK16 // TODO: Does not actually have mandatory support, though R5G6B5 does! See #14602 37 #define VULKAN_8888_FORMAT VK_FORMAT_R8G8B8A8_UNORM 38 39 // Vulkan doesn't really have the concept of an FBO that owns the images, 40 // but it does have the concept of a framebuffer as a set of attachments. 41 // VulkanFBO is an approximation of the FBO concept the other backends use 42 // to make things as similar as possible without being suboptimal. 43 // 44 // An FBO can be rendered to and used as a texture multiple times in a frame. 45 // Even at multiple sizes, while keeping the same contents. 46 // With GL or D3D we'd just rely on the driver managing duplicates for us, but in 47 // Vulkan we will want to be able to batch up the whole frame and reorder passes 48 // so that all textures are ready before the main scene, instead of switching back and 49 // forth. This comes at a memory cost but will be worth it. 50 // 51 // When we render to a scene, then render to a texture, then go back to the scene and 52 // use that texture, we will register that as a dependency. Then we will walk the DAG 53 // to find the final order of command buffers, and execute it. 54 // 55 // Each FBO will get its own command buffer for each pass. 56 57 // Similar to a subset of Thin3D, but separate. 58 // This is used for things like postprocessing shaders, depal, etc. 59 // No UBO data is used, only PushConstants. 60 // No transform matrices, only post-proj coordinates. 61 // Two textures can be sampled. 62 // Some simplified depth/stencil modes available. 63 64 class Vulkan2D { 65 public: 66 Vulkan2D(VulkanContext *vulkan); 67 ~Vulkan2D(); 68 GetVulkanContext()69 VulkanContext *GetVulkanContext() const { return vulkan_; } 70 71 void DeviceLost(); 72 void DeviceRestore(VulkanContext *vulkan); 73 void Shutdown(); 74 75 enum class VK2DDepthStencilMode { 76 NONE, 77 STENCIL_REPLACE_ALWAYS, // Does not draw to color. 78 }; 79 80 // The only supported primitive is the triangle strip, for simplicity. 81 // ReadVertices can be used for vertex-less rendering where you generate verts in the vshader. 82 VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices = true, VK2DDepthStencilMode depthStencilMode = VK2DDepthStencilMode::NONE); GetPipelineLayout()83 VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } 84 void BeginFrame(); 85 void EndFrame(); 86 87 void PurgeVertexShader(VkShaderModule s, bool keepPipeline = false); 88 void PurgeFragmentShader(VkShaderModule s, bool keepPipeline = false); 89 90 VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2); 91 92 struct Vertex { 93 float x, y, z; 94 float u, v; 95 }; 96 97 private: 98 void InitDeviceObjects(); 99 void DestroyDeviceObjects(); 100 101 VulkanContext *vulkan_ = nullptr; 102 VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; 103 VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; 104 VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; 105 106 // Yes, another one... 107 struct DescriptorSetKey { 108 VkImageView imageView[2]; 109 VkSampler sampler[2]; 110 111 bool operator < (const DescriptorSetKey &other) const { 112 return std::tie(imageView[0], imageView[1], sampler[0], sampler[1]) < 113 std::tie(other.imageView[0], other.imageView[1], other.sampler[0], other.sampler[1]); 114 } 115 }; 116 117 struct PipelineKey { 118 VkShaderModule vs; 119 VkShaderModule fs; 120 VkRenderPass rp; 121 VK2DDepthStencilMode depthStencilMode; 122 bool readVertices; 123 bool operator < (const PipelineKey &other) const { 124 return std::tie(vs, fs, rp, depthStencilMode, readVertices) < std::tie(other.vs, other.fs, other.rp, other.depthStencilMode, other.readVertices); 125 } 126 }; 127 128 struct FrameData { 129 VkDescriptorPool descPool; 130 std::map<DescriptorSetKey, VkDescriptorSet> descSets; 131 }; 132 133 FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; 134 135 std::map<PipelineKey, VkPipeline> pipelines_; 136 std::vector<VkPipeline> keptPipelines_; 137 }; 138 139 // Manager for compute shaders that upload things (and those have two bindings: a storage buffer to read from and an image to write to). 140 class VulkanComputeShaderManager { 141 public: 142 VulkanComputeShaderManager(VulkanContext *vulkan); 143 ~VulkanComputeShaderManager(); 144 DeviceLost()145 void DeviceLost() { 146 DestroyDeviceObjects(); 147 } DeviceRestore(VulkanContext * vulkan)148 void DeviceRestore(VulkanContext *vulkan) { 149 vulkan_ = vulkan; 150 InitDeviceObjects(); 151 } 152 153 // Note: This doesn't cache. The descriptor is for immediate use only. 154 VkDescriptorSet GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2 = VK_NULL_HANDLE, VkDeviceSize offset2 = 0, VkDeviceSize range2 = 0); 155 156 // This of course caches though. 157 VkPipeline GetPipeline(VkShaderModule cs); GetPipelineLayout()158 VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } 159 160 void BeginFrame(); 161 void EndFrame(); 162 163 private: 164 void InitDeviceObjects(); 165 void DestroyDeviceObjects(); 166 167 VulkanContext *vulkan_ = nullptr; 168 VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; 169 VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; 170 VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; 171 172 struct FrameData { 173 VkDescriptorPool descPool; 174 int numDescriptors; 175 }; 176 FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; 177 178 struct PipelineKey { 179 VkShaderModule module; 180 }; 181 182 DenseHashMap<PipelineKey, VkPipeline, (VkPipeline)VK_NULL_HANDLE> pipelines_; 183 }; 184 185 186 VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error); 187