We've now had two different bug reports involving Adobe AIR
SWFs, so I'm going to go ahead and start adding a framework
for AIR support.
This commit just adds a command-line option
`--player-runtime <flash-player|air>` (defaulting to `flash-player`),
and passes it along to the `Player`. The actual value is currently
unused - in a follow-up PR, I'm going to implement namespace versioning
for AIR.
A `Substream` can be read in one of two ways:
* Converting it to an iterator that yields individual chunks of the substream in `Slice` form.
* Converting it to a `SubstreamCursor`, which implements `Read` for bytewise read access.
Chunk-based reading is intended for code that needs to know about chunk boundaries. Self-terminating chunk formats should be read with the cursor type.
This is ultimately intended to replace `Vec<u8>` in both `SwfMovie` and `NetStream`, but for the time being we only apply this to streams. SWF-related types are deeply embedded elsewhere and the changes to slice dereferencing will be quite invasive.
We also remove some unnecessary reader drops in `NetStream.tick` at the same time.
* wpgu: Initial implementation of PixelBender shader execution
The implementation is split across four crates:
* `ruffle_render` now holds the main PixelBender bytecode parsing
implementation (previously, this was in `ruffle_core`).
* `ruffle_core` holds some helper functions for converting between
AVM2 `Value`s and the PixelBender vector types.
* `naga-pixelbender` (newly created) constructs a Naga `Module`
from parsed PixelBender bytecode
* `ruffle_render_wgpu` sets up the render pipeline for the shader
constructed by `naga-pixelbender`, and actually executes the shader.
The Actionscript-side shader parameters are passed in through uniforms.
This allows us to cache the compiled `naga::Module` and associated
wgpu types inside `ShaderData`, when it's first created. Each invocation
of a `ShaderJob` only needs to create a bind group and render pass.
Limitations:
* Only a few of the PixelBender opcodes are implemented - however, this is
enough to get Stemlands cannon rotation working, as well as a cool
"donut" shader that I found and included as a test.
* PixelBender matrix types are not supported.
* Only BitmapData is supported as an input/output type - Flash Player
also supports using Vector and ByteArray
* ShaderJob execution is always synchronous.
* Adjust comments
* Address review comments
We now parse PixelBender bytecode, and populate the parameters
from the bytecode on `ShaderData`. This is enough to progress
Steamlands, which needs to access dynamically set properties
on `ShaderData`
Bytecode execution is not implemented yet.
The Newgrounds API checks `Security.sandboxType` to see if it should
run in debug mode or not (which determines whether or not medals
can actually be unlocked).
For now, desktop continues to use `localTrusted` as the default,
while web now uses `remote`. We might want to make this configurable
at some point, but this should be good enough for now (and better
match Flash's behavior).
Backends that need synchronous preload behavior now explicitly ask for it as follows:
* `tests` - repeatedly call `preload` in a loop with an exhausted execution limit to stress-test the chunked preload
* `exporter`, `scanner` - synchronous/unlimited preload to match prior behavior
These may change in the future.
Actions are abstract; here we're using it to count bytes loaded (as a proxy for execution time). AVM code could potentially be adapted to count operations run instead.
In several cases, the current code seems preferable to the
code required by `clippy::bool_to_int_with_if`. Let's suppress
this for now to get the build passing, and decide later if this
is something that we want to enable.
* avm2: Implement call stack
* avm2: Class traits should have a special prefix
* avm2: Stack tracebacks should also contain error message
* avm2: Move method naming to Executable
* avm2: Handle getter and setter methods in tracebacks
* chore: Formatting
* chore: Add comments
* avm2: Make full_name write to a string, instead of creating a new one
* core: Make GcArena publicly accessible
* core: Add Deref impl for Either type
* desktop: Add AVM2 call stack to panic message
* avm2: Prefix native methods with a `/`
* chore: Appease clippy
* avm2: Check if method actually contains bytecode instead of unwrapping
* web: Add AVM2 stack trace to panic message
* chore: Formatting
* chore: Clippy
* avm2: Fix stack traces for free standing functions
* core: Remove global data from context
* core: Rename GcGlobalData to GcCallstack
* core: Introduce StaticCallstack, make GcArena private again
Co-authored-by: Adrian Wielgosik <4729533+adrian17@users.noreply.github.com>
Previously, the viewport height and width were stored in
both `Stage` and the `RenderBackend`. Any changes to the viewport
dimensions (e.g. due to window resizing) needed to be updated in both
places to keep our handling of the viewport consistent.
This PR adds a new `ViewportDimensions` type, which holds the
width, height, and scale factor. It is stored inside the
`RenderBackend` impl, and is retrieved using the newly added
method `RenderBackend.get_viewport_dimensions`. After a `Player`
has been constructed, any code that needes access to the viewport
dimensions will ultimate go through this method.
Unfortunately, `Stage` needs to use the viewport dimensions
in `build_matrices`. Therefore, any code modifying the viewport
dimensions should go through `player.set_viewport_dimensions`,
which ensures that the stage matrices are rebuilt after the render
backend is updated.
* avm2: Include class name in ScriptObject debug
Currently, the `ScriptObject` debug impl is almost useless -
while you determine if two printed objects are the same
by comparing the pointer value, you'll have no idea what
kind of object it actually is.
This PR now formats the `ScriptObject` output as a struct,
printing a (fake) "class" field containing the class name.
Before/after:
```
[ERROR ruffle_core::avm2::activation] AVM2 error: Cannot coerce Object(ScriptObject(ScriptObject(GcCell(Gc { ptr: 0x55f863936db8 })))) to an QName { ns: Private("Test.as$38"), name: "Second" }
[ERROR ruffle_core::avm2::activation] AVM2 error: Cannot coerce Object(ScriptObject(ScriptObject { class: "Object", ptr: 0x55ee0ad161e0 })) to an QName { ns: Private("Test.as$38"), name: "Second" }
```
Getting access to the class name from a `Debug` impl is tricky:
Developers can (and should be able to) insert logging statements
whereever they want, so any `GcCell` may be mutably borrowed.
Panics in debug impls are extremely frustrating to deal with,
so I've ensured that we only use `try_borrow` at each step.
If any of the attempted borrows fail, we print out an error message
in the "class_name" field, but we're still able to print the
rest of the `ScriptObject`.
Additionally, we have no access to a `MutationContext`, so we
cannot allocate a new `AvmString`. To get around this,
I've created a new method `QName::to_qualified_name_no_mc`,
which uses an `Either` to return a `WString` instead of allocating
an `AvmString`. This is more cumbersome to work with than the
nrmal `QName::to_qualified_name`, so we'll only want to use
it when we have no other choice.
The current 'setInterval/setTimeout' implementation is
moved to 'core/src/timers.rs', and now works with both
AVM1 and AVM2 objects. The `flash.utils.Timer` class is implemented
mostly in ActionScript, with minimal modifications to the actual
Ruffle timer code.
Previously there were 3 implementations of `LocaleBackend`:
`DesktopLocaleBackend`, `WebLocaleBackend` and `NullLocaleBackend`.
While `DesktopLocaleBackend`, `WebLocaleBackend` were identical,
`NullLocaleBackend` always returned a fixed date/time for tests
determinism.
Unify them in a single file, and use `cfg!(test)` and a new dedicated
`deterministic` feature to decide whether to mock date/time or not.
This should not cause any behavioral changes.
Currently it is not directly possible to configure lints for the
entire workspace via TOML, which forced us to repeat `#![allow]`
blocks in each crate.
embark pointed out this workaround to configure lints at the
workspace level via RUSTFLAGS:
https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395
Remove the common `#![allow]` blocks and switch to this method for
global lint config.
Temporarily allow `needless_borrow` lint, buggy pending this fix:
https://github.com/rust-lang/rust-clippy/pull/8355
Matrices in an SWF file store their scale/skew components in
in 16.16 format (fbits).
Split `ruffle_core::Matrix` and `swf::Matrix`. `swf::Matrix` now
stores its data as `Fixed16` instead of immediately converting to
`f32`.