1 /* Copyright (c) 2017-2018 Hans-Kristian Arntzen 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining 4 * a copy of this software and associated documentation files (the 5 * "Software"), to deal in the Software without restriction, including 6 * without limitation the rights to use, copy, modify, merge, publish, 7 * distribute, sublicense, and/or sell copies of the Software, and to 8 * permit persons to whom the Software is furnished to do so, subject to 9 * the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be 12 * included in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 23 #pragma once 24 25 #include "cookie.hpp" 26 #include "hash.hpp" 27 #include "image.hpp" 28 #include "intrusive.hpp" 29 #include "limits.hpp" 30 #include "object_pool.hpp" 31 #include "temporary_hashmap.hpp" 32 #include "vulkan.hpp" 33 34 namespace Vulkan 35 { 36 enum RenderPassOp 37 { 38 RENDER_PASS_OP_CLEAR_DEPTH_STENCIL_BIT = 1 << 0, 39 RENDER_PASS_OP_LOAD_DEPTH_STENCIL_BIT = 1 << 1, 40 RENDER_PASS_OP_STORE_DEPTH_STENCIL_BIT = 1 << 2, 41 RENDER_PASS_OP_DEPTH_STENCIL_READ_ONLY_BIT = 1 << 3, 42 RENDER_PASS_OP_ENABLE_TRANSIENT_STORE_BIT = 1 << 4, 43 RENDER_PASS_OP_ENABLE_TRANSIENT_LOAD_BIT = 1 << 5 44 }; 45 using RenderPassOpFlags = uint32_t; 46 47 class ImageView; 48 struct RenderPassInfo 49 { 50 ImageView *color_attachments[VULKAN_NUM_ATTACHMENTS]; 51 ImageView *depth_stencil = nullptr; 52 unsigned num_color_attachments = 0; 53 RenderPassOpFlags op_flags = 0; 54 uint32_t clear_attachments = 0; 55 uint32_t load_attachments = 0; 56 uint32_t store_attachments = 0; 57 uint32_t layer = 0; 58 59 // Render area will be clipped to the actual framebuffer. 60 VkRect2D render_area = { { 0, 0 }, { UINT32_MAX, UINT32_MAX } }; 61 62 VkClearColorValue clear_color[VULKAN_NUM_ATTACHMENTS] = {}; 63 VkClearDepthStencilValue clear_depth_stencil = { 1.0f, 0 }; 64 65 enum class DepthStencil 66 { 67 None, 68 ReadOnly, 69 ReadWrite 70 }; 71 72 struct Subpass 73 { 74 uint32_t color_attachments[VULKAN_NUM_ATTACHMENTS]; 75 uint32_t input_attachments[VULKAN_NUM_ATTACHMENTS]; 76 uint32_t resolve_attachments[VULKAN_NUM_ATTACHMENTS]; 77 unsigned num_color_attachments = 0; 78 unsigned num_input_attachments = 0; 79 unsigned num_resolve_attachments = 0; 80 DepthStencil depth_stencil_mode = DepthStencil::ReadWrite; 81 }; 82 // If 0/nullptr, assume a default subpass. 83 const Subpass *subpasses = nullptr; 84 unsigned num_subpasses = 0; 85 }; 86 87 class RenderPass : public HashedObject<RenderPass>, public NoCopyNoMove 88 { 89 public: 90 struct SubpassInfo 91 { 92 VkAttachmentReference color_attachments[VULKAN_NUM_ATTACHMENTS]; 93 unsigned num_color_attachments; 94 VkAttachmentReference input_attachments[VULKAN_NUM_ATTACHMENTS]; 95 unsigned num_input_attachments; 96 VkAttachmentReference depth_stencil_attachment; 97 98 unsigned samples; 99 }; 100 101 RenderPass(Util::Hash hash, Device *device, const RenderPassInfo &info); 102 RenderPass(Util::Hash hash, Device *device, const VkRenderPassCreateInfo &create_info); 103 ~RenderPass(); 104 get_num_subpasses() const105 unsigned get_num_subpasses() const 106 { 107 return unsigned(subpasses.size()); 108 } 109 get_render_pass() const110 VkRenderPass get_render_pass() const 111 { 112 return render_pass; 113 } 114 get_sample_count(unsigned subpass) const115 uint32_t get_sample_count(unsigned subpass) const 116 { 117 VK_ASSERT(subpass < subpasses.size()); 118 return subpasses[subpass].samples; 119 } 120 get_num_color_attachments(unsigned subpass) const121 unsigned get_num_color_attachments(unsigned subpass) const 122 { 123 VK_ASSERT(subpass < subpasses.size()); 124 return subpasses[subpass].num_color_attachments; 125 } 126 get_num_input_attachments(unsigned subpass) const127 unsigned get_num_input_attachments(unsigned subpass) const 128 { 129 VK_ASSERT(subpass < subpasses.size()); 130 return subpasses[subpass].num_input_attachments; 131 } 132 get_color_attachment(unsigned subpass,unsigned index) const133 const VkAttachmentReference &get_color_attachment(unsigned subpass, unsigned index) const 134 { 135 VK_ASSERT(subpass < subpasses.size()); 136 VK_ASSERT(index < subpasses[subpass].num_color_attachments); 137 return subpasses[subpass].color_attachments[index]; 138 } 139 get_input_attachment(unsigned subpass,unsigned index) const140 const VkAttachmentReference &get_input_attachment(unsigned subpass, unsigned index) const 141 { 142 VK_ASSERT(subpass < subpasses.size()); 143 VK_ASSERT(index < subpasses[subpass].num_input_attachments); 144 return subpasses[subpass].input_attachments[index]; 145 } 146 has_depth(unsigned subpass) const147 bool has_depth(unsigned subpass) const 148 { 149 VK_ASSERT(subpass < subpasses.size()); 150 return subpasses[subpass].depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED && 151 format_has_depth_aspect(depth_stencil); 152 } 153 has_stencil(unsigned subpass) const154 bool has_stencil(unsigned subpass) const 155 { 156 VK_ASSERT(subpass < subpasses.size()); 157 return subpasses[subpass].depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED && 158 format_has_stencil_aspect(depth_stencil); 159 } 160 161 private: 162 Device *device; 163 VkRenderPass render_pass = VK_NULL_HANDLE; 164 165 VkFormat color_attachments[VULKAN_NUM_ATTACHMENTS] = {}; 166 VkFormat depth_stencil = VK_FORMAT_UNDEFINED; 167 std::vector<SubpassInfo> subpasses; 168 169 void setup_subpasses(const VkRenderPassCreateInfo &create_info); 170 171 void fixup_render_pass_nvidia(VkRenderPassCreateInfo &create_info, VkAttachmentDescription *attachments); 172 void fixup_wsi_barrier(VkRenderPassCreateInfo &create_info, VkAttachmentDescription *attachments); 173 }; 174 175 class Framebuffer : public Cookie, public NoCopyNoMove, public InternalSyncEnabled 176 { 177 public: 178 Framebuffer(Device *device, const RenderPass &rp, const RenderPassInfo &info); 179 ~Framebuffer(); 180 get_framebuffer() const181 VkFramebuffer get_framebuffer() const 182 { 183 return framebuffer; 184 } 185 get_attachment(unsigned index) const186 ImageView *get_attachment(unsigned index) const 187 { 188 assert(index < attachments.size()); 189 return attachments[index]; 190 } 191 get_width() const192 uint32_t get_width() const 193 { 194 return width; 195 } 196 get_height() const197 uint32_t get_height() const 198 { 199 return height; 200 } 201 get_compatible_render_pass() const202 const RenderPass &get_compatible_render_pass() const 203 { 204 return render_pass; 205 } 206 207 private: 208 Device *device; 209 VkFramebuffer framebuffer = VK_NULL_HANDLE; 210 const RenderPass &render_pass; 211 RenderPassInfo info; 212 uint32_t width = 0; 213 uint32_t height = 0; 214 215 std::vector<ImageView *> attachments; 216 }; 217 218 static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 8; 219 class FramebufferAllocator 220 { 221 public: 222 FramebufferAllocator(Device *device); 223 Framebuffer &request_framebuffer(const RenderPassInfo &info); 224 225 void begin_frame(); 226 void clear(); 227 228 private: 229 struct FramebufferNode : Util::TemporaryHashmapEnabled<FramebufferNode>, 230 Util::IntrusiveListEnabled<FramebufferNode>, 231 Framebuffer 232 { FramebufferNodeVulkan::FramebufferAllocator::FramebufferNode233 FramebufferNode(Device *device, const RenderPass &rp, const RenderPassInfo &info) 234 : Framebuffer(device, rp, info) 235 { 236 set_internal_sync_object(); 237 } 238 }; 239 240 Device *device; 241 Util::TemporaryHashmap<FramebufferNode, VULKAN_FRAMEBUFFER_RING_SIZE, false> framebuffers; 242 #ifdef GRANITE_VULKAN_MT 243 std::mutex lock; 244 #endif 245 }; 246 247 class AttachmentAllocator 248 { 249 public: AttachmentAllocator(Device * device,bool transient)250 AttachmentAllocator(Device *device, bool transient) 251 : device(device), transient(transient) 252 { 253 } 254 255 ImageView &request_attachment(unsigned width, unsigned height, VkFormat format, 256 unsigned index = 0, unsigned samples = 1, unsigned layers = 1); 257 258 void begin_frame(); 259 void clear(); 260 261 private: 262 struct TransientNode : Util::TemporaryHashmapEnabled<TransientNode>, Util::IntrusiveListEnabled<TransientNode> 263 { TransientNodeVulkan::AttachmentAllocator::TransientNode264 TransientNode(ImageHandle handle) 265 : handle(handle) 266 { 267 } 268 269 ImageHandle handle; 270 }; 271 272 Device *device; 273 Util::TemporaryHashmap<TransientNode, VULKAN_FRAMEBUFFER_RING_SIZE, false> attachments; 274 #ifdef GRANITE_VULKAN_MT 275 std::mutex lock; 276 #endif 277 bool transient; 278 }; 279 280 class TransientAttachmentAllocator : public AttachmentAllocator 281 { 282 public: TransientAttachmentAllocator(Device * device)283 TransientAttachmentAllocator(Device *device) 284 : AttachmentAllocator(device, true) 285 { 286 } 287 }; 288 289 class PhysicalAttachmentAllocator : public AttachmentAllocator 290 { 291 public: PhysicalAttachmentAllocator(Device * device)292 PhysicalAttachmentAllocator(Device *device) 293 : AttachmentAllocator(device, false) 294 { 295 } 296 }; 297 298 } 299 300