Skip to content

Conversation

@ggcrunchy
Copy link
Contributor

(This probably needs to wait for word that the Vulkan backend behaves right, beyond just compiling, but otherwise feels good.)


In Discord it came up that time transforms still have a few issues. Assigning one to a shader was broken. Adding isTimeDependent to a shader when it wasn't used could cause some things to break, as well.

It was also mentioned that the marching ants builtin (a time-based effect) got worse over time.


The broken shader assignment was something silly: doing a lua_isstring(), when the string in question had already been popped off the stack, rather than check for the non-NULL result.

The fixed behavior is used by the time-based builtins, described below.


isTimeDependent:

Once a scene has been drawn, Solar will leave it alone if nothing changes, i.e. no new objects; objects being moved or rotated; etc. This is mostly the right behavior, but time-based effects do need to be redrawn. The isTimeDependent flag, passed to shader definitions, was used to convey this information.

The shader author needed to remember to add this flag. And to be careful to remove it / not add it otherwise, apparently.

The time transforms could also use this information as a hint about when they needed to be called. I was attempting some caching here (from #511), and it apparently blew up if you wrongly provided the flag.


This PR ignores isTimeDependent entirely and discovers the dependency in a different way.

We still query ShaderResource::UsesTime(), but the state itself is being found automatically: if the CoronaTotalTime uniform is bound, following a GLProgram::Create(), the flag is set. (Any variant, e.g. # of masks or distorted rects, can be responsible, and the flag is taken as normative for all of them.) This is within the first GLProgram::Bind() of the variant; there's an untested DEFER path too, if anything ever wants to use that.

There are corresponding Vulkan backend parallels, too.

The scene will have already been updated by the time the Create() is performed, and thus be unaware of any new time dependency. If any ShaderResource had its flag set, the scene is therefore invalidated; it will pick up on anything new the next time around.

Some commentary about this may be found in Rtt_Scene.cpp.

The default time transform is applied to the raw total time when the scene begins rendering, and saved in a value. This is used by shaders without custom transforms; otherwise the raw time is provided, so that it can be transformed if necessary. In the latter case the CoronaTotalTime uniform's update is couched between bind and unbind commands for the transform.

Other than the default up front, no attempt is now made at transform caching, or even checking that the shader uses time, while issuing commands. If the time uniform is actually bound, any transform is resolved when the corresponding command is executed.


There are three time-based builtins: generator.marchingAnts, filter.wobble, and generator.random.

Each was unbounded and so would eventually suffer some artifacts. However, all three are "modulo"-friendly cases and were given such a transform.

Marching ants is a repeating pattern. It moves at a certain speed (16), and once it's covered two bars' worth of distance (off and on, so 6 + 6), we have an identical-looking effect. Thus its range is (6 + 6) / 16 = .75 seconds.

Wobble uses time inside a sine. One period will have elapsed when 3 * t = 2 * pi, so our range is 2 / 3 * pi.

Random was just doing a fract(), so there the range is 1.

These numbers are found in the effects themselves. I added similar commentary in there. Old code is still present, commented out; forgot about it during cleanup, but I guess it's fine for comparison purposes.


I was testing with this:

fx_tests.zip

I had it set up to try the time-based builtins as well as copies of their code, with minor modifications in how they calculated time, and now without using isTimeDependent at all. (Those versions might be a little odd themselves over a certain duration, since they should now use the default "pingpong" behavior, but were used to manually check the modulo ranges and the code still reflects that.)

Some of the if trues and such were for quickly disabling certain effects to make sure we didn't still have isTimeDependent at work, as well as to verify that a delay between shader creation and bind would still behave right. (This is currently in place and the reason the effects take a few moments to start.)

ggcrunchy and others added 26 commits February 7, 2019 17:09
…nd buffer and assignment logic

Next up, prepare details from graphics.defineEffect()
…from Program

Slight redesign of TimeTransform with caching (to synchronize multiple invocations with a frame) and slightly less heavyweight call site
Err, was calling Modulo for sine method, heh

That said, time transform seems to work in basic test
Fixed error detection for positive number time transform inputs

Relaxed amplitude positivity requirement
Maintenance Revert Test
My mistake adding back changes made
…far)

isTimeDependent code disabled; now checking that shader objects were created and had either the TotalTime or DeltaTime uniform bound (or both, of course)

ShaderResource's flag instead set when doing that

Added a global flag as well, to say if ANY such flag was set (gets reset when read)

Scene logic now checks the aforementioned flag and ensures the scene is invalidated if so (needed for flag to take hold, on first frame)

Some commentary on these details

The ApplyUniforms() logic is a bit different; the default time transform, if any, is done once up front each frame, so the default transformed time, plus the raw time, are on hand

Any shader that specifies a transform passes it along via a command, and it will lazily be evaluated on the render side if TotalTime is bound

On that note, no more attempts are done to check if transforms are dirty

The code for the same is dummied out

The built-ins that use time (marching ants, wobble, random) now use modulo-type transforms
Slight signature change for time transform Func type, plus changes for usage

Removed some previously commented-out bits
… absent :/

Now just setting the transform back to NULL after ApplyUniform() call
@ggcrunchy ggcrunchy requested a review from Shchvova as a code owner September 25, 2025 19:33
Copy link
Contributor

@Shchvova Shchvova left a comment

Choose a reason for hiding this comment

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

This is very nice. I'm worried about backwards compatibility.
It wouldn't affect existing code, would it?

@ggcrunchy
Copy link
Contributor Author

Any incompatibility would indicate a bug, as far as I can tell. It shouldn't stomp on any existing expected behavior. Also, there were only those three builtins, and I confirmed they were using the transforms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants