blob: ced8fd1da5733b09a768701fd7df03e9ce0ec44a [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>
John Zulaufb58415b2019-12-09 15:02:32 -070036static inline InitialLayoutState* UpdateInitialLayoutStateImpl(StatesMap* initial_layout_state_map,
37 InitialLayoutStates* states_storage, const IndexRange& range,
38 InitialLayoutState* initial_state, const CMD_BUFFER_STATE& cb_state,
39 const IMAGE_VIEW_STATE* view_state) {
John Zulauf81408f12019-11-27 16:40:27 -070040 auto& initial_layout_states = *states_storage;
41 if (!initial_state) {
42 // Allocate on demand... initial_layout_states_ holds ownership as a unique_ptr, while
43 // each subresource has a non-owning copy of the plain pointer.
44 initial_state = new InitialLayoutState(cb_state, view_state);
45 initial_layout_states.emplace_back(initial_state);
46 }
47 assert(initial_state);
48 sparse_container::update_range_value(*initial_layout_state_map, range, initial_state, WritePolicy::prefer_dest);
49 return initial_state;
50}
51
John Zulauf11211402019-11-15 14:02:36 -070052InitialLayoutState::InitialLayoutState(const CMD_BUFFER_STATE& cb_state_, const IMAGE_VIEW_STATE* view_state_)
53 : image_view(VK_NULL_HANDLE), aspect_mask(0), label(cb_state_.debug_label) {
54 if (view_state_) {
55 image_view = view_state_->image_view;
56 aspect_mask = view_state_->create_info.subresourceRange.aspectMask;
57 }
58}
59bool ImageSubresourceLayoutMap::SubresourceLayout::operator==(const ImageSubresourceLayoutMap::SubresourceLayout& rhs) const {
60 bool is_equal =
61 (current_layout == rhs.current_layout) && (initial_layout == rhs.initial_layout) && (subresource == rhs.subresource);
62 return is_equal;
63}
64ImageSubresourceLayoutMap::ImageSubresourceLayoutMap(const IMAGE_STATE& image_state)
John Zulaufb58415b2019-12-09 15:02:32 -070065 : image_state_(image_state),
66 encoder_(image_state.range_encoder),
John Zulauf81408f12019-11-27 16:40:27 -070067 layouts_(encoder_.SubresourceCount()),
John Zulauf6066f732019-11-21 13:15:10 -070068 current_layout_view_(layouts_.current, encoder_),
69 initial_layout_view_(layouts_.initial, encoder_),
John Zulauf11211402019-11-15 14:02:36 -070070 initial_layout_states_(),
John Zulauf81408f12019-11-27 16:40:27 -070071 initial_layout_state_map_(encoder_.SubresourceCount()) {}
John Zulauf11211402019-11-15 14:02:36 -070072
73ImageSubresourceLayoutMap::ConstIterator ImageSubresourceLayoutMap::Begin(bool always_get_initial) const {
74 return Find(image_state_.full_range, /* skip_invalid */ true, always_get_initial);
75}
John Zulauf81408f12019-11-27 16:40:27 -070076
77// Use the unwrapped maps from the BothMap in the actual implementation
78template <typename LayoutMap, typename InitialStateMap>
79static bool SetSubresourceRangeLayoutImpl(LayoutMap* current_layouts, LayoutMap* initial_layouts,
80 InitialStateMap* initial_state_map, InitialLayoutStates* initial_layout_states,
81 RangeGenerator* range_gen_arg, const CMD_BUFFER_STATE& cb_state, VkImageLayout layout,
82 VkImageLayout expected_layout) {
83 bool updated = false;
84 auto& range_gen = *range_gen_arg;
85 InitialLayoutState* initial_state = nullptr;
86 // Empty range are the range tombstones
87 for (; range_gen->non_empty(); ++range_gen) {
88 // In order to track whether we've changed anything, we'll do this in a slightly convoluted way...
89 // We'll traverse the range looking for values different from ours, then overwrite the range.
90 bool updated_current =
91 sparse_container::update_range_value(*current_layouts, *range_gen, layout, WritePolicy::prefer_source);
92 if (updated_current) {
93 updated = true;
94 bool updated_init =
95 sparse_container::update_range_value(*initial_layouts, *range_gen, expected_layout, WritePolicy::prefer_dest);
96 if (updated_init) {
97 initial_state = UpdateInitialLayoutStateImpl(initial_state_map, initial_layout_states, *range_gen, initial_state,
98 cb_state, nullptr);
99 }
100 }
101 }
102 return updated;
103}
104
John Zulauf11211402019-11-15 14:02:36 -0700105bool ImageSubresourceLayoutMap::SetSubresourceRangeLayout(const CMD_BUFFER_STATE& cb_state, const VkImageSubresourceRange& range,
106 VkImageLayout layout, VkImageLayout expected_layout) {
John Zulauf11211402019-11-15 14:02:36 -0700107 if (expected_layout == kInvalidLayout) {
108 // Set the initial layout to the set layout as we had no other layout to reference
109 expected_layout = layout;
110 }
111 if (!InRange(range)) return false; // Don't even try to track bogus subreources
112
John Zulauf11211402019-11-15 14:02:36 -0700113 RangeGenerator range_gen(encoder_, range);
John Zulauf81408f12019-11-27 16:40:27 -0700114 if (layouts_.initial.SmallMode()) {
115 return SetSubresourceRangeLayoutImpl(&layouts_.current.GetSmallMap(), &layouts_.initial.GetSmallMap(),
116 &initial_layout_state_map_.GetSmallMap(), &initial_layout_states_, &range_gen,
117 cb_state, layout, expected_layout);
118 } else {
119 assert(!layouts_.initial.Tristate());
120 return SetSubresourceRangeLayoutImpl(&layouts_.current.GetBigMap(), &layouts_.initial.GetBigMap(),
121 &initial_layout_state_map_.GetBigMap(), &initial_layout_states_, &range_gen, cb_state,
122 layout, expected_layout);
123 }
124}
125
126// Use the unwrapped maps from the BothMap in the actual implementation
127template <typename LayoutMap, typename InitialStateMap>
128static bool SetSubresourceRangeInitialLayoutImpl(LayoutMap* initial_layouts, InitialStateMap* initial_state_map,
129 InitialLayoutStates* initial_layout_states, RangeGenerator* range_gen_arg,
130 const CMD_BUFFER_STATE& cb_state, VkImageLayout layout,
131 const IMAGE_VIEW_STATE* view_state) {
132 bool updated = false;
133 InitialLayoutState* initial_state = nullptr;
134 auto& range_gen = *range_gen_arg;
135
John Zulauf11211402019-11-15 14:02:36 -0700136 for (; range_gen->non_empty(); ++range_gen) {
John Zulauf81408f12019-11-27 16:40:27 -0700137 bool updated_range = sparse_container::update_range_value(*initial_layouts, *range_gen, layout, WritePolicy::prefer_dest);
138 if (updated_range) {
139 initial_state = UpdateInitialLayoutStateImpl(initial_state_map, initial_layout_states, *range_gen, initial_state,
140 cb_state, view_state);
John Zulauf11211402019-11-15 14:02:36 -0700141 updated = true;
John Zulauf11211402019-11-15 14:02:36 -0700142 }
143 }
144 return updated;
145}
John Zulauf81408f12019-11-27 16:40:27 -0700146
147// Unwrap the BothMaps entry here as this is a performance hotspot.
John Zulauf11211402019-11-15 14:02:36 -0700148bool ImageSubresourceLayoutMap::SetSubresourceRangeInitialLayout(const CMD_BUFFER_STATE& cb_state,
149 const VkImageSubresourceRange& range, VkImageLayout layout,
150 const IMAGE_VIEW_STATE* view_state) {
John Zulauf11211402019-11-15 14:02:36 -0700151 if (!InRange(range)) return false; // Don't even try to track bogus subreources
152
John Zulauf11211402019-11-15 14:02:36 -0700153 RangeGenerator range_gen(encoder_, range);
John Zulauf81408f12019-11-27 16:40:27 -0700154 assert(layouts_.initial.GetMode() == initial_layout_state_map_.GetMode());
155 if (layouts_.initial.SmallMode()) {
156 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetSmallMap(), &initial_layout_state_map_.GetSmallMap(),
157 &initial_layout_states_, &range_gen, cb_state, layout, view_state);
158 } else {
159 assert(!layouts_.initial.Tristate());
160 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetBigMap(), &initial_layout_state_map_.GetBigMap(),
161 &initial_layout_states_, &range_gen, cb_state, layout, view_state);
John Zulauf11211402019-11-15 14:02:36 -0700162 }
John Zulauf11211402019-11-15 14:02:36 -0700163}
164
John Zulaufb58415b2019-12-09 15:02:32 -0700165// Unwrap the BothMaps entry here as this is a performance hotspot.
166bool ImageSubresourceLayoutMap::SetSubresourceRangeInitialLayout(const CMD_BUFFER_STATE& cb_state, VkImageLayout layout,
167 const IMAGE_VIEW_STATE& view_state) {
168 RangeGenerator range_gen(view_state.range_generator);
169 assert(layouts_.initial.GetMode() == initial_layout_state_map_.GetMode());
170 if (layouts_.initial.SmallMode()) {
171 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetSmallMap(), &initial_layout_state_map_.GetSmallMap(),
172 &initial_layout_states_, &range_gen, cb_state, layout, &view_state);
173 } else {
174 assert(!layouts_.initial.Tristate());
175 return SetSubresourceRangeInitialLayoutImpl(&layouts_.initial.GetBigMap(), &initial_layout_state_map_.GetBigMap(),
176 &initial_layout_states_, &range_gen, cb_state, layout, &view_state);
177 }
178}
179
John Zulauf11211402019-11-15 14:02:36 -0700180static VkImageLayout FindInMap(IndexType index, const ImageSubresourceLayoutMap::RangeMap& map) {
181 auto found = map.find(index);
182 VkImageLayout value = kInvalidLayout;
183 if (found != map.end()) {
184 value = found->second;
185 }
186 return value;
187}
188VkImageLayout ImageSubresourceLayoutMap::GetSubresourceLayout(const VkImageSubresource& subresource) const {
189 IndexType index = encoder_.Encode(subresource);
190 return FindInMap(index, layouts_.current);
191}
192
193VkImageLayout ImageSubresourceLayoutMap::GetSubresourceInitialLayout(const VkImageSubresource& subresource) const {
194 IndexType index = encoder_.Encode(subresource);
195 return FindInMap(index, layouts_.initial);
196}
197
198// Saves an encode to fetch both in the same call
199ImageSubresourceLayoutMap::Layouts ImageSubresourceLayoutMap::GetSubresourceLayouts(const VkImageSubresource& subresource,
200 bool always_get_initial) const {
201 IndexType index = encoder_.Encode(subresource);
202 Layouts layouts{FindInMap(index, layouts_.current), kInvalidLayout};
203 if (always_get_initial || (layouts.current_layout != kInvalidLayout)) {
204 layouts.initial_layout = FindInMap(index, layouts_.initial);
205 }
206 return layouts;
207}
208
209const InitialLayoutState* ImageSubresourceLayoutMap::GetSubresourceInitialLayoutState(const VkImageSubresource subresource) const {
210 if (!InRange(subresource)) return nullptr;
211 const auto index = encoder_.Encode(subresource);
212 const auto found = initial_layout_state_map_.find(index);
213 if (found != initial_layout_state_map_.end()) {
214 return found->second;
215 }
216 return nullptr;
217}
218
219// TODO: make sure this paranoia check is sufficient and not too much.
220uintptr_t ImageSubresourceLayoutMap::CompatibilityKey() const {
221 return (reinterpret_cast<const uintptr_t>(&image_state_) ^ encoder_.AspectMask());
222}
223
224bool ImageSubresourceLayoutMap::UpdateFrom(const ImageSubresourceLayoutMap& other) {
John Zulauf81408f12019-11-27 16:40:27 -0700225 using Arbiter = sparse_container::value_precedence;
John Zulauf11211402019-11-15 14:02:36 -0700226
227 using sparse_container::range;
228 // Must be from matching images for the reinterpret cast to be valid
229 assert(CompatibilityKey() == other.CompatibilityKey());
230 if (CompatibilityKey() != other.CompatibilityKey()) return false;
231
232 bool updated = false;
233 updated |= sparse_container::splice(&layouts_.initial, other.layouts_.initial, Arbiter::prefer_dest);
234 updated |= sparse_container::splice(&layouts_.current, other.layouts_.current, Arbiter::prefer_source);
235 // NOTE -- we are copying plain pointers from 'other' which owns them as unique_ptr. This works because
236 // currently this function is only used to import from secondary command buffers, destruction of which
237 // invalidate the referencing primary command buffer, meaning that the dangling pointer will either be
238 // cleaned up in invalidation, on not referenced by validation code.
239 sparse_container::splice(&initial_layout_state_map_, other.initial_layout_state_map_, Arbiter::prefer_dest);
240
241 return updated;
242}
John Zulauf11211402019-11-15 14:02:36 -0700243
244// Loop over the given range calling the callback, primarily for
245// validation checks. By default the initial_value is only looked
246// up if the set value isn't found.
247bool ImageSubresourceLayoutMap::ForRange(const VkImageSubresourceRange& range, const Callback& callback, bool skip_invalid,
248 bool always_get_initial) const {
249 if (!InRange(range)) return false; // Don't even try to process bogus subreources
250
251 RangeGenerator range_gen(encoder_, range);
John Zulauf2ea823e2019-11-19 08:54:59 -0700252 SubresourceGenerator& subres_gen = range_gen.GetSubresourceGenerator();
John Zulauf11211402019-11-15 14:02:36 -0700253 ParallelIterator<const RangeMap, const RangeMap> parallel_it(layouts_.current, layouts_.initial, range_gen->begin);
254
255 bool keep_on = true;
256 IndexType current;
257 for (; range_gen->non_empty(); ++range_gen) {
258 current = range_gen->begin;
259 if (!parallel_it->range.includes(current)) { // NOTE: empty ranges can't include anything
260 parallel_it.seek(current);
261 }
262 if (parallel_it->range.empty() && skip_invalid) {
263 // We're past the end of mapped data, and we aren't interested, so we're done
264 break;
265 }
266 while (range_gen->includes(current)) {
267 VkImageLayout layout = kInvalidLayout;
268 VkImageLayout initial_layout = kInvalidLayout;
269 IndexType constant_value_bound = range_gen->end;
270 // The generated range can validly traverse past the end of stored data
271 if (!parallel_it->range.empty()) {
272 layout = sparse_container::evaluate(parallel_it->pos_A, kInvalidLayout);
273 if (layout == kInvalidLayout || always_get_initial) {
274 initial_layout = sparse_container::evaluate(parallel_it->pos_B, kInvalidLayout);
275 }
276 constant_value_bound = std::min(parallel_it->range.end, constant_value_bound);
277 }
278
279 if (!skip_invalid || (layout != kInvalidLayout) || (initial_layout != kInvalidLayout)) {
280 for (; current < constant_value_bound; current++, ++subres_gen) {
281 keep_on = callback(*subres_gen, layout, initial_layout);
282 if (!keep_on) return keep_on; // False value from the callback aborts the range traversal
283 }
284 } else {
John Zulaufdd18b3a2019-11-20 08:30:23 -0700285 subres_gen.Seek(constant_value_bound); // Move the subresource to the end of the skipped range
John Zulauf11211402019-11-15 14:02:36 -0700286 current = constant_value_bound;
287 }
288 // Advance the parallel it if needed and possible
289 if (!parallel_it->range.empty() && !parallel_it->range.includes(current)) {
290 ++parallel_it;
291 }
292 }
293 // ++range_gen will update subres_gen.
294 }
295 return keep_on;
296}
297
298// This is the same constant value range, subreource position advance logic as ForRange above, but suitable for use with
299// an Increment operator.
300void ImageSubresourceLayoutMap::ConstIterator::UpdateRangeAndValue() {
301 bool not_found = true;
302 while (range_gen_->non_empty() && not_found) {
303 if (!parallel_it_->range.includes(current_index_)) { // NOTE: empty ranges can't include anything
304 parallel_it_.seek(current_index_);
305 }
306 if (parallel_it_->range.empty() && skip_invalid_) {
307 // We're past the end of mapped data, and we aren't interested, so we're done
308 // Set end condtion....
309 ForceEndCondition();
310 }
311 // Search within the current range_ for a constant valid constant value interval
312 // The while condition allows the parallel iterator to advance constant value ranges as needed.
313 while (range_gen_->includes(current_index_) && not_found) {
314 pos_.current_layout = kInvalidLayout;
315 pos_.initial_layout = kInvalidLayout;
316 constant_value_bound_ = range_gen_->end;
317 // The generated range can validly traverse past the end of stored data
318 if (!parallel_it_->range.empty()) {
319 pos_.current_layout = sparse_container::evaluate(parallel_it_->pos_A, kInvalidLayout);
320 if (pos_.current_layout == kInvalidLayout || always_get_initial_) {
321 pos_.initial_layout = sparse_container::evaluate(parallel_it_->pos_B, kInvalidLayout);
322 }
323 // The constant value bound marks the end of contiguous (w.r.t. range_gen_) indices with the same value, allowing
324 // Increment (for example) to forgo this logic until finding a new range is needed.
325 constant_value_bound_ = std::min(parallel_it_->range.end, constant_value_bound_);
326 }
327 if (!skip_invalid_ || (pos_.current_layout != kInvalidLayout) || (pos_.initial_layout != kInvalidLayout)) {
328 // we found it ... set the position and exit condition.
John Zulauf2ea823e2019-11-19 08:54:59 -0700329 pos_.subresource = range_gen_.GetSubresource();
John Zulauf11211402019-11-15 14:02:36 -0700330 not_found = false;
331 } else {
332 // We're skipping this constant value range, set the index to the exclusive end and look again
John Zulaufdd18b3a2019-11-20 08:30:23 -0700333 // Note that we ONLY need to Seek the Subresource generator on a skip condition.
334 range_gen_.GetSubresourceGenerator().Seek(
John Zulauf11211402019-11-15 14:02:36 -0700335 constant_value_bound_); // Move the subresource to the end of the skipped range
336 current_index_ = constant_value_bound_;
337
338 // Advance the parallel it if needed and possible
339 // NOTE: We don't need to seek, as current_index_ can only be in the current or next constant value range
340 if (!parallel_it_->range.empty() && !parallel_it_->range.includes(current_index_)) {
341 ++parallel_it_;
342 }
343 }
344 }
345
346 if (not_found) {
347 // ++range_gen will update subres_gen.
348 ++range_gen_;
349 current_index_ = range_gen_->begin;
350 }
351 }
352
353 if (range_gen_->empty()) {
354 ForceEndCondition();
355 }
356}
357
358void ImageSubresourceLayoutMap::ConstIterator::Increment() {
359 ++current_index_;
John Zulauf2ea823e2019-11-19 08:54:59 -0700360 ++(range_gen_.GetSubresourceGenerator());
John Zulauf11211402019-11-15 14:02:36 -0700361 if (constant_value_bound_ <= current_index_) {
362 UpdateRangeAndValue();
363 } else {
John Zulauf2ea823e2019-11-19 08:54:59 -0700364 pos_.subresource = range_gen_.GetSubresource();
John Zulauf11211402019-11-15 14:02:36 -0700365 }
366}
367ImageSubresourceLayoutMap::ConstIterator::ConstIterator(const RangeMap& current, const RangeMap& initial, const Encoder& encoder,
368 const VkImageSubresourceRange& subres, bool skip_invalid,
369 bool always_get_initial)
370 : range_gen_(encoder, subres),
371 parallel_it_(current, initial, range_gen_->begin),
372 skip_invalid_(skip_invalid),
373 always_get_initial_(always_get_initial),
374 pos_(),
375 current_index_(range_gen_->begin),
376 constant_value_bound_() {
377 UpdateRangeAndValue();
378}
379
380} // namespace image_layout_map