/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2024 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "core/settings.h" #include "vk_core.h" #include "vk_replay.h" RDOC_EXTERN_CONFIG(bool, Vulkan_Debug_SingleSubmitFlushing); VulkanReplay::OutputWindow::OutputWindow() : m_WindowSystem(WindowingSystem::Unknown), width(0), height(0) { surface = VK_NULL_HANDLE; swap = VK_NULL_HANDLE; for(size_t i = 0; i < ARRAY_COUNT(colimg); i++) colimg[i] = VK_NULL_HANDLE; WINDOW_HANDLE_INIT; hasDepth = false; failures = recreatePause = 0; bb = VK_NULL_HANDLE; bbmem = VK_NULL_HANDLE; bbview = VK_NULL_HANDLE; resolveimg = VK_NULL_HANDLE; resolvemem = VK_NULL_HANDLE; dsimg = VK_NULL_HANDLE; dsmem = VK_NULL_HANDLE; dsview = VK_NULL_HANDLE; fb = VK_NULL_HANDLE; fbdepth = VK_NULL_HANDLE; rp = VK_NULL_HANDLE; rpdepth = VK_NULL_HANDLE; numImgs = 0; curidx = 0; m_ResourceManager = NULL; VkImageMemoryBarrier t = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, VK_NULL_HANDLE, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, }; for(size_t i = 0; i < ARRAY_COUNT(colBarrier); i++) colBarrier[i] = t; bbBarrier = t; t.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; depthBarrier = t; depthBarrier.srcAccessMask = depthBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } void VulkanReplay::OutputWindow::Destroy(WrappedVulkan *driver, VkDevice device) { driver->SubmitCmds(); driver->FlushQ(); const VkDevDispatchTable *vt = ObjDisp(device); vt->DeviceWaitIdle(Unwrap(device)); if(bb != VK_NULL_HANDLE) { vt->DestroyRenderPass(Unwrap(device), Unwrap(rp), NULL); GetResourceManager()->ReleaseWrappedResource(rp); rp = VK_NULL_HANDLE; vt->DestroyImage(Unwrap(device), Unwrap(bb), NULL); GetResourceManager()->ReleaseWrappedResource(bb); vt->DestroyImageView(Unwrap(device), Unwrap(bbview), NULL); GetResourceManager()->ReleaseWrappedResource(bbview); vt->FreeMemory(Unwrap(device), Unwrap(bbmem), NULL); GetResourceManager()->ReleaseWrappedResource(bbmem); vt->DestroyFramebuffer(Unwrap(device), Unwrap(fb), NULL); GetResourceManager()->ReleaseWrappedResource(fb); bb = VK_NULL_HANDLE; bbview = VK_NULL_HANDLE; bbmem = VK_NULL_HANDLE; fb = VK_NULL_HANDLE; } // not owned - freed with the swapchain for(size_t i = 0; i < ARRAY_COUNT(colimg); i++) { if(colimg[i] != VK_NULL_HANDLE) GetResourceManager()->ReleaseWrappedResource(colimg[i]); colimg[i] = VK_NULL_HANDLE; } if(dsimg != VK_NULL_HANDLE) { vt->DestroyRenderPass(Unwrap(device), Unwrap(rpdepth), NULL); GetResourceManager()->ReleaseWrappedResource(rpdepth); rpdepth = VK_NULL_HANDLE; vt->DestroyImage(Unwrap(device), Unwrap(dsimg), NULL); GetResourceManager()->ReleaseWrappedResource(dsimg); vt->DestroyImageView(Unwrap(device), Unwrap(dsview), NULL); GetResourceManager()->ReleaseWrappedResource(dsview); vt->FreeMemory(Unwrap(device), Unwrap(dsmem), NULL); GetResourceManager()->ReleaseWrappedResource(dsmem); vt->DestroyFramebuffer(Unwrap(device), Unwrap(fbdepth), NULL); GetResourceManager()->ReleaseWrappedResource(fbdepth); vt->DestroyImage(Unwrap(device), Unwrap(resolveimg), NULL); GetResourceManager()->ReleaseWrappedResource(resolveimg); vt->FreeMemory(Unwrap(device), Unwrap(resolvemem), NULL); GetResourceManager()->ReleaseWrappedResource(resolvemem); resolveimg = VK_NULL_HANDLE; resolvemem = VK_NULL_HANDLE; dsview = VK_NULL_HANDLE; dsimg = VK_NULL_HANDLE; dsmem = VK_NULL_HANDLE; fbdepth = VK_NULL_HANDLE; rpdepth = VK_NULL_HANDLE; } if(swap != VK_NULL_HANDLE) { vt->DestroySwapchainKHR(Unwrap(device), Unwrap(swap), NULL); GetResourceManager()->ReleaseWrappedResource(swap); } if(surface != VK_NULL_HANDLE) { ObjDisp(driver->GetInstance()) ->DestroySurfaceKHR(Unwrap(driver->GetInstance()), Unwrap(surface), NULL); GetResourceManager()->ReleaseWrappedResource(surface); surface = VK_NULL_HANDLE; } } void VulkanReplay::OutputWindow::Create(WrappedVulkan *driver, VkDevice device, bool depth) { const VkDevDispatchTable *vt = ObjDisp(device); VkInstance inst = driver->GetInstance(); VkPhysicalDevice phys = driver->GetPhysDev(); hasDepth = depth; // save the old swapchain so it isn't destroyed VkSwapchainKHR old = swap; swap = VK_NULL_HANDLE; // we can't destroy the surface until all swapchains are destroyed, so // we also save the surface here and restore it back after destroy VkSurfaceKHR oldsurf = surface; surface = VK_NULL_HANDLE; Destroy(driver, device); surface = oldsurf; fresh = true; if(surface == VK_NULL_HANDLE && m_WindowSystem != WindowingSystem::Headless) { CreateSurface(driver, inst); GetResourceManager()->WrapResource(Unwrap(inst), surface); } // sensible defaults VkFormat imformat = VK_FORMAT_B8G8R8A8_SRGB; VkPresentModeKHR presentmode = VK_PRESENT_MODE_FIFO_KHR; VkColorSpaceKHR imcolspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; VkResult vkr = VK_SUCCESS; uint32_t numImages = 2; if(m_WindowSystem != WindowingSystem::Headless) { VkSurfaceCapabilitiesKHR capabilities = {}; ObjDisp(inst)->GetPhysicalDeviceSurfaceCapabilitiesKHR(Unwrap(phys), Unwrap(surface), &capabilities); if(capabilities.minImageCount < 8) numImages = RDCMAX(numImages, capabilities.minImageCount); if(capabilities.supportedUsageFlags == 0) { if(old != VK_NULL_HANDLE) { vt->DestroySwapchainKHR(Unwrap(device), Unwrap(old), NULL); GetResourceManager()->ReleaseWrappedResource(old); } RDCERR("Surface reported unsuccessful. %d consecutive failures!", failures); failures++; return; } RDCASSERT(capabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); // AMD didn't report this capability for a while. If the assert fires for you, update // your drivers! RDCASSERT(capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT); VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // find a supported alpha compositing mode if((capabilities.supportedCompositeAlpha & compositeAlpha) == 0) { VkCompositeAlphaFlagBitsKHR compositingBits[] = { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR}; for(VkCompositeAlphaFlagBitsKHR &compositingBit : compositingBits) { if(capabilities.supportedCompositeAlpha & compositingBit) { compositeAlpha = compositingBit; break; } } } // check format and present mode from driver { uint32_t numFormats = 0; vkr = ObjDisp(inst)->GetPhysicalDeviceSurfaceFormatsKHR(Unwrap(phys), Unwrap(surface), &numFormats, NULL); driver->CheckVkResult(vkr); if(numFormats > 0) { VkSurfaceFormatKHR *formats = new VkSurfaceFormatKHR[numFormats]; vkr = ObjDisp(inst)->GetPhysicalDeviceSurfaceFormatsKHR(Unwrap(phys), Unwrap(surface), &numFormats, formats); driver->CheckVkResult(vkr); if(numFormats == 1 && formats[0].format == VK_FORMAT_UNDEFINED) { // 1 entry with undefined means no preference, just use our default imformat = VK_FORMAT_B8G8R8A8_SRGB; imcolspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } else { // try and find a format with SRGB correction imformat = VK_FORMAT_UNDEFINED; imcolspace = formats[0].colorSpace; for(uint32_t i = 0; i < numFormats; i++) { if(IsSRGBFormat(formats[i].format)) { imformat = formats[i].format; imcolspace = formats[i].colorSpace; RDCASSERT(imcolspace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); break; } } if(imformat == VK_FORMAT_UNDEFINED) { RDCWARN("Couldn't find SRGB correcting output swapchain format"); imformat = formats[0].format; } } SAFE_DELETE_ARRAY(formats); } uint32_t numModes = 0; vkr = ObjDisp(inst)->GetPhysicalDeviceSurfacePresentModesKHR(Unwrap(phys), Unwrap(surface), &numModes, NULL); driver->CheckVkResult(vkr); if(numModes > 0) { VkPresentModeKHR *modes = new VkPresentModeKHR[numModes]; vkr = ObjDisp(inst)->GetPhysicalDeviceSurfacePresentModesKHR(Unwrap(phys), Unwrap(surface), &numModes, modes); driver->CheckVkResult(vkr); // If mailbox mode is available, use it, as is the lowest-latency non- // tearing mode. If not, try IMMEDIATE which will usually be available, // and is fastest (though it tears). If not, fall back to FIFO which is // always available. for(size_t i = 0; i < numModes; i++) { if(modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { presentmode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if(modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) presentmode = VK_PRESENT_MODE_IMMEDIATE_KHR; } SAFE_DELETE_ARRAY(modes); } } VkBool32 supported = false; ObjDisp(inst)->GetPhysicalDeviceSurfaceSupportKHR(Unwrap(phys), driver->GetQFamilyIdx(), Unwrap(surface), &supported); // can't really recover from this anyway RDCASSERT(supported); VkSwapchainCreateInfoKHR swapInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, NULL, 0, Unwrap(surface), numImages, imformat, imcolspace, {width, height}, 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, compositeAlpha, presentmode, true, Unwrap(old), }; vkr = vt->CreateSwapchainKHR(Unwrap(device), &swapInfo, NULL, &swap); driver->CheckVkResult(vkr); if(old != VK_NULL_HANDLE) { vt->DestroySwapchainKHR(Unwrap(device), Unwrap(old), NULL); GetResourceManager()->ReleaseWrappedResource(old); } if(swap == VK_NULL_HANDLE) { RDCERR("Failed to create swapchain. %d consecutive failures!", failures); failures++; // do some sort of backoff. // the first time, try to recreate again next frame if(failures == 1) recreatePause = 0; // the next few times, wait 200 'frames' between attempts else if(failures < 10) recreatePause = 100; // otherwise, only reattempt very infrequently. A resize will // always retrigger a recreate, so ew probably don't want to // try again else recreatePause = 1000; return; } failures = 0; GetResourceManager()->WrapResource(Unwrap(device), swap); vkr = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(swap), &numImgs, NULL); driver->CheckVkResult(vkr); RDCASSERT(numImgs <= 8, numImgs); VkImage *imgs = new VkImage[numImgs]; vkr = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(swap), &numImgs, imgs); driver->CheckVkResult(vkr); for(size_t i = 0; i < numImgs; i++) { colimg[i] = imgs[i]; GetResourceManager()->WrapResource(Unwrap(device), colimg[i]); colBarrier[i].image = Unwrap(colimg[i]); colBarrier[i].oldLayout = colBarrier[i].newLayout = VK_IMAGE_LAYOUT_UNDEFINED; } delete[] imgs; } curidx = 0; // for our 'fake' backbuffer, create in RGBA8 imformat = VK_FORMAT_R8G8B8A8_SRGB; if(depth) { VkImageCreateInfo imInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, VK_IMAGE_TYPE_2D, VK_FORMAT_D32_SFLOAT, {width, height, 1}, 1, 1, VULKAN_MESH_VIEW_SAMPLES, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED, }; vkr = vt->CreateImage(Unwrap(device), &imInfo, NULL, &dsimg); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), dsimg); NameVulkanObject(dsimg, "outputwindow dsimg"); VkMemoryRequirements mrq = {0}; vt->GetImageMemoryRequirements(Unwrap(device), Unwrap(dsimg), &mrq); VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size, driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits), }; vkr = vt->AllocateMemory(Unwrap(device), &allocInfo, NULL, &dsmem); driver->CheckVkResult(vkr); if(vkr != VK_SUCCESS) return; GetResourceManager()->WrapResource(Unwrap(device), dsmem); vkr = vt->BindImageMemory(Unwrap(device), Unwrap(dsimg), Unwrap(dsmem), 0); driver->CheckVkResult(vkr); depthBarrier.image = Unwrap(dsimg); depthBarrier.oldLayout = depthBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, Unwrap(dsimg), VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_D32_SFLOAT, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}, }; vkr = vt->CreateImageView(Unwrap(device), &info, NULL, &dsview); driver->CheckVkResult(vkr); NameUnwrappedVulkanObject(dsview, "output window dsview"); GetResourceManager()->WrapResource(Unwrap(device), dsview); // create resolve target, since it must precisely match the pre-resolve format, it doesn't allow // any format conversion. imInfo.samples = VK_SAMPLE_COUNT_1_BIT; imInfo.format = imformat; imInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; vkr = vt->CreateImage(Unwrap(device), &imInfo, NULL, &resolveimg); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), resolveimg); NameVulkanObject(resolveimg, "outputwindow resolveimg"); vt->GetImageMemoryRequirements(Unwrap(device), Unwrap(resolveimg), &mrq); allocInfo.allocationSize = mrq.size; allocInfo.memoryTypeIndex = driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits); vkr = vt->AllocateMemory(Unwrap(device), &allocInfo, NULL, &resolvemem); driver->CheckVkResult(vkr); if(vkr != VK_SUCCESS) return; GetResourceManager()->WrapResource(Unwrap(device), resolvemem); vkr = vt->BindImageMemory(Unwrap(device), Unwrap(resolveimg), Unwrap(resolvemem), 0); driver->CheckVkResult(vkr); } { VkAttachmentDescription attDesc[] = { {0, imformat, depth ? VULKAN_MESH_VIEW_SAMPLES : VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, {0, VK_FORMAT_D32_SFLOAT, depth ? VULKAN_MESH_VIEW_SAMPLES : VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}}; VkAttachmentReference attRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkAttachmentReference dsRef = {1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; VkSubpassDescription sub = { 0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, NULL, // inputs 1, &attRef, // color NULL, // resolve NULL, // depth-stencil 0, NULL, // preserve }; VkRenderPassCreateInfo rpinfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, attDesc, 1, &sub, 0, NULL, // dependencies }; vkr = vt->CreateRenderPass(Unwrap(device), &rpinfo, NULL, &rp); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), rp); if(dsimg != VK_NULL_HANDLE) { sub.pDepthStencilAttachment = &dsRef; rpinfo.attachmentCount = 2; vkr = vt->CreateRenderPass(Unwrap(device), &rpinfo, NULL, &rpdepth); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), rpdepth); } } { VkImageCreateInfo imInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, VK_IMAGE_TYPE_2D, imformat, {width, height, 1}, 1, 1, depth ? VULKAN_MESH_VIEW_SAMPLES : VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED, }; vkr = vt->CreateImage(Unwrap(device), &imInfo, NULL, &bb); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), bb); NameVulkanObject(bb, "outputwindow bb"); VkMemoryRequirements mrq = {0}; vt->GetImageMemoryRequirements(Unwrap(device), Unwrap(bb), &mrq); VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size, driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits), }; vkr = vt->AllocateMemory(Unwrap(device), &allocInfo, NULL, &bbmem); driver->CheckVkResult(vkr); if(vkr != VK_SUCCESS) return; GetResourceManager()->WrapResource(Unwrap(device), bbmem); vkr = vt->BindImageMemory(Unwrap(device), Unwrap(bb), Unwrap(bbmem), 0); driver->CheckVkResult(vkr); bbBarrier.image = Unwrap(bb); bbBarrier.oldLayout = bbBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED; } { VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, Unwrap(bb), VK_IMAGE_VIEW_TYPE_2D, imformat, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, }; vkr = vt->CreateImageView(Unwrap(device), &info, NULL, &bbview); driver->CheckVkResult(vkr); NameUnwrappedVulkanObject(bbview, "output window bbview"); GetResourceManager()->WrapResource(Unwrap(device), bbview); { VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, Unwrap(rp), 1, UnwrapPtr(bbview), (uint32_t)width, (uint32_t)height, 1, }; vkr = vt->CreateFramebuffer(Unwrap(device), &fbinfo, NULL, &fb); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), fb); } if(dsimg != VK_NULL_HANDLE) { VkImageView views[] = {Unwrap(bbview), Unwrap(dsview)}; VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, Unwrap(rpdepth), 2, views, (uint32_t)width, (uint32_t)height, 1, }; vkr = vt->CreateFramebuffer(Unwrap(device), &fbinfo, NULL, &fbdepth); driver->CheckVkResult(vkr); GetResourceManager()->WrapResource(Unwrap(device), fbdepth); } } } void VulkanReplay::GetOutputWindowData(uint64_t id, bytebuf &retData) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; VkDevice device = m_pDriver->GetDev(); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); if(cmd == VK_NULL_HANDLE) return; const VkDevDispatchTable *vt = ObjDisp(device); vt->DeviceWaitIdle(Unwrap(device)); VkBuffer readbackBuf = VK_NULL_HANDLE; VkResult vkr = VK_SUCCESS; // create readback buffer VkBufferCreateInfo bufInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0, GetByteSize(outw.width, outw.height, 1, VK_FORMAT_R8G8B8A8_UNORM, 0), VK_BUFFER_USAGE_TRANSFER_DST_BIT, }; vt->CreateBuffer(Unwrap(device), &bufInfo, NULL, &readbackBuf); m_pDriver->CheckVkResult(vkr); VkMemoryRequirements mrq = {0}; vt->GetBufferMemoryRequirements(Unwrap(device), readbackBuf, &mrq); VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size, m_pDriver->GetReadbackMemoryIndex(mrq.memoryTypeBits), }; VkDeviceMemory readbackMem = VK_NULL_HANDLE; vkr = vt->AllocateMemory(Unwrap(device), &allocInfo, NULL, &readbackMem); m_pDriver->CheckVkResult(vkr); if(vkr != VK_SUCCESS) return; vkr = vt->BindBufferMemory(Unwrap(device), readbackBuf, readbackMem, 0); m_pDriver->CheckVkResult(vkr); VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; // do image copy vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); m_pDriver->CheckVkResult(vkr); VkBufferImageCopy cpy = { 0, 0, 0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, { 0, 0, 0, }, {outw.width, outw.height, 1}, }; outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; outw.bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); VkImage copySource = outw.bb; if(outw.resolveimg != VK_NULL_HANDLE) { VkImageResolve resolve = { {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {outw.width, outw.height, 1}, }; VkImageMemoryBarrier resolveBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(outw.resolveimg), {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, }; // discard previous contents of resolve buffer and finish any work with it. DoPipelineBarrier(cmd, 1, &resolveBarrier); // resolve from the backbuffer to resolve buffer (identical format) vt->CmdResolveImage(Unwrap(cmd), Unwrap(outw.bb), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(outw.resolveimg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &resolve); // wait for resolve to finish before we blit copySource = outw.resolveimg; resolveBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; resolveBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; DoPipelineBarrier(cmd, 1, &resolveBarrier); } vt->CmdCopyImageToBuffer(Unwrap(cmd), Unwrap(copySource), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuf, 1, &cpy); outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; outw.bbBarrier.srcAccessMask = outw.bbBarrier.dstAccessMask; vkr = vt->EndCommandBuffer(Unwrap(cmd)); m_pDriver->CheckVkResult(vkr); m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); // need to wait so we can readback // map memory and readback byte *pData = NULL; vkr = vt->MapMemory(Unwrap(device), readbackMem, 0, bufInfo.size, 0, (void **)&pData); m_pDriver->CheckVkResult(vkr); if(vkr != VK_SUCCESS) return; if(!pData) { RDCERR("Manually reporting failed memory map"); CheckVkResult(VK_ERROR_MEMORY_MAP_FAILED); return; } VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, readbackMem, 0, VK_WHOLE_SIZE, }; vkr = vt->InvalidateMappedMemoryRanges(Unwrap(device), 1, &range); m_pDriver->CheckVkResult(vkr); { retData.resize(outw.width * outw.height * 3); byte *src = (byte *)pData; byte *dst = retData.data(); for(uint32_t row = 0; row < outw.height; row++) { for(uint32_t x = 0; x < outw.width; x++) { dst[x * 3 + 0] = src[x * 4 + 0]; dst[x * 3 + 1] = src[x * 4 + 1]; dst[x * 3 + 2] = src[x * 4 + 2]; } src += outw.width * 4; dst += outw.width * 3; } } vt->UnmapMemory(Unwrap(device), readbackMem); // delete all vt->DestroyBuffer(Unwrap(device), readbackBuf, NULL); vt->FreeMemory(Unwrap(device), readbackMem, NULL); } void VulkanReplay::SetOutputWindowDimensions(uint64_t id, int32_t w, int32_t h) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; // can't resize an output with an actual window backing if(outw.m_WindowSystem != WindowingSystem::Headless) return; outw.width = w; outw.height = h; outw.Create(m_pDriver, m_pDriver->GetDev(), outw.hasDepth); } bool VulkanReplay::CheckResizeOutputWindow(uint64_t id) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return false; OutputWindow &outw = m_OutputWindows[id]; if(outw.m_WindowSystem == WindowingSystem::Unknown || outw.m_WindowSystem == WindowingSystem::Headless) return false; int32_t w, h; GetOutputWindowDimensions(id, w, h); if((uint32_t)w != outw.width || (uint32_t)h != outw.height) { outw.width = w; outw.height = h; if(outw.width > 0 && outw.height > 0) outw.Create(m_pDriver, m_pDriver->GetDev(), outw.hasDepth); return true; } if(outw.swap == VK_NULL_HANDLE && outw.width > 0 && outw.height > 0) { if(outw.recreatePause <= 0) outw.Create(m_pDriver, m_pDriver->GetDev(), outw.hasDepth); else outw.recreatePause--; return true; } if(outw.outofdate) { outw.outofdate = false; return true; } return false; } void VulkanReplay::BindOutputWindow(uint64_t id, bool depth) { m_ActiveWinID = id; m_BindDepth = depth; auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; // if the swapchain failed to create, do nothing. We will try to recreate it // again in CheckResizeOutputWindow (once per render 'frame') if(outw.m_WindowSystem != WindowingSystem::Headless && outw.swap == VK_NULL_HANDLE) return; m_DebugWidth = (int32_t)outw.width; m_DebugHeight = (int32_t)outw.height; VkDevice dev = m_pDriver->GetDev(); const VkDevDispatchTable *vt = ObjDisp(dev); VkResult vkr = VK_SUCCESS; // if we have a swapchain, acquire the next image. if(outw.swap != VK_NULL_HANDLE) { // semaphore is short lived, so not wrapped, if it's cached (ideally) // then it should be wrapped VkSemaphore sem; VkPipelineStageFlags stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSemaphoreCreateInfo semInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, NULL, 0}; vkr = vt->CreateSemaphore(Unwrap(dev), &semInfo, NULL, &sem); m_pDriver->CheckVkResult(vkr); vkr = vt->AcquireNextImageKHR(Unwrap(dev), Unwrap(outw.swap), 2000000000ULL, sem, VK_NULL_HANDLE, &outw.curidx); if(vkr == VK_ERROR_OUT_OF_DATE_KHR) { // force a swapchain recreate. outw.width = 0; outw.height = 0; CheckResizeOutputWindow(id); // then try again to acquire. vkr = vt->AcquireNextImageKHR(Unwrap(dev), Unwrap(outw.swap), 2000000000ULL, sem, VK_NULL_HANDLE, &outw.curidx); } if(vkr == VK_SUBOPTIMAL_KHR) vkr = VK_SUCCESS; m_pDriver->CheckVkResult(vkr); VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, NULL, 1, &sem, &stage, 0, NULL, // cmd buffers 0, NULL, // signal semaphores }; vkr = vt->QueueSubmit(Unwrap(m_pDriver->GetQ()), 1, &submitInfo, VK_NULL_HANDLE); m_pDriver->CheckVkResult(vkr); vt->QueueWaitIdle(Unwrap(m_pDriver->GetQ())); vt->DestroySemaphore(Unwrap(dev), sem, NULL); } VkCommandBuffer cmd = m_pDriver->GetNextCmd(); if(cmd == VK_NULL_HANDLE) return; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); m_pDriver->CheckVkResult(vkr); outw.depthBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // first time rendering to the backbuffer, clear it, since our typical render pass // is set to LOAD_OP_LOAD if(outw.fresh) { outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; outw.bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); float black[] = {0.0f, 0.0f, 0.0f, 0.0f}; vt->CmdClearColorImage(Unwrap(cmd), Unwrap(outw.bb), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (VkClearColorValue *)black, 1, &outw.bbBarrier.subresourceRange); outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; outw.bbBarrier.srcAccessMask = outw.bbBarrier.dstAccessMask; outw.fresh = false; } outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; outw.bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; outw.colBarrier[outw.curidx].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; outw.colBarrier[outw.curidx].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); if(outw.colimg[0] != VK_NULL_HANDLE) DoPipelineBarrier(cmd, 1, &outw.colBarrier[outw.curidx]); if(outw.dsimg != VK_NULL_HANDLE) DoPipelineBarrier(cmd, 1, &outw.depthBarrier); outw.depthBarrier.oldLayout = outw.depthBarrier.newLayout; outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; outw.bbBarrier.srcAccessMask = outw.bbBarrier.dstAccessMask; outw.colBarrier[outw.curidx].oldLayout = outw.colBarrier[outw.curidx].newLayout; outw.colBarrier[outw.curidx].srcAccessMask = outw.colBarrier[outw.curidx].dstAccessMask; vt->EndCommandBuffer(Unwrap(cmd)); if(Vulkan_Debug_SingleSubmitFlushing()) m_pDriver->SubmitCmds(); } void VulkanReplay::ClearOutputWindowColor(uint64_t id, FloatVector col) { auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; // if the swapchain failed to create, do nothing. We will try to recreate it // again in CheckResizeOutputWindow (once per render 'frame') if(outw.m_WindowSystem != WindowingSystem::Headless && outw.swap == VK_NULL_HANDLE) return; VkDevice dev = m_pDriver->GetDev(); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); const VkDevDispatchTable *vt = ObjDisp(dev); if(cmd == VK_NULL_HANDLE) return; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; VkResult vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); m_pDriver->CheckVkResult(vkr); VkMarkerRegion::Begin("ClearOutputWindowColor", cmd); outw.bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; outw.bbBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; outw.bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); vt->CmdClearColorImage(Unwrap(cmd), Unwrap(outw.bb), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (VkClearColorValue *)&col.x, 1, &outw.bbBarrier.subresourceRange); outw.bbBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; outw.bbBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; outw.bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); outw.bbBarrier.srcAccessMask = outw.bbBarrier.dstAccessMask; outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; VkMarkerRegion::End(cmd); vt->EndCommandBuffer(Unwrap(cmd)); if(Vulkan_Debug_SingleSubmitFlushing()) m_pDriver->SubmitCmds(); } void VulkanReplay::ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil) { auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; // if the swapchain failed to create, do nothing. We will try to recreate it // again in CheckResizeOutputWindow (once per render 'frame') if(outw.m_WindowSystem != WindowingSystem::Headless && outw.swap == VK_NULL_HANDLE) return; VkDevice dev = m_pDriver->GetDev(); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); const VkDevDispatchTable *vt = ObjDisp(dev); if(cmd == VK_NULL_HANDLE) return; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; VkResult vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); m_pDriver->CheckVkResult(vkr); VkClearDepthStencilValue ds = {depth, stencil}; outw.depthBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; outw.depthBarrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; outw.depthBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; outw.depthBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; DoPipelineBarrier(cmd, 1, &outw.depthBarrier); vt->CmdClearDepthStencilImage(Unwrap(cmd), Unwrap(outw.dsimg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &ds, 1, &outw.depthBarrier.subresourceRange); outw.depthBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; outw.depthBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; outw.depthBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; outw.depthBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; DoPipelineBarrier(cmd, 1, &outw.depthBarrier); outw.depthBarrier.oldLayout = outw.depthBarrier.newLayout; vt->EndCommandBuffer(Unwrap(cmd)); if(Vulkan_Debug_SingleSubmitFlushing()) m_pDriver->SubmitCmds(); } void VulkanReplay::FlipOutputWindow(uint64_t id) { auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; // if the swapchain failed to create, do nothing. We will try to recreate it // again in CheckResizeOutputWindow (once per render 'frame') if(outw.swap == VK_NULL_HANDLE) return; VkDevice dev = m_pDriver->GetDev(); VkCommandBuffer cmd = m_pDriver->GetNextCmd(); const VkDevDispatchTable *vt = ObjDisp(dev); if(cmd == VK_NULL_HANDLE) return; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; VkResult vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); m_pDriver->CheckVkResult(vkr); VkMarkerRegion::Begin("FlipOutputWindow", cmd); // ensure rendering has completed before copying outw.bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; outw.bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); DoPipelineBarrier(cmd, 1, &outw.colBarrier[outw.curidx]); outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; outw.bbBarrier.srcAccessMask = 0; outw.bbBarrier.dstAccessMask = 0; VkImageBlit blit = { {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, { {0, 0, 0}, {(int32_t)outw.width, (int32_t)outw.height, 1}, }, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, { {0, 0, 0}, {(int32_t)outw.width, (int32_t)outw.height, 1}, }, }; VkImage blitSource = outw.bb; if(outw.dsimg != VK_NULL_HANDLE) { VkImageResolve resolve = { {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {outw.width, outw.height, 1}, }; VkImageMemoryBarrier resolveBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(outw.resolveimg), {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; // discard previous contents of resolve buffer and finish any work with it. DoPipelineBarrier(cmd, 1, &resolveBarrier); // resolve from the backbuffer to resolve buffer (identical format) vt->CmdResolveImage(Unwrap(cmd), Unwrap(outw.bb), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(outw.resolveimg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &resolve); // wait for resolve to finish before we blit blitSource = outw.resolveimg; resolveBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; resolveBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; DoPipelineBarrier(cmd, 1, &resolveBarrier); } vt->CmdBlitImage(Unwrap(cmd), Unwrap(blitSource), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(outw.colimg[outw.curidx]), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); outw.bbBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; outw.bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; outw.bbBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; outw.colBarrier[outw.curidx].newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // make sure copy has completed before present outw.colBarrier[outw.curidx].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; outw.colBarrier[outw.curidx].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; DoPipelineBarrier(cmd, 1, &outw.bbBarrier); DoPipelineBarrier(cmd, 1, &outw.colBarrier[outw.curidx]); outw.bbBarrier.oldLayout = outw.bbBarrier.newLayout; outw.bbBarrier.srcAccessMask = outw.bbBarrier.dstAccessMask; outw.colBarrier[outw.curidx].oldLayout = outw.colBarrier[outw.curidx].newLayout; outw.colBarrier[outw.curidx].srcAccessMask = 0; outw.colBarrier[outw.curidx].dstAccessMask = 0; VkMarkerRegion::End(cmd); vt->EndCommandBuffer(Unwrap(cmd)); // submit all the cmds we recorded m_pDriver->SubmitCmds(); VkPresentInfoKHR presentInfo = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, NULL, 0, NULL, // wait semaphores 1, UnwrapPtr(outw.swap), &outw.curidx, &vkr}; VkResult retvkr = vt->QueuePresentKHR(Unwrap(m_pDriver->GetQ()), &presentInfo); if(retvkr != VK_ERROR_OUT_OF_DATE_KHR && retvkr != VK_SUBOPTIMAL_KHR && retvkr != VK_ERROR_SURFACE_LOST_KHR) m_pDriver->CheckVkResult(retvkr); m_pDriver->FlushQ(); if(retvkr == VK_ERROR_OUT_OF_DATE_KHR) { // this will check the current extent and use that if possible outw.Create(m_pDriver, m_pDriver->GetDev(), outw.hasDepth); outw.outofdate = true; } } void VulkanReplay::DestroyOutputWindow(uint64_t id) { auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; outw.Destroy(m_pDriver, m_pDriver->GetDev()); m_OutputWindows.erase(it); } rdcarray VulkanReplay::GetSupportedWindowSystems() { return m_pDriver->m_SupportedWindowSystems; } uint64_t VulkanReplay::MakeOutputWindow(WindowingData window, bool depth) { uint64_t id = m_OutputWinID; m_OutputWinID++; m_OutputWindows[id].m_WindowSystem = window.system; m_OutputWindows[id].m_ResourceManager = GetResourceManager(); if(window.system != WindowingSystem::Unknown && window.system != WindowingSystem::Headless) m_OutputWindows[id].SetWindowHandle(window); if(window.system != WindowingSystem::Unknown) { int32_t w, h; if(window.system == WindowingSystem::Headless) { w = window.headless.width; h = window.headless.height; } else { GetOutputWindowDimensions(id, w, h); } m_OutputWindows[id].width = w; m_OutputWindows[id].height = h; m_OutputWindows[id].Create(m_pDriver, m_pDriver->GetDev(), depth); } return id; }