Skip to content

Coroutines #2982

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

Open
wants to merge 64 commits into
base: master
Choose a base branch
from
Open

Conversation

mtszkarbowiak
Copy link
Contributor

@mtszkarbowiak mtszkarbowiak commented Oct 11, 2024

Observations:

  • Enumerator methods may not be used, because they can't yield to C++.
  • C++ 20 coroutines may not be used, because they are C++ 20 after all.

This quickly suggest homebrew Flax-specific solution. Unfortunately, Flax API limits require using chain of methods to set up stages of the coroutine. Thus, they are not as sexy as Unity enumerator method-based coroutines, but share the same API in both managed and native scripts. CRs are welcome!

Oh please, and since now on, please stop suggesting people to use TPL. Using other threads just for what are coroutines is ridiculous. (Implementing our own single-threaded scheduler for async tasks does not count.)

@mafiesto4 mafiesto4 added enhancement New feature or request core scripting labels Oct 11, 2024
@mafiesto4 mafiesto4 added this to the 1.10 milestone Oct 17, 2024
@mtszkarbowiak
Copy link
Contributor Author

mtszkarbowiak commented Oct 18, 2024

While working on the manual, I realized the name CoroutineSequence is better than CoroutineBuilder. I want the users to have clear understanding of linear nature of this solution. If the change didn't break anything, I'll mark the PR as ready.

FlaxEngine/FlaxDocs#165

@mtszkarbowiak mtszkarbowiak marked this pull request as ready for review October 18, 2024 21:22
Copy link
Member

@mafiesto4 mafiesto4 left a comment

Choose a reason for hiding this comment

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

Sorry for late review. I've got some feedback. I can do some changes while merging.

@@ -9,6 +9,7 @@
#include "SceneRendering.h"
#include "SceneTicking.h"
#include "SceneNavigation.h"
#include "Engine/Scripting/Coroutines/CoroutineExecutor.h"
Copy link
Member

Choose a reason for hiding this comment

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

Use forward decl instead includes (compile time issue).

#include "ScriptingObject.h"
#include "ScriptingObjectReference.h"
#include "Coroutines/CoroutineHandle.h"
#include "Coroutines/CoroutineSequence.h"
Copy link
Member

Choose a reason for hiding this comment

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

Use forward decl instead includes (compile time issue).

/// This method uses shared per-scene executor. The order of execution is not guaranteed.
/// </remarks>
API_FUNCTION()
ScriptingObjectReference<CoroutineHandle> ExecuteCoroutineOnce(ScriptingObjectReference<CoroutineSequence> sequence) const;
Copy link
Member

Choose a reason for hiding this comment

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

This can use CoroutineSequence* raw pointer. Caller owns or knows the object lifetime and this actually moves the ownership into the executor.

/// Adds a coroutine to the executor to be executed multiple times.
/// </summary>
API_FUNCTION()
ScriptingObjectReference<CoroutineHandle> ExecuteRepeats(
Copy link
Member

Choose a reason for hiding this comment

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

Rename to ExecuteRepeated to match ExecuteLooped

/// <summary>
/// Returns the number of coroutines currently being executed.
/// </summary>
API_FUNCTION()
Copy link
Member

Choose a reason for hiding this comment

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

API_PROPERTY

/// <summary>
/// Reference to a coroutine that can be used to control its execution.
/// </summary>
API_CLASS(Sealed) class FLAXENGINE_API CoroutineHandle final : public ScriptingObject
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be a struct for more lightweight and prevent dynamic memory allocations.

/// Stores code to be executed once previous step is completed.
/// </summary>
/// <remarks>
/// This class is used due to limitations of Flax API.
Copy link
Member

Choose a reason for hiding this comment

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

If function takes Function<void()> as parameter then it will be passed as C# delegate to be invoked from C++. So this CoroutineRunnable is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Last time I tried it didn't work. Gonna try it again.

private:
void Clear();
void EmplaceCopy(const Step& other);
void EmplaceMove(Step&& other) noexcept;
Copy link
Member

Choose a reason for hiding this comment

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

I think memory management for such structure is overenginnering. It can be data-only (maybe with frames delta, seconds delay and suspension point as union). Only CoroutineSequence and CoroutineExecutor use it.

ScriptingObjectReference<CoroutineSequence> ThenRunFunc(const Function<void()>& runnable);

/// <summary> QOL wrapper for waiting until predicate in native scripts. </summary>
ScriptingObjectReference<CoroutineSequence> ThenWaitUntilFunc(const Function<void(bool&)>& predicate);
Copy link
Member

Choose a reason for hiding this comment

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

It can be Function<bool()> and API_FUNCTION so C# can pass Action directly.

execution.ContinueCoroutine(CoroutineSuspendPoint::Update, Delta{ 0.0f, 0 });
_executions.Add(MoveTemp(execution));

ScriptingObjectReference<CoroutineHandle> handle = NewObject<CoroutineHandle>();
Copy link
Member

Choose a reason for hiding this comment

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

CoroutineHandle as struct would not leak memory and be faster to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I remember I thought API structs must not have ScriptableObjectReference. If it is supported, then I can change it.

@mafiesto4 mafiesto4 removed this from the 1.10 milestone Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core enhancement New feature or request scripting
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants