Path: blob/master/Common/GPU/Vulkan/VulkanFramebuffer.cpp
3187 views
#include "Common/StringUtils.h"1#include "Common/GPU/Vulkan/VulkanFramebuffer.h"2#include "Common/GPU/Vulkan/VulkanQueueRunner.h"34static const char * const rpTypeDebugNames[] = {5"RENDER",6"RENDER_DEPTH",7"MV_RENDER",8"MV_RENDER_DEPTH",9"MS_RENDER",10"MS_RENDER_DEPTH",11"MS_MV_RENDER",12"MS_MV_RENDER_DEPTH",13"BACKBUF",14};1516const char *GetRPTypeName(RenderPassType rpType) {17uint32_t index = (uint32_t)rpType;18if (index < ARRAY_SIZE(rpTypeDebugNames)) {19return rpTypeDebugNames[index];20} else {21return "N/A";22}23}2425VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count) {26// TODO: Check hardware support here, or elsewhere?27// Some hardware only supports 4x.28switch (count) {29case 0: return VK_SAMPLE_COUNT_1_BIT;30case 1: return VK_SAMPLE_COUNT_2_BIT;31case 2: return VK_SAMPLE_COUNT_4_BIT; // The only non-1 level supported on some mobile chips.32case 3: return VK_SAMPLE_COUNT_8_BIT;33case 4: return VK_SAMPLE_COUNT_16_BIT; // rare but exists, on Intel for example34default:35_assert_(false);36return VK_SAMPLE_COUNT_1_BIT;37}38}3940void VKRImage::Delete(VulkanContext *vulkan) {41// Get rid of the views first, feels cleaner (but in reality doesn't matter).42if (rtView)43vulkan->Delete().QueueDeleteImageView(rtView);44if (texAllLayersView)45vulkan->Delete().QueueDeleteImageView(texAllLayersView);46for (int i = 0; i < 2; i++) {47if (texLayerViews[i]) {48vulkan->Delete().QueueDeleteImageView(texLayerViews[i]);49}50}5152if (image) {53_dbg_assert_(alloc);54vulkan->Delete().QueueDeleteImageAllocation(image, alloc);55}56}5758VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VulkanBarrierBatch *barriers, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag)59: vulkan_(vk), width(_width), height(_height), numLayers(_numLayers) {6061_dbg_assert_(tag);6263CreateImage(vulkan_, barriers, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);64if (createDepthStencilBuffer) {65CreateImage(vulkan_, barriers, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);66}6768if (_multiSampleLevel > 0) {69sampleCount = MultiSampleLevelToFlagBits(_multiSampleLevel);7071// TODO: Create a different tag for these?72CreateImage(vulkan_, barriers, msaaColor, width, height, numLayers, sampleCount, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);73if (createDepthStencilBuffer) {74CreateImage(vulkan_, barriers, msaaDepth, width, height, numLayers, sampleCount, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);75}76} else {77sampleCount = VK_SAMPLE_COUNT_1_BIT;78}7980UpdateTag(tag);8182// We create the actual framebuffer objects on demand, because some combinations might not make sense.83// Framebuffer objects are just pointers to a set of images, so no biggie.84}8586void VKRFramebuffer::UpdateTag(const char *newTag) {87tag_ = newTag;88char name[128];89snprintf(name, sizeof(name), "fb_color_%s", tag_.c_str());90vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, name);91vulkan_->SetDebugName(color.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, name);92if (depth.image) {93snprintf(name, sizeof(name), "fb_depth_%s", tag_.c_str());94vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, name);95vulkan_->SetDebugName(depth.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, name);96}97for (size_t rpType = 0; rpType < (size_t)RenderPassType::TYPE_COUNT; rpType++) {98if (framebuf[rpType]) {99snprintf(name, sizeof(name), "fb_%s", tag_.c_str());100vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, name);101}102}103}104105VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType) {106bool multiview = RenderPassTypeHasMultiView(rpType);107108if (framebuf[(int)rpType]) {109return framebuf[(int)rpType];110}111112VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };113VkImageView views[4]{};114115bool hasDepth = RenderPassTypeHasDepth(rpType);116int attachmentCount = 0;117views[attachmentCount++] = color.rtView; // 2D array texture if multilayered.118if (hasDepth) {119if (!depth.rtView) {120WARN_LOG(Log::G3D, "depth render type to non-depth fb: %p %p fmt=%d (%s %dx%d)", (void *)depth.image, (void *)depth.texAllLayersView, depth.format, tag_.c_str(), width, height);121// Will probably crash, depending on driver.122}123views[attachmentCount++] = depth.rtView;124}125if (rpType & RenderPassType::MULTISAMPLE) {126views[attachmentCount++] = msaaColor.rtView;127if (hasDepth) {128views[attachmentCount++] = msaaDepth.rtView;129}130}131132fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType, sampleCount);133fbci.attachmentCount = attachmentCount;134fbci.pAttachments = views;135fbci.width = width;136fbci.height = height;137fbci.layers = 1; // With multiview, this should be set as 1.138139VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]);140_assert_(res == VK_SUCCESS);141142if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {143vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());144}145146return framebuf[(int)rpType];147}148149VKRFramebuffer::~VKRFramebuffer() {150color.Delete(vulkan_);151depth.Delete(vulkan_);152msaaColor.Delete(vulkan_);153msaaDepth.Delete(vulkan_);154155for (auto &fb : framebuf) {156if (fb) {157vulkan_->Delete().QueueDeleteFramebuffer(fb);158}159}160}161162// NOTE: If numLayers > 1, it will create an array texture, rather than a normal 2D texture.163// This requires a different sampling path!164void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VulkanBarrierBatch *barriers, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {165// We don't support more exotic layer setups for now. Mono or stereo.166_dbg_assert_(numLayers == 1 || numLayers == 2);167168VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };169ici.arrayLayers = numLayers;170ici.mipLevels = 1;171ici.extent.width = width;172ici.extent.height = height;173ici.extent.depth = 1;174ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;175ici.imageType = VK_IMAGE_TYPE_2D;176ici.samples = sampleCount;177ici.tiling = VK_IMAGE_TILING_OPTIMAL;178ici.format = format;179ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;180if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {181ici.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;182}183if (color) {184ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;185} else {186ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;187}188189VmaAllocationCreateInfo allocCreateInfo{};190allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;191VmaAllocationInfo allocInfo{};192193VkResult res = vmaCreateImage(vulkan->Allocator(), &ici, &allocCreateInfo, &img.image, &img.alloc, &allocInfo);194_dbg_assert_(res == VK_SUCCESS);195196vulkan->SetDebugName(img.image, VK_OBJECT_TYPE_IMAGE, tag);197198VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);199200VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };201ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };202ivci.format = ici.format;203ivci.image = img.image;204ivci.viewType = numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;205ivci.subresourceRange.aspectMask = aspects;206ivci.subresourceRange.layerCount = numLayers;207ivci.subresourceRange.levelCount = 1;208res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.rtView);209vulkan->SetDebugName(img.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);210_dbg_assert_(res == VK_SUCCESS);211212// Separate view for texture sampling all layers together.213if (!color) {214ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;215}216217ivci.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; // layered for consistency, even if single image.218res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texAllLayersView);219_dbg_assert_(res == VK_SUCCESS);220vulkan->SetDebugName(img.texAllLayersView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);221222// Create 2D views for both layers.223// Useful when multipassing shaders that don't yet exist in a single-pass-stereo version.224for (int i = 0; i < numLayers; i++) {225ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;226ivci.subresourceRange.layerCount = 1;227ivci.subresourceRange.baseArrayLayer = i;228res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texLayerViews[i]);229if (vulkan->DebugLayerEnabled()) {230char temp[128];231snprintf(temp, sizeof(temp), "%s_layer%d", tag, i);232vulkan->SetDebugName(img.texLayerViews[i], VK_OBJECT_TYPE_IMAGE_VIEW, temp);233}234_dbg_assert_(res == VK_SUCCESS);235}236237VkPipelineStageFlags dstStage;238VkAccessFlagBits dstAccessMask;239switch (initialLayout) {240case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:241dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;242dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;243break;244case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:245dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;246dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;247break;248case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:249dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;250dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;251break;252default:253Crash();254return;255}256257VkImageMemoryBarrier *barrier = barriers->Add(img.image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage, 0);258barrier->subresourceRange.layerCount = numLayers;259barrier->subresourceRange.aspectMask = aspects;260barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;261barrier->newLayout = initialLayout;262barrier->srcAccessMask = 0;263barrier->dstAccessMask = dstAccessMask;264265img.layout = initialLayout;266img.format = format;267img.sampleCount = sampleCount;268img.tag = tag ? tag : "N/A";269img.numLayers = numLayers;270}271272static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {273switch (action) {274case VKRRenderPassLoadAction::CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;275case VKRRenderPassLoadAction::KEEP: return VK_ATTACHMENT_LOAD_OP_LOAD;276case VKRRenderPassLoadAction::DONT_CARE: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;277}278return VK_ATTACHMENT_LOAD_OP_DONT_CARE; // avoid compiler warning279}280281static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {282switch (action) {283case VKRRenderPassStoreAction::STORE: return VK_ATTACHMENT_STORE_OP_STORE;284case VKRRenderPassStoreAction::DONT_CARE: return VK_ATTACHMENT_STORE_OP_DONT_CARE;285}286return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning287}288289// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827290// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies291292VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {293bool isBackbuffer = rpType == RenderPassType::BACKBUFFER;294bool hasDepth = RenderPassTypeHasDepth(rpType);295bool multiview = RenderPassTypeHasMultiView(rpType);296bool multisample = RenderPassTypeHasMultisample(rpType);297298_dbg_assert_(!(isBackbuffer && multisample));299300if (isBackbuffer) {301_dbg_assert_(key.depthLoadAction != VKRRenderPassLoadAction::KEEP);302}303304if (multiview) {305// TODO: Assert that the device has multiview support enabled.306}307308int colorAttachmentIndex = 0;309int depthAttachmentIndex = 1;310311int attachmentCount = 0;312VkAttachmentDescription attachments[4]{};313attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;314attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;315attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.colorLoadAction);316attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);317attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;318attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;319attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;320attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;321attachmentCount++;322323if (hasDepth) {324attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;325attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;326attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.depthLoadAction);327attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);328attachments[attachmentCount].stencilLoadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.stencilLoadAction);329attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);330attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;331attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;332attachmentCount++;333}334335if (multisample) {336colorAttachmentIndex = attachmentCount;337attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;338attachments[attachmentCount].samples = sampleCount;339attachments[attachmentCount].loadOp = ConvertLoadAction(key.colorLoadAction);340attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);341attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;342attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;343attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;344attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;345attachmentCount++;346347if (hasDepth) {348depthAttachmentIndex = attachmentCount;349attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;350attachments[attachmentCount].samples = sampleCount;351attachments[attachmentCount].loadOp = ConvertLoadAction(key.depthLoadAction);352attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);353attachments[attachmentCount].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);354attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);355attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;356attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;357attachmentCount++;358}359}360361VkAttachmentReference colorReference{};362colorReference.attachment = colorAttachmentIndex;363colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;364365VkAttachmentReference depthReference{};366depthReference.attachment = depthAttachmentIndex;367depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;368369VkSubpassDescription subpass{};370subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;371subpass.flags = 0;372subpass.colorAttachmentCount = 1;373subpass.pColorAttachments = &colorReference;374375VkAttachmentReference colorResolveReference;376if (multisample) {377colorResolveReference.attachment = 0; // the non-msaa color buffer.378colorResolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;379subpass.pResolveAttachments = &colorResolveReference;380} else {381subpass.pResolveAttachments = nullptr;382}383if (hasDepth) {384subpass.pDepthStencilAttachment = &depthReference;385}386subpass.preserveAttachmentCount = 0;387subpass.pPreserveAttachments = nullptr;388389// Not sure if this is really necessary.390VkSubpassDependency deps[2]{};391size_t numDeps = 0;392393VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };394rp.attachmentCount = attachmentCount;395rp.pAttachments = attachments;396rp.subpassCount = 1;397rp.pSubpasses = &subpass;398399VkRenderPassMultiviewCreateInfoKHR mv{ VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR };400uint32_t viewMask = 0x3; // Must be outside the 'if (multiview)' scope!401int viewOffset = 0;402if (multiview) {403rp.pNext = &mv;404mv.subpassCount = 1;405mv.pViewMasks = &viewMask;406mv.dependencyCount = 0;407mv.pCorrelationMasks = &viewMask; // same masks408mv.correlationMaskCount = 1;409mv.pViewOffsets = &viewOffset;410}411412if (isBackbuffer) {413// We don't specify any explicit transitions for these, so let's use subpass dependencies.414// This makes sure that writes to the depth image are done before we try to write to it again.415// From Sascha's examples.416deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;417deps[numDeps].dstSubpass = 0;418deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;419deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;420deps[numDeps].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;421deps[numDeps].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;422deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;423numDeps++;424// Dependencies for the color image.425deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;426deps[numDeps].dstSubpass = 0;427deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;428deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;429deps[numDeps].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;430deps[numDeps].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;431deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;432numDeps++;433}434435if (numDeps > 0) {436rp.dependencyCount = (u32)numDeps;437rp.pDependencies = deps;438}439440VkRenderPass pass;441VkResult res;442443// We could always use renderpass2, but I think it'll get both paths better tested if we444// only use it with multisample enabled.445// if (vulkan->Extensions().KHR_create_renderpass2) {446if (multisample) {447// It's a bit unfortunate that we can't rely on vkCreateRenderPass2, because here we now have448// to do a bunch of struct conversion, just to not have to repeat the logic from above.449VkAttachmentDescription2KHR attachments2[4]{};450for (int i = 0; i < attachmentCount; i++) {451attachments2[i].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;452attachments2[i].format = attachments[i].format;453attachments2[i].samples = attachments[i].samples;454attachments2[i].loadOp = attachments[i].loadOp;455attachments2[i].storeOp = attachments[i].storeOp;456attachments2[i].stencilLoadOp = attachments[i].stencilLoadOp;457attachments2[i].stencilStoreOp = attachments[i].stencilStoreOp;458attachments2[i].initialLayout = attachments[i].initialLayout;459attachments2[i].finalLayout = attachments[i].finalLayout;460}461462VkAttachmentReference2KHR colorReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };463colorReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;464colorReference2.attachment = colorReference.attachment;465colorReference2.layout = colorReference.layout;466467VkAttachmentReference2KHR depthReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };468depthReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;469depthReference2.attachment = depthReference.attachment;470depthReference2.layout = depthReference.layout;471472VkSubpassDependency2KHR deps2[2]{};473for (int i = 0; i < numDeps; i++) {474deps2[i].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR;475deps2[i].dependencyFlags = deps[i].dependencyFlags;476deps2[i].srcAccessMask = deps[i].srcAccessMask;477deps2[i].dstAccessMask = deps[i].dstAccessMask;478deps2[i].srcStageMask = deps[i].srcStageMask;479deps2[i].dstStageMask = deps[i].dstStageMask;480deps2[i].srcSubpass = deps[i].srcSubpass;481deps2[i].dstSubpass = deps[i].dstSubpass;482deps2[i].dependencyFlags = deps[i].dependencyFlags;483deps2[i].viewOffset = 0;484}485486VkAttachmentReference2KHR colorResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };487488VkSubpassDescription2KHR subpass2{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR };489subpass2.colorAttachmentCount = subpass.colorAttachmentCount;490subpass2.flags = subpass.flags;491subpass2.pColorAttachments = &colorReference2;492if (hasDepth) {493subpass2.pDepthStencilAttachment = &depthReference2;494}495subpass2.pipelineBindPoint = subpass.pipelineBindPoint;496subpass2.viewMask = multiview ? viewMask : 0;497498// We check for multisample again, we want this path to also support non-multisample.499if (multisample) {500colorResolveReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;501colorResolveReference2.attachment = colorResolveReference.attachment; // the non-msaa color buffer.502colorResolveReference2.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;503subpass2.pResolveAttachments = &colorResolveReference2;504} else {505subpass2.pResolveAttachments = nullptr;506}507508VkAttachmentReference2KHR depthResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };509VkSubpassDescriptionDepthStencilResolveKHR depthStencilResolve{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR };510// We check for multisample again, we want this path to also support non-multisample.511if (hasDepth && multisample) {512ChainStruct(subpass2, &depthStencilResolve);513depthResolveReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;514depthResolveReference2.attachment = 1;515depthResolveReference2.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;516// TODO: Some games might benefit from the other depth resolve modes when depth texturing.517depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;518depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;519depthStencilResolve.pDepthStencilResolveAttachment = &depthResolveReference2;520}521522VkRenderPassCreateInfo2KHR rp2{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR };523rp2.pAttachments = attachments2;524rp2.pDependencies = deps2;525rp2.attachmentCount = rp.attachmentCount;526rp2.dependencyCount = rp.dependencyCount;527rp2.correlatedViewMaskCount = multiview ? 1 : 0;528rp2.pCorrelatedViewMasks = multiview ? &viewMask : nullptr;529rp2.pSubpasses = &subpass2;530rp2.subpassCount = 1;531res = vkCreateRenderPass2(vulkan->GetDevice(), &rp2, nullptr, &pass);532} else {533res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);534}535536if (pass) {537vulkan->SetDebugName(pass, VK_OBJECT_TYPE_RENDER_PASS, GetRPTypeName(rpType));538}539540_assert_(res == VK_SUCCESS);541_assert_(pass != VK_NULL_HANDLE);542return pass;543}544545VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {546// When we create a render pass, we create all "types" of it immediately,547// practical later when referring to it. Could change to on-demand if it feels motivated548// but I think the render pass objects are cheap.549550// WARNING: We don't include sampleCount in the key, there's only the distinction multisampled or not551// which comes from the rpType.552// So you CAN NOT mix and match different non-one sample counts.553554_dbg_assert_(!((rpType & RenderPassType::MULTISAMPLE) && sampleCount == VK_SAMPLE_COUNT_1_BIT));555556if (!pass[(int)rpType] || sampleCounts[(int)rpType] != sampleCount) {557if (pass[(int)rpType]) {558vulkan->Delete().QueueDeleteRenderPass(pass[(int)rpType]);559}560pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType, sampleCount);561sampleCounts[(int)rpType] = sampleCount;562}563return pass[(int)rpType];564}565566567