-
Notifications
You must be signed in to change notification settings - Fork 18
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
base: development
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job so far, but please mind the comments and update!
include/avk/commands.hpp
Outdated
inline semaphore_wait_info&& operator| (uint64_t aValue, semaphore_wait_info&& aInfo) | ||
{ | ||
aInfo.mValue = aValue; | ||
return std::move(aInfo); | ||
} | ||
|
||
inline semaphore_wait_info& operator| (uint64_t aValue, semaphore_wait_info& aInfo) | ||
{ | ||
aInfo.mValue = aValue; | ||
return aInfo; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not so sure if we should really abuse operator|
for that (because generally, Auto-Vk's API already abuses operators quite a lot).
Should we maybe rather, instead of aSrcSignalStage >> sem | aSignalValue
aim for something like aSrcSignalStage >> sem.at_value(aSignalValue)
?
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine by me. This seems like it would definitely be more understandable to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the semaphore type to construct the semaphore_wait_info
is avk::resource_argument<avk::semaphore_t>
I was hesitant to add an explicit template specialization for avk::semaphore_t
to avk::resource_argument
, adding the at_value
/ to_value
functions.
A return type for such a function would also need to be either a std::pair
, or some additional incomplete_semaphore_wait_info
struct type, which both seem weird to me.
My current solution is to just add the at_value
/ to_value
functions to the wait / signal info structs. Constructing the info structs would thus look like this (aSrcSignalStage >> sem).at_value(aSignalValue)
. If I'm missing something obvious or you can think of a better solution please let me know.
include/avk/commands.hpp
Outdated
inline semaphore_signal_info&& operator| (semaphore_signal_info&& aInfo, uint64_t aValue) | ||
{ | ||
aInfo.mValue = aValue; | ||
return std::move(aInfo); | ||
} | ||
|
||
inline semaphore_signal_info& operator| (semaphore_signal_info& aInfo, uint64_t aValue) | ||
{ | ||
aInfo.mValue = aValue; | ||
return aInfo; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here -- maybe better go for sem.to_value(aValue)
?
include/avk/semaphore.hpp
Outdated
// returns the current value of the timeline semaphore | ||
const uint64_t query_current_value() const; | ||
// sets the timeline semaphore to the specified value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consistently use /** ... */
comments for all the public methods!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also use new lines to separate two methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
61fa537
to
9c0f0b5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rework this once again. See also the changes that I'm going to push! And please don't see them as a final version, but improve them until everything looks and feels great!
include/avk/commands.hpp
Outdated
semaphore_wait_info& at_value(uint64_t aValue) { | ||
mValue = aValue; | ||
return *this; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I don't like this approach too much, because it leads to the impression that stage >> semaphore
is a unit, whereas semaphore + value
should be a unit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My most recent commit addresses this issue and semaphore_wait_info
/ semaphore_signal_info
can now be created via
semaphore_signal_info foo = pipelineStageFlags >> (signaledSemaphore, newValue);
semaphore_wait_info bar = (waitSemaphore, waitValue) >> pipelineStageFlags;
However, to get this to work I had to overload operator,(avk::resource_argument<avk::semaphore_t>, uint64_t)
. While I currently don't see any unwanted side effects this might have, it doesn't strike me as clean coding practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uff, operator,
sounds very adventurous. And what's more, it even requires the parens around it, right? So it actually isn't really of any benefit over constructing semaphore_value_info
directly via semaphore_value_info{signaledSemaphore, newValue}
or semaphore_value_info{waitSemaphore, waitValue}
.
One more thing comes to mind which might be cooler: Overloading opertator=
and operator==
. What do you think?
semaphore_signal_info foo = pipelineStageFlags >> signaledSemaphore = newValue;
semaphore_wait_info bar = waitSemaphore == waitValue >> pipelineStageFlags;
Is that expressive and well understandable and would that compile without parens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you're investigating operator=
and operator==
, please make sure that you handle both cases:
pipelineStageFlags >> signaledSemaphore = newValue
andpipelineStageFlags >> (signaledSemaphore = newValue)
waitSemaphore == waitValue >> pipelineStageFlags
and(waitSemaphore == waitValue) >> pipelineStageFlags
You might have to create different overloads for operator=
and operator==
to handle both cases. But this is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally got to it and implemented the suggested change.
semaphore_signal_info foo = pipelineStageFlags >> signaledSemaphore = newValue;
semaphore_wait_info bar = waitSemaphore >= waitValue >> pipelineStageFlags;
semaphore_value_info baz = semaphore = value;
semaphore_value_info quz = semaphore >= value;
Are all valid expressions now. Note that I used operator>=
instead of operator==
since this is the technically more correct way to specify it.
I again had to make some less desireable changes to get this to compile. I will highlight those in seperate threads so we can discuss potential workarounds (1, 2, 3)
include/avk/commands.hpp
Outdated
semaphore_signal_info& to_value(uint64_t aValue) { | ||
mValue = aValue; | ||
return *this; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...same here, but I have to agree that the at_value
/to_value
is not easy to get into class semaphore_t
.
So, I guess we should rather go back to operator|
or use operator>>
once again (as in the source code that I'm about to push).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above I now use operator,
to create semaphore_value_info
in a concise way, which can then be used together with operator>>
to create semaphore_wait_info
and semaphore_signal_info
.
include/avk/semaphore.hpp
Outdated
/** @brief Waits on host until the condition specified with the parameters is met. | ||
* @param aSemaphores Vector of timeline semaphores that should be waited on. All semaphores are required to be owned by the same logical device. | ||
* @param aTimestamps Vector of payload values to wait on. Is required to have the same size as aSemaphores. The n-th value in aTimestamps corresponds to the n-th entry in aSemaphores. | ||
* @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(const std::span<std::reference_wrapper<const semaphore_t>> aSemaphores, const std::span<uint64_t> &aTimestamps, bool aWaitOnAll = true, std::optional<uint64_t> aTimeout = {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is somewhat out of place here. Better turn it into a free function and move it somewhere into class root
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usage of std::span
is wrong here. std::span
should never be passed as const&
.
include/avk/semaphore.hpp
Outdated
/** @brief Waits on host until the condition specified with the parameters is met. | ||
* @param aDevice The logical device owning all referenced timeline semaphores. | ||
* @param aInfo Struct containing all relevant information about the wait operation. | ||
* @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(const vk::Device &aDevice, const vk::SemaphoreWaitInfo &aInfo, std::optional<uint64_t> aTimeout = {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...same applies here. Additionally, I just don't really know if there's value in having such a raw-Vulkan-version overload of this.
src/avk.cpp
Outdated
|
||
vk::Result semaphore_t::wait_until_signaled(uint64_t aRequiredValue, std::optional<uint64_t> aTimeout) const { | ||
std::vector<std::reference_wrapper<const semaphore_t>> semaphores{ *this }; | ||
return wait_until_signaled(semaphores, std::span<uint64_t>(&aRequiredValue, 1), true, aTimeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Construction of std::span
looks veeeery odd here. It actually defies the purpose of a std::span
. I.e, I would say, the point is to never explicitly create a std::span
, but use it to refer to just elements in contiguous memory (wherever they might be stored, e.g., std::array
or std::vector
, ...)
src/avk.cpp
Outdated
return wait_until_signaled(semaphores, std::span<uint64_t>(&aRequiredValue, 1), true, aTimeout); | ||
} | ||
|
||
vk::Result semaphore_t::wait_until_signaled(const std::span<std::reference_wrapper<const semaphore_t>> aSemaphores, const std::span<uint64_t>& aTimestamps, bool aWaitOnAll, std::optional<uint64_t> aTimeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having to pass two collections which must be aligned is very inconvenient and error-prone. This should be turned into a std::tuple
or a small helper struct instead -- then pass only one collection of that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mind the comments and once again, rebase on development
! That should fix the workflows which are failing.
include/avk/commands.hpp
Outdated
avk::resource_argument<avk::semaphore_t> mSemaphore; | ||
uint64_t mValue; | ||
}; | ||
inline semaphore_value_info operator,(avk::resource_argument<avk::semaphore_t> aSemaphore, uint64_t aValue) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add exactly one newline between each function/method!
Also make sure to add comments!
src/avk.cpp
Outdated
vk::SemaphoreWaitInfo info{ | ||
vk::SemaphoreWaitFlags{}, | ||
1u, | ||
handle_addr(), | ||
&aSignalValue | ||
}; | ||
mSemaphore.getOwner().waitSemaphores(info, aTimeout.value_or(UINT64_MAX)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad code formatting.
2d1439f
to
1e605e6
Compare
/** | ||
* @brief This is an explicit template specialization of owning_resource as defined in cpp_utils.hpp | ||
* It is a complete duplicate of the class that contains an additional overload for operator= (defined at the bottom). | ||
* | ||
* The definition of an explicit template specialization is required since operator= overloads cannot be defined as non-member functions. | ||
* (see https://en.cppreference.com/w/cpp/language/operators) | ||
* As far as I can tell, defining an explicit template specialization for a class requires duplicating the entire class even if only minor changes are made. | ||
* | ||
* | ||
* The overload allows instantiating semaphore_value_info structs with the expression `sem = val`. | ||
* Where the type of sem is `owning_resource<semaphore_t>` and the type of val is `uint64_t`. | ||
*/ | ||
template<> | ||
class owning_resource<semaphore_t> : public std::variant<std::monostate, semaphore_t, std::shared_ptr<semaphore_t>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I am defining an explicit template specialization for owning_resource<semaphore_t>
(and resource_argument<semaphore_t>
below) in order to add an overload for the =
operator. I can't simply use non-member operator overloads (like for >=
) since operator=
explicitly cannot be declared as non-member function.
This results in a LOT of duplicated code, since the remaining functions need to be redefined as well. As far as I am aware it isn't possible to define new member functions ( / operator overloads) for a specific template configuration without redefining the entire class, though I still hope I've missed something in the documentation.
/** | ||
* 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}; | ||
} |
There was a problem hiding this comment.
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 | ||
|
||
/** | ||
* 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}; | ||
} | ||
|
||
namespace avk { |
There was a problem hiding this comment.
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
.
add function create_timeline_semaphore allow specifying wait/signal values for device side sync
return vk::Result in semaphore_t::wait_until_signalled
change semaphore_wait/signal_info construction for timeline semaphores
…their current form. Consider moving the static functions out of semaphore_t and into class root!
And besides, NULL is C-esque. Use nullptr instead!
…>> value Not sure if it is prettier than operator|, but better than ().to_value(value)! Don't hesitate to introduce new small helper structs!
remove wait_unitl_signal overload that requires manual construction of vk::SemaphoreWaitInfo
achieved by adding operator,() overload allow creation of semaphore_signal_info via semaphore_value_info new semaphore_signal_info / semaphore_wait_info syntax: pipelineStageFlags >> (signalSemaphore, newValue) (waitSemaphore, waitValue) >> pipelineStageFlags
in some places tabs were mixed with 4 spaces for indentation note: only fixed cases related to timeline semaphore code to avoid merge conflicts
f9c647c
to
b713674
Compare
…esources which are no longer needed convert semaphore_t::mLifetimeHandledResources to a forward_list of owning resources together with a timestamp describing when they can be discarded choose forward_list due to constant insert at front and linear remove with predicate
Adds basic timeline semaphore support. Related issue: cg-tuwien/Auto-Vk-Toolkit#45
Changes:
Timeline semaphores functionality has been added to the existing class
avk::semaphore_t
, is well documented and nicely abstracted.Note: I added comments to some parts I would like feedback on.