blob: 677e925a6dcecaded4e43a84082902faf3742441 [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 */
21#include "core_validation_types.h"
22#include "chassis.h"
23#include "descriptor_sets.h"
24
25namespace image_layout_map {
26// Storage for the static state
27const ImageSubresourceLayoutMap::ConstIterator ImageSubresourceLayoutMap::end_iterator = ImageSubresourceLayoutMap::ConstIterator();
28
29InitialLayoutState::InitialLayoutState(const CMD_BUFFER_STATE& cb_state_, const IMAGE_VIEW_STATE* view_state_)
30 : image_view(VK_NULL_HANDLE), aspect_mask(0), label(cb_state_.debug_label) {
31 if (view_state_) {
32 image_view = view_state_->image_view;
33 aspect_mask = view_state_->create_info.subresourceRange.aspectMask;
34 }
35}
36bool ImageSubresourceLayoutMap::SubresourceLayout::operator==(const ImageSubresourceLayoutMap::SubresourceLayout& rhs) const {
37 bool is_equal =
38 (current_layout == rhs.current_layout) && (initial_layout == rhs.initial_layout) && (subresource == rhs.subresource);
39 return is_equal;
40}
41ImageSubresourceLayoutMap::ImageSubresourceLayoutMap(const IMAGE_STATE& image_state)
42 : encoder_(image_state.full_range),
43 image_state_(image_state),
44 layouts_(),
45 initial_layout_states_(),
46 initial_layout_state_map_() {}
47
48ImageSubresourceLayoutMap::ConstIterator ImageSubresourceLayoutMap::Begin(bool always_get_initial) const {
49 return Find(image_state_.full_range, /* skip_invalid */ true, always_get_initial);
50}
51bool ImageSubresourceLayoutMap::SetSubresourceRangeLayout(const CMD_BUFFER_STATE& cb_state, const VkImageSubresourceRange& range,
52 VkImageLayout layout, VkImageLayout expected_layout) {
53 bool updated = false;
54 if (expected_layout == kInvalidLayout) {
55 // Set the initial layout to the set layout as we had no other layout to reference
56 expected_layout = layout;
57 }
58 if (!InRange(range)) return false; // Don't even try to track bogus subreources
59
60 InitialLayoutState* initial_state = nullptr;
61 RangeGenerator range_gen(encoder_, range);
62 // Empty range are the range tombstones
63 for (; range_gen->non_empty(); ++range_gen) {
64 // In order to track whether we've changed anything, we'll do this in a slightly convoluted way...
65 // We'll traverse the range looking for values different from ours, then overwrite the range.
66 auto lower = layouts_.current.lower_bound(*range_gen);
67 bool all_same = false;
68 bool contiguous = false;
69 if (layouts_.current.is_contiguous(*range_gen, lower)) {
70 // The whole range is set to a value, see if assigning to it will change anything...
71 all_same = true;
72 contiguous = true;
73 for (auto pos = lower; (pos != layouts_.current.end()) && pos->first.intersects(*range_gen) && all_same; ++pos) {
74 all_same = pos->second == layout;
75 }
76 }
77 if (!all_same) {
78 // We only need to try setting anything, if we changed any of the layout values above
79 layouts_.current.overwrite_range(lower, std::make_pair(*range_gen, layout));
80 updated = true;
81 // We insert only into gaps (this is a write once semantic), and if the range
82 // isn't already contiguous, i.e. has no gaps.
83 if (!contiguous) {
84 // Note: Can't use "lower" as the hint here, because it's the lower_bound of the *current* map only
85 layouts_.initial.insert_range(std::make_pair(*range_gen, expected_layout), NoSplit());
86 initial_state = UpdateInitialLayoutState(*range_gen, initial_state, cb_state, nullptr);
87 }
88 }
89 }
90 return updated;
91}
92bool ImageSubresourceLayoutMap::SetSubresourceRangeInitialLayout(const CMD_BUFFER_STATE& cb_state,
93 const VkImageSubresourceRange& range, VkImageLayout layout,
94 const IMAGE_VIEW_STATE* view_state) {
95 bool updated = false;
96 if (!InRange(range)) return false; // Don't even try to track bogus subreources
97
98 InitialLayoutState* initial_state = nullptr;
99 RangeGenerator range_gen(encoder_, range);
100
101 for (; range_gen->non_empty(); ++range_gen) {
102 auto lower = layouts_.initial.lower_bound(*range_gen);
103 bool update_needed = !layouts_.initial.is_contiguous(*range_gen, lower);
104 if (update_needed) {
105 layouts_.initial.insert_range(lower, std::make_pair(*range_gen, layout), NoSplit());
106 initial_state = UpdateInitialLayoutState(*range_gen, initial_state, cb_state, view_state);
107 updated = true;
108 }
109 }
110 return updated;
111}
112
113static VkImageLayout FindInMap(IndexType index, const ImageSubresourceLayoutMap::RangeMap& map) {
114 auto found = map.find(index);
115 VkImageLayout value = kInvalidLayout;
116 if (found != map.end()) {
117 value = found->second;
118 }
119 return value;
120}
121VkImageLayout ImageSubresourceLayoutMap::GetSubresourceLayout(const VkImageSubresource& subresource) const {
122 IndexType index = encoder_.Encode(subresource);
123 return FindInMap(index, layouts_.current);
124}
125
126VkImageLayout ImageSubresourceLayoutMap::GetSubresourceInitialLayout(const VkImageSubresource& subresource) const {
127 IndexType index = encoder_.Encode(subresource);
128 return FindInMap(index, layouts_.initial);
129}
130
131// Saves an encode to fetch both in the same call
132ImageSubresourceLayoutMap::Layouts ImageSubresourceLayoutMap::GetSubresourceLayouts(const VkImageSubresource& subresource,
133 bool always_get_initial) const {
134 IndexType index = encoder_.Encode(subresource);
135 Layouts layouts{FindInMap(index, layouts_.current), kInvalidLayout};
136 if (always_get_initial || (layouts.current_layout != kInvalidLayout)) {
137 layouts.initial_layout = FindInMap(index, layouts_.initial);
138 }
139 return layouts;
140}
141
142const InitialLayoutState* ImageSubresourceLayoutMap::GetSubresourceInitialLayoutState(const VkImageSubresource subresource) const {
143 if (!InRange(subresource)) return nullptr;
144 const auto index = encoder_.Encode(subresource);
145 const auto found = initial_layout_state_map_.find(index);
146 if (found != initial_layout_state_map_.end()) {
147 return found->second;
148 }
149 return nullptr;
150}
151
152// TODO: make sure this paranoia check is sufficient and not too much.
153uintptr_t ImageSubresourceLayoutMap::CompatibilityKey() const {
154 return (reinterpret_cast<const uintptr_t>(&image_state_) ^ encoder_.AspectMask());
155}
156
157bool ImageSubresourceLayoutMap::UpdateFrom(const ImageSubresourceLayoutMap& other) {
158 using Arbiter = sparse_container::splice_precedence;
159
160 using sparse_container::range;
161 // Must be from matching images for the reinterpret cast to be valid
162 assert(CompatibilityKey() == other.CompatibilityKey());
163 if (CompatibilityKey() != other.CompatibilityKey()) return false;
164
165 bool updated = false;
166 updated |= sparse_container::splice(&layouts_.initial, other.layouts_.initial, Arbiter::prefer_dest);
167 updated |= sparse_container::splice(&layouts_.current, other.layouts_.current, Arbiter::prefer_source);
168 // NOTE -- we are copying plain pointers from 'other' which owns them as unique_ptr. This works because
169 // currently this function is only used to import from secondary command buffers, destruction of which
170 // invalidate the referencing primary command buffer, meaning that the dangling pointer will either be
171 // cleaned up in invalidation, on not referenced by validation code.
172 sparse_container::splice(&initial_layout_state_map_, other.initial_layout_state_map_, Arbiter::prefer_dest);
173
174 return updated;
175}
176InitialLayoutState* ImageSubresourceLayoutMap::UpdateInitialLayoutState(const IndexRange& range, InitialLayoutState* initial_state,
177 const CMD_BUFFER_STATE& cb_state,
178 const IMAGE_VIEW_STATE* view_state) {
179 if (!initial_state) {
180 // Allocate on demand... initial_layout_states_ holds ownership as a unique_ptr, while
181 // each subresource has a non-owning copy of the plain pointer.
182 initial_state = new InitialLayoutState(cb_state, view_state);
183 initial_layout_states_.emplace_back(initial_state);
184 }
185 assert(initial_state);
186 initial_layout_state_map_.insert_range(std::make_pair(range, initial_state), NoSplit());
187 return initial_state;
188}
189
190// Loop over the given range calling the callback, primarily for
191// validation checks. By default the initial_value is only looked
192// up if the set value isn't found.
193bool ImageSubresourceLayoutMap::ForRange(const VkImageSubresourceRange& range, const Callback& callback, bool skip_invalid,
194 bool always_get_initial) const {
195 if (!InRange(range)) return false; // Don't even try to process bogus subreources
196
197 RangeGenerator range_gen(encoder_, range);
198 SubresourceGenerator& subres_gen = range_gen.SubresourceGenerator();
199 ParallelIterator<const RangeMap, const RangeMap> parallel_it(layouts_.current, layouts_.initial, range_gen->begin);
200
201 bool keep_on = true;
202 IndexType current;
203 for (; range_gen->non_empty(); ++range_gen) {
204 current = range_gen->begin;
205 if (!parallel_it->range.includes(current)) { // NOTE: empty ranges can't include anything
206 parallel_it.seek(current);
207 }
208 if (parallel_it->range.empty() && skip_invalid) {
209 // We're past the end of mapped data, and we aren't interested, so we're done
210 break;
211 }
212 while (range_gen->includes(current)) {
213 VkImageLayout layout = kInvalidLayout;
214 VkImageLayout initial_layout = kInvalidLayout;
215 IndexType constant_value_bound = range_gen->end;
216 // The generated range can validly traverse past the end of stored data
217 if (!parallel_it->range.empty()) {
218 layout = sparse_container::evaluate(parallel_it->pos_A, kInvalidLayout);
219 if (layout == kInvalidLayout || always_get_initial) {
220 initial_layout = sparse_container::evaluate(parallel_it->pos_B, kInvalidLayout);
221 }
222 constant_value_bound = std::min(parallel_it->range.end, constant_value_bound);
223 }
224
225 if (!skip_invalid || (layout != kInvalidLayout) || (initial_layout != kInvalidLayout)) {
226 for (; current < constant_value_bound; current++, ++subres_gen) {
227 keep_on = callback(*subres_gen, layout, initial_layout);
228 if (!keep_on) return keep_on; // False value from the callback aborts the range traversal
229 }
230 } else {
231 subres_gen.seek(constant_value_bound); // Move the subresource to the end of the skipped range
232 current = constant_value_bound;
233 }
234 // Advance the parallel it if needed and possible
235 if (!parallel_it->range.empty() && !parallel_it->range.includes(current)) {
236 ++parallel_it;
237 }
238 }
239 // ++range_gen will update subres_gen.
240 }
241 return keep_on;
242}
243
244// This is the same constant value range, subreource position advance logic as ForRange above, but suitable for use with
245// an Increment operator.
246void ImageSubresourceLayoutMap::ConstIterator::UpdateRangeAndValue() {
247 bool not_found = true;
248 while (range_gen_->non_empty() && not_found) {
249 if (!parallel_it_->range.includes(current_index_)) { // NOTE: empty ranges can't include anything
250 parallel_it_.seek(current_index_);
251 }
252 if (parallel_it_->range.empty() && skip_invalid_) {
253 // We're past the end of mapped data, and we aren't interested, so we're done
254 // Set end condtion....
255 ForceEndCondition();
256 }
257 // Search within the current range_ for a constant valid constant value interval
258 // The while condition allows the parallel iterator to advance constant value ranges as needed.
259 while (range_gen_->includes(current_index_) && not_found) {
260 pos_.current_layout = kInvalidLayout;
261 pos_.initial_layout = kInvalidLayout;
262 constant_value_bound_ = range_gen_->end;
263 // The generated range can validly traverse past the end of stored data
264 if (!parallel_it_->range.empty()) {
265 pos_.current_layout = sparse_container::evaluate(parallel_it_->pos_A, kInvalidLayout);
266 if (pos_.current_layout == kInvalidLayout || always_get_initial_) {
267 pos_.initial_layout = sparse_container::evaluate(parallel_it_->pos_B, kInvalidLayout);
268 }
269 // The constant value bound marks the end of contiguous (w.r.t. range_gen_) indices with the same value, allowing
270 // Increment (for example) to forgo this logic until finding a new range is needed.
271 constant_value_bound_ = std::min(parallel_it_->range.end, constant_value_bound_);
272 }
273 if (!skip_invalid_ || (pos_.current_layout != kInvalidLayout) || (pos_.initial_layout != kInvalidLayout)) {
274 // we found it ... set the position and exit condition.
275 pos_.subresource = *range_gen_.SubresourceGenerator();
276 not_found = false;
277 } else {
278 // We're skipping this constant value range, set the index to the exclusive end and look again
279 // Note that we ONLY need to seek the Subresource generator on a skip condition.
280 range_gen_.SubresourceGenerator().seek(
281 constant_value_bound_); // Move the subresource to the end of the skipped range
282 current_index_ = constant_value_bound_;
283
284 // Advance the parallel it if needed and possible
285 // NOTE: We don't need to seek, as current_index_ can only be in the current or next constant value range
286 if (!parallel_it_->range.empty() && !parallel_it_->range.includes(current_index_)) {
287 ++parallel_it_;
288 }
289 }
290 }
291
292 if (not_found) {
293 // ++range_gen will update subres_gen.
294 ++range_gen_;
295 current_index_ = range_gen_->begin;
296 }
297 }
298
299 if (range_gen_->empty()) {
300 ForceEndCondition();
301 }
302}
303
304void ImageSubresourceLayoutMap::ConstIterator::Increment() {
305 ++current_index_;
306 ++(range_gen_.SubresourceGenerator());
307 if (constant_value_bound_ <= current_index_) {
308 UpdateRangeAndValue();
309 } else {
310 pos_.subresource = *(range_gen_.SubresourceGenerator());
311 }
312}
313ImageSubresourceLayoutMap::ConstIterator::ConstIterator(const RangeMap& current, const RangeMap& initial, const Encoder& encoder,
314 const VkImageSubresourceRange& subres, bool skip_invalid,
315 bool always_get_initial)
316 : range_gen_(encoder, subres),
317 parallel_it_(current, initial, range_gen_->begin),
318 skip_invalid_(skip_invalid),
319 always_get_initial_(always_get_initial),
320 pos_(),
321 current_index_(range_gen_->begin),
322 constant_value_bound_() {
323 UpdateRangeAndValue();
324}
325
326} // namespace image_layout_map