Skip to content

Commit

Permalink
layers: Refactor SyncOp functor factories
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-lunarg committed Jan 23, 2025
1 parent 6cf616f commit 5fb114b
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 122 deletions.
207 changes: 98 additions & 109 deletions layers/sync/sync_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,98 @@ class FilteredGeneratorGenerator {

using EventImageRangeGenerator = FilteredGeneratorGenerator<AccessContext::ScopeMap, subresource_adapter::ImageRangeGenerator>;


namespace PipelineBarrier {
namespace {
SingleRangeGenerator<ResourceAccessRange> MakeRangeGen(const vvl::Buffer &buffer, const ResourceAccessRange &range) {
if (!SimpleBinding(buffer)) return ResourceAccessRange();
const auto base_address = ResourceBaseAddress(buffer);
return (range + base_address);
}

subresource_adapter::ImageRangeGenerator MakeRangeGen(const syncval_state::ImageState &image,
const VkImageSubresourceRange &subresource_range) {
return image.MakeImageRangeGen(subresource_range, false);
}
} // namespace

template <typename Barriers>
void ApplyBarriers(const Barriers &barriers, QueueId queue_id, AccessContext *access_context) {
for (const auto &barrier : barriers) {
const auto *state = barrier.GetState();
if (state) {
auto update_action =
ApplyBarrierFunctor<PipelineBarrierOp>(PipelineBarrierOp(queue_id, barrier.barrier, barrier.IsLayoutTransition()));
auto range_gen = MakeRangeGen(*state, barrier.Range());
access_context->UpdateMemoryAccessState(update_action, range_gen);
}
}
}

void ApplyGlobalBarriers(const std::vector<SyncBarrier> &barriers, QueueId queue_id, ResourceUsageTag tag,
AccessContext *access_context) {
auto barriers_functor = ApplyBarrierOpsFunctor<PipelineBarrierOp>(true, barriers.size(), tag);
for (const auto &barrier : barriers) {
barriers_functor.EmplaceBack(PipelineBarrierOp(queue_id, barrier, false));
}
auto range_gen = SingleRangeGenerator<ResourceAccessRange>(kFullRange);
access_context->UpdateMemoryAccessState(barriers_functor, range_gen);
}
} // namespace PipelineBarrier

namespace Events {
namespace {
// Need to restrict to only valid exec and access scope for this event
// Pass by value is intentional to get a copy we can change without modifying the passed barrier
SyncBarrier RestrictToEvent(const SyncEventState &sync_event, const SyncBarrier &barrier) {
SyncBarrier result = barrier;
result.src_exec_scope.exec_scope = sync_event.scope.exec_scope & barrier.src_exec_scope.exec_scope;
result.src_access_scope = sync_event.scope.valid_accesses & barrier.src_access_scope;
return result;
}

EventSimpleRangeGenerator MakeRangeGen(const SyncEventState &sync_event, const vvl::Buffer &buffer,
const ResourceAccessRange &range_arg) {
const auto base_address = ResourceBaseAddress(buffer);
ResourceAccessRange range = SimpleBinding(buffer) ? (range_arg + base_address) : ResourceAccessRange();
EventSimpleRangeGenerator filtered_range_gen(sync_event.FirstScope(), range);
return filtered_range_gen;
}

EventImageRangeGenerator MakeRangeGen(const SyncEventState &sync_event, const syncval_state::ImageState &image,
const VkImageSubresourceRange &subresource_range) {
ImageRangeGen image_range_gen = image.MakeImageRangeGen(subresource_range, false);
EventImageRangeGenerator filtered_range_gen(sync_event.FirstScope(), image_range_gen);
return filtered_range_gen;
}
} // namespace

template <typename Barriers>
void ApplyBarriers(const SyncEventState &sync_event, const Barriers &barriers, QueueId queue_id, AccessContext *access_context) {
for (const auto &barrier : barriers) {
const auto *state = barrier.GetState();
if (state) {
auto restricted_barrier = RestrictToEvent(sync_event, barrier.barrier);
auto update_action = ApplyBarrierFunctor<WaitEventBarrierOp>(
WaitEventBarrierOp(queue_id, sync_event.first_scope_tag, restricted_barrier, barrier.IsLayoutTransition()));
auto range_gen = MakeRangeGen(sync_event, *state, barrier.Range());
access_context->UpdateMemoryAccessState(update_action, range_gen);
}
}
}

void ApplyGlobalBarriers(const SyncEventState &sync_event, const std::vector<SyncBarrier> &barriers, QueueId queue_id,
ResourceUsageTag tag, AccessContext *access_context) {
auto barriers_functor = ApplyBarrierOpsFunctor<WaitEventBarrierOp>(false, barriers.size(), tag);
for (const auto &barrier : barriers) {
auto restricted_barrier = RestrictToEvent(sync_event, barrier);
barriers_functor.EmplaceBack(WaitEventBarrierOp(queue_id, sync_event.first_scope_tag, restricted_barrier, false));
}
auto range_gen = EventSimpleRangeGenerator(sync_event.FirstScope(), kFullRange);
access_context->UpdateMemoryAccessState(barriers_functor, range_gen);
}
} // namespace Events

SyncOpBarriers::SyncOpBarriers(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount,
Expand Down Expand Up @@ -280,61 +372,6 @@ bool SyncOpPipelineBarrier::Validate(const CommandBufferAccessContext &cb_contex
return skip;
}

struct SyncOpPipelineBarrierFunctorFactory {
using BarrierOpFunctor = PipelineBarrierOp;
using ApplyFunctor = ApplyBarrierFunctor<BarrierOpFunctor>;
using GlobalBarrierOpFunctor = PipelineBarrierOp;
using GlobalApplyFunctor = ApplyBarrierOpsFunctor<GlobalBarrierOpFunctor>;
using BufferRange = SingleRangeGenerator<ResourceAccessRange>;
using ImageRange = subresource_adapter::ImageRangeGenerator;
using GlobalRange = SingleRangeGenerator<ResourceAccessRange>;
using ImageState = syncval_state::ImageState;

ApplyFunctor MakeApplyFunctor(QueueId queue_id, const SyncBarrier &barrier, bool layout_transition) const {
return ApplyFunctor(BarrierOpFunctor(queue_id, barrier, layout_transition));
}
GlobalApplyFunctor MakeGlobalApplyFunctor(size_t size_hint, ResourceUsageTag tag) const {
return GlobalApplyFunctor(true /* resolve */, size_hint, tag);
}
GlobalBarrierOpFunctor MakeGlobalBarrierOpFunctor(QueueId queue_id, const SyncBarrier &barrier) const {
return GlobalBarrierOpFunctor(queue_id, barrier, false);
}

BufferRange MakeRangeGen(const vvl::Buffer &buffer, const ResourceAccessRange &range) const {
if (!SimpleBinding(buffer)) return ResourceAccessRange();
const auto base_address = ResourceBaseAddress(buffer);
return (range + base_address);
}
ImageRange MakeRangeGen(const ImageState &image, const VkImageSubresourceRange &subresource_range) const {
return image.MakeImageRangeGen(subresource_range, false);
}
GlobalRange MakeGlobalRangeGen() const { return kFullRange; }
};

template <typename Barriers, typename FunctorFactory>
void SyncOpBarriers::ApplyBarriers(const Barriers &barriers, const FunctorFactory &factory, const QueueId queue_id,
const ResourceUsageTag tag, AccessContext *access_context) {
for (const auto &barrier : barriers) {
const auto *state = barrier.GetState();
if (state) {
auto update_action = factory.MakeApplyFunctor(queue_id, barrier.barrier, barrier.IsLayoutTransition());
auto range_gen = factory.MakeRangeGen(*state, barrier.Range());
access_context->UpdateMemoryAccessState(update_action, range_gen);
}
}
}

template <typename Barriers, typename FunctorFactory>
void SyncOpBarriers::ApplyGlobalBarriers(const Barriers &barriers, const FunctorFactory &factory, const QueueId queue_id,
const ResourceUsageTag tag, AccessContext *access_context) {
auto barriers_functor = factory.MakeGlobalApplyFunctor(barriers.size(), tag);
for (const auto &barrier : barriers) {
barriers_functor.EmplaceBack(factory.MakeGlobalBarrierOpFunctor(queue_id, barrier));
}
auto range_gen = factory.MakeGlobalRangeGen();
access_context->UpdateMemoryAccessState(barriers_functor, range_gen);
}

ResourceUsageTag SyncOpPipelineBarrier::Record(CommandBufferAccessContext *cb_context) {
const auto tag = cb_context->NextCommandTag(command_);
for (const auto &barrier_set : barriers_) {
Expand All @@ -350,7 +387,6 @@ ResourceUsageTag SyncOpPipelineBarrier::Record(CommandBufferAccessContext *cb_co
}

void SyncOpPipelineBarrier::ReplayRecord(CommandExecutionContext &exec_context, const ResourceUsageTag exec_tag) const {
SyncOpPipelineBarrierFunctorFactory factory;
// Pipeline barriers only have a single barrier set, unlike WaitEvents2
assert(barriers_.size() == 1);
const auto &barrier_set = barriers_[0];
Expand All @@ -359,9 +395,9 @@ void SyncOpPipelineBarrier::ReplayRecord(CommandExecutionContext &exec_context,
SyncEventsContext *events_context = exec_context.GetCurrentEventsContext();
AccessContext *access_context = exec_context.GetCurrentAccessContext();
const auto queue_id = exec_context.GetQueueId();
ApplyBarriers(barrier_set.buffer_memory_barriers, factory, queue_id, exec_tag, access_context);
ApplyBarriers(barrier_set.image_memory_barriers, factory, queue_id, exec_tag, access_context);
ApplyGlobalBarriers(barrier_set.memory_barriers, factory, queue_id, exec_tag, access_context);
PipelineBarrier::ApplyBarriers(barrier_set.buffer_memory_barriers, queue_id, access_context);
PipelineBarrier::ApplyBarriers(barrier_set.image_memory_barriers, queue_id, access_context);
PipelineBarrier::ApplyGlobalBarriers(barrier_set.memory_barriers, queue_id, exec_tag, access_context);
if (barrier_set.single_exec_scope) {
events_context->ApplyBarrier(barrier_set.src_exec_scope, barrier_set.dst_exec_scope, exec_tag);
} else {
Expand Down Expand Up @@ -680,52 +716,6 @@ bool SyncOpWaitEvents::DoValidate(const CommandExecutionContext &exec_context, c
return skip;
}

struct SyncOpWaitEventsFunctorFactory {
using BarrierOpFunctor = WaitEventBarrierOp;
using ApplyFunctor = ApplyBarrierFunctor<BarrierOpFunctor>;
using GlobalBarrierOpFunctor = WaitEventBarrierOp;
using GlobalApplyFunctor = ApplyBarrierOpsFunctor<GlobalBarrierOpFunctor>;
using BufferRange = EventSimpleRangeGenerator;
using ImageRange = EventImageRangeGenerator;
using GlobalRange = EventSimpleRangeGenerator;
using ImageState = syncval_state::ImageState;

// Need to restrict to only valid exec and access scope for this event
// Pass by value is intentional to get a copy we can change without modifying the passed barrier
SyncBarrier RestrictToEvent(SyncBarrier barrier) const {
barrier.src_exec_scope.exec_scope = sync_event->scope.exec_scope & barrier.src_exec_scope.exec_scope;
barrier.src_access_scope = sync_event->scope.valid_accesses & barrier.src_access_scope;
return barrier;
}
ApplyFunctor MakeApplyFunctor(QueueId queue_id, const SyncBarrier &barrier_arg, bool layout_transition) const {
auto barrier = RestrictToEvent(barrier_arg);
return ApplyFunctor(BarrierOpFunctor(queue_id, sync_event->first_scope_tag, barrier, layout_transition));
}
GlobalApplyFunctor MakeGlobalApplyFunctor(size_t size_hint, ResourceUsageTag tag) const {
return GlobalApplyFunctor(false /* don't resolve */, size_hint, tag);
}
GlobalBarrierOpFunctor MakeGlobalBarrierOpFunctor(const QueueId queue_id, const SyncBarrier &barrier_arg) const {
auto barrier = RestrictToEvent(barrier_arg);
return GlobalBarrierOpFunctor(queue_id, sync_event->first_scope_tag, barrier, false);
}

BufferRange MakeRangeGen(const vvl::Buffer &buffer, const ResourceAccessRange &range_arg) const {
const auto base_address = ResourceBaseAddress(buffer);
ResourceAccessRange range = SimpleBinding(buffer) ? (range_arg + base_address) : ResourceAccessRange();
EventSimpleRangeGenerator filtered_range_gen(sync_event->FirstScope(), range);
return filtered_range_gen;
}
ImageRange MakeRangeGen(const ImageState &image, const VkImageSubresourceRange &subresource_range) const {
ImageRangeGen image_range_gen = image.MakeImageRangeGen(subresource_range, false);
EventImageRangeGenerator filtered_range_gen(sync_event->FirstScope(), image_range_gen);

return filtered_range_gen;
}
GlobalRange MakeGlobalRangeGen() const { return EventSimpleRangeGenerator(sync_event->FirstScope(), kFullRange); }
SyncOpWaitEventsFunctorFactory(SyncEventState *sync_event_) : sync_event(sync_event_) { assert(sync_event); }
SyncEventState *sync_event;
};

ResourceUsageTag SyncOpWaitEvents::Record(CommandBufferAccessContext *cb_context) {
const auto tag = cb_context->NextCommandTag(command_);

Expand Down Expand Up @@ -760,10 +750,9 @@ void SyncOpWaitEvents::ReplayRecord(CommandExecutionContext &exec_context, Resou
// These apply barriers one at a time as the are restricted to the resource ranges specified per each barrier,
// but do not update the dependency chain information (but set the "pending" state) // s.t. the order independence
// of the barriers is maintained.
SyncOpWaitEventsFunctorFactory factory(sync_event);
ApplyBarriers(barrier_set.buffer_memory_barriers, factory, queue_id, exec_tag, access_context);
ApplyBarriers(barrier_set.image_memory_barriers, factory, queue_id, exec_tag, access_context);
ApplyGlobalBarriers(barrier_set.memory_barriers, factory, queue_id, exec_tag, access_context);
Events::ApplyBarriers(*sync_event, barrier_set.buffer_memory_barriers, queue_id, access_context);
Events::ApplyBarriers(*sync_event, barrier_set.image_memory_barriers, queue_id, access_context);
Events::ApplyGlobalBarriers(*sync_event, barrier_set.memory_barriers, queue_id, exec_tag, access_context);

// Apply the global barrier to the event itself (for race condition tracking)
// Events don't happen at a stage, so we need to store the unexpanded ALL_COMMANDS if set for inter-event-calls
Expand Down
14 changes: 1 addition & 13 deletions layers/sync/sync_op.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class RenderPass;
class CommandBuffer;
} // namespace vvl

using SyncMemoryBarrier = SyncBarrier;

struct SyncEventState {
enum IgnoreReason { NotIgnored = 0, ResetWaitRace, Reset2WaitRace, SetRace, MissingStageBits, SetVsWait2, MissingSetEvent };
using EventPointer = std::shared_ptr<const vvl::Event>;
Expand Down Expand Up @@ -167,21 +165,11 @@ class SyncOpBase {
virtual void ReplayRecord(CommandExecutionContext &exec_context, ResourceUsageTag exec_tag) const = 0;

protected:
// Only non-null and valid for SyncOps within a render pass instance WIP -- think about how to manage for non RPI calls within
// RPI and 2ndarys...
uint32_t subpass_ = VK_SUBPASS_EXTERNAL;
vvl::Func command_;
};

class SyncOpBarriers : public SyncOpBase {
protected:
template <typename Barriers, typename FunctorFactory>
static void ApplyBarriers(const Barriers &barriers, const FunctorFactory &factory, QueueId queue_id, ResourceUsageTag tag,
AccessContext *context);
template <typename Barriers, typename FunctorFactory>
static void ApplyGlobalBarriers(const Barriers &barriers, const FunctorFactory &factory, QueueId queue_id, ResourceUsageTag tag,
AccessContext *access_context);

SyncOpBarriers(vvl::Func command, const SyncValidator &sync_state, VkQueueFlags queue_flags, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount,
const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount,
Expand All @@ -198,7 +186,7 @@ class SyncOpBarriers : public SyncOpBase {
VkDependencyFlags dependency_flags;
SyncExecScope src_exec_scope;
SyncExecScope dst_exec_scope;
std::vector<SyncMemoryBarrier> memory_barriers;
std::vector<SyncBarrier> memory_barriers;
std::vector<SyncBufferMemoryBarrier> buffer_memory_barriers;
std::vector<SyncImageMemoryBarrier> image_memory_barriers;
bool single_exec_scope;
Expand Down

0 comments on commit 5fb114b

Please sign in to comment.