blob: 276c4a7617dc567d5b85aab151a28bf5662ebfdb [file] [log] [blame]
John Zulauf11211402019-11-15 14:02:36 -07001/* Copyright (c) 2019 The Khronos Group Inc.
2 * Copyright (c) 2019 Valve Corporation
3 * Copyright (c) 2019 LunarG, Inc.
4 * Copyright (C) 2019 Google Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * John Zulauf <jzulauf@lunarg.com>
19 *
20 */
John Zulauf5823c622019-11-25 13:33:44 -070021#ifdef SPARSE_CONTAINER_UNIT_TEST
22#include "image_layout_map.h"
23#else
John Zulauf11211402019-11-15 14:02:36 -070024#include "core_validation_types.h"
25#include "chassis.h"
26#include "descriptor_sets.h"
John Zulauf5823c622019-11-25 13:33:44 -070027#endif
John Zulauf11211402019-11-15 14:02:36 -070028
29namespace image_layout_map {
30// Storage for the static state
31const ImageSubresourceLayoutMap::ConstIterator ImageSubresourceLayoutMap::end_iterator = ImageSubresourceLayoutMap::ConstIterator();
32
John Zulauf81408f12019-11-27 16:40:27 -070033using InitialLayoutStates = ImageSubresourceLayoutMap::InitialLayoutStates;
34
35template <typename StatesMap>
36static InitialLayoutState* UpdateInitialLayoutStateImpl(StatesMap* initial_layout_state_map, InitialLayoutStates* states_storage,
37 const IndexRange& range, InitialLayoutState* initial_state,
38 const CMD_BUFFER_STATE& cb_state, const IMAGE_VIEW_STATE* view_state) {
39 auto& initial_layout_states = *states_storage;
40 if (!initial_state) {
41 // Allocate on demand... initial_layout_states_ holds ownership as a unique_ptr, while
42 // each subresource has a non-owning copy of the plain pointer.
43 initial_state = new InitialLayoutState(cb_state, view_state);
44 initial_layout_states.emplace_back(initial_state);
45 }
46 assert(initial_state);
47 sparse_container::update_range_value(*initial_layout_state_map, range, initial_state, WritePolicy::prefer_dest);
48 return initial_state;
49}
50
John Zulauf11211402019-11-15 14:02:36 -070051InitialLayoutState::InitialLayoutState(const CMD_BUFFER_STATE& cb_state_, const IMAGE_VIEW_STATE* view_state_)
52 : image_view(VK_NULL_HANDLE), aspect_mask(0), label(cb_state_.debug_label) {
53 if (view_state_) {
54 image_view = view_state_->image_view;
55 aspect_mask = view_state_->create_info.subresourceRange.aspectMask;
56 }
57}
58bool ImageSubresourceLayoutMap::SubresourceLayout::operator==(const ImageSubresourceLayoutMap::SubresourceLayout& rhs) const {
59 bool is_equal =
60 (current_layout == rhs.current_layout) && (initial_layout == rhs.initial_layout) && (subresource == rhs.subresource);
61 return is_equal;
62}
63ImageSubresourceLayoutMap::ImageSubresourceLayoutMap(const IMAGE_STATE& image_state)
64 : encoder_(image_state.full_range),
65 image_state_(image_state),
John Zulauf81408f12019-11-27 16:40:27 -070066 layouts_(encoder_.SubresourceCount()),
John Zulauf6066f732019-11-21 13:15:10 -070067 current_layout_view_(layouts_.current, encoder_),
68 initial_layout_view_(layouts_.initial, encoder_),
John Zulauf11211402019-11-15 14:02:36 -070069 initial_layout_states_(),
John Zulauf81408f12019-11-27 16:40:27 -070070 initial_layout_state_map_(encoder_.SubresourceCount()) {}
John Zulauf11211402019-11-15 14:02:36 -070071
72ImageSubresourceLayoutMap::ConstIterator ImageSubresourceLayoutMap::Begin(bool always_get_initial) const {
73 return Find(image_state_.full_range, /* skip_invalid */ true, always_get_initial);
74}
John Zulauf81408f12019-11-27 16:40:27 -070075
76// Use the unwrapped maps from the BothMap in the actual implementation
77template <typename LayoutMap, typename InitialStateMap>
78static bool SetSubresourceRangeLayoutImpl(LayoutMap* current_layouts, LayoutMap* initial_layouts,
79 InitialStateMap* initial_state_map, InitialLayoutStates* initial_layout_states,
80 RangeGenerator* range_gen_arg, const CMD_BUFFER_STATE& cb_state, VkImageLayout layout,
81 VkImageLayout expected_layout) {
82 bool updated = false;
83 auto& range_gen = *range_gen_arg;
84 InitialLayoutState* initial_state = nullptr;
85 // Empty range are the range tombstones
86 for (; range_gen->non_empty(); ++range_gen) {
87 // In order to track whether we've changed anything, we'll do this in a slightly convoluted way...
88 // We'll traverse the range looking for values different from ours, then overwrite the range.
89 bool updated_current =
90 sparse_container::update_range_value(*current_layouts, *range_gen, layout, WritePolicy::prefer_source);
91 if (updated_current) {
92 updated = true;
93 bool updated_init =
94 sparse_container::update_range_value(*initial_layouts, *range_gen, expected_layout, WritePolicy::prefer_dest);
95 if (updated_init) {
96 initial_state = UpdateInitialLayoutStateImpl(initial_state_map, initial_layout_states, *range_gen, initial_state,
97 cb_state, nullptr);
98 }
99 }
100 }
101 return updated;
102}
103
John Zulauf11211402019-11-15 14:02:36 -0700104bool ImageSubresourceLayoutMap::SetSubresourceRangeLayout(const CMD_BUFFER_STATE& cb_state, const VkImageSubresourceRange& range,
105 VkImageLayout layout, VkImageLayout expected_layout) {
John Zulauf11211402019-11-15 14:02:36 -0700106 if (expected_layout == kInvalidLayout) {
107 // Set the initial layout to the set layout as we had no other layout to reference
108 expected_layout = layout;
109 }
110 if (!InRange(range)) return false; // Don't even try to track bogus subreources
111
John Zulauf11211402019-11-15 14:02:36 -0700112 RangeGenerator range_gen(encoder_, range);
John Zulauf81408f12019-11-27 16:40:27 -0700113 if (layouts_.initial.SmallMode()) {
114 return SetSubresourceRangeLayoutImpl(&layouts_.current.GetSmallMap(), &layouts_.initial.GetSmallMap(),
115 &initial_layout_state_map_.GetSmallMap(), &initial_layout_states_, &range_gen,
116 cb_state, layout, expected_layout);
117 } else {
118 assert(!layouts_.initial.Tristate());
119 return SetSubresourceRangeLayoutImpl(&layouts_.current.GetBigMap(), &layouts_.initial.GetBigMap(),
120 &initial_layout_state_map_.GetBigMap(), &initial_layout_states_, &range_gen, cb_state,
121 layout, expected_layout);
122 }
123}
124
125// Use the unwrapped maps from the BothMap in the actual implementation
126template <typename LayoutMap, typename InitialStateMap>
127static bool SetSubresourceRangeInitialLayoutImpl(LayoutMap* initial_layouts, InitialStateMap* initial_state_map,
128 InitialLayoutStates* initial_layout_states, RangeGenerator* range_gen_arg,
129 const CMD_BUFFER_STATE& cb_state, VkImageLayout layout,
130 const IMAGE_VIEW_STATE* view_state) {
131 bool updated = false;
132 InitialLayoutState* initial_state = nullptr;
133 auto& range_gen = *range_gen_arg;
134
John Zulauf11211402019-11-15 14:02:36 -0700135 for (; range_gen->non_empty(); ++range_gen) {
John Zulauf81408f12019-11-27 16:40:27 -0700136 bool updated_range = sparse_container::update_range_value(*initial_layouts, *range_gen, layout, WritePolicy::prefer_dest);
137 if (updated_range) {
138 initial_state = UpdateInitialLayoutStateImpl(initial_state_map, initial_layout_states, *range_gen, initial_state,
139 cb_state, view_state);
John Zulauf11211402019-11-15 14:02:36 -0700140 updated = true;
John Zulauf11211402019-11-15 14:02:36 -0700141 }
142 }
143 return updated;
144}
John Zulauf81408f12019-11-27 16:40:27 -0700145
146// Unwrap the BothMaps entry here as this is a performance hotspot.
John Zulauf11211402019-11-15 14:02:36 -0700147bool ImageSubresourceLayoutMap::SetSubresourceRangeInitialLayout(const CMD_BUFFER_STATE& cb_state,
148 const VkImageSubresourceRange& range, VkImageLayout layout,
149 const IMAGE_VIEW_STATE* view_state) {
John Zulauf11211402019-11-15 14:02:36 -0700150 if (!InRange(range)) return false; // Don't even try to track bogus subreources
151
John Zulauf11211402019-11-15 14:02:36 -0700152 RangeGenerator range_gen(encoder_, range);
John Zulauf81408f12019-11-27 16:40:27 -0700153 assert(layouts_.initial.GetMode() == initial_layout_state_map_.GetMode());
154 if (layouts_.initial.SmallMode()) {
155 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetSmallMap(), &initial_layout_state_map_.GetSmallMap(),
156 &initial_layout_states_, &range_gen, cb_state, layout, view_state);
157 } else {
158 assert(!layouts_.initial.Tristate());
159 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetBigMap(), &initial_layout_state_map_.GetBigMap(),
160 &initial_layout_states_, &range_gen, cb_state, layout, view_state);
John Zulauf11211402019-11-15 14:02:36 -0700161 }
John Zulauf11211402019-11-15 14:02:36 -0700162}
163
164static VkImageLayout FindInMap(IndexType index, const ImageSubresourceLayoutMap::RangeMap& map) {
165 auto found = map.find(index);
166 VkImageLayout value = kInvalidLayout;
167 if (found != map.end()) {
168 value = found->second;
169 }
170 return value;
171}
172VkImageLayout ImageSubresourceLayoutMap::GetSubresourceLayout(const VkImageSubresource& subresource) const {
173 IndexType index = encoder_.Encode(subresource);
174 return FindInMap(index, layouts_.current);
175}
176
177VkImageLayout ImageSubresourceLayoutMap::GetSubresourceInitialLayout(const VkImageSubresource& subresource) const {
178 IndexType index = encoder_.Encode(subresource);
179 return FindInMap(index, layouts_.initial);
180}
181
182// Saves an encode to fetch both in the same call
183ImageSubresourceLayoutMap::Layouts ImageSubresourceLayoutMap::GetSubresourceLayouts(const VkImageSubresource& subresource,
184 bool always_get_initial) const {
185 IndexType index = encoder_.Encode(subresource);
186 Layouts layouts{FindInMap(index, layouts_.current), kInvalidLayout};
187 if (always_get_initial || (layouts.current_layout != kInvalidLayout)) {
188 layouts.initial_layout = FindInMap(index, layouts_.initial);
189 }
190 return layouts;
191}
192
193const InitialLayoutState* ImageSubresourceLayoutMap::GetSubresourceInitialLayoutState(const VkImageSubresource subresource) const {
194 if (!InRange(subresource)) return nullptr;
195 const auto index = encoder_.Encode(subresource);
196 const auto found = initial_layout_state_map_.find(index);
197 if (found != initial_layout_state_map_.end()) {
198 return found->second;
199 }
200 return nullptr;
201}
202
203// TODO: make sure this paranoia check is sufficient and not too much.
204uintptr_t ImageSubresourceLayoutMap::CompatibilityKey() const {
205 return (reinterpret_cast<const uintptr_t>(&image_state_) ^ encoder_.AspectMask());
206}
207
208bool ImageSubresourceLayoutMap::UpdateFrom(const ImageSubresourceLayoutMap& other) {
John Zulauf81408f12019-11-27 16:40:27 -0700209 using Arbiter = sparse_container::value_precedence;
John Zulauf11211402019-11-15 14:02:36 -0700210
211 using sparse_container::range;
212 // Must be from matching images for the reinterpret cast to be valid
213 assert(CompatibilityKey() == other.CompatibilityKey());
214 if (CompatibilityKey() != other.CompatibilityKey()) return false;
215
216 bool updated = false;
217 updated |= sparse_container::splice(&layouts_.initial, other.layouts_.initial, Arbiter::prefer_dest);
218 updated |= sparse_container::splice(&layouts_.current, other.layouts_.current, Arbiter::prefer_source);
219 // NOTE -- we are copying plain pointers from 'other' which owns them as unique_ptr. This works because
220 // currently this function is only used to import from secondary command buffers, destruction of which
221 // invalidate the referencing primary command buffer, meaning that the dangling pointer will either be
222 // cleaned up in invalidation, on not referenced by validation code.
223 sparse_container::splice(&initial_layout_state_map_, other.initial_layout_state_map_, Arbiter::prefer_dest);
224
225 return updated;
226}
John Zulauf11211402019-11-15 14:02:36 -0700227
228// Loop over the given range calling the callback, primarily for
229// validation checks. By default the initial_value is only looked
230// up if the set value isn't found.
231bool ImageSubresourceLayoutMap::ForRange(const VkImageSubresourceRange& range, const Callback& callback, bool skip_invalid,
232 bool always_get_initial) const {
233 if (!InRange(range)) return false; // Don't even try to process bogus subreources
234
235 RangeGenerator range_gen(encoder_, range);
John Zulauf2ea823e2019-11-19 08:54:59 -0700236 SubresourceGenerator& subres_gen = range_gen.GetSubresourceGenerator();
John Zulauf11211402019-11-15 14:02:36 -0700237 ParallelIterator<const RangeMap, const RangeMap> parallel_it(layouts_.current, layouts_.initial, range_gen->begin);
238
239 bool keep_on = true;
240 IndexType current;
241 for (; range_gen->non_empty(); ++range_gen) {
242 current = range_gen->begin;
243 if (!parallel_it->range.includes(current)) { // NOTE: empty ranges can't include anything
244 parallel_it.seek(current);
245 }
246 if (parallel_it->range.empty() && skip_invalid) {
247 // We're past the end of mapped data, and we aren't interested, so we're done
248 break;
249 }
250 while (range_gen->includes(current)) {
251 VkImageLayout layout = kInvalidLayout;
252 VkImageLayout initial_layout = kInvalidLayout;
253 IndexType constant_value_bound = range_gen->end;
254 // The generated range can validly traverse past the end of stored data
255 if (!parallel_it->range.empty()) {
256 layout = sparse_container::evaluate(parallel_it->pos_A, kInvalidLayout);
257 if (layout == kInvalidLayout || always_get_initial) {
258 initial_layout = sparse_container::evaluate(parallel_it->pos_B, kInvalidLayout);
259 }
260 constant_value_bound = std::min(parallel_it->range.end, constant_value_bound);
261 }
262
263 if (!skip_invalid || (layout != kInvalidLayout) || (initial_layout != kInvalidLayout)) {
264 for (; current < constant_value_bound; current++, ++subres_gen) {
265 keep_on = callback(*subres_gen, layout, initial_layout);
266 if (!keep_on) return keep_on; // False value from the callback aborts the range traversal
267 }
268 } else {
John Zulaufdd18b3a2019-11-20 08:30:23 -0700269 subres_gen.Seek(constant_value_bound); // Move the subresource to the end of the skipped range
John Zulauf11211402019-11-15 14:02:36 -0700270 current = constant_value_bound;
271 }
272 // Advance the parallel it if needed and possible
273 if (!parallel_it->range.empty() && !parallel_it->range.includes(current)) {
274 ++parallel_it;
275 }
276 }
277 // ++range_gen will update subres_gen.
278 }
279 return keep_on;
280}
281
282// This is the same constant value range, subreource position advance logic as ForRange above, but suitable for use with
283// an Increment operator.
284void ImageSubresourceLayoutMap::ConstIterator::UpdateRangeAndValue() {
285 bool not_found = true;
286 while (range_gen_->non_empty() && not_found) {
287 if (!parallel_it_->range.includes(current_index_)) { // NOTE: empty ranges can't include anything
288 parallel_it_.seek(current_index_);
289 }
290 if (parallel_it_->range.empty() && skip_invalid_) {
291 // We're past the end of mapped data, and we aren't interested, so we're done
292 // Set end condtion....
293 ForceEndCondition();
294 }
295 // Search within the current range_ for a constant valid constant value interval
296 // The while condition allows the parallel iterator to advance constant value ranges as needed.
297 while (range_gen_->includes(current_index_) && not_found) {
298 pos_.current_layout = kInvalidLayout;
299 pos_.initial_layout = kInvalidLayout;
300 constant_value_bound_ = range_gen_->end;
301 // The generated range can validly traverse past the end of stored data
302 if (!parallel_it_->range.empty()) {
303 pos_.current_layout = sparse_container::evaluate(parallel_it_->pos_A, kInvalidLayout);
304 if (pos_.current_layout == kInvalidLayout || always_get_initial_) {
305 pos_.initial_layout = sparse_container::evaluate(parallel_it_->pos_B, kInvalidLayout);
306 }
307 // The constant value bound marks the end of contiguous (w.r.t. range_gen_) indices with the same value, allowing
308 // Increment (for example) to forgo this logic until finding a new range is needed.
309 constant_value_bound_ = std::min(parallel_it_->range.end, constant_value_bound_);
310 }
311 if (!skip_invalid_ || (pos_.current_layout != kInvalidLayout) || (pos_.initial_layout != kInvalidLayout)) {
312 // we found it ... set the position and exit condition.
John Zulauf2ea823e2019-11-19 08:54:59 -0700313 pos_.subresource = range_gen_.GetSubresource();
John Zulauf11211402019-11-15 14:02:36 -0700314 not_found = false;
315 } else {
316 // We're skipping this constant value range, set the index to the exclusive end and look again
John Zulaufdd18b3a2019-11-20 08:30:23 -0700317 // Note that we ONLY need to Seek the Subresource generator on a skip condition.
318 range_gen_.GetSubresourceGenerator().Seek(
John Zulauf11211402019-11-15 14:02:36 -0700319 constant_value_bound_); // Move the subresource to the end of the skipped range
320 current_index_ = constant_value_bound_;
321
322 // Advance the parallel it if needed and possible
323 // NOTE: We don't need to seek, as current_index_ can only be in the current or next constant value range
324 if (!parallel_it_->range.empty() && !parallel_it_->range.includes(current_index_)) {
325 ++parallel_it_;
326 }
327 }
328 }
329
330 if (not_found) {
331 // ++range_gen will update subres_gen.
332 ++range_gen_;
333 current_index_ = range_gen_->begin;
334 }
335 }
336
337 if (range_gen_->empty()) {
338 ForceEndCondition();
339 }
340}
341
342void ImageSubresourceLayoutMap::ConstIterator::Increment() {
343 ++current_index_;
John Zulauf2ea823e2019-11-19 08:54:59 -0700344 ++(range_gen_.GetSubresourceGenerator());
John Zulauf11211402019-11-15 14:02:36 -0700345 if (constant_value_bound_ <= current_index_) {
346 UpdateRangeAndValue();
347 } else {
John Zulauf2ea823e2019-11-19 08:54:59 -0700348 pos_.subresource = range_gen_.GetSubresource();
John Zulauf11211402019-11-15 14:02:36 -0700349 }
350}
351ImageSubresourceLayoutMap::ConstIterator::ConstIterator(const RangeMap& current, const RangeMap& initial, const Encoder& encoder,
352 const VkImageSubresourceRange& subres, bool skip_invalid,
353 bool always_get_initial)
354 : range_gen_(encoder, subres),
355 parallel_it_(current, initial, range_gen_->begin),
356 skip_invalid_(skip_invalid),
357 always_get_initial_(always_get_initial),
358 pos_(),
359 current_index_(range_gen_->begin),
360 constant_value_bound_() {
361 UpdateRangeAndValue();
362}
363
364} // namespace image_layout_map