Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timeline semaphore support #75

Open
wants to merge 15 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/avk/avk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <queue>
#include <set>
#include <unordered_set>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -975,6 +976,22 @@ namespace avk
#pragma region semaphore
static semaphore create_semaphore(vk::Device aDevice, const DISPATCH_LOADER_CORE_TYPE& aDispatchLoader, std::function<void(semaphore_t&)> aAlterConfigBeforeCreation = {});
semaphore create_semaphore(std::function<void(semaphore_t&)> aAlterConfigBeforeCreation = {});
/**
* @brief Creates a timeline semaphore
* @param aPayload (optional) The initial value of the payload. Defaults to 0.
* @param aAlterConfigBeforeCreation (optional) Use it to alter the timeline semaphore configuration before it is actually being created.
* @return The created semaphore.
*/
semaphore create_timeline_semaphore(uint64_t aPayload = 0, std::function<void(semaphore_t&)> aAlterConfigBeforeCreation = {});

/**
* @brief Waits on host until the condition specified with the parameters is met.
* @param aSemaphoreValueInfos Span of semaphore_value_info structs, each containing a semaphore and a payload value to wait on. All semaphores are required to be owned by the same logical device.
* @param aWaitOnAll (optional) If true, waits until ALL semaphores have reached their target timestamps. If false, waits until ANY semaphore has reached its target timestamp.
* @param aTimeout (optional) Defines a timeout (in nanoseconds) after which the function returns regardless of the semaphore state.
* @return Value of type vk::Result containing information about whether the wait operation succeeded, or the timeout has been triggered.
*/
static vk::Result wait_until_signaled(std::span<avk::semaphore_value_info> aSemaphoreValueInfos, bool aWaitOnAll = true, std::optional<uint64_t> aTimeout = {});
#pragma endregion

#pragma region shader
Expand Down
109 changes: 105 additions & 4 deletions include/avk/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1235,31 +1235,132 @@ namespace avk
#endif
}

/** Helper struct to specify a semaphore and a value for it.
* This is used when waiting for / signaling timeline semaphores.
*
* Can be created in the following ways:
* 1) sem = val
* 2) sem >= val
*
* Case 1) makes sense when signaling semaphores, while case 2) makes sense when waiting for semaphores.
*/
struct semaphore_value_info
{
avk::resource_argument<avk::semaphore_t> mSemaphore;
uint64_t mValue;
};

inline auto operator>=(avk::resource_argument<avk::semaphore_t> aSemaphore, uint64_t aValue) -> semaphore_value_info {
return semaphore_value_info{ std::move(aSemaphore), aValue };
}

/**
* @brief Info struct that defines blocking behavior on the gpu at specific pipeline stages for a specific semaphore
*
* Can be created in the following ways:
* 1) sem >> pipelineFlags
* 2) sem >= val >> pipelineFlags
* 3) (sem >= val) >> pipelineFlags
*
* sem... the semaphore to wait for
* val... which semaphore value to wait on (timeline semaphores only)
* pipelineFlags... defines which pipeline stages should wait for the semaphore
*/
struct semaphore_wait_info
{
avk::resource_argument<avk::semaphore_t> mWaitSemaphore;
avk::stage::pipeline_stage_flags mDstStage;
uint64_t mValue;
};

inline semaphore_wait_info operator>> (avk::resource_argument<avk::semaphore_t> a, avk::stage::pipeline_stage_flags b)
inline auto operator>> (avk::resource_argument<avk::semaphore_t> aSemaphore, avk::stage::pipeline_stage_flags aStageFlags) -> semaphore_wait_info
{
return semaphore_wait_info{ std::move(aSemaphore), aStageFlags, 0 };
}

inline auto operator>> (semaphore_value_info aSemaphoreValueInfo, avk::stage::pipeline_stage_flags aStageFlags) -> semaphore_wait_info
{
return semaphore_wait_info{ std::move(a), b };
return semaphore_wait_info{ std::move(aSemaphoreValueInfo.mSemaphore), aStageFlags, aSemaphoreValueInfo.mValue };
}

/**
* Allows `waitSemaphore >= waitValue >> pipelineStageFlags` without parentheses around `>=`.
* Requires `operator>>(uint64_t, avk::stage::pipeline_stage_flags) -> semaphore_wait_info` to work
*
* Unwanted side effect: `waitValue >> pipelineStageFlags` compiles but produces an invalid semaphore_wait_info
*/
inline auto operator>=(avk::resource_argument<avk::semaphore_t> aSemaphore, semaphore_wait_info aSemWaitInfo) -> semaphore_wait_info {
aSemWaitInfo.mWaitSemaphore = std::move(aSemaphore);
return aSemWaitInfo;
}

/**
* Allows `waitSemaphore >= waitValue >> pipelineStageFlags` without parentheses around `>=`
* Requires `operator>>(uint64_t, avk::stage::pipeline_stage_flags) -> semaphore_wait_info` to work
*
* Unwanted side effect: `waitValue >> pipelineStageFlags` compiles but produces an invalid semaphore_wait_info
*/
inline auto operator>=(avk::owning_resource<avk::semaphore_t> aSemaphore, semaphore_wait_info aSemWaitInfo)->semaphore_wait_info {
aSemWaitInfo.mWaitSemaphore = std::move(aSemaphore);
return aSemWaitInfo;
}
} // namespace avk

/**
* Allows `waitSemaphore >= waitValue >> pipelineStageFlags` without parentheses around `>=`
* Requires `operator>=(avk::resource_argument<avk::semaphore_t>, semaphore_wait_info) -> semaphore_wait_info` to work
*
* Unwanted side effect: `waitValue >> pipelineStageFlags` compiles but produces an invalid semaphore_wait_info
* @note This operator overload is defined in global scope because it weirdly wasn't found by auto_vk_toolkit applications otherwise.
*/
inline auto operator>>(uint64_t aValue, avk::stage::pipeline_stage_flags aStageFlags) -> avk::semaphore_wait_info {
return avk::semaphore_wait_info{ avk::owning_resource<avk::semaphore_t>(), aStageFlags, aValue};
}
Comment on lines +1297 to +1318
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the expression below, the fixed operator precedence of C++ leads to an undesireable evaluation order:
sem >= val >> flags
sem >= (val >> flags)
Because of this I had to define operator>>(uint64_t, avk::stage::pipeline_stage_flags) which results in an incomplete semaphore_wait_info that has to be completed with operator>=.


namespace avk {
Comment on lines +1307 to +1320
Copy link
Collaborator Author

@MoritzRoth MoritzRoth Dec 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to use this operator overload from another solution (i.e. model_loader) the compiler ran into issues finding the function. This strangely does not happen when I define the function in global scope. Since argument-dependent lookup cannot consider the return type during name lookup and the function definition isn't inside the namespaces of uint64_t or avk::stage::pipeline_stage_flags the overload can't be found. A cleaner fix probably just defines the overload in namespace avk::stage.


/**
* @brief Info struct that defines semaphore signaling bahavior after specific pipeline stages have concluded
*
* Can be created in the following ways:
* 1) pipelineFlags >> sem
* 2) pipelineFlags >> sem = val
* 3) pipelineFlags >> (sem = val)
*
* pipelineFlags... defines which pipeline stages must be cleared before the semaphore may be signaled
* sem... the semaphore to signal
* val... the value to signal the semaphore to (timeline semaphores only)
*/
struct semaphore_signal_info
{
avk::stage::pipeline_stage_flags mSrcStage;
avk::resource_argument<avk::semaphore_t> mSignalSemaphore;
uint64_t mValue;

/**
* @brief Allows `pipelineFlags >> sem = val`
*
* Due to right associativity of operator= shouldn't cause unwanted side-effects.
*
* @note This does not cover the case `pipelineFlags >> (sem = val)`.
* To allow this ^, explicit template specializations of owning_resource and resource_argument for semaphore_t are defined in semaphore.hpp
*
*/
auto operator=(uint64_t aValue) -> semaphore_signal_info& {
mValue = aValue;
return *this;
}
};

inline semaphore_signal_info operator>> (avk::stage::pipeline_stage_flags a, avk::resource_argument<avk::semaphore_t> b)
inline auto operator>> (avk::stage::pipeline_stage_flags aStageFlags, avk::resource_argument<avk::semaphore_t> aSemaphore) -> semaphore_signal_info
{
return semaphore_signal_info{ a, std::move(b) };
return semaphore_signal_info{ aStageFlags, std::move(aSemaphore), 0 };
}

inline auto operator>> (avk::stage::pipeline_stage_flags aStageFlags, semaphore_value_info aSemaphoreValueInfo) -> semaphore_signal_info
{
return semaphore_signal_info{ aStageFlags, std::move(aSemaphoreValueInfo.mSemaphore), aSemaphoreValueInfo.mValue };
}

class recorded_command_buffer;

Expand Down
12 changes: 12 additions & 0 deletions include/avk/cpp_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,12 @@ namespace avk
std::end(x);
};

class semaphore_t; // defined in semaphore.hpp
template<typename T> requires non_const<T>
class owning_resource;
template<>
class owning_resource<semaphore_t>; // defined in semaphore.hpp

// This class represents a/the owner of a specific resource T.
//
// The resource is either held locally on the stack, or -- as an additional features -- moved onto
Expand Down Expand Up @@ -579,6 +585,12 @@ namespace avk
}
};


template<typename T>
class resource_argument;
template<>
class resource_argument<semaphore_t>; // defined in semaphore.hpp

// A type for passing resources as arguments. Can be used to express that
// ownership shall be passed along with it, or only a reference to it.
template <typename T>
Expand Down
Loading
Loading