1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
/* Copyright (c) 2019-2025 The Khronos Group Inc.
* Copyright (c) 2019-2025 Valve Corporation
* Copyright (c) 2019-2025 LunarG, Inc.
* Copyright (C) 2019-2025 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* John Zulauf <jzulauf@lunarg.com>
*
*/
#include "state_tracker/image_layout_map.h"
#include "state_tracker/image_state.h"
#include "utils/image_layout_utils.h"
using IndexRange = subresource_adapter::IndexRange;
using RangeGenerator = subresource_adapter::RangeGenerator;
template <typename LayoutsMap>
static bool UpdateLayoutMapRange(LayoutsMap& layout_map, const IndexRange& range, const ImageLayoutState& new_entry) {
using CachedLowerBound = typename sparse_container::cached_lower_bound_impl<LayoutsMap>;
CachedLowerBound pos(layout_map, range.begin);
bool updated_current = false;
while (range.includes(pos->index)) {
if (!pos->valid) {
// Fill in the leading space (or in the case of pos at end the trailing space
const auto start = pos->index;
auto it = pos->lower_bound;
const auto limit = (it != layout_map.end()) ? std::min(it->first.begin, range.end) : range.end;
auto insert_result = layout_map.insert(it, std::make_pair(IndexRange(start, limit), new_entry));
pos.invalidate(insert_result, start);
pos.seek(limit);
updated_current = true;
}
// Note that after the "fill" operation pos may have become valid so we check again
if (pos->valid) {
ImageLayoutState entry = pos->lower_bound->second; // intentional copy
// existing entry always has first layout initialized (current layout might be unknown though)
assert(entry.first_layout != kInvalidLayout);
// TODO: does it make sense to initialize current layout together with the first layout (set current as first),
// assuming that submit time validation will detect mismatch between first layout and global layout?
// Are there contexts when this does not work?
const bool update_current = new_entry.current_layout != kInvalidLayout &&
!ImageLayoutMatches(entry.aspect_mask, new_entry.current_layout, entry.current_layout);
auto intersected_range = pos->lower_bound->first & range;
if (!intersected_range.empty() && update_current) {
entry.current_layout = new_entry.current_layout;
auto overwrite_result = layout_map.overwrite_range(pos->lower_bound, std::make_pair(intersected_range, entry));
// If we didn't cover the whole range, we'll need to go around again
pos.invalidate(overwrite_result, intersected_range.begin);
pos.seek(intersected_range.end);
updated_current = true;
} else {
// Point just past the end of this section, if it's within the given range, it will get filled next iteration
// ++pos could move us past the end of range (which would exit the loop) so we don't use it.
pos.seek(pos->lower_bound->first.end);
}
}
}
return updated_current;
}
template <typename LayoutMap>
static bool UpdateLayoutMap(LayoutMap& image_layout_map, RangeGenerator&& range_gen, const ImageLayoutState& entry) {
bool updated = false;
// Unwrap the BothMaps entry here as this is a performance hotspot
if (image_layout_map.UsesSmallMap()) {
auto& layout_map = image_layout_map.GetSmallMap();
for (; range_gen->non_empty(); ++range_gen) {
updated |= UpdateLayoutMapRange(layout_map, *range_gen, entry);
}
} else {
auto& layout_map = image_layout_map.GetBigMap();
for (; range_gen->non_empty(); ++range_gen) {
updated |= UpdateLayoutMapRange(layout_map, *range_gen, entry);
}
}
return updated;
}
template <typename LayoutMap>
static bool IterateLayoutMapRanges(
const LayoutMap& image_layout_map, RangeGenerator&& gen,
std::function<bool(const IndexRange& range, const typename LayoutMap::mapped_type& layout_state)>&& func) {
for (; gen->non_empty(); ++gen) {
for (auto pos = image_layout_map.lower_bound(*gen); pos != image_layout_map.end() && gen->intersects(pos->first); ++pos) {
// TODO: Usually func returns skip status. Often we accumulate skip and do not initiate immediate return.
// Investigate if this function should accumuate skip value instead of immediate return.
if (func(pos->first, pos->second)) {
return true;
}
}
}
return false;
}
bool UpdateCurrentLayout(CommandBufferImageLayoutMap& image_layout_map, RangeGenerator&& range_gen, VkImageLayout layout,
VkImageLayout expected_layout, VkImageAspectFlags aspect_mask) {
assert(layout != kInvalidLayout);
ImageLayoutState entry{};
entry.current_layout = layout;
entry.first_layout = (expected_layout != kInvalidLayout) ? expected_layout : layout;
entry.aspect_mask = aspect_mask;
return UpdateLayoutMap(image_layout_map, std::move(range_gen), entry);
}
void TrackFirstLayout(CommandBufferImageLayoutMap& image_layout_map, RangeGenerator&& range_gen, VkImageLayout expected_layout,
VkImageAspectFlags aspect_mask) {
assert(expected_layout != kInvalidLayout);
ImageLayoutState entry{};
entry.current_layout = kInvalidLayout;
entry.first_layout = expected_layout;
entry.aspect_mask = aspect_mask;
UpdateLayoutMap(image_layout_map, std::move(range_gen), entry);
}
bool ForEachMatchingLayoutMapRange(const CommandBufferImageLayoutMap& image_layout_map, RangeGenerator&& gen,
std::function<bool(const IndexRange& range, const ImageLayoutState& entry)>&& func) {
return IterateLayoutMapRanges(image_layout_map, std::move(gen), std::move(func));
}
bool ForEachMatchingLayoutMapRange(const ImageLayoutMap& image_layout_map, RangeGenerator&& gen,
std::function<bool(const ImageLayoutMap::key_type& range, VkImageLayout image_layout)>&& func) {
return IterateLayoutMapRanges(image_layout_map, std::move(gen), std::move(func));
}
|