#pragma once #include "Common/Common.h" #include "Common/GPU/Vulkan/VulkanContext.h" class VKRRenderPass; class VulkanBarrierBatch; // Pipelines need to be created for the right type of render pass. // TODO: Rename to RenderPassFlags? // When you add more flags, don't forget to update rpTypeDebugNames[]. enum class RenderPassType { DEFAULT = 0, // These eight are organized so that bit 0 is DEPTH and bit 1 is INPUT and bit 2 is MULTIVIEW, so // they can be OR-ed together in MergeRPTypes. HAS_DEPTH = 1, MULTIVIEW = 2, MULTISAMPLE = 4, // This is the odd one out, and gets special handling in MergeRPTypes. // If this flag is set, none of the other flags can be set. // For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible // so we don't bother with a non-depth version. BACKBUFFER = 8, TYPE_COUNT = BACKBUFFER + 1, }; ENUM_CLASS_BITOPS(RenderPassType); // Simple independent framebuffer image. struct VKRImage { // These four are "immutable". VkImage image; VkImageView rtView; // Used for rendering to, and readbacks of stencil. 2D if single layer, 2D_ARRAY if multiple. Includes both depth and stencil if depth/stencil. // This is for texturing all layers at once. If aspect is depth/stencil, does not include stencil. VkImageView texAllLayersView; // If it's a layered image (for stereo), this is two 2D views of it, to make it compatible with shaders that don't yet support stereo. // If there's only one layer, layerViews[0] only is initialized. VkImageView texLayerViews[2]{}; VmaAllocation alloc; VkFormat format; VkSampleCountFlagBits sampleCount; // This one is used by QueueRunner's Perform functions to keep track. CANNOT be used anywhere else due to sync issues. VkImageLayout layout; int numLayers; // For debugging. std::string tag; void Delete(VulkanContext *vulkan); }; class VKRFramebuffer { public: VKRFramebuffer(const VKRFramebuffer &) = delete; VKRFramebuffer &operator=(const VKRFramebuffer &) = delete; VKRFramebuffer(VulkanContext *vk, VulkanBarrierBatch *barriers, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag); ~VKRFramebuffer(); VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType); int width = 0; int height = 0; int numLayers = 0; VkSampleCountFlagBits sampleCount; VKRImage color{}; // color.image is always there. VKRImage depth{}; // depth.image is allowed to be VK_NULL_HANDLE. // These are only initialized and used if numSamples > 1. VKRImage msaaColor{}; VKRImage msaaDepth{}; const char *Tag() const { return tag_.c_str(); } void UpdateTag(const char *newTag); bool HasDepth() const { return depth.image != VK_NULL_HANDLE; } VkImageView GetRTView() { if (sampleCount == VK_SAMPLE_COUNT_1_BIT) { return color.rtView; } else { return msaaColor.rtView; } } VulkanContext *Vulkan() const { return vulkan_; } private: static void CreateImage(VulkanContext *vulkan, VulkanBarrierBatch *barriers, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag); VkFramebuffer framebuf[(size_t)RenderPassType::TYPE_COUNT]{}; VulkanContext *vulkan_; std::string tag_; }; inline bool RenderPassTypeHasDepth(RenderPassType type) { return (type & RenderPassType::HAS_DEPTH) || type == RenderPassType::BACKBUFFER; } inline bool RenderPassTypeHasMultiView(RenderPassType type) { return (type & RenderPassType::MULTIVIEW) != 0; } inline bool RenderPassTypeHasMultisample(RenderPassType type) { return (type & RenderPassType::MULTISAMPLE) != 0; } VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count); // Must be the same order as Draw::RPAction enum class VKRRenderPassLoadAction : uint8_t { KEEP, // default. avoid when possible. CLEAR, DONT_CARE, }; enum class VKRRenderPassStoreAction : uint8_t { STORE, // default. avoid when possible. DONT_CARE, }; struct RPKey { // Only render-pass-compatibility-volatile things can be here. VKRRenderPassLoadAction colorLoadAction; VKRRenderPassLoadAction depthLoadAction; VKRRenderPassLoadAction stencilLoadAction; VKRRenderPassStoreAction colorStoreAction; VKRRenderPassStoreAction depthStoreAction; VKRRenderPassStoreAction stencilStoreAction; // Dense index over every possible key, for the render pass table. enum { INDEX_COUNT = 3 * 3 * 3 * 2 * 2 * 2 }; int Index() const { return ((((int)colorLoadAction * 3 + (int)depthLoadAction) * 3 + (int)stencilLoadAction) * 8) + (int)colorStoreAction * 4 + (int)depthStoreAction * 2 + (int)stencilStoreAction; } }; class VKRRenderPass { public: explicit VKRRenderPass(const RPKey &key) : key_(key) {} // Any thread: the main thread (EndCurRenderStep) and the render thread (PerformRenderPass) both // create passes lazily. VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount); // Only called from VulkanQueueRunner::DestroyDeviceObjects, with the threads stopped. void Destroy(VulkanContext *vulkan); const RPKey &Key() const { return key_; } private: // Immutable once published. A replaced one is freed from the delete list, after any reader. struct Created { VkRenderPass pass; VkSampleCountFlagBits sampleCount; }; retro_atomic_ptr_t pass_[(size_t)RenderPassType::TYPE_COUNT]{}; RPKey key_; }; const char *GetRPTypeName(RenderPassType rpType);