This is a very large diff, but most of it comes from test files and
output.
This PR ads partial support for the following Stage3D shader features:
* Normal (square), rectangle, and cube textures
* Varying and temporary registers
* Lots of opcodes
The combination of these allows us to get a raytracing program
fully working in Ruffle. I've included it as image test.
Currently, this test is very slow (about 90 seconds on my machine),
as the code I'm using (https://github.com/saharan/OGSL) includes
its own shader language and compiler. THe raytracing demo
first compiles its own shader language to AGAL, and then starts
rendering the scene.
Limitations:
* Many opcodes are still unimplemented
* Most non-default texture options (e.g. mipmaps) are not implemented
* Take two: Delay reading image back from render backend using `SyncHandle`
This allows us to avoid blocking immediately after a `BitmapData.draw` call.
Instead, we only attempt to use the `SyncHandle` when performing an operation
that requires the CPU-side pixels (e.g. BitmapData.getPixel or BitmapData.setPixel).
In the best case, the SWF will never explicitly access the pixels of
the target BitmapData, removing the need to ever copy back the render backend
image to our BitmapData. If the SWF doesn't require access to the pixels immediately,
we can delay copying the pixels until they're actually needed, hopefully allowing
the render backend to finish processing the BitmapData.draw operation in
the backenground before we need the result.
Now that the CPU and GPU pixels can be intentionally out of sync with
each other, we need to ensure that we don't accidentally expose 'stale'
CPU-side pixels to ActionScript (which needs to remain unaware of
our internal laziness). We now use a wrapper type `BitmapDataWrapper`
to enforce that the `SyncHandle` is consumed before accessing the
underlying `BitmapData.
* core: Skip GPU->CPU sync for source and target BitmapData during draw
* Introduce DirtyState enum
There were two issues:
1. We were accidentally calling `as_any` on `handle,` rather than
`handle.0`
2. Calling `as_any` can invoke the wrong implementation, depending on
what traits are in scope. We want the method implemented on the
underlying type (`RegistryData`) to be used, but if `Downcast` is
explicitly imported, then we appear to invoke it on the trait object
`dyn BitmapHandleImpl` itself (using the fact that trait objects
themselves implement `Any`). We now explicitly call the generated
method on the trait object, which avoids this issue.
`BitmapHandle` now holds `Arc<dyn BitmapHandleImpl>`.
This allows us to move all of the per-bitmap backend data into
`BitmapHandle`, instead of holding an id to a backend-specific
hashmap.
This fixes the memory leak issue with bitmaps. Once the AVM side of a
bitmap (`Bitmap`/`BitmapData`) gets garbage-collected, the
`BitmapHandle` will get dropped, freeing all of the GPU resources
assoicated with the bitmap.
This PR implements core 'stage3D' APIs. We are now able
to render at least two demos from the Context3D docs - a simple
triangle render, and a rotating cube.
Implemented in this PR:
* Stage3D access and Context3D creation
* IndexBuffer3D and VertexBuffer3D creation, uploading, and usage
* Program3D uploading and usage (via `naga-agal`)
* Context3D: configureBackBuffer, clear, drawTriangles, and present
Not yet implemented:
* Any 'dispose()' methods
* Depth and stencil buffers
* Context3D texture apis
* Scissor rectangle
General implementation strategy:
A new `Object` variant is added for each of the Stage3D objects
(VertexBuffer3D, Program3D, etc). This stores a handle to the
parent `Context3D`, and (depending on the object) a handle
to the underlying native resource, via `Rc<dyn
SomeRenderBackendTrait>`).
Calling methods on Context3D does not usually result in an immediate
call to a `wgpu` method. Instead, we queue up commands in our
`Context3D` instance, and execute them all on a call to `present`.
This avoids some nasty wgpu lifetime issues, and is very similar
to the approah we use for normal rendering.
The actual rendering happens on a `Texture`, with dimensions
determined by `createBackBuffer`. During 'Stage' rendering,
we render all of these Stage3D textures *behind* the normal
stage (but in front of the overall stage background color).
We only called `get_bitmap_pixels` when creating a `BitmapData`
for an SWF-provided `Bitmap`. We now store the initial pixels
in `Character::Bitmap`, and use them to initialize a `BitmapData`
when needed.
This lets us simplify the wgpu backend, which no longer needs
to store a `Bitmap` object. In addition to saving space for
`BitmapData` objects that lack an SWF `Bitmap`, this will make
it easier to move data from `bitmap_registry` into `BitmapHandle`
itself.
Currently, we rely on ShapeTessellator being able to get a BitmapHandle
without a RenderBackend. With the upcoming BitmapData refactor,
we will always need a RenderBackend to get a BitmapHandle, which creates
borrow-checker issues in ShapeTessellator (which is stored in a
RenderBackend).
To solve this, we split BitmapSource.bitmap into two methods -
BitmapSource.bitmap and BitmapSource.bitmap_handle. ShapeTessellator
continues to use BitmapSource.bitmap, and uses the u16 bitmap id
instead of a BitmapHandle. The BitmapSource.bitmap_handle method
is used inside each render backend to convert the id to a BitmapHandle,
avoiding borrow-checker issues.
This PR fixes a numbe of interconnected bugs:
* We weren't consistently uploading a dirty BitmapData to the render
backend before drawing to/from it.
* BitmapData.draw should *not* add a fill color - it should draw over
the current contents of the BitmapData
* After drawing to a non-transparent BitmapData, we need to manually
set the opacity back to 255 for each pixel (the drawing process
takes transparency into account, but the opacity information is
thrown away at the end).
As usual, also bump its helper crates (`js-sys`, `web-sys` and
`wasm-bindgen-futures`) to the latest versions.
Due to https://github.com/rustwasm/wasm-bindgen/pull/3031, use the
`serde-wasm-bindgen` crate as a replacement to the deprecated
`JsValue::from_serde` function.
* avm2: Implement `BitmapData.draw` for `wgpu` backend
This method requires us to have the ability to render directly to a
texture. Fortunately, the `wgpu` backend already supports this in
the form of `TextureTarget`. However, the rendering code required
some refactoring in order to avoid creating duplicate `wgpu` resources.
The current implementation blocks on copying the pixels back
from the GPU to the CPU, so that we can immediately set them in
the Ruffle `BitmapData`. This is likely very inefficient, but will
work for a first implementation.
In the future, we could explore allowing the CPU image data and GPU
texture to be out of sync, and only synchronized when explicitly
necessary (e.g. on `getPixel` or `setPixel` calls).
* Rename `with_offscreen_backend` to `render_offscreen` and use Bitmap
* Don't panic when backend doesn't implement `render_offscreen`
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.
Each render backend keeps track of a stack of BlenModes,
which are pushed and popped by 'core' as we render objects
in the displaay tree. For now, I've just implemented BlendMode.ADD,
which maps directly onto blend mode supported by each backend.
All other blend modes (besides 'NORMAL') will produce a warning
when we try to render using them. This may produce a very large amount
of log output, but it's simpler than emitting each warning only once,
and will help to point developers in the right direction when they
get otherwise inexplicable rendering issues (due to a blend mode
not being implemented).
The wgpu implementation is by far the most complicated, as we need
to construct a `RenderPipeline` for each possible
`(BlendMode, MaskState)`. I haven't been able to find any documentation
about the maximum supported number of (simultaneous) WebGPU render
pipelines - if this becomes an issue, we may need to register them
on-demand when a particular blend mode is requested.
Though https://github.com/rust-lang/rust-clippy/pull/8355 has been
merged, it seems to still report false-positives on nightly channel.
For now just fix the instances reported by stable clippy, and keep
`needless_borrow` allowed.
Based on the work in #6717, plus additional adaptions mentioned in
https://github.com/gfx-rs/wgpu/blob/master/CHANGELOG.md#wgpu-013-2022-06-30,
and more not-mentioned but required changes.
Also bump `wasm-bindgen` to `0.2.81` (along with its helper crates), as
required by the new `wgpu` version.
Note that I don't fully understand some of the required changes, notably:
* `wgpu::PresentMode::Mailbox` no longer works on my machine (Windows 11) -
The `wgpu` documentation says that `wgpu::PresentMode::Fifo` is the
only guaranteed to be supported, so I switched over to it instead.
* `self.staging_belt.recall()` doesn't return a `Future` anymore -
I assume it became synchronous so I simply removed the `executor`
from there.
Currently, all three render backends hold on texture-related
resources indefinitely (`register_bitmap` pushes to a `Vec`,
and never removes anything). As a result, the resources used
by the render backend (which may include GPU memory) will grow
over time, even if the corresponding `BitmapData` has been deallocated.
This commit adds a new `unregister_bitmap` method, which is called from
`BitmapData.dispose`. All render backs are changed to now use an
`FnvHashMap<BitmapHandle, _>` instead of a `Vec`, allowing us to
remove individual entries.
Currently, we only call `unregister_bitmap in response to
`BitmapData.dispose` - when `BitmapData` is freed by the
garbage collector, `unregister_bitmap` is *not* called.
This will be addressed in a future PR.
Since all `RenderBackend::register_bitmap_*` implementations are
identical now, move them to the default implementation of `RenderBackend`.
Also, turn `RenderBackend::register_bitmap_raw` into `RenderBackend::register_bitmap`,
which accepts a single `Bitmap` parameter.
* Remove `BitmapDataStorage` as its no longer necessary, and store
the bitmap canvas/context in `BitmapData` instead.
* Store the `Bitmap` RBGA buffer in the canvas backend. Previously
this was thrown away when converted to `ImageData`, but this
causes the glitchy pixels mentioned in:
https://github.com/ruffle-rs/ruffle/pull/6975#issuecomment-1127942520
`ImageData` does not copy the buffer passed to it, so store it
to keep it alive. See:
https://github.com/rustwasm/wasm-bindgen/issues/2445
This was added in fecb981 to workaround an issue with additive
alpha color transforms in Firefox. This seems to have been fixed
and is no longer necessary, so Firefox can use the normal code
path.
Guarantee bitmap data after decoding has the expected length for
the given width+height+format. This eases the burden from backends
to worry about this. Pad or truncate the data if it doesn't have
the expected size.
They're now unused thanks to #6975.
Also remove the `crate-type` field from `Cargo.toml`. I'm not sure
exactly what it does and why it was introduced, but seems working
without it.
Remove the `ImageElement` data path for storing bitmaps, as the
asynciness of this is difficult to manage when we need to render
bitmaps immediately. This code path was only being used for JPEG
images that could be decoded by the browser (no alpha).
Instead, always use canvases for bitmap storage. This simplifies
the code and solves some issues with bitmaps not being rendered
properly, although it is likely a little slower toe decode these
JPEGs on the Rust side.
Most gradients are "simply transformed" and can be rendered
directly using canvas commands. But even for complex transforms,
we can push the gradient's transform to the canvas, then
un-transform the path using the inverse of the gradient transform.
* Remove LineStyle::color, instead using fill_style with
FillStyle::Color to indicate solid color.
* Store `flags` in the struct instead of separate bools/values.
* Add getters/setters for ease of use.
* Add builder-style methods for setting LineStyle properties.
* Fix misnamed ALLOW_CLOSE flag to NO_CLOSE.
Since Rust 1.45, the `as` keyword performs a saturating cast when
casting from float to int. Therefore the `clamped_u8_color` function
is useless, and `as` can be simply used instead.
I (Michael R. Welsh) assign to Ruffle LLC all rights, title, and
interest to copyrights of my personal contributions to Ruffle,
effective March 26, 2022.
To avoid PNG-encoding every frame of every video for example.
If it needs to be converted to a base64 "data:" URL anyway,
compute it lazily and cache it behind a RefCell.
Raw pixel manipulation can be done through temporary ImageData objects.
The potential to use an image element is retained, so the native JPEG
decoder of the browser can still be utilized.