Possible DLL glitch [SOLVED]

Started by Nickenstien, Sun 26/04/2009 20:55:27

Previous topic - Next topic

Nickenstien

Hi.

I am having a slight problem with DLL calls.

For the most part my DLL is successfully called every frame, however there are instances where it seems that on certain frames, DLL's are not called by the game/AGS-Engine.

This appears to happen when different dialogue options are highlighted with the mouse.

EG: When the user has a set of dialogue choices, moving the cursor over them causes them to highlight/change-colour. At this very instant it seems that DLLs are not called for one frame.

This is causing the Direct3D visual effects that I am overlaying to not be rendered during that frame, so they briefly disappear.

This also happens for one frame when a conversation is initiated. So when clicking on a character with a speech-bubble icon, I get a single frame flash of my effects not being rendered.

Specifically, the following events do not occur during the frame in question:

AGSE_PRESCREENDRAW
AGSE_PREGUIDRAW
AGSE_POSTSCREENDRAW

It is probable that no events are passed to the DLL during that frame, although these are the ones I am using to know when to render specific layers of my effects.

Are there any known workarounds for this?

Or maybe something I can do to ensure that the events are passed to my DLL every-frame, regardless of any special states that the AGS-Engine might be processing, which would normally cause the DLL-events to be skipped?

Cheers,
Nick

Pumaman

You need to enable "Run game loops while dialog options are displayed" in General Settings if you want this to be called while dialog options are displayed./

Nickenstien

I forgot to mention in my previous post that "Run game loops while dialog options are displayed" is already set to TRUE.

With it set to FALSE, the DLL is not called for the whole duration of the dialogue, as you would expect. However, with it set to TRUE, the DLL fails to be called at the instant you highlight new text. Causing it to flicker when you move over different dialogue options.

Pumaman

Interesting, I'll look into it.

Nickenstien

#4
Thanks. :)

Another possible culprit has just occurred to me. Would there be any new/different render-states set on the frame that the highlighted text changes colour? Or maybe some kind of stencil or mask?

If so, I wonder if the effects/textures I am rendering are being masked for a frame or something like that.

Nickenstien

#5
Hello. :)

Just a polite bump to ask if you have had a chance to look at this before it slides off the screen.
It's been very sunny recently, so this issue is probably way low down on your priority list right now. (I know it would be a triviality on mine.) ;)

Pumaman

No, my first priority at the moment is to finish the audio system changes. Once that is done, I will go through my list of other reported issues including this one, and investigate them.

Nickenstien


Nickenstien

#8
Hi. :)

I've done some more investigating into this, and it turns out that the DLLs are in fact being called every-frame.

However, the current room's repeatedly_execute_always() function is the thing that is being skipped (for a single frame) when new text is highlighted on a set of dialogue options.

Is this the expected behaviour or should repeatedly_execute_always never be blocked/skipped?

Pumaman

repeatedly_execute_always is called once every game loop. Occasionally, in some situations such as this the screen will get re-drawn but a new game loop will not take place. When that happens, all the screen redraw code (including the plugin) will be called, but rep_exec_always may not do.

Nickenstien

Gotcha.

So is this considered to be a bug, or correct behaviour?

As it is, it makes it impossible to rely on it for doing any external drawing calls, as they will glitch when this occurs.

Pumaman

I would consider this correct behaviour. The rep_exec_always function should run once every game loop.

There is no official behaviour in AGS that the screen only gets drawn once every game loop -- it just happens to be this way most of the time. There are other times when the screen is updated without a new game loop taking place, such as when the highlighted dialog option changes.

If you're relying on script code to call your plugin from rep_Exec_always to draw things, I think you'd need to cache the information from the last loop within the plugin, and then use this information in your screen drawing event handler.

Nickenstien

#12
Yeah, basically I do things both ways, depending on the specific effect's needs.

For instance, I have a particle system whereby I create an emitter and set up its properties (textures, positions, life, velocities, rotations, etc...) then tell it to start.  These are not effected by the issue as they don't rely on Repeatedly_execute_always.

Then in other cases where I need finer real-time control based on changing game variables, I use my sprite/overlay system, and call it from Repeatedly_execute_always.

A crude Example:

   // some function to position an effect overlay above an in-game character
   SomeScriptFunctionCalledFromRepExecAlways()
   {
        EFFECTS_SPRITE_SetPosition(SpriteHandle, GameCharacterPosition.X, GameCharacterPosition.Y - 27.0);
        EFFECTS_SPRITE_SetChannel(SpriteHandle, EFFECTS_CHANNEL_ID_PRE_AGS_SPRITES);
        EFFECTS_SPRITE_AddSpriteToDrawList(SpriteHandle);
   }



Your idea of caching whatever was drawn during the last frame is promising. However, I would need some way to detect that a render-loop had occurred without a game-loop happening from inside the DLL, as the draw list is flushed after rendering, so it is clean each frame.


EDIT: Actually, I could probably detect this myself, by checking if any 'EFFECTS_SPRITE_AddSpriteToDrawList()' calls have occurred between the current and last DLL update, and if none have, render the last frame's draw-lists (multiple lists, nested between AGS' different layers), before flushing them out for the next.

EDIT 2: Although, this would stop overlays from disappearing, anything scrolling would still visually stutter.

Another crude example:

   // cloud effects
   SomeScriptFunctionCalledFromRepExecAlways()
   {
        CloudScrollOffset_X+= Cloud_Scroll_Speed_X;
        CloudScrollOffset_Y+= Cloud_Scroll_Speed_Y;
        EFFECTS_SPRITE_SetPosition(CloudSpriteHandle, CloudScrollOffset_X, CloudScrollOffset_Y);
        EFFECTS_SPRITE_SetChannel(CloudSpriteHandle, EFFECTS_CHANNEL_ID_POST_AGS_SPRITES);
        EFFECTS_SPRITE_AddSpriteToDrawList(CloudSpriteHandle);
   }

So in this example, moving the mouse up and down over dialogue options would cause the scrolling overlays to frequently stutter/pause (as opposed to flickering on and off like they do currently). So it would be a slight improvement, yet still very ugly.

Is there a way to force Repeatedly_execute_always to happen regardless of the game-loop being skipped, or would that cause some internal issues with AGS's script processing?

If it would cause problems, is there any way a new script function could be added that I could use instead of repeatedly_execute_always? Something along the lines of 'Execute_before_every_render_loop()' maybe?

Or alternatively, could something be done to stop AGS from rendering a frame when no game loop occurs? So if the game-loop is skipped (for whatever reason), then AGS doesn't do a Clear-display & Render-loop, so the last frame (and any external overlays) remain as-is for that skipped game-loop's frame?


Pumaman

Perhaps the easiest thing is to add some sort of function like

EFFECTS_SPRITE_NewGameLoop

that your script calls from the start of rep_exec_always, which clears the draw list. That way, if no game loop occurs, the draw list will not get wiped.

Nickenstien

#14
Quote from: Pumaman on Sun 24/05/2009 17:11:54
Perhaps the easiest thing is to add some sort of function like

EFFECTS_SPRITE_NewGameLoop

that your script calls from the start of rep_exec_always, which clears the draw list. That way, if no game loop occurs, the draw list will not get wiped.

I have done something that has the same effect as that without the need for a new function call. Basically if no calls to EFFECTS_SPRITE_AddToDrawList happen between two DLL updates, it re-renders the previous draw list and then flushes it. Also the first call to EFFECTS_SPRITE_AddToDrawList in any given frame, will flush the old lists before adding the new object, to stop things from being rendered twice.

It's working nicely so I am marking this as solved. :)

Pumaman


Nickenstien

#16
Another thought just occurred to me.

Would there be any instances whereby multiple render loops happen without a game loop occurring?

For instance, could the changing of text selection on a dialogue menu take 2,3,X amount of frames on a particularly slow PC?

Because if so, then your suggestion of having a flush-call at the start of RepExecAlways would be a more appropriate solution, as any effect-overlays would continually persist until the next true game loop. (Whereas my current solution will only keep things cached for one extra frame.)

But if one is the maximum frame skip then I will stick with what I have, as it needs no extra calls, purely for the sake of simplicity for the end user.

Pumaman

I can't make any guarantees about there only being one extra frame drawn. But shouldn't your logic work anyway, just always re-draw the existing draw list until EFFECTS_SPRITE_AddToDrawList is next called, at which point reset the list?

Nickenstien

#18
No, my system can only _safely_ work for caching one frame.

I have to flush it after one re-draw, otherwise things would persist forever even if they are meant to have gone.

IE, the way I am doing it internally, I can't be sure why no sprites were added. It could be because a Game-Loop was skipped, but it could also be because a game-loop occurred that was not supposed to draw any effect sprites.

I think just to be on the safe side, I am going to add that EFFECTS_SPRITE_NewGameLoop function.

Cheers for your input. :)

SMF spam blocked by CleanTalk