Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - fernewelten

#1
Engine Development / Re: Why is AGS slow?
Sat 16/04/2022 20:16:02
Quote from: Crimson Wizard on Sat 16/04/2022 19:25:38
What fernewelten is refering to, probably, is of is checks that are done by the script interpreter during certain bytecode operations. Like - stack operations. They probably happen all the time, for every little move on the stack (I may be mistaken here, because some were disabled in the past).

Yes, that's the gist of what I was trying to say. When the script interpreter is told to float-add two things, for example, it doesn't just do just that, it checks beforehand whether the respective register etc. has been loaded, whether a value of type 'float' has been written into it, and so on. That's good for checking whether the Compiler has done its thing correctly. That mechanism is bound to have discovered lots of bugs by this time.

But the trouble is, if the Compiler has done the job properly that it was programmed for, then those checks might still be done thousands of times at runtime, over and over again, even for the same source code statement and the same block of Bytecode, when it needn't have been done at all.

There are certain things that the Compiler can't check, for instance whether a pointer location that needs to be dereferenced contains a null pointer at runtime. It's sensible to make the script interpreter check these things. The Compiler may even tell the script interpreter to check something specific, by issuing a suitable Bytecode instruction.

On the other hand, there are a lot of things that the Compiler could make sure of, given our AGS language. For instance, it should be able to make sure that a memory location that is supposed to contain a float hasn't been loaded with an int instead. In these cases, a lot of time can be saved by letting the Compiler do its thing, and telling the interpreter: “Look, Buster, Master has told you that these two things are floats and commanded you to add them, so just obey right now without wasting Master's time!”

It would mean requiring that every Bytecode that is given to the Engine has been produced by the Compiler, or at least, that if you do give "hand-written assembly Bytecode" to the Engine, side-stepping the Compiler, you do it at your own risk.
#2
Engine Development / Re: Why is AGS slow?
Sat 16/04/2022 16:54:55
As far as I know, the Engine does much more than the microprocessor would do that the Engine has been based on. Mainly, double checks, e.g., that the registers contain the type of data that they're supposed to. Seen from this vantage point, the script runner is a “debugging” script runner, only the “debugging” runs all the time no matter whether the code is still debugged.

There might be two angles to speed this up:

  • Make the script runner trust the Compiler. The Compiler only runs once whereas the script runner runs each time. So when the script runner abstains from checking those things that the compiler should have already made sure of, then this can potentially cut out a lot of superfluous runtime activity.
  • Provide a sleek “production”  script runner that only does the barest necessary, side-to-side to the current script runner. Some kind of flag would determine whether the code is run with the careful current runner or with the fast “production” runner. Those users that need a very fast engine could set the flag appropriately and forego the checks. Or else we could set up things as follows: When the game is started with F5 then the engine uses the careful current script runner, when it is compiled with F7 and run then the engine uses the sleek “production” script runner.
#3
Engine Development / Re: Why is AGS slow?
Wed 22/09/2021 16:17:26
If you take a “typical” AGS adventure program then I'd say that most of the heavywork is done in functions such as Character.Say. So if the aim is in speeding up the engine, I'd start looking for bottlenecks in these functions first, and then in the way that calls are handled.

Most probably the engine will be bored stiff most of the time waiting for the d*mn user to move their mouse around or click it again at long last.

I'd also plead for getting at actual data as a first step. Collect some typical games and some “problematical” games and get heat maps.

As far as I know, the engine misuses the "high bits" of memory locations in some circumstances to store additional information there (e.g., for linking purposes). They evidently don't believe that even all 32 bits of the address space are needed. So do we have proof that doubling the memory bits will actually buy actual game writers or actual gamers some coffee?
#4
What the engine needs of all this is only in structs, at what offsets the pointers are and what the types of thes pointers are.

We could store this separately for the benefit of the engine as a compact list of (offset, type-id), e.g.,
<id> -->
<number_of_entries>
<offset_1><type_id_1>
<offset_2><type_id_2>
....
<offset_n><type_id_n>
#5
So currently, what I basically have as type information in the symbol table is this:

A type is either atomic

  • primitive types: int, short, char; float; string
  • enumerations (currently equivalent to int)
or compound

  • non-dynamic array with specific_number elements of a type
  • dynarray of a type
  • dynpointer of type
  • struct, composed of

    • optionally the parent (another struct)
    • a list of (fieldname, type)

There's no built-in restriction in the symbol table itself that compound types must consist of atomic types. Compound types can consist of compound types. (But the compiler ensures with code that the types are limited by what the engine can currently handle. For instance, no dynarrays of structs that contain pointers and non-pointers.)

That's what has already been implemented, i.e., what the (new) compiler is currently working with.
#6
Quote from: Crimson Wizard on Thu 15/07/2021 11:35:59
I did a small experiment for fun, generating such table along with the script data, and letting engine consult it when disposing managed structs.
https://github.com/ivan-mogilko/ags-refactoring/commits/experiment--rtti

Having a look to see just what the compiler would need to provide.
#7
I've been thinking about Crimson's idea a little.

Here's a very tentative and high-level idea that builds on this.

Currently, the Editor is holding a complete copy of the Compiler.
When a program is run in debug mode and halted, then the Editor is running.
This means that the Compiler is available at that point in time and can be called.
It might "compile" an expression such as "Weapon[15].Shield.Damage",
and then you'd get the Bytecode for it, i.e., a step-for-step instruction for the Engine of just how to reach that value at this point in time.
The Engine already knows how to run Bytecode.
It could be configured to save its current state (content of the registers and so on) and then temporarily execute the Bytecode of the expression.
Then it would have the expression result in AX (or in the memory cell that MAR points to), ready to be passed to the Editor to be displayed.
Afterwards, it would retrieve its current state and continue running.

Now we don't need to see the compiler as a cumbersome and heavy-weight blob of software that can only deal with complete programs:
it consists of several separate classes that can operate independently, such as a scanning class..
These parts of the compiler that specifically deal with expressions could be further split off, giving an "expression parser class".
The compiler itself would use this "expression parser class", and the "expression parser class" could also be used independently.
That's what would happen at debugging time:
For each expression to be watched, the debugging mode of the Editor would call the scanner class and then the "expression parser class".

It's inevitable that the compiler will evolve over time.
Modifications will be made, new features will be added, and so on.
In these cases, the "debugger" will neatly follow suit.
It uses the same parts that the compiler uses, so when the compiler changes, the debugger changes.
So the "debugger code" can't become out ouf sync to the "compiler code".

This is an advantage of the approach. But this can also be a disadvantage:
The compiler would no longer be free to do whatever it wants to.
There would be an interface that the debugger uses,
and that interface would need to remain rather stable.
And so the compiler would also need to be rather stable, as concerns calling this interface.

An expression such as  "Weapon[15].Shield.Damage" changes its meaning dependent on the context.
For instance, "Weapon" might be a local variable at some point in time, and another local variable at another point in time.
So we will need to construct the context at the point that is being debugged.
When the compiler runs its proper compiling run, it has this context available: When it's in the middle of compiling some function, it knows exactly what variables are at what stack offsets and so on.
When the debugger runs and wants to query a variable, this context must be reconstructed.
For instance, when the debugger halts, e.g., on line 42 of "globalscript.asc", then the context at this line 42 must be reconstructed.
This means that the compiler must write the various contexts into, e.g., a debugging file in the proper compiling run,
and a function such as "recall_context(debugging_file, "globalscript.asc", 42)" must be provided that the debugger can use.

So in total, when the debugger halts alt line 42 of file "globalscript.asc" and wants to know the value of "Weapon[15].Shield.Damage", it must;
- call recall_context(debugging_file, "globalscript.asc", 42)
- call expression_parse(scan("Weapon[15].Shield.Damage"))
- pass the resulting Bytecode to the Engine for executing
- the Engine must save its current state,
- execute the Bytecode
- report AX (or the value of the memory that MAR points to)
- restore its current state

------

We have:
the compiler
the Engine

We need
- to split off the expression parser from the compiler. This is time-consuming, but will strongly improve the compiler (it will become more modularized and easier to modify and extend).
- to make the compiler write the context information. This information is available in the symbol table. It gets there incrementally, so at the point where a local variable is added or invalidated, this can also be written into the "debugging file". This part is probably fairly easy.
- to write recall_context(). One way of going about it is to start with a given context, e.g., at the start of a function, and then "replay" the context steps until the line in question is reached. This can probably be done with middling effort.
- The last two steps must be developed in tandem, of course.

- to prepare the Engine for
    = saving its state, executing "temporary" bytecode, restoring its state
    = passing register AX (or the value that MAR points to) to the Editor
I don't know enough of the Engine to make educated guesses on how cumbrous this is.

By the way, if the compiler can find an instruction sequence that makes MAR point to the memory that contains the value, then it is possible in principle to "patch in" another value at debugging time. So variables can not only be watched but even modified. This may open a can of worms, so we'd better not do this right from the beginning. But it's a perspective.
#8
Quote from: Crimson Wizard on Thu 15/07/2021 11:35:59
It looks like we may be few steps away from actually supporting managed pointers inside managed structs.
:-D :-D

#9
Quote from: Snarky on Wed 02/12/2020 13:52:02
that the rewrites […] of the compiler […] have led to worse performance.

I'm currently not aware of any situation in which the rewritten compiler emits less efficient code than the old compiler.
#10
Quote from: Crimson Wizard on Fri 20/03/2020 23:24:25
how to hide player character in particular room (main menu, intro, etc).

To the best of searching abilities, indeed, "Character.on" isn't described anywhere in the in-Editor doc of the newest official AGS version.

The way to do this that I'd think of first: Set player.on to false in room_Load() and set it to true again in room_Leave().
#11
Quote from: bx83 on Sun 18/08/2019 11:31:35
… colour to text in the speech bubble. How do I do this elsewhere in the game?

As concerns the colour of the outline in standard AGS without modules, see game.text_shadow_color (look for "game. variables" in the index of the online doc). You'll need to set the variable immediately before the  Say() or  Display() or whatever command and reset it immediately afterwards because this variable pertains to all outlines -- keep it at a certain value and the outlines will be coloured that way everywhere.

For my recent game Mamma mia Winter Ice Cream Mayhem, I've patched the Engine at the same place that the new code for variable outline thickness is going to go. I got thick outlines in the status line, too. So I think that the thick outlines will come everywhere, not only in Say().
#12
Artificial noise: Midnight Bazaar

I also highly recommend the cat purring machine from the same website author (Stéphane Pigeon, Ph.D.), or for the more musical inclined, the Sleeping Dragon.
#13
Critics' Lounge / "Next-level" animations
Fri 15/06/2018 01:23:04
Hey folks,

Say hello to Linkattus, a brash adventurer that is about to rescue an imprisoned beautiful lady.


He lives in an entry for MAGS May, which I am currently upgrading with superior animations as a first step to creating a non-MAGS version.

So, Linkattus will have to:

  • reach for the soft soap bottle on the cabinet behind him,
  • take it,
  • turn facing downstage,
  • shake the bottle
  • remark that the bottle is empty
  • turn facing the cabinet
  • and then put it back.
I know the guy's a little short, but he intends to stand on his toes and stretch a little.

So, this has a technical (practical) and an artistic side.

For the technical side, both Linkattus and the surroundings are originally vector (.svg) drawings. They have been converted to .png for the AGS sprites import. The bottle is an object.

  • What would be a good program to do the animation in? I've done the walking cycles in Synfig, which can import .svg files in a broken kind of way that usually ruins the scaling of some body parts. Also, Synfig is fairly unstable and needs constant saving in order to guard against crashes. On the other hand, Synfig can work with vectors "natively", and it knows bones animation (you can attach a group of vector shapes to a "bone", and when you move or distort the bone, the vectors will follow suit). I'm not really happy with Synfig, but I don't know alternatives.
  • What do I need to do on the AGS side to pull this off? Note that the bottle is already standing there when Linkattus arrives. So Linkattus will probably need to be at a very exact position when the animation starts so his hands are exactly aligned when grabbing the bottle. Then, the bottle needs to "disappear" at the same time that a copy of the bottle that is part of the animation sequence shows up.
  • I think I will need to have the background available in the animation program in order to get the positioning just right. But the background must not be part of the animation images, or it might interfere with the graphics that AGS presents in realtime.

Watch Kathy Rain going to the sofa and taking a seat there. Kathy lives in a commercial AGS adventure (that is very worthwhile, BTW.). I assume that AGS turns her, then she uses a standard walkcycle animation which she fluidly changes to a "sitting down" animation on arriving at the sofa. (The jerkiness in the GIF is due to its low frame rate; there is no jerkiness whatsoever in the live game).  Note that Kathy is in seamless action throughout the sequence.



So there must be a way of doing these kinds of animations.
SMF spam blocked by CleanTalk