blob: 38b1f7ded05036972823def061e90017054b4a3a [file] [log] [blame]
John Zulauf9cb530d2019-09-30 14:14:10 -06001/* Copyright (c) 2019 The Khronos Group Inc.
2 * Copyright (c) 2019 Valve Corporation
3 * Copyright (c) 2019 LunarG, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Author: John Zulauf <jzulauf@lunarg.com>
18 */
19
20#include <limits>
21#include <vector>
22#include "synchronization_validation.h"
23
24static const char *string_SyncHazardVUID(SyncHazard hazard) {
25 switch (hazard) {
26 case SyncHazard::NONE:
27 return "SYNC-NONE";
28 break;
29 case SyncHazard::READ_AFTER_WRITE:
30 return "SYNC-HAZARD-READ_AFTER_WRITE";
31 break;
32 case SyncHazard::WRITE_AFTER_READ:
33 return "SYNC-HAZARD-WRITE_AFTER_READ";
34 break;
35 case SyncHazard::WRITE_AFTER_WRITE:
36 return "SYNC-HAZARD-WRITE_AFTER_WRITE";
37 break;
38 default:
39 assert(0);
40 }
41 return "SYNC-HAZARD-INVALID";
42}
43
44static const char *string_SyncHazard(SyncHazard hazard) {
45 switch (hazard) {
46 case SyncHazard::NONE:
47 return "NONR";
48 break;
49 case SyncHazard::READ_AFTER_WRITE:
50 return "READ_AFTER_WRITE";
51 break;
52 case SyncHazard::WRITE_AFTER_READ:
53 return "WRITE_AFTER_READ";
54 break;
55 case SyncHazard::WRITE_AFTER_WRITE:
56 return "WRITE_AFTER_WRITE";
57 break;
58 default:
59 assert(0);
60 }
61 return "INVALID HAZARD";
62}
63
John Zulauf0cb5be22020-01-23 12:18:22 -070064// Expand the pipeline stage without regard to whether the are valid w.r.t. queue or extension
65VkPipelineStageFlags ExpandPipelineStages(VkQueueFlags queue_flags, VkPipelineStageFlags stage_mask) {
66 VkPipelineStageFlags expanded = stage_mask;
67 if (VK_PIPELINE_STAGE_ALL_COMMANDS_BIT & stage_mask) {
68 expanded = expanded & ~VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
69 for (const auto &all_commands : syncAllCommandStagesByQueueFlags) {
70 if (all_commands.first & queue_flags) {
71 expanded |= all_commands.second;
72 }
73 }
74 }
75 if (VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT & stage_mask) {
76 expanded = expanded & ~VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
77 expanded |= syncAllCommandStagesByQueueFlags.at(VK_QUEUE_GRAPHICS_BIT) & ~VK_PIPELINE_STAGE_HOST_BIT;
78 }
79 return expanded;
80}
81
John Zulauf5c5e88d2019-12-26 11:22:02 -070082static const ResourceAccessRange full_range(std::numeric_limits<VkDeviceSize>::min(), std::numeric_limits<VkDeviceSize>::max());
83static ResourceAccessRange MakeMemoryAccessRange(const BUFFER_STATE &buffer, VkDeviceSize offset, VkDeviceSize size) {
John Zulauf9cb530d2019-09-30 14:14:10 -060084 assert(!buffer.sparse);
85 const auto base = offset + buffer.binding.offset;
John Zulauf5c5e88d2019-12-26 11:22:02 -070086 return ResourceAccessRange(base, base + size);
87}
88
89HazardResult DetectHazard(const ResourceAccessRangeMap &accesses, SyncStageAccessIndex current_usage,
90 const ResourceAccessRange &range) {
91 const auto from = accesses.lower_bound(range);
92 const auto to = accesses.upper_bound(range);
93 for (auto pos = from; pos != to; ++pos) {
94 const auto &access_state = pos->second;
95 HazardResult hazard = access_state.DetectHazard(current_usage);
96 if (hazard.hazard) return hazard;
97 }
98 return HazardResult();
99}
100
101HazardResult DetectHazard(const IMAGE_STATE &image, const ResourceAccessRangeMap &accesses, SyncStageAccessIndex current_usage,
102 const VkImageSubresourceLayers &subresource, const VkOffset3D &offset, const VkExtent3D &extent) {
103 // TODO: replace the encoder/generator with offset3D/extent3D aware versions
104 VkImageSubresourceRange subresource_range = {subresource.aspectMask, subresource.mipLevel, 1, subresource.baseArrayLayer,
105 subresource.layerCount};
106 subresource_adapter::RangeGenerator range_gen(image.range_encoder, subresource_range);
107 for (; range_gen->non_empty(); ++range_gen) {
108 HazardResult hazard = DetectHazard(accesses, current_usage, *range_gen);
109 if (hazard.hazard) return hazard;
110 }
111 return HazardResult();
John Zulauf9cb530d2019-09-30 14:14:10 -0600112}
113
John Zulauf0cb5be22020-01-23 12:18:22 -0700114HazardResult DetectBarrierHazard(const ResourceAccessRangeMap &accesses, SyncStageAccessIndex current_usage,
115 VkPipelineStageFlags src_stage_mask, SyncStageAccessFlags src_scope,
116 const ResourceAccessRange &range) {
117 const auto from = accesses.lower_bound(range);
118 const auto to = accesses.upper_bound(range);
119 for (auto pos = from; pos != to; ++pos) {
120 const auto &access_state = pos->second;
121 HazardResult hazard = access_state.DetectBarrierHazard(current_usage, src_stage_mask, src_scope);
122 if (hazard.hazard) return hazard;
123 }
124 return HazardResult();
125}
126
127HazardResult DetectImageBarrierHazard(const ResourceAccessTracker &tracker, const IMAGE_STATE &image,
128 VkPipelineStageFlags src_stage_mask, SyncStageAccessFlags src_stage_scope,
129 const VkImageMemoryBarrier &barrier) {
130 auto *accesses = tracker.GetImageAccesses(image.image);
131 if (!accesses) return HazardResult();
132
133 auto subresource_range = NormalizeSubresourceRange(image.createInfo, barrier.subresourceRange);
134 subresource_adapter::RangeGenerator range_gen(image.range_encoder, subresource_range);
135 const auto src_scope = SyncStageAccess::AccessScope(src_stage_scope, barrier.srcAccessMask);
136 for (; range_gen->non_empty(); ++range_gen) {
137 HazardResult hazard = DetectBarrierHazard(*accesses, SyncStageAccessIndex::SYNC_IMAGE_LAYOUT_TRANSITION, src_stage_mask,
138 src_scope, *range_gen);
139 if (hazard.hazard) return hazard;
140 }
141 return HazardResult();
142}
143
John Zulauf9cb530d2019-09-30 14:14:10 -0600144template <typename Flags, typename Map>
145SyncStageAccessFlags AccessScopeImpl(Flags flag_mask, const Map &map) {
146 SyncStageAccessFlags scope = 0;
147 for (const auto &bit_scope : map) {
148 if (flag_mask < bit_scope.first) break;
149
150 if (flag_mask & bit_scope.first) {
151 scope |= bit_scope.second;
152 }
153 }
154 return scope;
155}
156
157SyncStageAccessFlags SyncStageAccess::AccessScopeByStage(VkPipelineStageFlags stages) {
158 return AccessScopeImpl(stages, syncStageAccessMaskByStageBit);
159}
160
161SyncStageAccessFlags SyncStageAccess::AccessScopeByAccess(VkAccessFlags accesses) {
162 return AccessScopeImpl(accesses, syncStageAccessMaskByAccessBit);
163}
164
165// Getting from stage mask and access mask to stage/acess masks is something we need to be good at...
166SyncStageAccessFlags SyncStageAccess::AccessScope(VkPipelineStageFlags stages, VkAccessFlags accesses) {
167 // The access scope is the intersection of all stage/access types possible for the enabled stages and the enables accesses
168 // (after doing a couple factoring of common terms the union of stage/access intersections is the intersections of the
169 // union of all stage/access types for all the stages and the same unions for the access mask...
170 return AccessScopeByStage(stages) & AccessScopeByAccess(accesses);
171}
172
173template <typename Action>
John Zulauf5c5e88d2019-12-26 11:22:02 -0700174void UpdateMemoryAccessState(ResourceAccessRangeMap *accesses, const ResourceAccessRange &range, const Action &action) {
John Zulauf9cb530d2019-09-30 14:14:10 -0600175 // TODO -- region/mem-range accuracte update
176 auto pos = accesses->lower_bound(range);
177 if (pos == accesses->end() || !pos->first.intersects(range)) {
178 // The range is empty, fill it with a default value.
179 pos = action.Infill(accesses, pos, range);
180 } else if (range.begin < pos->first.begin) {
181 // Leading empty space, infill
John Zulauf5c5e88d2019-12-26 11:22:02 -0700182 pos = action.Infill(accesses, pos, ResourceAccessRange(range.begin, pos->first.begin));
John Zulauf9cb530d2019-09-30 14:14:10 -0600183 } else if (pos->first.begin < range.begin) {
184 // Trim the beginning if needed
185 pos = accesses->split(pos, range.begin, sparse_container::split_op_keep_both());
186 ++pos;
187 }
188
189 const auto the_end = accesses->end();
190 while ((pos != the_end) && pos->first.intersects(range)) {
191 if (pos->first.end > range.end) {
192 pos = accesses->split(pos, range.end, sparse_container::split_op_keep_both());
193 }
194
195 pos = action(accesses, pos);
196 if (pos == the_end) break;
197
198 auto next = pos;
199 ++next;
200 if ((pos->first.end < range.end) && (next != the_end) && !next->first.is_subsequent_to(pos->first)) {
201 // Need to infill if next is disjoint
202 VkDeviceSize limit = (next == the_end) ? range.end : std::min(range.end, next->first.begin);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700203 ResourceAccessRange new_range(pos->first.end, limit);
John Zulauf9cb530d2019-09-30 14:14:10 -0600204 next = action.Infill(accesses, next, new_range);
205 }
206 pos = next;
207 }
208}
209
210struct UpdateMemoryAccessStateFunctor {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700211 using Iterator = ResourceAccessRangeMap::iterator;
212 Iterator Infill(ResourceAccessRangeMap *accesses, Iterator pos, ResourceAccessRange range) const {
John Zulauf9cb530d2019-09-30 14:14:10 -0600213 return accesses->insert(pos, std::make_pair(range, ResourceAccessState()));
214 }
John Zulauf5c5e88d2019-12-26 11:22:02 -0700215 Iterator operator()(ResourceAccessRangeMap *accesses, Iterator pos) const {
John Zulauf9cb530d2019-09-30 14:14:10 -0600216 auto &access_state = pos->second;
217 access_state.Update(usage, tag);
218 return pos;
219 }
220
221 UpdateMemoryAccessStateFunctor(SyncStageAccessIndex usage_, const ResourceUsageTag &tag_) : usage(usage_), tag(tag_) {}
222 SyncStageAccessIndex usage;
223 const ResourceUsageTag &tag;
224};
225
226struct ApplyMemoryAccessBarrierFunctor {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700227 using Iterator = ResourceAccessRangeMap::iterator;
228 inline Iterator Infill(ResourceAccessRangeMap *accesses, Iterator pos, ResourceAccessRange range) const { return pos; }
John Zulauf9cb530d2019-09-30 14:14:10 -0600229
John Zulauf5c5e88d2019-12-26 11:22:02 -0700230 Iterator operator()(ResourceAccessRangeMap *accesses, Iterator pos) const {
John Zulauf9cb530d2019-09-30 14:14:10 -0600231 auto &access_state = pos->second;
232 access_state.ApplyMemoryAccessBarrier(src_stage_mask, src_scope, dst_stage_mask, dst_scope);
233 return pos;
234 }
235
236 ApplyMemoryAccessBarrierFunctor(VkPipelineStageFlags src_stage_mask_, SyncStageAccessFlags src_scope_,
237 VkPipelineStageFlags dst_stage_mask_, SyncStageAccessFlags dst_scope_)
238 : src_stage_mask(src_stage_mask_), src_scope(src_scope_), dst_stage_mask(dst_stage_mask_), dst_scope(dst_scope_) {}
239
240 VkPipelineStageFlags src_stage_mask;
241 SyncStageAccessFlags src_scope;
242 VkPipelineStageFlags dst_stage_mask;
243 SyncStageAccessFlags dst_scope;
244};
245
246struct ApplyGlobalBarrierFunctor {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700247 using Iterator = ResourceAccessRangeMap::iterator;
248 inline Iterator Infill(ResourceAccessRangeMap *accesses, Iterator pos, ResourceAccessRange range) const { return pos; }
John Zulauf9cb530d2019-09-30 14:14:10 -0600249
John Zulauf5c5e88d2019-12-26 11:22:02 -0700250 Iterator operator()(ResourceAccessRangeMap *accesses, Iterator pos) const {
John Zulauf9cb530d2019-09-30 14:14:10 -0600251 auto &access_state = pos->second;
252 access_state.ApplyExecutionBarrier(src_stage_mask, dst_stage_mask);
253
254 for (const auto &functor : barrier_functor) {
255 functor(accesses, pos);
256 }
257 return pos;
258 }
259
260 ApplyGlobalBarrierFunctor(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
261 SyncStageAccessFlags src_stage_scope, SyncStageAccessFlags dst_stage_scope,
262 uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers)
263 : src_stage_mask(srcStageMask), dst_stage_mask(dstStageMask) {
264 // Don't want to create this per tracked item, but don't want to loop through all tracked items per barrier...
265 barrier_functor.reserve(memoryBarrierCount);
266 for (uint32_t barrier_index = 0; barrier_index < memoryBarrierCount; barrier_index++) {
267 const auto &barrier = pMemoryBarriers[barrier_index];
268 barrier_functor.emplace_back(srcStageMask, SyncStageAccess::AccessScope(src_stage_scope, barrier.srcAccessMask),
269 dstStageMask, SyncStageAccess::AccessScope(dst_stage_scope, barrier.dstAccessMask));
270 }
271 }
272
273 const VkPipelineStageFlags src_stage_mask;
274 const VkPipelineStageFlags dst_stage_mask;
275 std::vector<ApplyMemoryAccessBarrierFunctor> barrier_functor;
276};
277
John Zulauf5c5e88d2019-12-26 11:22:02 -0700278void UpdateAccessState(ResourceAccessRangeMap *accesses, SyncStageAccessIndex current_usage, const ResourceAccessRange &range,
279 const ResourceUsageTag &tag) {
280 UpdateMemoryAccessStateFunctor action(current_usage, tag);
281 UpdateMemoryAccessState(accesses, range, action);
282}
283
284void UpdateAccessState(const IMAGE_STATE &image, ResourceAccessRangeMap *accesses, SyncStageAccessIndex current_usage,
285 const VkImageSubresourceLayers &subresource, const VkOffset3D &offset, const VkExtent3D &extent,
286 const ResourceUsageTag &tag) {
287 // TODO: replace the encoder/generator with offset3D aware versions
288 VkImageSubresourceRange subresource_range = {subresource.aspectMask, subresource.mipLevel, 1, subresource.baseArrayLayer,
289 subresource.layerCount};
290 subresource_adapter::RangeGenerator range_gen(image.range_encoder, subresource_range);
291 for (; range_gen->non_empty(); ++range_gen) {
292 UpdateAccessState(accesses, current_usage, *range_gen, tag);
293 }
294}
295
John Zulauf9cb530d2019-09-30 14:14:10 -0600296HazardResult ResourceAccessState::DetectHazard(SyncStageAccessIndex usage_index) const {
297 HazardResult hazard;
298 auto usage = FlagBit(usage_index);
299 if (IsRead(usage)) {
300 if (IsWriteHazard(usage)) {
301 hazard.Set(READ_AFTER_WRITE, write_tag);
302 }
303 } else {
304 // Assume write
305 // TODO determine what to do with READ-WRITE usage states if any
306 // Write-After-Write check -- if we have a previous write to test against
307 if (last_write && IsWriteHazard(usage)) {
308 hazard.Set(WRITE_AFTER_WRITE, write_tag);
309 } else {
310 // Only look for casus belli for WAR
311 const auto usage_stage = PipelineStageBit(usage_index);
312 for (uint32_t read_index = 0; read_index < last_read_count; read_index++) {
313 if (IsReadHazard(usage_stage, last_reads[read_index])) {
314 hazard.Set(WRITE_AFTER_READ, last_reads[read_index].tag);
315 break;
316 }
317 }
318 }
319 }
320 return hazard;
321}
322
John Zulauf0cb5be22020-01-23 12:18:22 -0700323HazardResult ResourceAccessState::DetectBarrierHazard(SyncStageAccessIndex usage_index, VkPipelineStageFlags src_stage_mask,
324 SyncStageAccessFlags src_scope) const {
325 // Only supporting image layout transitions for now
326 assert(usage_index == SyncStageAccessIndex::SYNC_IMAGE_LAYOUT_TRANSITION);
327 HazardResult hazard;
328 if (last_write) {
329 // If the previous write is *not* in the 1st access scope
330 // *AND* the current barrier is not in the dependency chain
331 // *AND* the there is no prior memory barrier for the previous write in the dependency chain
332 // then the barrier access is unsafe (R/W after W)
333 if (((last_write & src_scope) == 0) && ((src_stage_mask & write_dependency_chain) == 0) && (write_barriers == 0)) {
334 // TODO: Do we need a difference hazard name for this?
335 hazard.Set(WRITE_AFTER_WRITE, write_tag);
336 }
337 } else {
338 // Look at the reads
339 for (uint32_t read_index = 0; read_index < last_read_count; read_index++) {
340 if (IsReadHazard(src_stage_mask, last_reads[read_index])) {
341 hazard.Set(WRITE_AFTER_READ, last_reads[read_index].tag);
342 break;
343 }
344 }
345 }
346 return hazard;
347}
348
John Zulauf9cb530d2019-09-30 14:14:10 -0600349void ResourceAccessState::Update(SyncStageAccessIndex usage_index, const ResourceUsageTag &tag) {
350 // Move this logic in the ResourceStateTracker as methods, thereof (or we'll repeat it for every flavor of resource...
351 const auto usage_bit = FlagBit(usage_index);
352 if (IsRead(usage_index)) {
353 // Mulitple outstanding reads may be of interest and do dependency chains independently
354 // However, for purposes of barrier tracking, only one read per pipeline stage matters
355 const auto usage_stage = PipelineStageBit(usage_index);
356 if (usage_stage & last_read_stages) {
357 for (uint32_t read_index = 0; read_index < last_read_count; read_index++) {
358 ReadState &access = last_reads[read_index];
359 if (access.stage == usage_stage) {
360 access.barriers = 0;
361 access.tag = tag;
362 break;
363 }
364 }
365 } else {
366 // We don't have this stage in the list yet...
367 assert(last_read_count < last_reads.size());
368 ReadState &access = last_reads[last_read_count++];
369 access.stage = usage_stage;
370 access.barriers = 0;
371 access.tag = tag;
372 last_read_stages |= usage_stage;
373 }
374 } else {
375 // Assume write
376 // TODO determine what to do with READ-WRITE operations if any
377 // Clobber last read and both sets of barriers... because all we have is DANGER, DANGER, WILL ROBINSON!!!
378 // if the last_reads/last_write were unsafe, we've reported them,
379 // in either case the prior access is irrelevant, we can overwrite them as *this* write is now after them
380 last_read_count = 0;
381 last_read_stages = 0;
382
383 write_barriers = 0;
384 write_dependency_chain = 0;
385 write_tag = tag;
386 last_write = usage_bit;
387 }
388}
389void ResourceAccessState::ApplyExecutionBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask) {
390 // Execution Barriers only protect read operations
391 for (uint32_t read_index = 0; read_index < last_read_count; read_index++) {
392 ReadState &access = last_reads[read_index];
393 // The | implements the "dependency chain" logic for this access, as the barriers field stores the second sync scope
394 if (srcStageMask & (access.stage | access.barriers)) {
395 access.barriers |= dstStageMask;
396 }
397 }
398 if (write_dependency_chain & srcStageMask) write_dependency_chain |= dstStageMask;
399}
400
401void ResourceAccessState::ApplyMemoryAccessBarrier(VkPipelineStageFlags src_stage_mask, SyncStageAccessFlags src_scope,
402 VkPipelineStageFlags dst_stage_mask, SyncStageAccessFlags dst_scope) {
403 // Assuming we've applied the execution side of this barrier, we update just the write
404 // The || implements the "dependency chain" logic for this barrier
405 if ((src_scope & last_write) || (write_dependency_chain & src_stage_mask)) {
406 write_barriers |= dst_scope;
407 write_dependency_chain |= dst_stage_mask;
408 }
409}
410
411void SyncValidator::ResetCommandBuffer(VkCommandBuffer command_buffer) {
412 auto *tracker = GetAccessTrackerNoInsert(command_buffer);
413 if (tracker) {
414 tracker->Reset();
415 }
416}
417
John Zulauf5c5e88d2019-12-26 11:22:02 -0700418void SyncValidator::ApplyGlobalBarriers(ResourceAccessTracker *tracker, VkPipelineStageFlags srcStageMask,
John Zulauf9cb530d2019-09-30 14:14:10 -0600419 VkPipelineStageFlags dstStageMask, SyncStageAccessFlags src_stage_scope,
420 SyncStageAccessFlags dst_stage_scope, uint32_t memoryBarrierCount,
421 const VkMemoryBarrier *pMemoryBarriers) {
422 // TODO: Implement this better (maybe some delayed/on-demand integration).
423 ApplyGlobalBarrierFunctor barriers_functor(srcStageMask, dstStageMask, src_stage_scope, dst_stage_scope, memoryBarrierCount,
424 pMemoryBarriers);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700425 for (auto &mem_access_pair : tracker->GetMemoryAccessMap()) {
John Zulauf9cb530d2019-09-30 14:14:10 -0600426 UpdateMemoryAccessState(&mem_access_pair.second, full_range, barriers_functor);
427 }
John Zulauf5c5e88d2019-12-26 11:22:02 -0700428 for (auto &image_access_pair : tracker->GetImageAccessMap()) {
429 UpdateMemoryAccessState(&image_access_pair.second, full_range, barriers_functor);
430 }
John Zulauf9cb530d2019-09-30 14:14:10 -0600431}
432
John Zulauf5c5e88d2019-12-26 11:22:02 -0700433void SyncValidator::ApplyBufferBarriers(ResourceAccessTracker *tracker, VkPipelineStageFlags src_stage_mask,
John Zulauf9cb530d2019-09-30 14:14:10 -0600434 SyncStageAccessFlags src_stage_scope, VkPipelineStageFlags dst_stage_mask,
435 SyncStageAccessFlags dst_stage_scope, uint32_t barrier_count,
436 const VkBufferMemoryBarrier *barriers) {
437 // TODO Implement this at subresource/memory_range accuracy
438 for (uint32_t index = 0; index < barrier_count; index++) {
439 const auto &barrier = barriers[index];
440 const auto *buffer = Get<BUFFER_STATE>(barrier.buffer);
441 if (!buffer) continue;
John Zulauf5c5e88d2019-12-26 11:22:02 -0700442 auto *accesses = tracker->GetMemoryAccessesNoInsert(buffer->binding.mem_state->mem);
John Zulauf9cb530d2019-09-30 14:14:10 -0600443 if (!accesses) continue;
John Zulauf5c5e88d2019-12-26 11:22:02 -0700444 ResourceAccessRange range = MakeMemoryAccessRange(*buffer, barrier.offset, barrier.size);
John Zulauf9cb530d2019-09-30 14:14:10 -0600445 UpdateMemoryAccessState(
446 accesses, range,
447 ApplyMemoryAccessBarrierFunctor(src_stage_mask, AccessScope(src_stage_scope, barrier.srcAccessMask), dst_stage_mask,
448 AccessScope(dst_stage_scope, barrier.dstAccessMask)));
449 }
450}
451
John Zulauf5c5e88d2019-12-26 11:22:02 -0700452void SyncValidator::ApplyImageBarriers(ResourceAccessTracker *tracker, VkPipelineStageFlags src_stage_mask,
453 SyncStageAccessFlags src_stage_scope, VkPipelineStageFlags dst_stage_mask,
454 SyncStageAccessFlags dst_stage_scope, uint32_t barrier_count,
455 const VkImageMemoryBarrier *barriers) {
456 for (uint32_t index = 0; index < barrier_count; index++) {
457 const auto &barrier = barriers[index];
458 const auto *image = Get<IMAGE_STATE>(barrier.image);
459 if (!image) continue;
460 auto *accesses = tracker->GetImageAccessesNoInsert(image->image);
461 if (!accesses) continue;
462 auto subresource_range = NormalizeSubresourceRange(image->createInfo, barrier.subresourceRange);
463 subresource_adapter::RangeGenerator range_gen(image->range_encoder, subresource_range);
464 const ApplyMemoryAccessBarrierFunctor barrier_action(src_stage_mask, AccessScope(src_stage_scope, barrier.srcAccessMask),
465 dst_stage_mask, AccessScope(dst_stage_scope, barrier.dstAccessMask));
466 for (; range_gen->non_empty(); ++range_gen) {
467 UpdateMemoryAccessState(accesses, *range_gen, barrier_action);
468 }
John Zulauf9cb530d2019-09-30 14:14:10 -0600469 }
John Zulauf9cb530d2019-09-30 14:14:10 -0600470}
471
472bool SyncValidator::PreCallValidateCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer,
473 uint32_t regionCount, const VkBufferCopy *pRegions) const {
474 bool skip = false;
475 const auto *const const_this = this;
476 const auto *tracker = const_this->GetAccessTracker(commandBuffer);
477 if (tracker) {
478 // If we have no previous accesses, we have no hazards
479 // TODO: make this sub-resource capable
480 // TODO: make this general, and stuff it into templates/utility functions
481 const auto *src_buffer = Get<BUFFER_STATE>(srcBuffer);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700482 const auto src_access =
483 (src_buffer && !src_buffer->sparse) ? tracker->GetMemoryAccesses(src_buffer->binding.mem_state->mem) : nullptr;
John Zulauf9cb530d2019-09-30 14:14:10 -0600484 const auto *dst_buffer = Get<BUFFER_STATE>(dstBuffer);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700485 const auto dst_access =
486 (dst_buffer && !dst_buffer->sparse) ? tracker->GetMemoryAccesses(dst_buffer->binding.mem_state->mem) : nullptr;
John Zulauf9cb530d2019-09-30 14:14:10 -0600487
488 for (uint32_t region = 0; region < regionCount; region++) {
489 const auto &copy_region = pRegions[region];
490 if (src_access) {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700491 ResourceAccessRange src_range = MakeMemoryAccessRange(*src_buffer, copy_region.srcOffset, copy_region.size);
John Zulauf9cb530d2019-09-30 14:14:10 -0600492 auto hazard = DetectHazard(*src_access, SYNC_TRANSFER_TRANSFER_READ, src_range);
493 if (hazard.hazard) {
494 // TODO -- add tag information to log msg when useful.
495 skip |= LogError(srcBuffer, string_SyncHazardVUID(hazard.hazard), "Hazard %s for srcBuffer %s, region %" PRIu32,
496 string_SyncHazard(hazard.hazard), report_data->FormatHandle(srcBuffer).c_str(), region);
497 }
498 }
499 if (dst_access && !skip) {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700500 ResourceAccessRange dst_range = MakeMemoryAccessRange(*dst_buffer, copy_region.dstOffset, copy_region.size);
John Zulauf9cb530d2019-09-30 14:14:10 -0600501 auto hazard = DetectHazard(*dst_access, SYNC_TRANSFER_TRANSFER_WRITE, dst_range);
502 if (hazard.hazard) {
503 skip |= LogError(dstBuffer, string_SyncHazardVUID(hazard.hazard), "Hazard %s for dstBuffer %s, region %" PRIu32,
504 string_SyncHazard(hazard.hazard), report_data->FormatHandle(dstBuffer).c_str(), region);
505 }
506 }
507 if (skip) break;
508 }
509 }
510 return skip;
511}
512
513void SyncValidator::PreCallRecordCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer,
514 uint32_t regionCount, const VkBufferCopy *pRegions) {
515 auto *tracker = GetAccessTracker(commandBuffer);
516 assert(tracker);
517 const auto *src_buffer = Get<BUFFER_STATE>(srcBuffer);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700518 const auto src_access =
519 (src_buffer && !src_buffer->sparse) ? tracker->GetMemoryAccesses(src_buffer->binding.mem_state->mem) : nullptr;
John Zulauf9cb530d2019-09-30 14:14:10 -0600520 const auto *dst_buffer = Get<BUFFER_STATE>(dstBuffer);
John Zulauf5c5e88d2019-12-26 11:22:02 -0700521 const auto dst_access =
522 (dst_buffer && !dst_buffer->sparse) ? tracker->GetMemoryAccesses(dst_buffer->binding.mem_state->mem) : nullptr;
John Zulauf9cb530d2019-09-30 14:14:10 -0600523
524 for (uint32_t region = 0; region < regionCount; region++) {
525 const auto &copy_region = pRegions[region];
526 if (src_access) {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700527 ResourceAccessRange src_range = MakeMemoryAccessRange(*src_buffer, copy_region.srcOffset, copy_region.size);
528 UpdateAccessState(src_access, SYNC_TRANSFER_TRANSFER_READ, src_range, tag);
John Zulauf9cb530d2019-09-30 14:14:10 -0600529 }
530 if (dst_access) {
John Zulauf5c5e88d2019-12-26 11:22:02 -0700531 ResourceAccessRange dst_range = MakeMemoryAccessRange(*dst_buffer, copy_region.dstOffset, copy_region.size);
532 UpdateAccessState(dst_access, SYNC_TRANSFER_TRANSFER_WRITE, dst_range, tag);
533 }
534 }
535}
536
537bool SyncValidator::PreCallValidateCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
538 VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
539 const VkImageCopy *pRegions) const {
540 bool skip = false;
541 auto *tracker = GetAccessTracker(commandBuffer);
542 if (tracker) {
543 const auto *src_image = Get<IMAGE_STATE>(srcImage);
544 const auto src_access = tracker->GetImageAccesses(srcImage);
545 const auto *dst_image = Get<IMAGE_STATE>(dstImage);
546 const auto dst_access = tracker->GetImageAccesses(dstImage);
547
548 for (uint32_t region = 0; region < regionCount; region++) {
549 const auto &copy_region = pRegions[region];
550 if (src_access) {
551 auto hazard = DetectHazard(*src_image, *src_access, SYNC_TRANSFER_TRANSFER_READ, copy_region.srcSubresource,
552 copy_region.srcOffset, copy_region.extent);
553 if (hazard.hazard) {
554 skip |= LogError(srcImage, string_SyncHazardVUID(hazard.hazard), "Hazard %s for srcImage %s, region %" PRIu32,
555 string_SyncHazard(hazard.hazard), report_data->FormatHandle(srcImage).c_str(), region);
556 }
557 }
558 if (dst_access) {
559 auto hazard = DetectHazard(*dst_image, *dst_access, SYNC_TRANSFER_TRANSFER_WRITE, copy_region.dstSubresource,
560 copy_region.dstOffset, copy_region.extent);
561 if (hazard.hazard) {
562 skip |= LogError(dstImage, string_SyncHazardVUID(hazard.hazard), "Hazard %s for dstImage %s, region %" PRIu32,
563 string_SyncHazard(hazard.hazard), report_data->FormatHandle(dstImage).c_str(), region);
564 }
565 }
566 }
567 }
568 return skip;
569}
570
571void SyncValidator::PreCallRecordCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
572 VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
573 const VkImageCopy *pRegions) {
574 auto *tracker = GetAccessTracker(commandBuffer);
575 assert(tracker);
576 auto *src_image = Get<IMAGE_STATE>(srcImage);
577 auto src_access = tracker->GetImageAccesses(srcImage);
578 auto *dst_image = Get<IMAGE_STATE>(dstImage);
579 auto dst_access = tracker->GetImageAccesses(dstImage);
580
581 for (uint32_t region = 0; region < regionCount; region++) {
582 const auto &copy_region = pRegions[region];
583 if (src_access) {
584 UpdateAccessState(*src_image, src_access, SYNC_TRANSFER_TRANSFER_READ, copy_region.srcSubresource,
585 copy_region.srcOffset, copy_region.extent, tag);
586 }
587 if (dst_access) {
588 UpdateAccessState(*dst_image, dst_access, SYNC_TRANSFER_TRANSFER_WRITE, copy_region.dstSubresource,
589 copy_region.dstOffset, copy_region.extent, tag);
John Zulauf9cb530d2019-09-30 14:14:10 -0600590 }
591 }
592}
593
594bool SyncValidator::PreCallValidateCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
595 VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
596 uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
597 uint32_t bufferMemoryBarrierCount,
598 const VkBufferMemoryBarrier *pBufferMemoryBarriers,
599 uint32_t imageMemoryBarrierCount,
600 const VkImageMemoryBarrier *pImageMemoryBarriers) const {
601 bool skip = false;
John Zulauf0cb5be22020-01-23 12:18:22 -0700602 const auto *tracker = GetAccessTracker(commandBuffer);
603 if (!tracker) return skip;
604
605 const auto cb_state = Get<CMD_BUFFER_STATE>(commandBuffer);
606 assert(cb_state);
607 if (!cb_state) return skip;
608
609 const auto src_stage_mask = ExpandPipelineStages(GetQueueFlags(*cb_state), srcStageMask);
610 auto src_stage_scope = AccessScopeByStage(src_stage_mask);
611 // Validate Image Layout transitions
612 for (uint32_t index = 0; index < imageMemoryBarrierCount; index++) {
613 const auto &barrier = pImageMemoryBarriers[index];
614 if (barrier.newLayout == barrier.oldLayout) continue; // Only interested in layout transitions at this point.
615 const auto *image_state = Get<IMAGE_STATE>(barrier.image);
616 if (!image_state) continue;
617 const auto hazard = DetectImageBarrierHazard(*tracker, *image_state, src_stage_mask, src_stage_scope, barrier);
618 if (hazard.hazard) {
619 // TODO -- add tag information to log msg when useful.
620 skip |= LogError(barrier.image, string_SyncHazardVUID(hazard.hazard), "Hazard %s for image barrier %" PRIu32 " %s",
621 string_SyncHazard(hazard.hazard), index, report_data->FormatHandle(barrier.image).c_str());
622 }
623 }
John Zulauf9cb530d2019-09-30 14:14:10 -0600624
625 return skip;
626}
627
628void SyncValidator::PreCallRecordCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
629 VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
630 uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
631 uint32_t bufferMemoryBarrierCount,
632 const VkBufferMemoryBarrier *pBufferMemoryBarriers,
633 uint32_t imageMemoryBarrierCount,
634 const VkImageMemoryBarrier *pImageMemoryBarriers) {
635 // Just implement the buffer barrier for now
636 auto *tracker = GetAccessTracker(commandBuffer);
637 assert(tracker);
John Zulauf9cb530d2019-09-30 14:14:10 -0600638
John Zulauf0cb5be22020-01-23 12:18:22 -0700639 const auto cb_state = Get<CMD_BUFFER_STATE>(commandBuffer);
640 assert(cb_state);
641 if (!cb_state) return;
642
643 const auto src_stage_mask = ExpandPipelineStages(GetQueueFlags(*cb_state), srcStageMask);
644 auto src_stage_scope = AccessScopeByStage(src_stage_mask);
645 const auto dst_stage_mask = ExpandPipelineStages(GetQueueFlags(*cb_state), dstStageMask);
646 auto dst_stage_scope = AccessScopeByStage(dst_stage_mask);
647
648 ApplyBufferBarriers(tracker, src_stage_mask, src_stage_scope, dst_stage_mask, dst_stage_scope, bufferMemoryBarrierCount,
John Zulauf9cb530d2019-09-30 14:14:10 -0600649 pBufferMemoryBarriers);
John Zulauf0cb5be22020-01-23 12:18:22 -0700650 ApplyImageBarriers(tracker, src_stage_mask, src_stage_scope, dst_stage_mask, dst_stage_scope, imageMemoryBarrierCount,
John Zulauf5c5e88d2019-12-26 11:22:02 -0700651 pImageMemoryBarriers);
John Zulauf9cb530d2019-09-30 14:14:10 -0600652
653 // Apply these last in-case there operation is a superset of the other two and would clean them up...
John Zulauf0cb5be22020-01-23 12:18:22 -0700654 ApplyGlobalBarriers(tracker, src_stage_mask, dst_stage_mask, src_stage_scope, dst_stage_scope, memoryBarrierCount,
655 pMemoryBarriers);
John Zulauf9cb530d2019-09-30 14:14:10 -0600656}
657
658void SyncValidator::PostCallRecordCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo,
659 const VkAllocationCallbacks *pAllocator, VkDevice *pDevice, VkResult result) {
660 // The state tracker sets up the device state
661 StateTracker::PostCallRecordCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, result);
662
663 // Add the callback hooks for the functions that are either broadly or deeply used and that the ValidationStateTracker refactor
664 // would be messier without.
665 // TODO: Find a good way to do this hooklessly.
666 ValidationObject *device_object = GetLayerDataPtr(get_dispatch_key(*pDevice), layer_data_map);
667 ValidationObject *validation_data = GetValidationObject(device_object->object_dispatch, LayerObjectTypeSyncValidation);
668 SyncValidator *sync_device_state = static_cast<SyncValidator *>(validation_data);
669
670 sync_device_state->SetCommandBufferResetCallback(
671 [sync_device_state](VkCommandBuffer command_buffer) -> void { sync_device_state->ResetCommandBuffer(command_buffer); });
672}