File: image_layout_map.cpp

package info (click to toggle)
vulkan-validationlayers 1.4.321.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,412 kB
  • sloc: cpp: 594,175; python: 11,321; sh: 24; makefile: 20; xml: 14
file content (139 lines) | stat: -rw-r--r-- 6,847 bytes parent folder | download | duplicates (5)
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));
}