From 96bbeea4a508d868097db6238a85563a4dcc8aff Mon Sep 17 00:00:00 2001 From: Benson Joeris Date: Tue, 17 Dec 2019 11:12:34 -0500 Subject: [PATCH] Add ImageState `ImageState` tracks the state of images and their subresources. This functionality was previously split between the `ImageLayouts` and `ImgRefs` classes. Change-Id: I3242417dacf73fe07765f9bcfd449599e373e10d --- renderdoc/core/resource_manager.cpp | 5 + renderdoc/core/resource_manager.h | 5 + renderdoc/driver/vulkan/CMakeLists.txt | 1 + renderdoc/driver/vulkan/imagestate_tests.cpp | 735 ++++++++++ .../driver/vulkan/renderdoc_vulkan.vcxproj | 1 + .../vulkan/renderdoc_vulkan.vcxproj.filters | 3 + renderdoc/driver/vulkan/vk_common.cpp | 7 + renderdoc/driver/vulkan/vk_common.h | 1 + renderdoc/driver/vulkan/vk_image_states.cpp | 1301 +++++++++++++++++ renderdoc/driver/vulkan/vk_resources.cpp | 12 - renderdoc/driver/vulkan/vk_resources.h | 346 ++++- 11 files changed, 2397 insertions(+), 20 deletions(-) create mode 100644 renderdoc/driver/vulkan/imagestate_tests.cpp diff --git a/renderdoc/core/resource_manager.cpp b/renderdoc/core/resource_manager.cpp index 77674c387..29c572b62 100644 --- a/renderdoc/core/resource_manager.cpp +++ b/renderdoc/core/resource_manager.cpp @@ -133,6 +133,11 @@ FrameRefType ComposeFrameRefsFirstKnown(FrameRefType first, FrameRefType second) return second; } +FrameRefType KeepOldFrameRef(FrameRefType first, FrameRefType second) +{ + return first; +} + bool IncludesRead(FrameRefType refType) { switch(refType) diff --git a/renderdoc/core/resource_manager.h b/renderdoc/core/resource_manager.h index 370d23255..aa842d9e7 100644 --- a/renderdoc/core/resource_manager.h +++ b/renderdoc/core/resource_manager.h @@ -123,6 +123,8 @@ const double PERSISTENT_RESOURCE_AGE = 3000; DECLARE_REFLECTION_ENUM(FrameRefType); +typedef FrameRefType (*FrameRefCompFunc)(FrameRefType, FrameRefType); + // Compose frame refs that occur in a known order. // This can be thought of as a state (`first`) and a transition from that state // (`second`), returning the new state (see the state diagram for @@ -143,6 +145,9 @@ FrameRefType ComposeFrameRefsDisjoint(FrameRefType x, FrameRefType y); // Returns whichever of `first` or `second` is valid. FrameRefType ComposeFrameRefsFirstKnown(FrameRefType first, FrameRefType second); +// Dummy frame ref composition that always keeps the old ref. +FrameRefType KeepOldFrameRef(FrameRefType first, FrameRefType second); + bool IsDirtyFrameRef(FrameRefType refType); // Captures the possible initialization/reset requirements for resources. diff --git a/renderdoc/driver/vulkan/CMakeLists.txt b/renderdoc/driver/vulkan/CMakeLists.txt index 00c01d529..ad11b4292 100644 --- a/renderdoc/driver/vulkan/CMakeLists.txt +++ b/renderdoc/driver/vulkan/CMakeLists.txt @@ -40,6 +40,7 @@ set(sources vk_serialise.cpp vk_stringise.cpp vk_layer.cpp + imagestate_tests.cpp imgrefs_tests.cpp official/vk_layer.h official/vk_platform.h diff --git a/renderdoc/driver/vulkan/imagestate_tests.cpp b/renderdoc/driver/vulkan/imagestate_tests.cpp new file mode 100644 index 000000000..032c6e40e --- /dev/null +++ b/renderdoc/driver/vulkan/imagestate_tests.cpp @@ -0,0 +1,735 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "common/globalconfig.h" + +#if ENABLED(ENABLE_UNIT_TESTS) + +#include "3rdparty/catch/catch.hpp" + +#include "vk_resources.h" + +#include +#include + +void CheckSubresourceRanges(const ImageState &state, bool expectAspectsSplit, + bool expectLevelsSplit, bool expectLayersSplit, bool expectDepthSplit) +{ + std::vector splitAspects; + if(expectAspectsSplit) + { + for(auto it = ImageAspectFlagIter::begin(state.GetImageInfo().Aspects()); + it != ImageAspectFlagIter::end(); ++it) + { + splitAspects.push_back(*it); + } + } + else + { + splitAspects.push_back(state.GetImageInfo().Aspects()); + } + uint32_t splitLevelCount = expectLevelsSplit ? state.GetImageInfo().levelCount : 1; + uint32_t splitLayerCount = expectLayersSplit ? state.GetImageInfo().layerCount : 1; + uint32_t splitSliceCount = expectDepthSplit ? state.GetImageInfo().extent.depth : 1; + size_t splitSize = splitAspects.size() * (size_t)splitLevelCount * (size_t)splitLayerCount * + (size_t)splitSliceCount; + CHECK(state.subresourceStates.size() == splitSize); + + auto substateIt = state.subresourceStates.begin(); + auto aspectIt = splitAspects.begin(); + uint32_t level = 0; + uint32_t layer = 0; + uint32_t slice = 0; + uint32_t index = 0; + for(; aspectIt != splitAspects.end() && substateIt != state.subresourceStates.end(); + ++substateIt, ++index) + { + const ImageSubresourceRange &range = substateIt->range(); + CHECK(range.aspectMask == *aspectIt); + + if(expectLevelsSplit) + { + CHECK(range.baseMipLevel == level); + CHECK(range.levelCount == 1); + } + else + { + CHECK(range.baseMipLevel == 0); + CHECK(range.levelCount == (uint32_t)state.GetImageInfo().levelCount); + } + + if(expectLayersSplit) + { + CHECK(range.baseArrayLayer == layer); + CHECK(range.layerCount == 1); + } + else + { + CHECK(range.baseArrayLayer == 0); + CHECK(range.layerCount == (uint32_t)state.GetImageInfo().layerCount); + } + + if(expectDepthSplit) + { + CHECK(range.baseDepthSlice == slice); + CHECK(range.sliceCount == 1); + } + else + { + CHECK(range.baseDepthSlice == 0); + CHECK(range.sliceCount == (uint32_t)state.GetImageInfo().extent.depth); + } + + ++slice; + if(slice < splitSliceCount) + continue; + slice = 0; + + ++layer; + if(layer < splitLayerCount) + continue; + layer = 0; + + ++level; + if(level < splitLevelCount) + continue; + level = 0; + + ++aspectIt; + } + CHECK(index == splitSize); +} + +void CheckSubresourceState(const ImageSubresourceState &substate, + const ImageSubresourceState &expected) +{ + CHECK(substate.oldQueueFamilyIndex == expected.oldQueueFamilyIndex); + CHECK(substate.newQueueFamilyIndex == expected.newQueueFamilyIndex); + CHECK(substate.oldLayout == expected.oldLayout); + CHECK(substate.newLayout == expected.newLayout); + CHECK(substate.refType == expected.refType); +} + +TEST_CASE("Test ImageState type", "[imagestate]") +{ + ImageTransitionInfo transitionInfo(CaptureState::ActiveCapturing, 0); + VkImage image = (VkImage)123; + VkFormat format = VK_FORMAT_D16_UNORM_S8_UINT; + VkExtent3D extent = {100, 100, 13}; + int levelCount = 11; + int layerCount = 17; + int sampleCount = 1; + ImageInfo imageInfo(format, extent, levelCount, layerCount, sampleCount, + VK_IMAGE_LAYOUT_UNDEFINED, VK_SHARING_MODE_EXCLUSIVE); + + ImageSubresourceState initSubstate(VK_QUEUE_FAMILY_IGNORED, UNKNOWN_PREV_IMG_LAYOUT, + eFrameRef_None); + + ImageSubresourceState readSubstate(initSubstate); + readSubstate.oldQueueFamilyIndex = readSubstate.newQueueFamilyIndex = 0; + readSubstate.refType = eFrameRef_Read; + + SECTION("Initial state") + { + ImageState state(image, imageInfo, eFrameRef_None); + CheckSubresourceRanges(state, false, false, false, false); + CheckSubresourceState(state.subresourceStates.begin()->state(), initSubstate); + }; + + SECTION("Split aspects") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range = imageInfo.FullRange(); + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + state.RecordUse(range, eFrameRef_Read, 0); + + CheckSubresourceRanges(state, true, false, false, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Split mip levels") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range = imageInfo.FullRange(); + range.baseMipLevel = 1; + range.levelCount = 3; + state.RecordUse(range, eFrameRef_Read, 0); + + CheckSubresourceRanges(state, false, true, false, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().baseMipLevel >= range.baseMipLevel && + it->range().baseMipLevel - range.baseMipLevel < range.levelCount) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Split array layers") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range = imageInfo.FullRange(); + range.baseArrayLayer = 3; + range.layerCount = 5; + state.RecordUse(range, eFrameRef_Read, 0); + + CheckSubresourceRanges(state, false, false, true, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().baseArrayLayer >= range.baseArrayLayer && + it->range().baseArrayLayer - range.baseArrayLayer < range.layerCount) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Split depth slices") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range = imageInfo.FullRange(); + range.baseDepthSlice = 1; + range.sliceCount = 1; + state.RecordUse(range, eFrameRef_Read, 0); + + CheckSubresourceRanges(state, false, false, false, true); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().baseDepthSlice >= range.baseDepthSlice && + it->range().baseDepthSlice - range.baseDepthSlice < range.sliceCount) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Split aspect to depth") + { + ImageState state(image, imageInfo, eFrameRef_None); + + ImageSubresourceRange aspectRange(imageInfo.FullRange()); + aspectRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + state.RecordUse(aspectRange, eFrameRef_Read, 0); + CheckSubresourceRanges(state, true, false, false, false); + + ImageSubresourceRange levelRange(imageInfo.FullRange()); + levelRange.baseMipLevel = 0; + levelRange.levelCount = 1; + state.RecordUse(levelRange, eFrameRef_PartialWrite, 1); + CheckSubresourceRanges(state, true, true, false, false); + + ImageSubresourceRange layerRange(imageInfo.FullRange()); + layerRange.baseArrayLayer = 0; + layerRange.layerCount = 1; + state.RecordUse(layerRange, eFrameRef_Read, 2); + CheckSubresourceRanges(state, true, true, true, false); + + ImageSubresourceRange sliceRange(imageInfo.FullRange()); + sliceRange.baseDepthSlice = 0; + sliceRange.sliceCount = 1; + state.RecordUse(sliceRange, eFrameRef_CompleteWrite, 3); + CheckSubresourceRanges(state, true, true, true, true); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(it->range().aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_Read); + substate.newQueueFamilyIndex = 0; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(levelRange.baseMipLevel <= it->range().baseMipLevel && + it->range().baseMipLevel - levelRange.baseMipLevel < levelRange.levelCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_PartialWrite); + substate.newQueueFamilyIndex = 1; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(layerRange.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - layerRange.baseArrayLayer < layerRange.layerCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_Read); + substate.newQueueFamilyIndex = 2; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(sliceRange.baseDepthSlice <= it->range().baseDepthSlice && + it->range().baseDepthSlice - sliceRange.baseDepthSlice < sliceRange.sliceCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_CompleteWrite); + substate.newQueueFamilyIndex = 3; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + + CheckSubresourceState(it->state(), substate); + } + }; + + SECTION("Split depth to aspect") + { + ImageState state(image, imageInfo, eFrameRef_None); + + ImageSubresourceRange sliceRange(imageInfo.FullRange()); + sliceRange.baseDepthSlice = 0; + sliceRange.sliceCount = 1; + state.RecordUse(sliceRange, eFrameRef_CompleteWrite, 3); + CheckSubresourceRanges(state, false, false, false, true); + + ImageSubresourceRange layerRange(imageInfo.FullRange()); + layerRange.baseArrayLayer = 0; + layerRange.layerCount = 1; + state.RecordUse(layerRange, eFrameRef_Read, 2); + CheckSubresourceRanges(state, false, false, true, true); + + ImageSubresourceRange levelRange(imageInfo.FullRange()); + levelRange.baseMipLevel = 0; + levelRange.levelCount = 1; + state.RecordUse(levelRange, eFrameRef_PartialWrite, 1); + CheckSubresourceRanges(state, false, true, true, true); + + ImageSubresourceRange aspectRange(imageInfo.FullRange()); + aspectRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + state.RecordUse(aspectRange, eFrameRef_Read, 0); + CheckSubresourceRanges(state, true, true, true, true); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(sliceRange.baseDepthSlice <= it->range().baseDepthSlice && + it->range().baseDepthSlice - sliceRange.baseDepthSlice < sliceRange.sliceCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_CompleteWrite); + substate.newQueueFamilyIndex = 3; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(layerRange.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - layerRange.baseArrayLayer < layerRange.layerCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_Read); + substate.newQueueFamilyIndex = 2; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(levelRange.baseMipLevel <= it->range().baseMipLevel && + it->range().baseMipLevel - levelRange.baseMipLevel < levelRange.levelCount) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_PartialWrite); + substate.newQueueFamilyIndex = 1; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + if(it->range().aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + { + substate.refType = ComposeFrameRefs(substate.refType, eFrameRef_Read); + substate.newQueueFamilyIndex = 0; + if(substate.oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex; + } + + CheckSubresourceState(it->state(), substate); + } + }; + + SECTION("Single barrier") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range(imageInfo.FullRange()); + range.baseArrayLayer = 1; + range.layerCount = 1; + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ 0, + /* dstAccessMask = */ 0, + /* oldLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, + /* newLayout = */ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /* srcQueueFamilyIndex = */ 0, + /* dstQueueFamilyIndex = */ 0, + /* image = */ image, + /* subresourceRange = */ range, + }; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(range.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - range.baseArrayLayer < range.layerCount) + { + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex = 0; + substate.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + substate.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + CheckSubresourceState(it->state(), substate); + } + }; + + SECTION("Layout barriers") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range(imageInfo.FullRange()); + range.baseArrayLayer = 0; + range.layerCount = 1; + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ 0, + /* dstAccessMask = */ 0, + /* oldLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, + /* newLayout = */ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /* srcQueueFamilyIndex = */ 0, + /* dstQueueFamilyIndex = */ 0, + /* image = */ image, + /* subresourceRange = */ range, + }; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + barrier.subresourceRange.baseArrayLayer = 1; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + barrier.subresourceRange.baseArrayLayer = range.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = range.layerCount = 2; + barrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(range.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - range.baseArrayLayer < range.layerCount) + { + substate.oldQueueFamilyIndex = substate.newQueueFamilyIndex = 0; + substate.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + substate.newLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + CheckSubresourceState(it->state(), substate); + } + }; + + SECTION("Unmatched queue family acquire") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range(imageInfo.FullRange()); + range.baseArrayLayer = 1; + range.layerCount = 2; + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ 0, + /* dstAccessMask = */ 0, + /* oldLayout = */ VK_IMAGE_LAYOUT_GENERAL, + /* newLayout = */ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /* srcQueueFamilyIndex = */ 0, + /* dstQueueFamilyIndex = */ 1, + /* image = */ image, + /* subresourceRange = */ range, + }; + state.RecordBarrier(barrier, 1, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(range.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - range.baseArrayLayer < range.layerCount) + { + substate.oldQueueFamilyIndex = 0; + substate.newQueueFamilyIndex = 1; + substate.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + substate.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + CheckSubresourceState(it->state(), substate); + } + + REQUIRE(state.oldQueueFamilyTransfers.size() == 1); + CHECK(state.oldQueueFamilyTransfers[0].oldLayout == VK_IMAGE_LAYOUT_GENERAL); + CHECK(state.oldQueueFamilyTransfers[0].newLayout == + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + CHECK(state.oldQueueFamilyTransfers[0].srcQueueFamilyIndex == 0); + CHECK(state.oldQueueFamilyTransfers[0].dstQueueFamilyIndex == 1); + + CHECK(state.newQueueFamilyTransfers.size() == 0); + }; + + SECTION("Unmatched queue family release") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range(imageInfo.FullRange()); + range.baseArrayLayer = 1; + range.layerCount = 2; + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ 0, + /* dstAccessMask = */ 0, + /* oldLayout = */ VK_IMAGE_LAYOUT_GENERAL, + /* newLayout = */ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /* srcQueueFamilyIndex = */ 0, + /* dstQueueFamilyIndex = */ 1, + /* image = */ image, + /* subresourceRange = */ range, + }; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, false, false); + CheckSubresourceState(state.subresourceStates.begin()->state(), initSubstate); + + REQUIRE(state.newQueueFamilyTransfers.size() == 1); + CHECK(state.newQueueFamilyTransfers[0].oldLayout == VK_IMAGE_LAYOUT_GENERAL); + CHECK(state.newQueueFamilyTransfers[0].newLayout == + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + CHECK(state.newQueueFamilyTransfers[0].srcQueueFamilyIndex == 0); + CHECK(state.newQueueFamilyTransfers[0].dstQueueFamilyIndex == 1); + + CHECK(state.oldQueueFamilyTransfers.size() == 0); + }; + + SECTION("Matched queue family transfer") + { + ImageState state(image, imageInfo, eFrameRef_None); + ImageSubresourceRange range(imageInfo.FullRange()); + range.baseArrayLayer = 1; + range.layerCount = 2; + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ 0, + /* dstAccessMask = */ 0, + /* oldLayout = */ VK_IMAGE_LAYOUT_GENERAL, + /* newLayout = */ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + /* srcQueueFamilyIndex = */ 0, + /* dstQueueFamilyIndex = */ 1, + /* image = */ image, + /* subresourceRange = */ range, + }; + state.RecordBarrier(barrier, 0, transitionInfo); + CheckSubresourceRanges(state, false, false, false, false); + state.RecordBarrier(barrier, 1, transitionInfo); + CheckSubresourceRanges(state, false, false, true, false); + + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + ImageSubresourceState substate(initSubstate); + if(range.baseArrayLayer <= it->range().baseArrayLayer && + it->range().baseArrayLayer - range.baseArrayLayer < range.layerCount) + { + substate.oldQueueFamilyIndex = 0; + substate.newQueueFamilyIndex = 1; + substate.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + substate.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + CheckSubresourceState(it->state(), substate); + } + + CHECK(state.oldQueueFamilyTransfers.size() == 0); + + CHECK(state.newQueueFamilyTransfers.size() == 0); + }; + + SECTION("Unsplit aspects") + { + ImageState state(image, imageInfo, eFrameRef_None); + + // read subresource, triggering a split in every dimension except depth + ImageSubresourceRange range0 = imageInfo.FullRange(); + range0.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + range0.baseMipLevel = 1; + range0.levelCount = imageInfo.levelCount - 1; + range0.baseArrayLayer = 1; + range0.layerCount = imageInfo.layerCount - 1; + range0.baseDepthSlice = 0; + range0.sliceCount = imageInfo.extent.depth; + state.RecordUse(range0, eFrameRef_Read, 0); + + // read all aspects + ImageSubresourceRange range1 = range0; + range1.aspectMask = imageInfo.Aspects(); + state.RecordUse(range1, eFrameRef_Read, 0); + + state.subresourceStates.Unsplit(); + + CheckSubresourceRanges(state, false, true, true, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().baseMipLevel > 0 && it->range().baseArrayLayer > 0) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Unsplit mip levels") + { + ImageState state(image, imageInfo, eFrameRef_None); + + // read subresource, triggering a split in every dimension except aspect + ImageSubresourceRange range0 = imageInfo.FullRange(); + range0.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + range0.baseMipLevel = 1; + range0.levelCount = imageInfo.levelCount - 1; + range0.baseArrayLayer = 1; + range0.layerCount = imageInfo.layerCount - 1; + range0.baseDepthSlice = 1; + range0.sliceCount = imageInfo.extent.depth - 1; + state.RecordUse(range0, eFrameRef_Read, 0); + + // read all mip levels + ImageSubresourceRange range1 = range0; + range1.baseMipLevel = 0; + range1.levelCount = imageInfo.levelCount; + state.RecordUse(range1, eFrameRef_Read, 0); + + state.subresourceStates.Unsplit(); + + CheckSubresourceRanges(state, false, false, true, true); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().baseArrayLayer > 0 && it->range().baseDepthSlice > 0) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Unsplit array layers") + { + ImageState state(image, imageInfo, eFrameRef_None); + + // read subresource, triggering a split in every dimension except mip levels + ImageSubresourceRange range0 = imageInfo.FullRange(); + range0.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + range0.baseMipLevel = 0; + range0.levelCount = imageInfo.levelCount; + range0.baseArrayLayer = 1; + range0.layerCount = imageInfo.layerCount - 1; + range0.baseDepthSlice = 1; + range0.sliceCount = imageInfo.extent.depth - 1; + state.RecordUse(range0, eFrameRef_Read, 0); + + // read all array layers + ImageSubresourceRange range1 = range0; + range1.baseArrayLayer = 0; + range1.layerCount = imageInfo.layerCount; + state.RecordUse(range1, eFrameRef_Read, 0); + + state.subresourceStates.Unsplit(); + + CheckSubresourceRanges(state, true, false, false, true); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && it->range().baseDepthSlice > 0) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Unsplit depth slices") + { + ImageState state(image, imageInfo, eFrameRef_None); + + // read subresource, triggering a split in every dimension except array layers + ImageSubresourceRange range0 = imageInfo.FullRange(); + range0.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + range0.baseMipLevel = 1; + range0.levelCount = imageInfo.levelCount - 1; + range0.baseArrayLayer = 0; + range0.layerCount = imageInfo.layerCount; + range0.baseDepthSlice = 1; + range0.sliceCount = imageInfo.extent.depth - 1; + state.RecordUse(range0, eFrameRef_Read, 0); + + // read all depth slices + ImageSubresourceRange range1 = range0; + range1.baseDepthSlice = 0; + range1.sliceCount = imageInfo.extent.depth; + state.RecordUse(range1, eFrameRef_Read, 0); + + state.subresourceStates.Unsplit(); + + CheckSubresourceRanges(state, true, true, false, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + if(it->range().aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT && it->range().baseMipLevel > 0) + CheckSubresourceState(it->state(), readSubstate); + else + CheckSubresourceState(it->state(), initSubstate); + } + }; + + SECTION("Unsplit all") + { + ImageState state(image, imageInfo, eFrameRef_None); + + // read subresource, triggering a split in every dimension + ImageSubresourceRange range0 = imageInfo.FullRange(); + range0.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + range0.baseMipLevel = 1; + range0.levelCount = imageInfo.levelCount - 1; + range0.baseArrayLayer = 1; + range0.layerCount = imageInfo.layerCount - 1; + range0.baseDepthSlice = 1; + range0.sliceCount = imageInfo.extent.depth - 1; + state.RecordUse(range0, eFrameRef_Read, 0); + + // read all subresources + ImageSubresourceRange range1 = imageInfo.FullRange(); + state.RecordUse(range1, eFrameRef_Read, 0); + + state.subresourceStates.Unsplit(); + + CheckSubresourceRanges(state, false, false, false, false); + for(auto it = state.subresourceStates.begin(); it != state.subresourceStates.end(); ++it) + { + CheckSubresourceState(it->state(), readSubstate); + } + }; +}; + +#endif // ENABLED(ENABLE_UNIT_TESTS) diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj index d09ab8a5e..f1c0e5ce6 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj @@ -100,6 +100,7 @@ + Create diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters index 0850c0f75..176b58798 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters @@ -145,6 +145,9 @@ Replay + + Util + Util diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index bef90443d..186ac02a5 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -370,6 +370,13 @@ VkAccessFlags MakeAccessMask(VkImageLayout layout) return VkAccessFlags(0); } +void SanitiseReplayImageLayout(VkImageLayout &layout) +{ + // we don't replay with present layouts since we don't create actual swapchains. So change any + // present layouts to general layouts + if(layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || layout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) + layout = VK_IMAGE_LAYOUT_GENERAL; +} void SanitiseOldImageLayout(VkImageLayout &layout) { diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 6073edfa7..0c2cefd3f 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -91,6 +91,7 @@ VkAccessFlags MakeAccessMask(VkImageLayout layout); void SanitiseOldImageLayout(VkImageLayout &layout); void SanitiseNewImageLayout(VkImageLayout &layout); +void SanitiseReplayImageLayout(VkImageLayout &layout); void CombineDepthStencilLayouts(rdcarray &barriers); diff --git a/renderdoc/driver/vulkan/vk_image_states.cpp b/renderdoc/driver/vulkan/vk_image_states.cpp index 7b85d02cf..7ed0b7a07 100644 --- a/renderdoc/driver/vulkan/vk_image_states.cpp +++ b/renderdoc/driver/vulkan/vk_image_states.cpp @@ -24,6 +24,655 @@ #include "vk_resources.h" +ImageSubresourceRange ImageInfo::FullRange() const +{ + return ImageSubresourceRange( + /* aspectMask = */ Aspects(), + /* baseMipLevel = */ 0u, + /* levelCount = */ (uint32_t)levelCount, + /* baseArrayLayer = */ 0u, + /* layerCount = */ (uint32_t)layerCount, + /* baseDepthSlice = */ 0u, + /* sliceCount = */ extent.depth); +} + +void ImageSubresourceState::Update(const ImageSubresourceState &other, FrameRefCompFunc compose) +{ + if(oldQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + oldQueueFamilyIndex = other.oldQueueFamilyIndex; + + if(other.newQueueFamilyIndex != VK_QUEUE_FAMILY_IGNORED) + newQueueFamilyIndex = other.newQueueFamilyIndex; + + if(oldLayout == UNKNOWN_PREV_IMG_LAYOUT) + oldLayout = other.oldLayout; + + if(other.newLayout != UNKNOWN_PREV_IMG_LAYOUT) + newLayout = other.newLayout; + + refType = compose(refType, other.refType); +} + +bool ImageSubresourceState::Update(const ImageSubresourceState &other, + ImageSubresourceState &result, FrameRefCompFunc compose) const +{ + result = *this; + result.Update(other, compose); + + return result != *this; +} + +template +typename ImageSubresourceMap::SubresourceRangeIterTemplate + &ImageSubresourceMap::SubresourceRangeIterTemplate::operator++() +{ + if(!IsValid()) + return *this; + FixSubRange(); + + ++m_slice; + if(IsDepthSplit(m_splitFlags) && m_slice < m_range.baseDepthSlice + m_range.sliceCount) + { + m_value.m_range.baseDepthSlice = m_slice; + return *this; + } + m_value.m_range.baseDepthSlice = m_slice = m_range.baseDepthSlice; + + ++m_layer; + if(AreLayersSplit(m_splitFlags) && m_layer < m_range.baseArrayLayer + m_range.layerCount) + { + m_value.m_range.baseArrayLayer = m_layer; + return *this; + } + m_value.m_range.baseArrayLayer = m_layer = m_range.baseArrayLayer; + + ++m_level; + if(AreLevelsSplit(m_splitFlags) && m_level < m_range.baseMipLevel + m_range.levelCount) + { + m_value.m_range.baseMipLevel = m_level; + return *this; + } + m_value.m_range.baseMipLevel = m_level = m_range.baseMipLevel; + + if(AreAspectsSplit(m_splitFlags)) + { + auto aspectIt = ImageAspectFlagIter(m_map->GetImageInfo().Aspects(), + (VkImageAspectFlagBits)m_value.m_range.aspectMask); + while(true) + { + ++m_aspectIndex; + ++aspectIt; + if(aspectIt == ImageAspectFlagIter::end()) + { + break; + } + else if(m_range.aspectMask & *aspectIt) + { + m_value.m_range.aspectMask = *aspectIt; + return *this; + } + } + } + + // iterator is at the end. + // make `m_aspectIndex` out of range to mark this. + m_aspectIndex = m_map->m_aspectCount; + return *this; +} +template + typename ImageSubresourceMap::SubresourceRangeIterTemplate + &ImageSubresourceMap::SubresourceRangeIterTemplate< + ImageSubresourceMap, ImageSubresourceMap::SubresourcePairRef>::operator++(); +template typename ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef> + &ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef>::operator++(); + +void ImageSubresourceMap::Split(bool splitAspects, bool splitLevels, bool splitLayers, bool splitDepth) +{ + uint16_t newFlags = m_flags; + if(splitAspects) + newFlags |= (uint16_t)FlagBits::AreAspectsSplit; + else + splitAspects = AreAspectsSplit(); + + if(splitLevels) + newFlags |= (uint16_t)FlagBits::AreLevelsSplit; + else + splitLevels = AreLevelsSplit(); + + if(splitLayers) + newFlags |= (uint16_t)FlagBits::AreLayersSplit; + else + splitLayers = AreLayersSplit(); + + if(splitDepth) + newFlags |= (uint16_t)FlagBits::IsDepthSplit; + else + splitDepth = IsDepthSplit(); + + if(newFlags == m_flags) + // not splitting anything new + return; + + uint32_t oldSplitAspectCount = AreAspectsSplit() ? m_aspectCount : 1; + uint32_t newSplitAspectCount = splitAspects ? m_aspectCount : oldSplitAspectCount; + + uint32_t oldSplitLevelCount = AreLevelsSplit() ? GetImageInfo().levelCount : 1; + uint32_t newSplitLevelCount = splitLevels ? GetImageInfo().levelCount : oldSplitLevelCount; + + uint32_t oldSplitLayerCount = AreLayersSplit() ? GetImageInfo().layerCount : 1; + uint32_t newSplitLayerCount = splitLayers ? GetImageInfo().layerCount : oldSplitLayerCount; + + uint32_t oldSplitSliceCount = IsDepthSplit() ? GetImageInfo().extent.depth : 1; + uint32_t newSplitSliceCount = splitDepth ? GetImageInfo().extent.depth : oldSplitSliceCount; + + uint32_t oldSize = (uint32_t)m_values.size(); + RDCASSERT(oldSize > 0); + + uint32_t newSize = + newSplitAspectCount * newSplitLevelCount * newSplitLayerCount * newSplitSliceCount; + RDCASSERT(newSize > oldSize); + + m_values.resize(newSize); + + uint32_t newAspectIndex = newSplitAspectCount - 1; + uint32_t oldAspectIndex = AreAspectsSplit() ? newAspectIndex : 0; + uint32_t newLevel = newSplitLevelCount - 1; + uint32_t oldLevel = AreLevelsSplit() ? newLevel : 0; + uint32_t newLayer = newSplitLayerCount - 1; + uint32_t oldLayer = AreLayersSplit() ? newLayer : 0; + uint32_t newSlice = newSplitSliceCount - 1; + uint32_t oldSlice = IsDepthSplit() ? newSlice : 0; + uint32_t newIndex = newSize - 1; + while(true) + { + uint32_t oldIndex = + ((oldAspectIndex * oldSplitLevelCount + oldLevel) * oldSplitLayerCount + oldLayer) * + oldSplitSliceCount + + oldSlice; + m_values[newIndex] = m_values[oldIndex]; + + if(newIndex == 0) + { + RDCASSERT(oldIndex == 0); + break; + } + --newIndex; + + if(newSlice > 0) + { + --newSlice; + oldSlice = IsDepthSplit() ? newSlice : 0; + continue; + } + newSlice = newSplitSliceCount - 1; + oldSlice = oldSplitSliceCount - 1; + + if(newLayer > 0) + { + --newLayer; + oldLayer = AreLayersSplit() ? newLayer : 0; + continue; + } + newLayer = newSplitLayerCount - 1; + oldLayer = oldSplitLayerCount - 1; + + if(newLevel > 0) + { + --newLevel; + oldLevel = AreLevelsSplit() ? newLevel : 0; + continue; + } + newLevel = newSplitLevelCount - 1; + oldLevel = oldSplitLevelCount - 1; + + if(newAspectIndex > 0) + { + --newAspectIndex; + oldAspectIndex = AreAspectsSplit() ? newAspectIndex : 0; + continue; + } + RDCERR("Too many subresources in ImageSubresourceMap::Split"); + break; + } + + m_flags = newFlags; +} + +void ImageSubresourceMap::Unsplit(bool unsplitAspects, bool unsplitLevels, bool unsplitLayers, + bool unsplitDepth) +{ + uint16_t newFlags = m_flags; + if(unsplitAspects) + newFlags &= ~(uint16_t)FlagBits::AreAspectsSplit; + + if(unsplitLevels) + newFlags &= ~(uint16_t)FlagBits::AreLevelsSplit; + + if(unsplitLayers) + newFlags &= ~(uint16_t)FlagBits::AreLayersSplit; + + if(unsplitDepth) + newFlags &= ~(uint16_t)FlagBits::IsDepthSplit; + + if(newFlags == m_flags) + // not splitting anything new + return; + + uint32_t oldSplitAspectCount = AreAspectsSplit() ? m_aspectCount : 1; + uint32_t newSplitAspectCount = unsplitAspects ? 1 : oldSplitAspectCount; + + uint32_t oldSplitLevelCount = AreLevelsSplit() ? GetImageInfo().levelCount : 1; + uint32_t newSplitLevelCount = unsplitLevels ? 1 : oldSplitLevelCount; + + uint32_t oldSplitLayerCount = AreLayersSplit() ? GetImageInfo().layerCount : 1; + uint32_t newSplitLayerCount = unsplitLayers ? 1 : oldSplitLayerCount; + + uint32_t oldSplitSliceCount = IsDepthSplit() ? GetImageInfo().extent.depth : 1; + uint32_t newSplitSliceCount = unsplitDepth ? 1 : oldSplitSliceCount; + + uint32_t oldSize = (uint32_t)m_values.size(); + RDCASSERT(oldSize > 0); + + uint32_t newSize = + newSplitAspectCount * newSplitLevelCount * newSplitLayerCount * newSplitSliceCount; + RDCASSERT(newSize < oldSize); + + rdcarray newValues; + newValues.resize(newSize); + + uint32_t aspectIndex = 0; + uint32_t level = 0; + uint32_t layer = 0; + uint32_t slice = 0; + uint32_t newIndex = 0; + + while(newIndex < newValues.size()) + { + uint32_t oldIndex = ((aspectIndex * oldSplitLevelCount + level) * oldSplitLayerCount + layer) * + oldSplitSliceCount + + slice; + newValues[newIndex] = m_values[oldIndex]; + + ++newIndex; + + ++slice; + if(slice < newSplitSliceCount) + continue; + slice = 0; + + ++layer; + if(layer < newSplitLayerCount) + continue; + layer = 0; + + ++level; + if(level < newSplitLevelCount) + continue; + level = 0; + + ++aspectIndex; + } + + newValues.swap(m_values); + m_flags = newFlags; +} + +void ImageSubresourceMap::Unsplit() +{ + if(m_values.size() == 1) + return; + + uint32_t aspectCount = AreAspectsSplit() ? m_aspectCount : 1; + uint32_t aspectIndex = 0; + uint32_t levelCount = AreLevelsSplit() ? m_imageInfo.levelCount : 1; + uint32_t level = 0; + uint32_t layerCount = AreLayersSplit() ? m_imageInfo.layerCount : 1; + uint32_t layer = 0; + uint32_t sliceCount = IsDepthSplit() ? m_imageInfo.extent.depth : 1; + uint32_t slice = 0; + uint32_t index = 0; + + bool canUnsplitAspects = aspectCount > 1; + bool canUnsplitLevels = levelCount > 1; + bool canUnsplitLayers = layerCount > 1; + bool canUnsplitDepth = sliceCount > 1; + + RDCASSERT(aspectCount * levelCount * layerCount * sliceCount == m_values.size()); +#define UNSPLIT_INDEX(ASPECT, LEVEL, LAYER, SLICE) \ + (((ASPECT * levelCount + LEVEL) * layerCount + LAYER) * sliceCount + SLICE) + while(index < m_values.size() && + (canUnsplitAspects || canUnsplitLevels || canUnsplitLayers || canUnsplitDepth)) + { + if(canUnsplitAspects && aspectIndex > 0) + { + uint32_t index0 = UNSPLIT_INDEX(0, level, layer, slice); + if(m_values[index] != m_values[index0]) + canUnsplitAspects = false; + } + if(canUnsplitLevels && level > 0) + { + uint32_t index0 = UNSPLIT_INDEX(aspectIndex, 0, layer, slice); + if(m_values[index] != m_values[index0]) + canUnsplitLevels = false; + } + if(canUnsplitLayers && layer > 0) + { + uint32_t index0 = UNSPLIT_INDEX(aspectIndex, level, 0, slice); + if(m_values[index] != m_values[index0]) + canUnsplitLayers = false; + } + if(canUnsplitDepth && slice > 0) + { + uint32_t index0 = UNSPLIT_INDEX(aspectIndex, level, layer, 0); + if(m_values[index] != m_values[index0]) + canUnsplitDepth = false; + } + + ++index; + + ++slice; + if(slice < sliceCount) + continue; + slice = 0; + + ++layer; + if(layer < layerCount) + continue; + layer = 0; + + ++level; + if(level < levelCount) + continue; + level = 0; + + ++aspectIndex; + if(aspectIndex >= aspectCount) + break; + } +#undef UNSPLIT_INDEX + + Unsplit(canUnsplitAspects, canUnsplitLevels, canUnsplitLayers, canUnsplitDepth); +} + +inline FrameRefType ImageSubresourceMap::Merge(const ImageSubresourceMap &other, + FrameRefCompFunc compose) +{ + FrameRefType maxRefType = eFrameRef_None; + bool didSplit = false; + for(auto oIt = other.begin(); oIt != other.end(); ++oIt) + { + for(auto it = RangeBegin(oIt->range()); it != end(); ++it) + { + ImageSubresourceState subState; + if(it->state().Update(oIt->state(), subState, compose)) + { + if(!didSplit) + { + Split(oIt->range()); + didSplit = true; + } + RDCASSERT(it->range().ContainedIn(oIt->range())); + it->SetState(subState); + maxRefType = ComposeFrameRefsDisjoint(maxRefType, subState.refType); + } + } + } + return maxRefType; +} + +size_t ImageSubresourceMap::SubresourceIndex(uint32_t aspectIndex, uint32_t level, uint32_t layer, + uint32_t slice) const +{ + if(!AreAspectsSplit()) + aspectIndex = 0; + int splitLevelCount = 1; + if(AreLevelsSplit()) + splitLevelCount = GetImageInfo().levelCount; + else + level = 0; + int splitLayerCount = 1; + if(AreLayersSplit()) + splitLayerCount = GetImageInfo().layerCount; + else + layer = 0; + int splitSliceCount = 1; + if(IsDepthSplit()) + splitSliceCount = GetImageInfo().extent.depth; + else + slice = 0; + return ((aspectIndex * splitLevelCount + level) * splitLayerCount + layer) * splitSliceCount + + slice; +} + +bool IntervalsOverlap(uint32_t base1, uint32_t count1, uint32_t base2, uint32_t count2) +{ + if((base1 + count1) < base1) + { + // integer overflow + if(count1 != VK_REMAINING_MIP_LEVELS) + RDCWARN("Integer overflow in interval: base=%u, count=%u", base1, count1); + count1 = UINT32_MAX - base1; + } + if((base2 + count2) < base2) + { + // integer overflow + if(count2 != VK_REMAINING_MIP_LEVELS) + RDCWARN("Integer overflow in interval: base=%u, count=%u", base2, count2); + count2 = UINT32_MAX - base2; + } + if(count1 == 0 || count2 == 0) + return false; // one of the intervals is empty, so no overlap + if(base1 > base2) + { + std::swap(base1, base2); + std::swap(count1, count2); + } + return base2 < base1 + count1; +} + +bool IntervalContainedIn(uint32_t base1, uint32_t count1, uint32_t base2, uint32_t count2) +{ + if((base1 + count1) < base1) + { + // integer overflow + if(count1 != VK_REMAINING_MIP_LEVELS) + RDCWARN("Integer overflow in interval: base=%u, count=%u", base1, count1); + count1 = UINT32_MAX - base1; + } + if((base2 + count2) < base2) + { + // integer overflow + if(count2 != VK_REMAINING_MIP_LEVELS) + RDCWARN("Integer overflow in interval: base=%u, count=%u", base2, count2); + count2 = UINT32_MAX - base2; + } + return base1 >= base2 && base1 + count1 <= base2 + count2; +} + +bool SanitiseLevelRange(uint32_t &baseMipLevel, uint32_t &levelCount, uint32_t imageLevelCount) +{ + bool res = true; + if(baseMipLevel > imageLevelCount) + { + RDCWARN("baseMipLevel (%u) is greater than image levelCount (%u)", baseMipLevel, imageLevelCount); + baseMipLevel = imageLevelCount; + res = false; + } + if(levelCount == VK_REMAINING_MIP_LEVELS) + { + levelCount = imageLevelCount - baseMipLevel; + } + else if(levelCount > imageLevelCount - baseMipLevel) + { + RDCWARN("baseMipLevel (%u) + levelCount (%u) is greater than the image levelCount (%u)", + baseMipLevel, levelCount, imageLevelCount); + levelCount = imageLevelCount - baseMipLevel; + res = false; + } + return res; +} + +bool SanitiseLayerRange(uint32_t &baseArrayLayer, uint32_t &layerCount, uint32_t imageLayerCount) +{ + bool res = true; + if(baseArrayLayer > imageLayerCount) + { + RDCWARN("baseArrayLayer (%u) is greater than image layerCount (%u)", baseArrayLayer, + imageLayerCount); + baseArrayLayer = imageLayerCount; + res = false; + } + if(layerCount == VK_REMAINING_ARRAY_LAYERS) + { + layerCount = imageLayerCount - baseArrayLayer; + } + else if(layerCount > imageLayerCount - baseArrayLayer) + { + RDCWARN("baseArrayLayer (%u) + layerCount (%u) is greater than the image layerCount (%u)", + baseArrayLayer, layerCount, imageLayerCount); + layerCount = imageLayerCount - baseArrayLayer; + res = false; + } + return res; +} + +bool SanitiseSliceRange(uint32_t &baseSlice, uint32_t &sliceCount, uint32_t imageSliceCount) +{ + bool res = true; + if(baseSlice > imageSliceCount) + { + RDCWARN("baseSlice (%u) is greater than image sliceCount (%u)", baseSlice, imageSliceCount); + baseSlice = imageSliceCount; + res = false; + } + if(sliceCount == VK_REMAINING_ARRAY_LAYERS) + { + sliceCount = imageSliceCount - baseSlice; + } + else if(sliceCount > imageSliceCount - baseSlice) + { + RDCWARN("baseSlice (%u) + sliceCount (%u) is greater than the image sliceCount (%u)", baseSlice, + sliceCount, imageSliceCount); + sliceCount = imageSliceCount - baseSlice; + res = false; + } + return res; +} + +template +ImageSubresourceMap::SubresourceRangeIterTemplate::SubresourceRangeIterTemplate( + Map &map, const ImageSubresourceRange &range) + : m_map(&map), + m_range(range), + m_level(range.baseMipLevel), + m_layer(range.baseArrayLayer), + m_slice(range.baseDepthSlice) +{ + m_range.Sanitise(m_map->GetImageInfo()); + m_splitFlags = (uint16_t)ImageSubresourceMap::FlagBits::IsUninitialized; + FixSubRange(); +} +template ImageSubresourceMap::SubresourceRangeIterTemplate:: + SubresourceRangeIterTemplate(ImageSubresourceMap &map, const ImageSubresourceRange &range); +template ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef>:: + SubresourceRangeIterTemplate(const ImageSubresourceMap &map, const ImageSubresourceRange &range); + +template +void ImageSubresourceMap::SubresourceRangeIterTemplate::FixSubRange() +{ + if(m_splitFlags == m_map->m_flags) + return; + uint16_t oldFlags = m_splitFlags; + m_splitFlags = m_map->m_flags; + + if(IsDepthSplit(m_splitFlags)) + { + m_value.m_range.baseDepthSlice = m_slice; + m_value.m_range.sliceCount = 1u; + } + else + { + m_value.m_range.baseDepthSlice = 0u; + m_value.m_range.sliceCount = m_map->GetImageInfo().extent.depth; + } + + if(AreLayersSplit(m_splitFlags)) + { + m_value.m_range.baseArrayLayer = m_layer; + m_value.m_range.layerCount = 1u; + } + else + { + m_value.m_range.baseArrayLayer = 0u; + m_value.m_range.layerCount = m_map->GetImageInfo().layerCount; + } + + if(AreLevelsSplit(m_splitFlags)) + { + m_value.m_range.baseMipLevel = m_level; + m_value.m_range.levelCount = 1u; + } + else + { + m_value.m_range.baseMipLevel = 0u; + m_value.m_range.levelCount = m_map->GetImageInfo().levelCount; + } + + if(!AreAspectsSplit(m_splitFlags)) + { + m_value.m_range.aspectMask = m_map->GetImageInfo().Aspects(); + } + else if(!AreAspectsSplit(oldFlags)) + { + // aspects are split in the map, but are not yet split in this iterator. + // We need to find the aspectMask. + uint32_t i = 0; + for(auto it = ImageAspectFlagIter::begin(m_map->GetImageInfo().Aspects()); + it != ImageAspectFlagIter::end(); ++it, ++i) + { + if(i >= m_aspectIndex && (((*it) & m_range.aspectMask) != 0)) + { + m_value.m_range.aspectMask = *it; + break; + } + } + m_aspectIndex = i; + } +} +template void ImageSubresourceMap::SubresourceRangeIterTemplate< + ImageSubresourceMap, ImageSubresourceMap::SubresourcePairRef>::FixSubRange(); +template void ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef>::FixSubRange(); + +template +Pair *ImageSubresourceMap::SubresourceRangeIterTemplate::operator->() +{ + FixSubRange(); + m_value.m_state = &m_map->SubresourceValue(m_aspectIndex, m_level, m_layer, m_slice); + return &m_value; +} +template ImageSubresourceMap::SubresourcePairRef *ImageSubresourceMap::SubresourceRangeIterTemplate< + ImageSubresourceMap, ImageSubresourceMap::SubresourcePairRef>::operator->(); +template ImageSubresourceMap::ConstSubresourcePairRef *ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef>::operator->(); + +template +Pair &ImageSubresourceMap::SubresourceRangeIterTemplate::operator*() +{ + FixSubRange(); + m_value.m_state = &m_map->SubresourceValue(m_aspectIndex, m_level, m_layer, m_slice); + return m_value; +} +template ImageSubresourceMap::SubresourcePairRef &ImageSubresourceMap::SubresourceRangeIterTemplate< + ImageSubresourceMap, ImageSubresourceMap::SubresourcePairRef>::operator*(); +template ImageSubresourceMap::ConstSubresourcePairRef &ImageSubresourceMap::SubresourceRangeIterTemplate< + const ImageSubresourceMap, ImageSubresourceMap::ConstSubresourcePairRef>::operator*(); + template void BarrierSequence::AddWrapped(uint32_t batchIndex, uint32_t queueFamilyIndex, const Barrier &barrier) @@ -132,3 +781,655 @@ void BarrierSequence::ExtractLastUnwrappedBatchForQueue(uint32_t queueF } template void BarrierSequence::ExtractLastUnwrappedBatchForQueue( uint32_t queueFamilyIndex, rdcarray &result); + +ImageState ImageState::InitialState() const +{ + ImageState result(wrappedHandle, GetImageInfo(), eFrameRef_Unknown); + InitialState(result); + return result; +} + +void ImageState::InitialState(ImageState &result) const +{ + result.subresourceStates = subresourceStates; + for(auto it = result.subresourceStates.begin(); it != result.subresourceStates.end(); ++it) + { + ImageSubresourceState &sub = it->state(); + sub.newLayout = sub.oldLayout = GetImageInfo().initialLayout; + sub.newQueueFamilyIndex = sub.oldQueueFamilyIndex; + sub.refType = eFrameRef_Unknown; + } +} + +ImageState ImageState::CommandBufferInitialState() const +{ + ImageSubresourceState sub; + sub.oldLayout = sub.newLayout = UNKNOWN_PREV_IMG_LAYOUT; + return UniformState(sub); +} + +ImageState ImageState::UniformState(const ImageSubresourceState &sub) const +{ + ImageState result(wrappedHandle, GetImageInfo(), eFrameRef_None); + result.subresourceStates.begin()->SetState(sub); + return result; +} + +ImageState ImageState::ContentInitializationState(InitPolicy policy, bool initialized, + uint32_t queueFamilyIndex, VkImageLayout copyLayout, + VkImageLayout clearLayout) const +{ + ImageState result = *this; + for(auto it = result.subresourceStates.begin(); it != result.subresourceStates.end(); ++it) + { + ImageSubresourceState &sub = it->state(); + InitReqType initReq = InitReq(sub.refType, policy, initialized); + if(initReq == eInitReq_None) + continue; + sub.newQueueFamilyIndex = queueFamilyIndex; + if(initReq == eInitReq_Copy) + sub.newLayout = copyLayout; + else if(initReq == eInitReq_Clear) + sub.newLayout = clearLayout; + } + return result; +} + +void ImageState::RemoveQueueFamilyTransfer(VkImageMemoryBarrier *it) +{ + if(it < newQueueFamilyTransfers.begin() || it >= newQueueFamilyTransfers.end()) + RDCERR("Attempting to remove queue family transfer at invalid address"); + std::swap(*it, newQueueFamilyTransfers.back()); + newQueueFamilyTransfers.erase(newQueueFamilyTransfers.size() - 1); +} + +void ImageState::Update(ImageSubresourceRange range, const ImageSubresourceState &dst, + FrameRefCompFunc compose) +{ + range.Sanitise(GetImageInfo()); + + bool didSplit = false; + for(auto it = subresourceStates.RangeBegin(range); it != subresourceStates.end(); ++it) + { + ImageSubresourceState subState; + if(it->state().Update(dst, subState, compose)) + { + if(!didSplit) + { + subresourceStates.Split(range); + didSplit = true; + } + RDCASSERT(it->range().ContainedIn(range)); + it->SetState(subState); + maxRefType = ComposeFrameRefsDisjoint(maxRefType, subState.refType); + } + } +} + +void ImageState::Merge(const ImageState &other, ImageTransitionInfo info) +{ + if(wrappedHandle == VK_NULL_HANDLE) + wrappedHandle = other.wrappedHandle; + for(auto it = other.oldQueueFamilyTransfers.begin(); it != other.oldQueueFamilyTransfers.end(); ++it) + { + RecordQueueFamilyAcquire(*it); + } + maxRefType = subresourceStates.Merge(other.subresourceStates, info.GetFrameRefCompFunc()); + for(auto it = other.newQueueFamilyTransfers.begin(); it != other.newQueueFamilyTransfers.end(); ++it) + { + RecordQueueFamilyRelease(*it); + } +} + +void ImageState::MergeCaptureBeginState(const ImageState &initialState) +{ + oldQueueFamilyTransfers = initialState.oldQueueFamilyTransfers; + subresourceStates = initialState.subresourceStates; + maxRefType = initialState.maxRefType; +} + +void ImageState::Merge(std::map &states, + const std::map &dstStates, ImageTransitionInfo info) +{ + auto it = states.begin(); + auto dstIt = dstStates.begin(); + while(dstIt != dstStates.end()) + { + if(it == states.end() || dstIt->first < it->first) + { + it = states.insert(it, {dstIt->first, dstIt->second.InitialState()}); + } + else if(it->first < dstIt->first) + { + ++it; + continue; + } + + it->second.Merge(dstIt->second, info); + ++it; + ++dstIt; + } +} + +void ImageState::DiscardContents(const ImageSubresourceRange &range) +{ + Update(range, ImageSubresourceState(VK_QUEUE_FAMILY_IGNORED, VK_IMAGE_LAYOUT_UNDEFINED), + KeepOldFrameRef); +} + +void ImageState::RecordQueueFamilyRelease(const VkImageMemoryBarrier &barrier) +{ + for(auto it = newQueueFamilyTransfers.begin(); it != newQueueFamilyTransfers.end(); ++it) + { + if(ImageSubresourceRange(barrier.subresourceRange).Overlaps(it->subresourceRange)) + { + RDCWARN("Queue family release barriers overlap"); + RemoveQueueFamilyTransfer(it); + --it; + } + } + newQueueFamilyTransfers.push_back(barrier); +} + +void ImageState::RecordQueueFamilyAcquire(const VkImageMemoryBarrier &barrier) +{ + bool foundRelease = false; + ImageSubresourceRange acquireRange(barrier.subresourceRange); + for(auto it = newQueueFamilyTransfers.begin(); it != newQueueFamilyTransfers.end(); ++it) + { + ImageSubresourceRange releaseRange(it->subresourceRange); + if(acquireRange.Overlaps(releaseRange)) + { + if(acquireRange != releaseRange) + RDCWARN( + "Overlapping queue family release and acquire barriers have different " + "subresourceRange"); + if(barrier.srcQueueFamilyIndex != it->srcQueueFamilyIndex || + barrier.dstQueueFamilyIndex != it->dstQueueFamilyIndex) + RDCWARN("Queue family mismatch between release and acquire barriers"); + if(barrier.oldLayout != it->oldLayout || barrier.newLayout != it->newLayout) + RDCWARN("Image layouts mismatch between release and acquire barriers"); + if(foundRelease) + RDCWARN("Found multiple release barriers for acquire barrier"); + RemoveQueueFamilyTransfer(it); + --it; + foundRelease = true; + } + } + if(!foundRelease) + { + oldQueueFamilyTransfers.push_back(barrier); + } +} + +void ImageState::RecordBarrier(VkImageMemoryBarrier barrier, uint32_t queueFamilyIndex, + ImageTransitionInfo info) +{ + if(barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || + barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT || + barrier.dstQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || + barrier.dstQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) + { + RDCERR("External/foreign queue families are not supported"); + return; + } + if(GetImageInfo().sharingMode == VK_SHARING_MODE_CONCURRENT) + { + if(!(barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED && + barrier.dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED)) + { + RDCWARN("Barrier contains invalid queue families for VK_SHARING_MODE_CONCURRENT"); + } + barrier.srcQueueFamilyIndex = barrier.dstQueueFamilyIndex = queueFamilyIndex; + } + else if(GetImageInfo().sharingMode == VK_SHARING_MODE_EXCLUSIVE) + { + if(barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED || + barrier.dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + if(barrier.srcQueueFamilyIndex != VK_QUEUE_FAMILY_IGNORED || + barrier.dstQueueFamilyIndex != VK_QUEUE_FAMILY_IGNORED) + { + RDCERR("Barrier contains invalid queue families for VK_SHARING_MODE_EXCLUSIVE: (%s, %s)", + ToStr(barrier.srcQueueFamilyIndex).c_str(), + ToStr(barrier.dstQueueFamilyIndex).c_str()); + return; + } + barrier.srcQueueFamilyIndex = queueFamilyIndex; + barrier.dstQueueFamilyIndex = queueFamilyIndex; + } + else if(barrier.srcQueueFamilyIndex == queueFamilyIndex) + { + if(barrier.dstQueueFamilyIndex != queueFamilyIndex) + { + RecordQueueFamilyRelease(barrier); + // Skip the updates to the subresource states. + // These will be updated by the acquire. + // This allows us to restore a released-but-not-acquired state by first transitioning to the + // subresource states (which will match the srcQueueFamilyIndex/oldLayout), and then + // applying the release barrier. + return; + } + } + else if(barrier.dstQueueFamilyIndex == queueFamilyIndex) + { + RecordQueueFamilyAcquire(barrier); + } + else + { + RDCERR("Ownership transfer from queue family %u to %u submitted to queue family %u", + barrier.srcQueueFamilyIndex, barrier.dstAccessMask, queueFamilyIndex); + } + } + + Update(barrier.subresourceRange, ImageSubresourceState(barrier), info.GetFrameRefCompFunc()); +} + +bool ImageState::CloseTransfers(uint32_t batchIndex, VkAccessFlags dstAccessMask, + ImageBarrierSequence &barriers, ImageTransitionInfo info) +{ + if(newQueueFamilyTransfers.empty()) + return false; + FrameRefCompFunc compose = info.GetFrameRefCompFunc(); + for(auto it = newQueueFamilyTransfers.begin(); it != newQueueFamilyTransfers.end(); ++it) + { + Update(it->subresourceRange, ImageSubresourceState(it->dstQueueFamilyIndex, it->newLayout), + compose); + + it->dstAccessMask = dstAccessMask; + it->image = wrappedHandle; + barriers.AddWrapped(batchIndex, it->dstQueueFamilyIndex, *it); + } + newQueueFamilyTransfers.clear(); + return true; +} + +bool ImageState::RestoreTransfers(uint32_t batchIndex, + const rdcarray &transfers, + VkAccessFlags srcAccessMask, ImageBarrierSequence &barriers, + ImageTransitionInfo info) +{ + // TODO: figure out why `transfers` has duplicate entries + if(transfers.empty()) + return false; + for(auto it = transfers.begin(); it != transfers.end(); ++it) + { + VkImageMemoryBarrier barrier = *it; + barrier.srcAccessMask = srcAccessMask; + barrier.image = wrappedHandle; + barriers.AddWrapped(batchIndex, barrier.srcQueueFamilyIndex, barrier); + RecordQueueFamilyRelease(barrier); + } + return true; +} + +void ImageState::ResetToOldState(ImageBarrierSequence &barriers, ImageTransitionInfo info) +{ + VkAccessFlags srcAccessMask = VK_ACCESS_ALL_WRITE_BITS; + VkAccessFlags dstAccessMask = VK_ACCESS_ALL_READ_BITS; + const uint32_t CLOSE_TRANSFERS_BATCH_INDEX = 0; + const uint32_t MAIN_BATCH_INDEX = 1; + const uint32_t ACQUIRE_BATCH_INDEX = 2; + const uint32_t RESTORE_TRANSFERS_BATCH_INDEX = 3; + CloseTransfers(CLOSE_TRANSFERS_BATCH_INDEX, dstAccessMask, barriers, info); + + for(auto subIt = subresourceStates.begin(); subIt != subresourceStates.end(); ++subIt) + { + VkImageLayout oldLayout = subIt->state().newLayout; + if(oldLayout == UNKNOWN_PREV_IMG_LAYOUT) + oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageLayout newLayout = subIt->state().oldLayout; + subIt->state().newLayout = subIt->state().oldLayout; + if(newLayout == UNKNOWN_PREV_IMG_LAYOUT || newLayout == VK_IMAGE_LAYOUT_UNDEFINED) + { + // contents discarded, no barrier necessary + continue; + } + SanitiseReplayImageLayout(oldLayout); + SanitiseReplayImageLayout(newLayout); + if(oldLayout != VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) + { + // Transitioning back to PREINITIALIZED; this is impossible, so transition to GENERAL instead. + newLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + uint32_t srcQueueFamilyIndex = subIt->state().newQueueFamilyIndex; + uint32_t dstQueueFamilyIndex = subIt->state().oldQueueFamilyIndex; + + if(srcQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || + srcQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) + { + srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + if(dstQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || + dstQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) + { + dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + + uint32_t submitQueueFamilyIndex = srcQueueFamilyIndex; + + if(GetImageInfo().sharingMode == VK_SHARING_MODE_EXCLUSIVE) + { + if(srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + submitQueueFamilyIndex = dstQueueFamilyIndex; + dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + else if(dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + } + else + { + if(submitQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + submitQueueFamilyIndex = dstQueueFamilyIndex; + srcQueueFamilyIndex = dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + + if(srcQueueFamilyIndex == dstQueueFamilyIndex && oldLayout == newLayout) + { + subIt->state().newQueueFamilyIndex = subIt->state().oldQueueFamilyIndex; + continue; + } + + if(submitQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + RDCWARN( + "ResetToOldState: barrier submitted to VK_QUEUE_FAMILY_IGNORED; defaulting to queue " + "family %u", + info.defaultQueueFamilyIndex); + submitQueueFamilyIndex = info.defaultQueueFamilyIndex; + } + subIt->state().newQueueFamilyIndex = subIt->state().oldQueueFamilyIndex; + + ImageSubresourceRange subRange = subIt->range(); + + if(subRange.baseDepthSlice != 0) + { + // We can't issue barriers per depth slice, so skip the barriers for non-zero depth slices. + // The zero depth slice barrier will implicitly cover the non-zerp depth slices. + continue; + } + + if((GetImageInfo().Aspects() & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == + (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + { + // This is a subresource of a depth and stencil image, and + // VK_KHR_separate_depth_stencil_layouts is not enabled, so the barrier needs to include both + // depth and stencil aspects. We skip the stencil-only aspect and expand the barrier for the + // depth-only aspect to include both depth and stencil aspects. + if(subRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) + continue; + if(subRange.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + subRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ srcAccessMask, + /* dstAccessMask = */ dstAccessMask, + /* oldLayout = */ oldLayout, + /* newLayout = */ newLayout, + /* srcQueueFamilyIndex = */ srcQueueFamilyIndex, + /* dstQueueFamilyIndex = */ dstQueueFamilyIndex, + /* image = */ wrappedHandle, + /* subresourceRange = */ subRange, + }; + barriers.AddWrapped(MAIN_BATCH_INDEX, submitQueueFamilyIndex, barrier); + + // acquire the subresource in the dstQueueFamily, if necessary + if(barrier.srcQueueFamilyIndex != barrier.dstQueueFamilyIndex) + { + barriers.AddWrapped(ACQUIRE_BATCH_INDEX, barrier.dstQueueFamilyIndex, barrier); + } + } + RestoreTransfers(RESTORE_TRANSFERS_BATCH_INDEX, oldQueueFamilyTransfers, srcAccessMask, barriers, + info); +} + +void ImageState::Transition(const ImageState &dstState, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, ImageBarrierSequence &barriers, + ImageTransitionInfo info) +{ + const uint32_t CLOSE_TRANSFERS_BATCH_INDEX = 0; + const uint32_t MAIN_BATCH_INDEX = 1; + const uint32_t ACQUIRE_BATCH_INDEX = 2; + const uint32_t RESTORE_TRANSFERS_BATCH_INDEX = 3; + CloseTransfers(CLOSE_TRANSFERS_BATCH_INDEX, dstAccessMask, barriers, info); + + for(auto dstIt = dstState.subresourceStates.begin(); dstIt != dstState.subresourceStates.end(); + ++dstIt) + { + const ImageSubresourceRange &dstRng = dstIt->range(); + const ImageSubresourceState &dstSub = dstIt->state(); + for(auto it = subresourceStates.RangeBegin(dstRng); it != subresourceStates.end(); ++it) + { + ImageSubresourceState srcSub; + if(!it->state().Update(dstSub, srcSub, info.GetFrameRefCompFunc())) + // subresource state did not change, so no need for a barrier + continue; + + subresourceStates.Split(dstRng); + std::swap(it->state(), srcSub); + + ImageSubresourceRange srcRng = it->range(); + + VkImageLayout oldLayout = srcSub.newLayout; + if(oldLayout == UNKNOWN_PREV_IMG_LAYOUT) + oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageLayout newLayout = dstSub.newLayout; + if(newLayout == UNKNOWN_PREV_IMG_LAYOUT || newLayout == VK_IMAGE_LAYOUT_UNDEFINED) + // ignore transitions to undefined + continue; + uint32_t srcQueueFamilyIndex = srcSub.newQueueFamilyIndex; + uint32_t dstQueueFamilyIndex = dstSub.newQueueFamilyIndex; + + if(oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) + // transitions from undefined discard the contents anyway, so no queue family ownership + // transfer is necessary + srcQueueFamilyIndex = dstQueueFamilyIndex; + + if(newLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && oldLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) + { + // Transitioning to PREINITIALIZED, which is invalid. This happens when we are resetting to + // an earlier image state. + // Instead, we transition to GENERAL, and make the image owned by oldQueueFamilyIndex. + newLayout = VK_IMAGE_LAYOUT_GENERAL; + dstQueueFamilyIndex = srcSub.oldQueueFamilyIndex; + RDCASSERT(dstQueueFamilyIndex != VK_QUEUE_FAMILY_IGNORED); + } + + if(IsReplayMode(info.capState)) + { + // Get rid of PRESENT layouts + SanitiseReplayImageLayout(oldLayout); + SanitiseReplayImageLayout(newLayout); + } + + uint32_t submitQueueFamilyIndex = (srcQueueFamilyIndex != VK_QUEUE_FAMILY_IGNORED) + ? srcQueueFamilyIndex + : dstQueueFamilyIndex; + if(submitQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED || + submitQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || + submitQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) + { + RDCERR("Ignoring state transition submitted to invalid queue family %u", + submitQueueFamilyIndex); + continue; + } + if(GetImageInfo().sharingMode == VK_SHARING_MODE_CONCURRENT) + { + srcQueueFamilyIndex = dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } + else + { + if(srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + RDCWARN("ImageState::Transition: src queue family == VK_QUEUE_FAMILY_IGNORED."); + srcQueueFamilyIndex = dstQueueFamilyIndex; + } + if(dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) + { + RDCWARN("ImageState::Transition: dst queue family == VK_QUEUE_FAMILY_IGNORED."); + dstQueueFamilyIndex = srcQueueFamilyIndex; + } + } + + if(srcQueueFamilyIndex == dstQueueFamilyIndex && oldLayout == newLayout) + // Skip the barriers, because it would do nothing + continue; + + if(srcRng.baseDepthSlice != 0 || dstRng.baseDepthSlice != 0) + { + // We can't issue barriers per depth slice, so skip the barriers for non-zero depth slices. + // The zero depth slice barrier will implicitly cover the non-zerp depth slices. + continue; + } + + VkImageAspectFlags aspectMask = srcRng.aspectMask & dstRng.aspectMask; + if((GetImageInfo().Aspects() & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == + (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + { + // This is a subresource of a depth and stencil image, and + // VK_KHR_separate_depth_stencil_layouts is not enabled, so the barrier needs to include + // both depth and stencil aspects. We skip the stencil-only aspect and expand the barrier + // for the depth-only aspect to include both depth and stencil aspects. + if(aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) + continue; + if(aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + uint32_t baseMipLevel = RDCMAX(dstRng.baseMipLevel, srcRng.baseMipLevel); + uint32_t endMipLevel = + RDCMIN(dstRng.baseMipLevel + dstRng.levelCount, srcRng.baseMipLevel + srcRng.levelCount); + uint32_t baseArrayLayer = RDCMAX(dstRng.baseArrayLayer, srcRng.baseArrayLayer); + uint32_t endArrayLayer = RDCMIN(dstRng.baseArrayLayer + dstRng.layerCount, + srcRng.baseArrayLayer + srcRng.layerCount); + VkImageMemoryBarrier barrier = { + /* sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + /* pNext = */ NULL, + /* srcAccessMask = */ srcAccessMask, + /* dstAccessMask = */ dstAccessMask, + /* oldLayout = */ oldLayout, + /* newLayout = */ newLayout, + /* srcQueueFamilyIndex = */ srcQueueFamilyIndex, + /* dstQueueFamilyIndex = */ dstQueueFamilyIndex, + /* image = */ wrappedHandle, + /* subresourceRange = */ + { + /* aspectMask = */ aspectMask, + /* baseMipLevel = */ baseMipLevel, + /* levelCount = */ endMipLevel - baseMipLevel, + /* baseArrayLayer = */ baseArrayLayer, + /* layerCount = */ endArrayLayer - baseArrayLayer, + }, + }; + barriers.AddWrapped(MAIN_BATCH_INDEX, submitQueueFamilyIndex, barrier); + + // acquire the subresource in the dstQueueFamily, if necessary + if(barrier.srcQueueFamilyIndex != barrier.dstQueueFamilyIndex) + { + barriers.AddWrapped(ACQUIRE_BATCH_INDEX, barrier.dstQueueFamilyIndex, barrier); + } + } + } + RestoreTransfers(RESTORE_TRANSFERS_BATCH_INDEX, dstState.newQueueFamilyTransfers, srcAccessMask, + barriers, info); +} + +void ImageState::Transition(uint32_t queueFamilyIndex, VkImageLayout layout, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, + ImageBarrierSequence &barriers, ImageTransitionInfo info) +{ + Transition(UniformState(ImageSubresourceState(queueFamilyIndex, layout)), srcAccessMask, + dstAccessMask, barriers, info); +} + +void ImageState::TempTransition(const ImageState &dstState, VkAccessFlags preSrcAccessMask, + VkAccessFlags preDstAccessMask, VkAccessFlags postSrcAccessmask, + VkAccessFlags postDstAccessMask, ImageBarrierSequence &setupBarriers, + ImageBarrierSequence &cleanupBarriers, ImageTransitionInfo info) const +{ + ImageState temp(*this); + temp.Transition(dstState, preSrcAccessMask, preDstAccessMask, setupBarriers, info); + temp.Transition(*this, postSrcAccessmask, postDstAccessMask, cleanupBarriers, info); +} + +void ImageState::TempTransition(uint32_t queueFamilyIndex, VkImageLayout layout, + VkAccessFlags accessMask, ImageBarrierSequence &setupBarriers, + ImageBarrierSequence &cleanupBarriers, ImageTransitionInfo info) const +{ + TempTransition(UniformState(ImageSubresourceState(queueFamilyIndex, layout)), + VK_ACCESS_ALL_WRITE_BITS, accessMask, accessMask, VK_ACCESS_ALL_READ_BITS, + setupBarriers, cleanupBarriers, info); +} + +void ImageState::InlineTransition(VkCommandBuffer cmd, uint32_t queueFamilyIndex, + const ImageState &dstState, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, ImageTransitionInfo info) +{ + ImageBarrierSequence barriers; + Transition(dstState, srcAccessMask, dstAccessMask, barriers, info); + if(barriers.empty()) + return; + rdcarray barriersArray; + barriers.ExtractFirstUnwrappedBatchForQueue(queueFamilyIndex, barriersArray); + if(!barriersArray.empty()) + DoPipelineBarrier(cmd, (uint32_t)barriersArray.size(), barriersArray.data()); + if(!barriers.empty()) + { + RDCERR("Could not inline all image state transition barriers"); + } +} + +void ImageState::InlineTransition(VkCommandBuffer cmd, uint32_t queueFamilyIndex, + VkImageLayout layout, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, ImageTransitionInfo info) +{ + InlineTransition(cmd, queueFamilyIndex, + UniformState(ImageSubresourceState(queueFamilyIndex, layout)), srcAccessMask, + dstAccessMask, info); +} + +InitReqType ImageState::MaxInitReq(const ImageSubresourceRange &range, InitPolicy policy, + bool initialized) const +{ + FrameRefType refType = eFrameRef_None; + for(auto it = subresourceStates.RangeBegin(range); it != subresourceStates.end(); ++it) + { + refType = ComposeFrameRefsDisjoint(refType, it->state().refType); + } + return InitReq(refType, policy, initialized); +} + +VkImageLayout ImageState::GetImageLayout(VkImageAspectFlagBits aspect, uint32_t mipLevel, + uint32_t arrayLayer) const +{ + return subresourceStates.SubresourceValue(aspect, mipLevel, arrayLayer, 0).newLayout; +} + +void ImageState::BeginCapture() +{ + maxRefType = eFrameRef_None; + + // Forget any pending queue family release operations. + // If the matching queue family acquire operation happens during the frame, + // an implicit release operation will be put into `oldQueueFamilyTransfers`. + newQueueFamilyTransfers.clear(); + + // Also clear implicit queue family acquire operations because these correspond to release + // operations already submitted (and therefore not part of the capture). + oldQueueFamilyTransfers.clear(); + + for(auto it = subresourceStates.begin(); it != subresourceStates.end(); ++it) + { + ImageSubresourceState state = it->state(); + state.oldLayout = state.newLayout; + state.oldQueueFamilyIndex = state.newQueueFamilyIndex; + state.refType = eFrameRef_None; + it->SetState(state); + } +} diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 4d86dce9c..13c08078e 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -3207,18 +3207,6 @@ VkImageAspectFlags FormatImageAspects(VkFormat fmt) return VK_IMAGE_ASPECT_COLOR_BIT; } -ImageSubresourceRange ImageInfo::FullRange() const -{ - return ImageSubresourceRange( - /* aspectMask = */ Aspects(), - /* baseMipLevel = */ 0u, - /* levelCount = */ (uint32_t)levelCount, - /* baseArrayLayer = */ 0u, - /* layerCount = */ (uint32_t)layerCount, - /* baseDepthSlice = */ 0u, - /* sliceCount = */ extent.depth); -} - int ImgRefs::GetAspectCount() const { int aspectCount = 0; diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index e7d5d9b8f..9f8a5ffb6 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -925,6 +925,14 @@ struct ImageInfo } VkImageAspectFlags Aspects() const { return FormatImageAspects(format); } ImageSubresourceRange FullRange() const; + inline bool operator==(const ImageInfo &other) const + { + return layerCount == other.layerCount && levelCount == other.levelCount && + sampleCount == other.sampleCount && extent.width == other.extent.width && + extent.height == other.extent.height && extent.depth == other.extent.depth && + imageType == other.imageType && format == other.format && + initialLayout == other.initialLayout && sharingMode == other.sharingMode; + } }; DECLARE_REFLECTION_STRUCT(ImageInfo); @@ -1146,9 +1154,9 @@ bool IntervalsOverlap(uint32_t base1, uint32_t count1, uint32_t base2, uint32_t bool IntervalContainedIn(uint32_t base1, uint32_t count1, uint32_t base2, uint32_t count2); -bool ValidateLevelRange(uint32_t &baseMipLevel, uint32_t &levelCount, uint32_t imageLevelCount); -bool ValidateLayerRange(uint32_t &baseArrayLayer, uint32_t &layerCount, uint32_t imageLayerCount); -bool ValidateSliceRange(uint32_t &baseSlice, uint32_t &sliceCount, uint32_t imageSliceCount); +bool SanitiseLevelRange(uint32_t &baseMipLevel, uint32_t &levelCount, uint32_t imageLevelCount); +bool SanitiseLayerRange(uint32_t &baseArrayLayer, uint32_t &layerCount, uint32_t imageLayerCount); +bool SanitiseSliceRange(uint32_t &baseSlice, uint32_t &sliceCount, uint32_t imageSliceCount); struct ImageSubresourceRange { @@ -1242,7 +1250,7 @@ struct ImageSubresourceRange { return other.ContainedIn(*this); } - void Validate(const ImageInfo &info) + void Sanitise(const ImageInfo &info) { if(aspectMask & ~info.Aspects()) { @@ -1250,16 +1258,230 @@ struct ImageSubresourceRange { RDCERR("Invalid aspect mask (%s) in image with aspects (%s)", ToStr(aspectMask).c_str(), ToStr(info.Aspects()).c_str()); - RDCDUMP(); } aspectMask &= ~info.Aspects(); } - ValidateLevelRange(baseMipLevel, levelCount, info.levelCount); - ValidateLayerRange(baseArrayLayer, layerCount, info.layerCount); - ValidateSliceRange(baseDepthSlice, sliceCount, info.extent.depth); + SanitiseLevelRange(baseMipLevel, levelCount, info.levelCount); + SanitiseLayerRange(baseArrayLayer, layerCount, info.layerCount); + SanitiseSliceRange(baseDepthSlice, sliceCount, info.extent.depth); } }; +struct ImageSubresourceState +{ + uint32_t oldQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + uint32_t newQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + VkImageLayout oldLayout = UNKNOWN_PREV_IMG_LAYOUT; + VkImageLayout newLayout = UNKNOWN_PREV_IMG_LAYOUT; + FrameRefType refType = eFrameRef_None; + + inline ImageSubresourceState() {} + inline ImageSubresourceState(const VkImageMemoryBarrier &barrier) + : oldQueueFamilyIndex(barrier.srcQueueFamilyIndex), + newQueueFamilyIndex(barrier.dstQueueFamilyIndex), + oldLayout(barrier.oldLayout), + newLayout(barrier.newLayout) + { + } + inline ImageSubresourceState(uint32_t queueFamilyIndex, VkImageLayout layout, + FrameRefType refType = eFrameRef_None) + : oldQueueFamilyIndex(queueFamilyIndex), + newQueueFamilyIndex(queueFamilyIndex), + oldLayout(layout), + newLayout(layout), + refType(refType) + { + } + inline bool operator==(const ImageSubresourceState &other) const + { + return oldQueueFamilyIndex == other.oldQueueFamilyIndex && + newQueueFamilyIndex == other.newQueueFamilyIndex && oldLayout == other.oldLayout && + newLayout == other.newLayout && refType == other.refType; + } + inline bool operator!=(const ImageSubresourceState &other) const { return !(*this == other); } + void Update(const ImageSubresourceState &other, FrameRefCompFunc compose); + bool Update(const ImageSubresourceState &other, ImageSubresourceState &result, + FrameRefCompFunc compose) const; +}; + +class ImageSubresourceMap +{ + friend class SubresourceRangeIterator; + + ImageInfo m_imageInfo; + + // The states of the subresources, without explicit ranges. + // The ranges associated with each state are determined by the index in + // `m_values` and the `*Split` flags in `m_flags`. + rdcarray m_values; + + // All aspects of the image. This is computed from `m_imageInfo.format` + VkImageAspectFlags m_aspectMask = 0u; + + // The bit count of `m_aspectMask` + uint16_t m_aspectCount = 0; + + enum class FlagBits : uint16_t + { + AreAspectsSplit = 0x1, + AreLevelsSplit = 0x2, + AreLayersSplit = 0x4, + IsDepthSplit = 0x8, + IsUninitialized = 0x8000, + }; + uint16_t m_flags = 0; + + inline static bool AreAspectsSplit(uint16_t flags) + { + return (flags & (uint16_t)FlagBits::AreAspectsSplit) != 0; + } + inline static bool AreLevelsSplit(uint16_t flags) + { + return (flags & (uint16_t)FlagBits::AreLevelsSplit) != 0; + } + inline static bool AreLayersSplit(uint16_t flags) + { + return (flags & (uint16_t)FlagBits::AreLayersSplit) != 0; + } + inline static bool IsDepthSplit(uint16_t flags) + { + return (flags & (uint16_t)FlagBits::IsDepthSplit) != 0; + } + inline bool AreAspectsSplit() const { return AreAspectsSplit(m_flags); } + inline bool AreLevelsSplit() const { return AreLevelsSplit(m_flags); } + inline bool AreLayersSplit() const { return AreLayersSplit(m_flags); } + inline bool IsDepthSplit() const { return IsDepthSplit(m_flags); } + void Split(bool splitAspects, bool splitLevels, bool splitLayers, bool splitDepth); + void Unsplit(bool unsplitAspects, bool unsplitLevels, bool unsplitLayers, bool unsplitDepth); + size_t SubresourceIndex(uint32_t aspectIndex, uint32_t level, uint32_t layer, uint32_t z) const; + +public: + inline const ImageInfo &GetImageInfo() const { return m_imageInfo; } + inline ImageSubresourceMap() {} + inline ImageSubresourceMap(const ImageInfo &imageInfo, FrameRefType refType) + : m_imageInfo(imageInfo), m_aspectMask(FormatImageAspects(imageInfo.format)) + { + for(auto it = ImageAspectFlagIter::begin(m_aspectMask); it != ImageAspectFlagIter::end(); ++it) + ++m_aspectCount; + m_values.push_back( + ImageSubresourceState(VK_QUEUE_FAMILY_IGNORED, UNKNOWN_PREV_IMG_LAYOUT, refType)); + } + + inline ImageSubresourceState &SubresourceValue(uint32_t aspectIndex, uint32_t level, + uint32_t layer, uint32_t slice) + { + return m_values[SubresourceIndex(aspectIndex, level, layer, slice)]; + } + inline const ImageSubresourceState &SubresourceValue(uint32_t aspectIndex, uint32_t level, + uint32_t layer, uint32_t slice) const + { + return m_values[SubresourceIndex(aspectIndex, level, layer, slice)]; + } + inline void Split(const ImageSubresourceRange &range) + { + Split(range.aspectMask != m_aspectMask, + range.baseMipLevel != 0u || range.levelCount < (uint32_t)GetImageInfo().levelCount, + range.baseArrayLayer != 0u || range.layerCount < (uint32_t)GetImageInfo().layerCount, + range.baseDepthSlice != 0u || range.sliceCount < GetImageInfo().extent.depth); + } + void Unsplit(); + inline void Clear() + { + m_values.clear(); + m_values.resize(1); + m_flags = 0; + } + FrameRefType Merge(const ImageSubresourceMap &other, FrameRefCompFunc compose); + + template + class SubresourceRangeIterTemplate + { + public: + inline bool operator==(const SubresourceRangeIterTemplate &other) + { + bool isValid = IsValid(); + bool otherIsValid = other.IsValid(); + return (!isValid && !otherIsValid) || + (isValid && otherIsValid && + (m_aspectIndex == other.m_aspectIndex || !m_map->AreAspectsSplit()) && + (m_level == other.m_level || !m_map->AreLevelsSplit()) && + (m_layer == other.m_layer || !m_map->AreLayersSplit()) && + (m_slice == other.m_slice || !m_map->IsDepthSplit())); + } + inline bool operator!=(const SubresourceRangeIterTemplate &other) { return !(*this == other); } + SubresourceRangeIterTemplate &operator++(); + Pair *operator->(); + Pair &operator*(); + + friend class ImageSubresourceMap; + + protected: + Map *m_map = NULL; + uint16_t m_splitFlags = 0u; + ImageSubresourceRange m_range = {}; + uint32_t m_aspectIndex = 0u; + uint32_t m_level = 0u; + uint32_t m_layer = 0u; + uint32_t m_slice = 0u; + Pair m_value; + SubresourceRangeIterTemplate() {} + SubresourceRangeIterTemplate(Map &map, const ImageSubresourceRange &range); + inline bool IsValid() const + { + return m_map && m_aspectIndex < m_map->m_aspectCount && + m_level < m_range.baseMipLevel + m_range.levelCount && + m_layer < m_range.baseArrayLayer + m_range.layerCount && + m_slice < m_range.baseDepthSlice + m_range.sliceCount; + } + void FixSubRange(); + }; + + template + class SubresourcePairRefTemplate + { + public: + inline const ImageSubresourceRange &range() const { return m_range; } + inline State &state() { return *m_state; } + inline const State &state() const { return *m_state; } + SubresourcePairRefTemplate &operator=(const SubresourcePairRefTemplate &other) = delete; + + protected: + template + friend class SubresourceRangeIterTemplate; + ImageSubresourceRange m_range; + State *m_state; + }; + + class SubresourcePairRef : public SubresourcePairRefTemplate + { + public: + inline SubresourcePairRef &SetState(const ImageSubresourceState &state) + { + *m_state = state; + return *this; + } + }; + using ConstSubresourcePairRef = SubresourcePairRefTemplate; + + using SubresourceRangeIter = SubresourceRangeIterTemplate; + using SubresourceRangeConstIter = + SubresourceRangeIterTemplate; + + inline SubresourceRangeIter RangeBegin(const ImageSubresourceRange &range) + { + return SubresourceRangeIter(*this, range); + } + inline SubresourceRangeConstIter RangeBegin(const ImageSubresourceRange &range) const + { + return SubresourceRangeConstIter(*this, range); + } + inline SubresourceRangeIter begin() { return RangeBegin(GetImageInfo().FullRange()); } + inline SubresourceRangeConstIter begin() const { return RangeBegin(GetImageInfo().FullRange()); } + inline SubresourceRangeIter end() { return SubresourceRangeIter(); } + inline SubresourceRangeConstIter end() const { return SubresourceRangeConstIter(); } + inline size_t size() const { return m_values.size(); } +}; + template struct BarrierSequence { @@ -1284,6 +1506,114 @@ struct BarrierSequence using ImageBarrierSequence = BarrierSequence; +struct ImageTransitionInfo +{ + CaptureState capState; + uint32_t defaultQueueFamilyIndex; + inline ImageTransitionInfo(CaptureState capState, uint32_t defaultQueueFamilyIndex) + : capState(capState), defaultQueueFamilyIndex(defaultQueueFamilyIndex) + { + } + inline FrameRefCompFunc GetFrameRefCompFunc() + { + if(IsCaptureMode(capState)) + return ComposeFrameRefs; + else + return KeepOldFrameRef; + } + inline FrameRefType GetDefaultRefType() + { + if(IsCaptureMode(capState)) + return eFrameRef_None; + else + return eFrameRef_Unknown; + } +}; + +struct ImageState +{ + ImageSubresourceMap subresourceStates; + rdcarray oldQueueFamilyTransfers; + rdcarray newQueueFamilyTransfers; + bool isMemoryBound = false; + ResourceId boundMemory = ResourceId(); + VkDeviceSize boundMemoryOffset = 0ull; + VkDeviceSize boundMemorySize = 0ull; + FrameRefType maxRefType = eFrameRef_None; + VkImage wrappedHandle = VK_NULL_HANDLE; + + inline const ImageInfo &GetImageInfo() const { return subresourceStates.GetImageInfo(); } + inline ImageState() {} + inline ImageState(VkImage wrappedHandle, const ImageInfo &imageInfo, FrameRefType refType) + : wrappedHandle(wrappedHandle), subresourceStates(imageInfo, refType), maxRefType(refType) + { + } + ImageState InitialState() const; + void InitialState(ImageState &result) const; + ImageState CommandBufferInitialState() const; + ImageState UniformState(const ImageSubresourceState &sub) const; + ImageState ContentInitializationState(InitPolicy policy, bool initialized, + uint32_t queueFamilyIndex, VkImageLayout copyLayout, + VkImageLayout clearLayout) const; + void RemoveQueueFamilyTransfer(VkImageMemoryBarrier *it); + void Update(ImageSubresourceRange range, const ImageSubresourceState &dst, + FrameRefCompFunc compose); + void Merge(const ImageState &other, ImageTransitionInfo info); + void MergeCaptureBeginState(const ImageState &initialState); + static void Merge(std::map &states, + const std::map &dstStates, ImageTransitionInfo info); + void DiscardContents(const ImageSubresourceRange &range); + inline void DiscardContents() { DiscardContents(GetImageInfo().FullRange()); } + inline void RecordUse(const ImageSubresourceRange &range, FrameRefType refType, + uint32_t queueFamilyIndex) + { + Update(range, ImageSubresourceState(queueFamilyIndex, UNKNOWN_PREV_IMG_LAYOUT, refType), + ComposeFrameRefs); + } + void RecordQueueFamilyRelease(const VkImageMemoryBarrier &barrier); + void RecordQueueFamilyAcquire(const VkImageMemoryBarrier &barrier); + void RecordBarrier(VkImageMemoryBarrier barrier, uint32_t queueFamilyIndex, + ImageTransitionInfo info); + bool CloseTransfers(uint32_t batchIndex, VkAccessFlags dstAccessMask, + ImageBarrierSequence &barriers, ImageTransitionInfo info); + bool RestoreTransfers(uint32_t batchIndex, const rdcarray &transfers, + VkAccessFlags srcAccessMask, ImageBarrierSequence &barriers, + ImageTransitionInfo info); + void ResetToOldState(ImageBarrierSequence &barriers, ImageTransitionInfo info); + void Transition(const ImageState &dstState, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, ImageBarrierSequence &barriers, + ImageTransitionInfo info); + void Transition(uint32_t queueFamilyIndex, VkImageLayout layout, VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, ImageBarrierSequence &barriers, + ImageTransitionInfo info); + + // Transitions the image state to `dstState` (via `preBarriers`) and back to the current state + // (via `postBarriers`). + // It is not always possible to return exactly to the current state, e.g. if the image is + // VK_IMAGE_LAYOUT_PREINITIALIZED, it will be returned to VK_IMAGE_LAYOUT_GENERAL instead. + void TempTransition(const ImageState &dstState, VkAccessFlags preSrcAccessMask, + VkAccessFlags preDstAccessMask, VkAccessFlags postSrcAccessmask, + VkAccessFlags postDstAccessMask, ImageBarrierSequence &setupBarriers, + ImageBarrierSequence &cleanupBarriers, ImageTransitionInfo info) const; + void TempTransition(uint32_t queueFamilyIndex, VkImageLayout layout, VkAccessFlags accessMask, + ImageBarrierSequence &setupBarriers, ImageBarrierSequence &cleanupBarriers, + ImageTransitionInfo info) const; + + void InlineTransition(VkCommandBuffer cmd, uint32_t queueFamilyIndex, const ImageState &dstState, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, + ImageTransitionInfo info); + void InlineTransition(VkCommandBuffer cmd, uint32_t queueFamilyIndex, VkImageLayout layout, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, + ImageTransitionInfo info); + + InitReqType MaxInitReq(const ImageSubresourceRange &range, InitPolicy policy, + bool initialized) const; + VkImageLayout GetImageLayout(VkImageAspectFlagBits aspect, uint32_t mipLevel, + uint32_t arrayLayer) const; + + void BeginCapture(); +}; + struct ImgRefs { rdcarray rangeRefs;