web: Add preferred renderer config
This commit is contained in:
parent
569e822044
commit
1e3701279c
|
@ -35,4 +35,5 @@ export const DEFAULT_CONFIG: Required<BaseLoadOptions> = {
|
||||||
publicPath: null,
|
publicPath: null,
|
||||||
polyfills: true,
|
polyfills: true,
|
||||||
playerVersion: null,
|
playerVersion: null,
|
||||||
|
preferredRenderer: null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -117,6 +117,39 @@ export const enum WindowMode {
|
||||||
Gpu = "gpu",
|
Gpu = "gpu",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The render backend of a Ruffle player.
|
||||||
|
*/
|
||||||
|
export const enum RenderBackend {
|
||||||
|
/*
|
||||||
|
* A draft API that is currently unavailable, but will be preferred if available in the future.
|
||||||
|
* Should behave the same as wgpu-webgl, except with lower overhead and thus better performance.
|
||||||
|
*/
|
||||||
|
WebGpu = "webgpu",
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The most featureful and currently preferred backend.
|
||||||
|
* Rendering is done the same way as in the desktop app, then translated to WebGL on-the-fly.
|
||||||
|
*/
|
||||||
|
WgpuWebgl = "wgpu-webgl",
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A vanilla WebGL backend. Was previously the default backend,
|
||||||
|
* but is now used as a fallback for devices that do not support WebGL 2.
|
||||||
|
* Supports fewer features and has a faster initialization time;
|
||||||
|
* may be useful for content that does not need advanced features like bitmap drawing or blend modes.
|
||||||
|
*/
|
||||||
|
Webgl = "webgl",
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The slowest and most basic backend, used as a fallback when all else fails.
|
||||||
|
* However, this is currently the only backend that accurately scales hairline strokes.
|
||||||
|
* If you notice excessively thick strokes in specific content,
|
||||||
|
* you may want to use the canvas renderer for that content until the issue is resolved.
|
||||||
|
*/
|
||||||
|
Canvas = "canvas",
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any options used for loading a movie.
|
* Any options used for loading a movie.
|
||||||
*/
|
*/
|
||||||
|
@ -326,6 +359,20 @@ export interface BaseLoadOptions {
|
||||||
*/
|
*/
|
||||||
playerVersion?: number | null;
|
playerVersion?: number | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred render backend of the Ruffle player.
|
||||||
|
*
|
||||||
|
* This option should only be used for testing;
|
||||||
|
* The available backends may change in future releases.
|
||||||
|
* By default, Ruffle chooses the most featureful backend supported by the user's system,
|
||||||
|
* falling back to more basic backends if necessary.
|
||||||
|
* The available values in order of default preference are:
|
||||||
|
* "webgpu", "wgpu-webgl", "webgl", "canvas".
|
||||||
|
*
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
preferredRenderer?: RenderBackend | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL at which Ruffle can load its extra files (i.e. `.wasm`).
|
* The URL at which Ruffle can load its extra files (i.e. `.wasm`).
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,13 +24,17 @@
|
||||||
<label for="ruffle_enable">Play Flash content in Ruffle</label>
|
<label for="ruffle_enable">Play Flash content in Ruffle</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="option checkbox">
|
<div class="option checkbox">
|
||||||
<input type="checkbox" id="ignore_optout" />
|
<input type="checkbox" id="show_swf_download" />
|
||||||
<label for="ignore_optout">Ignore website compatibility warnings</label>
|
<label for="show_swf_download">Show SWF download in context menu</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="option checkbox">
|
<div class="option checkbox">
|
||||||
<input type="checkbox" id="warn_on_unsupported_content" />
|
<input type="checkbox" id="warn_on_unsupported_content" />
|
||||||
<label for="warn_on_unsupported_content">Warn on unsupported content</label>
|
<label for="warn_on_unsupported_content">Warn on unsupported content</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="option checkbox">
|
||||||
|
<input type="checkbox" id="ignore_optout" />
|
||||||
|
<label for="ignore_optout">Ignore website compatibility warnings</label>
|
||||||
|
</div>
|
||||||
<div class="option select">
|
<div class="option select">
|
||||||
<select id="log_level">
|
<select id="log_level">
|
||||||
<option value="info">Info</option>
|
<option value="info">Info</option>
|
||||||
|
@ -39,9 +43,14 @@
|
||||||
</select>
|
</select>
|
||||||
<label for="log_level">Log level</label>
|
<label for="log_level">Log level</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="option checkbox">
|
<div class="option select">
|
||||||
<input type="checkbox" id="show_swf_download" />
|
<select id="preferred_renderer">
|
||||||
<label for="show_swf_download">Show SWF download in context menu</label>
|
<option value="webgpu">WebGPU</option>
|
||||||
|
<option value="wgpu-webgl">Wgpu (via WebGL)</option>
|
||||||
|
<option value="webgl">WebGL</option>
|
||||||
|
<option value="canvas">Canvas2D</option>
|
||||||
|
</select>
|
||||||
|
<label for="preferred_renderer">Preferred renderer</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="option checkbox">
|
<div class="option checkbox">
|
||||||
<input type="checkbox" id="autostart" />
|
<input type="checkbox" id="autostart" />
|
||||||
|
|
178
web/src/lib.rs
178
web/src/lib.rs
|
@ -170,6 +170,8 @@ struct Config {
|
||||||
max_execution_duration: Duration,
|
max_execution_duration: Duration,
|
||||||
|
|
||||||
player_version: Option<u8>,
|
player_version: Option<u8>,
|
||||||
|
|
||||||
|
preferred_renderer: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata about the playing SWF file to be passed back to JavaScript.
|
/// Metadata about the playing SWF file to be passed back to JavaScript.
|
||||||
|
@ -1292,89 +1294,123 @@ async fn create_renderer(
|
||||||
document: &web_sys::Document,
|
document: &web_sys::Document,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Result<(PlayerBuilder, HtmlCanvasElement), Box<dyn Error>> {
|
) -> Result<(PlayerBuilder, HtmlCanvasElement), Box<dyn Error>> {
|
||||||
#[cfg(not(any(feature = "canvas", feature = "webgpu", feature = "wgpu-webgl")))]
|
#[cfg(not(any(
|
||||||
|
feature = "canvas",
|
||||||
|
feature = "webgl",
|
||||||
|
feature = "webgpu",
|
||||||
|
feature = "wgpu-webgl"
|
||||||
|
)))]
|
||||||
std::compile_error!("You must enable one of the render backend features (e.g., webgl).");
|
std::compile_error!("You must enable one of the render backend features (e.g., webgl).");
|
||||||
|
|
||||||
let _is_transparent = config.wmode.as_deref() == Some("transparent");
|
let _is_transparent = config.wmode.as_deref() == Some("transparent");
|
||||||
|
|
||||||
|
let mut renderer_list = vec!["webgpu", "wgpu-webgl", "webgl", "canvas"];
|
||||||
|
if let Some(preferred_renderer) = &config.preferred_renderer {
|
||||||
|
if let Some(pos) = renderer_list.iter().position(|&r| r == preferred_renderer) {
|
||||||
|
renderer_list.remove(pos);
|
||||||
|
renderer_list.insert(0, preferred_renderer.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to create a backend, falling through to the next backend on failure.
|
// Try to create a backend, falling through to the next backend on failure.
|
||||||
// We must recreate the canvas each attempt, as only a single context may be created per canvas
|
// We must recreate the canvas each attempt, as only a single context may be created per canvas
|
||||||
// with `getContext`.
|
// with `getContext`.
|
||||||
#[cfg(all(feature = "webgpu", target_family = "wasm"))]
|
for renderer in renderer_list {
|
||||||
{
|
match renderer {
|
||||||
// Check that we have access to WebGPU (navigator.gpu should exist).
|
"webgpu" => {
|
||||||
if web_sys::window()
|
#[cfg(all(feature = "webgpu", target_family = "wasm"))]
|
||||||
.ok_or(JsValue::FALSE)
|
{
|
||||||
.and_then(|window| js_sys::Reflect::has(&window.navigator(), &JsValue::from_str("gpu")))
|
// Check that we have access to WebGPU (navigator.gpu should exist).
|
||||||
.unwrap_or_default()
|
if web_sys::window()
|
||||||
{
|
.ok_or(JsValue::FALSE)
|
||||||
tracing::info!("Creating wgpu webgpu renderer...");
|
.and_then(|window| {
|
||||||
let canvas: HtmlCanvasElement = document
|
js_sys::Reflect::has(&window.navigator(), &JsValue::from_str("gpu"))
|
||||||
.create_element("canvas")
|
})
|
||||||
.into_js_result()?
|
.unwrap_or_default()
|
||||||
.dyn_into()
|
{
|
||||||
.map_err(|_| "Expected HtmlCanvasElement")?;
|
tracing::info!("Creating wgpu webgpu renderer...");
|
||||||
|
let canvas: HtmlCanvasElement = document
|
||||||
|
.create_element("canvas")
|
||||||
|
.into_js_result()?
|
||||||
|
.dyn_into()
|
||||||
|
.map_err(|_| "Expected HtmlCanvasElement")?;
|
||||||
|
|
||||||
match ruffle_render_wgpu::backend::WgpuRenderBackend::for_canvas(&canvas).await {
|
match ruffle_render_wgpu::backend::WgpuRenderBackend::for_canvas(&canvas)
|
||||||
Ok(renderer) => {
|
.await
|
||||||
return Ok((builder.with_renderer(renderer), canvas));
|
{
|
||||||
|
Ok(renderer) => {
|
||||||
|
return Ok((builder.with_renderer(renderer), canvas));
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!("Error creating wgpu webgpu renderer: {}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => tracing::error!("Error creating wgpu webgpu renderer: {}", error),
|
}
|
||||||
|
"wgpu-webgl" => {
|
||||||
|
#[cfg(all(feature = "wgpu-webgl", target_family = "wasm"))]
|
||||||
|
{
|
||||||
|
tracing::info!("Creating wgpu webgl renderer...");
|
||||||
|
let canvas: HtmlCanvasElement = document
|
||||||
|
.create_element("canvas")
|
||||||
|
.into_js_result()?
|
||||||
|
.dyn_into()
|
||||||
|
.map_err(|_| "Expected HtmlCanvasElement")?;
|
||||||
|
|
||||||
|
match ruffle_render_wgpu::backend::WgpuRenderBackend::for_canvas(&canvas).await
|
||||||
|
{
|
||||||
|
Ok(renderer) => {
|
||||||
|
return Ok((builder.with_renderer(renderer), canvas));
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!("Error creating wgpu webgl renderer: {}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"webgl" => {
|
||||||
|
#[cfg(feature = "webgl")]
|
||||||
|
{
|
||||||
|
tracing::info!("Creating WebGL renderer...");
|
||||||
|
let canvas: HtmlCanvasElement = document
|
||||||
|
.create_element("canvas")
|
||||||
|
.into_js_result()?
|
||||||
|
.dyn_into()
|
||||||
|
.map_err(|_| "Expected HtmlCanvasElement")?;
|
||||||
|
match ruffle_render_webgl::WebGlRenderBackend::new(&canvas, _is_transparent) {
|
||||||
|
Ok(renderer) => {
|
||||||
|
return Ok((builder.with_renderer(renderer), canvas));
|
||||||
|
}
|
||||||
|
Err(error) => tracing::error!("Error creating WebGL renderer: {}", error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"canvas" => {
|
||||||
|
#[cfg(feature = "canvas")]
|
||||||
|
{
|
||||||
|
tracing::info!("Creating Canvas renderer...");
|
||||||
|
let canvas: HtmlCanvasElement = document
|
||||||
|
.create_element("canvas")
|
||||||
|
.into_js_result()?
|
||||||
|
.dyn_into()
|
||||||
|
.map_err(|_| "Expected HtmlCanvasElement")?;
|
||||||
|
match ruffle_render_canvas::WebCanvasRenderBackend::new(
|
||||||
|
&canvas,
|
||||||
|
_is_transparent,
|
||||||
|
) {
|
||||||
|
Ok(renderer) => {
|
||||||
|
return Ok((builder.with_renderer(renderer), canvas));
|
||||||
|
}
|
||||||
|
Err(error) => tracing::error!("Error creating canvas renderer: {}", error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tracing::error!("Unrecognized renderer name: {}", renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "wgpu-webgl", target_family = "wasm"))]
|
|
||||||
{
|
|
||||||
tracing::info!("Creating wgpu webgl renderer...");
|
|
||||||
let canvas: HtmlCanvasElement = document
|
|
||||||
.create_element("canvas")
|
|
||||||
.into_js_result()?
|
|
||||||
.dyn_into()
|
|
||||||
.map_err(|_| "Expected HtmlCanvasElement")?;
|
|
||||||
|
|
||||||
match ruffle_render_wgpu::backend::WgpuRenderBackend::for_canvas(&canvas).await {
|
|
||||||
Ok(renderer) => {
|
|
||||||
return Ok((builder.with_renderer(renderer), canvas));
|
|
||||||
}
|
|
||||||
Err(error) => tracing::error!("Error creating wgpu webgl renderer: {}", error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create a backend, falling through to the next backend on failure.
|
|
||||||
// We must recreate the canvas each attempt, as only a single context may be created per canvas
|
|
||||||
// with `getContext`.
|
|
||||||
#[cfg(feature = "webgl")]
|
|
||||||
{
|
|
||||||
tracing::info!("Creating WebGL renderer...");
|
|
||||||
let canvas: HtmlCanvasElement = document
|
|
||||||
.create_element("canvas")
|
|
||||||
.into_js_result()?
|
|
||||||
.dyn_into()
|
|
||||||
.map_err(|_| "Expected HtmlCanvasElement")?;
|
|
||||||
match ruffle_render_webgl::WebGlRenderBackend::new(&canvas, _is_transparent) {
|
|
||||||
Ok(renderer) => {
|
|
||||||
return Ok((builder.with_renderer(renderer), canvas));
|
|
||||||
}
|
|
||||||
Err(error) => tracing::error!("Error creating WebGL renderer: {}", error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "canvas")]
|
|
||||||
{
|
|
||||||
tracing::info!("Falling back to Canvas renderer...");
|
|
||||||
let canvas: HtmlCanvasElement = document
|
|
||||||
.create_element("canvas")
|
|
||||||
.into_js_result()?
|
|
||||||
.dyn_into()
|
|
||||||
.map_err(|_| "Expected HtmlCanvasElement")?;
|
|
||||||
match ruffle_render_canvas::WebCanvasRenderBackend::new(&canvas, _is_transparent) {
|
|
||||||
Ok(renderer) => {
|
|
||||||
return Ok((builder.with_renderer(renderer), canvas));
|
|
||||||
}
|
|
||||||
Err(error) => tracing::error!("Error creating canvas renderer: {}", error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err("Unable to create renderer".into())
|
Err("Unable to create renderer".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue