tests: Move rendering interface into its own trait
This commit is contained in:
parent
86939e0e82
commit
8697a313ac
|
@ -14,13 +14,25 @@ pub trait Environment {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a render backend for the given test.
|
/// Creates a render backend for a new test run.
|
||||||
|
///
|
||||||
|
/// This method must return both a [RenderBackend] and [RenderInterface] as a pair,
|
||||||
|
/// and will be treated as a pair for the purposes of this test framework.
|
||||||
|
///
|
||||||
|
/// All relevant methods in the [RenderInterface] will receive the same [RenderBackend]
|
||||||
|
/// that was provided here with that interface.
|
||||||
///
|
///
|
||||||
/// If [Self::is_render_supported] returned false, this won't be attempted.
|
/// If [Self::is_render_supported] returned false, this won't be attempted.
|
||||||
fn create_renderer(&self, _width: u32, _height: u32) -> Option<Box<dyn RenderBackend>> {
|
fn create_renderer(
|
||||||
|
&self,
|
||||||
|
_width: u32,
|
||||||
|
_height: u32,
|
||||||
|
) -> Option<(Box<dyn RenderInterface>, Box<dyn RenderBackend>)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RenderInterface {
|
||||||
/// Gets the name of this environment, for use in test reporting.
|
/// Gets the name of this environment, for use in test reporting.
|
||||||
///
|
///
|
||||||
/// This name may be used in file paths, so it should contain appropriate characters for such.
|
/// This name may be used in file paths, so it should contain appropriate characters for such.
|
||||||
|
@ -28,6 +40,6 @@ pub trait Environment {
|
||||||
|
|
||||||
/// Capture the stage rendered out by the given render backend.
|
/// Capture the stage rendered out by the given render backend.
|
||||||
///
|
///
|
||||||
/// The provided backend will have previously been created by [Environment::create_renderer].
|
/// The provided backend is guaranteed to be the same one paired with this interface.
|
||||||
fn capture_renderer(&self, renderer: &mut Box<dyn RenderBackend>) -> image::RgbaImage;
|
fn capture(&self, renderer: &mut Box<dyn RenderBackend>) -> image::RgbaImage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::backends::TestAudioBackend;
|
use crate::backends::TestAudioBackend;
|
||||||
use crate::environment::Environment;
|
use crate::environment::{Environment, RenderInterface};
|
||||||
use crate::image_trigger::ImageTrigger;
|
use crate::image_trigger::ImageTrigger;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use approx::assert_relative_eq;
|
use approx::assert_relative_eq;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ruffle_core::tag_utils::SwfMovie;
|
use ruffle_core::tag_utils::SwfMovie;
|
||||||
use ruffle_core::{PlayerBuilder, ViewportDimensions};
|
use ruffle_core::{PlayerBuilder, ViewportDimensions};
|
||||||
|
use ruffle_render::backend::RenderBackend;
|
||||||
use ruffle_render::quality::StageQuality;
|
use ruffle_render::quality::StageQuality;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -136,30 +137,11 @@ pub struct PlayerOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerOptions {
|
impl PlayerOptions {
|
||||||
pub fn setup(
|
pub fn setup(&self, mut player_builder: PlayerBuilder) -> Result<PlayerBuilder> {
|
||||||
&self,
|
|
||||||
mut player_builder: PlayerBuilder,
|
|
||||||
movie: &SwfMovie,
|
|
||||||
environment: &impl Environment,
|
|
||||||
) -> Result<PlayerBuilder> {
|
|
||||||
if let Some(max_execution_duration) = self.max_execution_duration {
|
if let Some(max_execution_duration) = self.max_execution_duration {
|
||||||
player_builder = player_builder.with_max_execution_duration(max_execution_duration);
|
player_builder = player_builder.with_max_execution_duration(max_execution_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (width, height) = if let Some(viewport_dimensions) = self.viewport_dimensions {
|
|
||||||
player_builder = player_builder.with_viewport_dimensions(
|
|
||||||
viewport_dimensions.width,
|
|
||||||
viewport_dimensions.height,
|
|
||||||
viewport_dimensions.scale_factor,
|
|
||||||
);
|
|
||||||
(viewport_dimensions.width, viewport_dimensions.height)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
movie.width().to_pixels() as u32,
|
|
||||||
movie.height().to_pixels() as u32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(render_options) = &self.with_renderer {
|
if let Some(render_options) = &self.with_renderer {
|
||||||
player_builder = player_builder.with_quality(match render_options.sample_count {
|
player_builder = player_builder.with_quality(match render_options.sample_count {
|
||||||
16 => StageQuality::High16x16,
|
16 => StageQuality::High16x16,
|
||||||
|
@ -168,10 +150,6 @@ impl PlayerOptions {
|
||||||
2 => StageQuality::Medium,
|
2 => StageQuality::Medium,
|
||||||
_ => StageQuality::Low,
|
_ => StageQuality::Low,
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(renderer) = environment.create_renderer(width, height) {
|
|
||||||
player_builder = player_builder.with_boxed_renderer(renderer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.with_audio {
|
if self.with_audio {
|
||||||
|
@ -197,6 +175,27 @@ impl PlayerOptions {
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn viewport_dimensions(&self, movie: &SwfMovie) -> ViewportDimensions {
|
||||||
|
self.viewport_dimensions
|
||||||
|
.unwrap_or_else(|| ViewportDimensions {
|
||||||
|
width: movie.width().to_pixels() as u32,
|
||||||
|
height: movie.height().to_pixels() as u32,
|
||||||
|
scale_factor: 1.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_renderer(
|
||||||
|
&self,
|
||||||
|
environment: &impl Environment,
|
||||||
|
dimensions: ViewportDimensions,
|
||||||
|
) -> Option<(Box<dyn RenderInterface>, Box<dyn RenderBackend>)> {
|
||||||
|
if self.with_renderer.is_some() {
|
||||||
|
environment.create_renderer(dimensions.width, dimensions.height)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone, Debug)]
|
#[derive(Deserialize, Default, Clone, Debug)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::backends::{TestLogBackend, TestNavigatorBackend, TestUiBackend};
|
use crate::backends::{TestLogBackend, TestNavigatorBackend, TestUiBackend};
|
||||||
use crate::environment::Environment;
|
use crate::environment::RenderInterface;
|
||||||
use crate::fs_commands::{FsCommand, TestFsCommandProvider};
|
use crate::fs_commands::{FsCommand, TestFsCommandProvider};
|
||||||
use crate::image_trigger::ImageTrigger;
|
use crate::image_trigger::ImageTrigger;
|
||||||
use crate::options::ImageComparison;
|
use crate::options::ImageComparison;
|
||||||
|
@ -15,7 +15,7 @@ use ruffle_input_format::{
|
||||||
AutomatedEvent, InputInjector, MouseButton as InputMouseButton,
|
AutomatedEvent, InputInjector, MouseButton as InputMouseButton,
|
||||||
TextControlCode as InputTextControlCode,
|
TextControlCode as InputTextControlCode,
|
||||||
};
|
};
|
||||||
use ruffle_render::backend::null::NullRenderer;
|
use ruffle_render::backend::{RenderBackend, ViewportDimensions};
|
||||||
use ruffle_socket_format::SocketEvent;
|
use ruffle_socket_format::SocketEvent;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
@ -23,17 +23,19 @@ use std::time::Duration;
|
||||||
|
|
||||||
/// Loads an SWF and runs it through the Ruffle core for a number of frames.
|
/// Loads an SWF and runs it through the Ruffle core for a number of frames.
|
||||||
/// Tests that the trace output matches the given expected output.
|
/// Tests that the trace output matches the given expected output.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn run_swf(
|
pub fn run_swf(
|
||||||
test: &Test,
|
test: &Test,
|
||||||
|
movie: SwfMovie,
|
||||||
mut injector: InputInjector,
|
mut injector: InputInjector,
|
||||||
socket_events: Option<Vec<SocketEvent>>,
|
socket_events: Option<Vec<SocketEvent>>,
|
||||||
before_start: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
before_start: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||||
before_end: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
before_end: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||||
environment: &impl Environment,
|
renderer: Option<(Box<dyn RenderInterface>, Box<dyn RenderBackend>)>,
|
||||||
|
viewport_dimensions: ViewportDimensions,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let base_path = Path::new(&test.output_path).parent().unwrap();
|
let base_path = Path::new(&test.output_path).parent().unwrap();
|
||||||
let mut executor = NullExecutor::new();
|
let mut executor = NullExecutor::new();
|
||||||
let movie = SwfMovie::from_path(&test.swf_path, None).map_err(|e| anyhow!(e.to_string()))?;
|
|
||||||
let mut frame_time = 1000.0 / movie.frame_rate().to_f64();
|
let mut frame_time = 1000.0 / movie.frame_rate().to_f64();
|
||||||
if let Some(tr) = test.options.tick_rate {
|
if let Some(tr) = test.options.tick_rate {
|
||||||
frame_time = tr;
|
frame_time = tr;
|
||||||
|
@ -50,35 +52,34 @@ pub fn run_swf(
|
||||||
test.options.log_fetch.then(|| log.clone()),
|
test.options.log_fetch.then(|| log.clone()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let builder = PlayerBuilder::new()
|
let mut builder = PlayerBuilder::new()
|
||||||
.with_log(log.clone())
|
.with_log(log.clone())
|
||||||
.with_navigator(navigator)
|
.with_navigator(navigator)
|
||||||
.with_max_execution_duration(Duration::from_secs(300))
|
.with_max_execution_duration(Duration::from_secs(300))
|
||||||
.with_fs_commands(Box::new(fs_command_provider))
|
.with_fs_commands(Box::new(fs_command_provider))
|
||||||
.with_ui(TestUiBackend)
|
.with_ui(TestUiBackend)
|
||||||
.with_viewport_dimensions(
|
.with_viewport_dimensions(
|
||||||
movie.width().to_pixels() as u32,
|
viewport_dimensions.width,
|
||||||
movie.height().to_pixels() as u32,
|
viewport_dimensions.height,
|
||||||
1.0,
|
viewport_dimensions.scale_factor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let render_interface = if let Some((interface, backend)) = renderer {
|
||||||
|
builder = builder.with_boxed_renderer(backend);
|
||||||
|
Some(interface)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Test player options may override anything set above
|
// Test player options may override anything set above
|
||||||
let player = test
|
let player = test
|
||||||
.options
|
.options
|
||||||
.player_options
|
.player_options
|
||||||
.setup(builder, &movie, environment)?
|
.setup(builder)?
|
||||||
.with_movie(movie)
|
.with_movie(movie)
|
||||||
.with_autoplay(true) //.tick() requires playback
|
.with_autoplay(true) //.tick() requires playback
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// If we set anything but a null renderer, then the Environment must have created a valid renderer.
|
|
||||||
let has_renderer = player
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.renderer()
|
|
||||||
.downcast_ref::<NullRenderer>()
|
|
||||||
.is_none();
|
|
||||||
|
|
||||||
let mut images = test.options.image_comparisons.clone();
|
let mut images = test.options.image_comparisons.clone();
|
||||||
|
|
||||||
before_start(player.clone())?;
|
before_start(player.clone())?;
|
||||||
|
@ -148,8 +149,7 @@ pub fn run_swf(
|
||||||
&name,
|
&name,
|
||||||
image_comparison,
|
image_comparison,
|
||||||
test.options.known_failure,
|
test.options.known_failure,
|
||||||
environment,
|
render_interface.as_deref(),
|
||||||
has_renderer,
|
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Encountered fscommand to capture and compare image '{name}', but no [image_comparison] was set up for this."));
|
return Err(anyhow!("Encountered fscommand to capture and compare image '{name}', but no [image_comparison] was set up for this."));
|
||||||
|
@ -221,8 +221,7 @@ pub fn run_swf(
|
||||||
&name,
|
&name,
|
||||||
image_comparison,
|
image_comparison,
|
||||||
test.options.known_failure,
|
test.options.known_failure,
|
||||||
environment,
|
render_interface.as_deref(),
|
||||||
has_renderer,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,8 +241,7 @@ pub fn run_swf(
|
||||||
&name,
|
&name,
|
||||||
image_comparison,
|
image_comparison,
|
||||||
test.options.known_failure,
|
test.options.known_failure,
|
||||||
environment,
|
render_interface.as_deref(),
|
||||||
has_renderer,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,16 +270,15 @@ fn capture_and_compare_image(
|
||||||
name: &String,
|
name: &String,
|
||||||
image_comparison: ImageComparison,
|
image_comparison: ImageComparison,
|
||||||
known_failure: bool,
|
known_failure: bool,
|
||||||
environment: &impl Environment,
|
render_interface: Option<&dyn RenderInterface>,
|
||||||
has_renderer: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
if has_renderer {
|
if let Some(render_interface) = render_interface {
|
||||||
let mut player_lock = player.lock().unwrap();
|
let mut player_lock = player.lock().unwrap();
|
||||||
player_lock.render();
|
player_lock.render();
|
||||||
|
|
||||||
let actual_image = environment.capture_renderer(player_lock.renderer_mut());
|
let actual_image = render_interface.capture(player_lock.renderer_mut());
|
||||||
|
|
||||||
let expected_image_path = base_path.join(format!("{name}.expected.png"));
|
let expected_image_path = base_path.join(format!("{name}.expected.png"));
|
||||||
if expected_image_path.is_file() {
|
if expected_image_path.is_file() {
|
||||||
|
@ -294,7 +291,7 @@ fn capture_and_compare_image(
|
||||||
actual_image,
|
actual_image,
|
||||||
expected_image,
|
expected_image,
|
||||||
base_path,
|
base_path,
|
||||||
environment.name(),
|
render_interface.name(),
|
||||||
known_failure,
|
known_failure,
|
||||||
)?;
|
)?;
|
||||||
} else if !known_failure {
|
} else if !known_failure {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::runner::run_swf;
|
||||||
use crate::set_logger;
|
use crate::set_logger;
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use pretty_assertions::Comparison;
|
use pretty_assertions::Comparison;
|
||||||
|
use ruffle_core::tag_utils::SwfMovie;
|
||||||
use ruffle_core::Player;
|
use ruffle_core::Player;
|
||||||
use ruffle_input_format::InputInjector;
|
use ruffle_input_format::InputInjector;
|
||||||
use ruffle_socket_format::SocketEvent;
|
use ruffle_socket_format::SocketEvent;
|
||||||
|
@ -63,13 +64,22 @@ impl Test {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let movie =
|
||||||
|
SwfMovie::from_path(&self.swf_path, None).map_err(|e| anyhow!(e.to_string()))?;
|
||||||
|
let viewport_dimensions = self.options.player_options.viewport_dimensions(&movie);
|
||||||
|
let renderer = self
|
||||||
|
.options
|
||||||
|
.player_options
|
||||||
|
.create_renderer(environment, viewport_dimensions);
|
||||||
let output = run_swf(
|
let output = run_swf(
|
||||||
self,
|
self,
|
||||||
|
movie,
|
||||||
injector,
|
injector,
|
||||||
socket_events,
|
socket_events,
|
||||||
before_start,
|
before_start,
|
||||||
before_end,
|
before_end,
|
||||||
environment,
|
renderer,
|
||||||
|
viewport_dimensions,
|
||||||
)?;
|
)?;
|
||||||
self.compare_output(&output)?;
|
self.compare_output(&output)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,33 +1,83 @@
|
||||||
use image::RgbaImage;
|
use ruffle_test_framework::environment::Environment;
|
||||||
use ruffle_test_framework::environment::{Environment, RenderBackend};
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
use ruffle_test_framework::options::RenderOptions;
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
use ruffle_render_wgpu::{
|
|
||||||
backend::{request_adapter_and_device, WgpuRenderBackend},
|
|
||||||
descriptors::Descriptors,
|
|
||||||
target::TextureTarget,
|
|
||||||
wgpu,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
use {std::sync::Arc, std::sync::OnceLock};
|
|
||||||
|
|
||||||
pub struct NativeEnvironment;
|
pub struct NativeEnvironment;
|
||||||
|
|
||||||
impl NativeEnvironment {
|
impl Environment for NativeEnvironment {
|
||||||
#[cfg(feature = "imgtests")]
|
#[cfg(feature = "imgtests")]
|
||||||
fn descriptors(&self) -> Option<&Arc<Descriptors>> {
|
fn is_render_supported(
|
||||||
WGPU.get_or_init(build_wgpu_descriptors).as_ref()
|
&self,
|
||||||
|
requirements: &ruffle_test_framework::options::RenderOptions,
|
||||||
|
) -> bool {
|
||||||
|
renderer::is_supported(requirements)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "imgtests")]
|
||||||
|
fn create_renderer(
|
||||||
|
&self,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Option<(
|
||||||
|
Box<dyn ruffle_test_framework::environment::RenderInterface>,
|
||||||
|
Box<dyn ruffle_test_framework::environment::RenderBackend>,
|
||||||
|
)> {
|
||||||
|
renderer::NativeRenderInterface::create_pair(width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment for NativeEnvironment {
|
|
||||||
#[cfg(feature = "imgtests")]
|
#[cfg(feature = "imgtests")]
|
||||||
fn is_render_supported(&self, requirements: &RenderOptions) -> bool {
|
mod renderer {
|
||||||
if let Some(descriptors) = self.descriptors() {
|
use image::RgbaImage;
|
||||||
|
use ruffle_render_wgpu::backend::{request_adapter_and_device, WgpuRenderBackend};
|
||||||
|
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||||
|
use ruffle_render_wgpu::target::TextureTarget;
|
||||||
|
use ruffle_render_wgpu::wgpu;
|
||||||
|
use ruffle_test_framework::environment::{RenderBackend, RenderInterface};
|
||||||
|
use ruffle_test_framework::options::RenderOptions;
|
||||||
|
use {std::sync::Arc, std::sync::OnceLock};
|
||||||
|
|
||||||
|
pub struct NativeRenderInterface;
|
||||||
|
|
||||||
|
impl NativeRenderInterface {
|
||||||
|
pub fn create_pair(
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Option<(Box<dyn RenderInterface>, Box<dyn RenderBackend>)> {
|
||||||
|
if let Some(descriptors) = descriptors() {
|
||||||
|
let target = TextureTarget::new(&descriptors.device, (width, height)).expect(
|
||||||
|
"WGPU Texture Target creation must not fail, everything was checked ahead of time",
|
||||||
|
);
|
||||||
|
|
||||||
|
Some( (Box::new(Self), Box::new(
|
||||||
|
WgpuRenderBackend::new(descriptors.clone(), target)
|
||||||
|
.expect("WGPU Render backend creation must not fail, everything was checked ahead of time"),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderInterface for NativeRenderInterface {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
if let Some(descriptors) = descriptors() {
|
||||||
|
let adapter_info = descriptors.adapter.get_info();
|
||||||
|
format!("{}-{:?}", std::env::consts::OS, adapter_info.backend)
|
||||||
|
} else {
|
||||||
|
std::env::consts::OS.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capture(&self, backend: &mut Box<dyn RenderBackend>) -> RgbaImage {
|
||||||
|
let renderer = backend
|
||||||
|
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
renderer.capture_frame().expect("Failed to capture image")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_supported(requirements: &RenderOptions) -> bool {
|
||||||
|
if let Some(descriptors) = descriptors() {
|
||||||
let adapter_info = descriptors.adapter.get_info();
|
let adapter_info = descriptors.adapter.get_info();
|
||||||
let is_warp =
|
let is_warp =
|
||||||
cfg!(windows) && adapter_info.vendor == 5140 && adapter_info.device == 140;
|
cfg!(windows) && adapter_info.vendor == 5140 && adapter_info.device == 140;
|
||||||
|
@ -38,53 +88,6 @@ impl Environment for NativeEnvironment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
fn create_renderer(&self, width: u32, height: u32) -> Option<Box<dyn RenderBackend>> {
|
|
||||||
if let Some(descriptors) = self.descriptors() {
|
|
||||||
let target = TextureTarget::new(&descriptors.device, (width, height)).expect(
|
|
||||||
"WGPU Texture Target creation must not fail, everything was checked ahead of time",
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(Box::new(
|
|
||||||
WgpuRenderBackend::new(descriptors.clone(), target)
|
|
||||||
.expect("WGPU Render backend creation must not fail, everything was checked ahead of time"),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "imgtests"))]
|
|
||||||
fn name(&self) -> String {
|
|
||||||
std::env::consts::OS.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
fn name(&self) -> String {
|
|
||||||
if let Some(descriptors) = self.descriptors() {
|
|
||||||
let adapter_info = descriptors.adapter.get_info();
|
|
||||||
format!("{}-{:?}", std::env::consts::OS, adapter_info.backend)
|
|
||||||
} else {
|
|
||||||
std::env::consts::OS.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "imgtests"))]
|
|
||||||
fn capture_renderer(&self, _backend: &mut Box<dyn RenderBackend>) -> RgbaImage {
|
|
||||||
panic!("Cannot capture renderer as imgtests are not enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
fn capture_renderer(&self, backend: &mut Box<dyn RenderBackend>) -> RgbaImage {
|
|
||||||
let renderer = backend
|
|
||||||
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
renderer.capture_frame().expect("Failed to capture image")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
static WGPU: OnceLock<Option<Arc<Descriptors>>> = OnceLock::new();
|
static WGPU: OnceLock<Option<Arc<Descriptors>>> = OnceLock::new();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -99,7 +102,6 @@ static WGPU: OnceLock<Option<Arc<Descriptors>>> = OnceLock::new();
|
||||||
but for `cargo nextest run` it's a big cost per test if it's not going to use it.
|
but for `cargo nextest run` it's a big cost per test if it's not going to use it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
fn create_wgpu_device() -> Option<(wgpu::Instance, wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
fn create_wgpu_device() -> Option<(wgpu::Instance, wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
||||||
let instance = wgpu::Instance::new(Default::default());
|
let instance = wgpu::Instance::new(Default::default());
|
||||||
futures::executor::block_on(request_adapter_and_device(
|
futures::executor::block_on(request_adapter_and_device(
|
||||||
|
@ -113,7 +115,6 @@ fn create_wgpu_device() -> Option<(wgpu::Instance, wgpu::Adapter, wgpu::Device,
|
||||||
.map(|(adapter, device, queue)| (instance, adapter, device, queue))
|
.map(|(adapter, device, queue)| (instance, adapter, device, queue))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "imgtests")]
|
|
||||||
fn build_wgpu_descriptors() -> Option<Arc<Descriptors>> {
|
fn build_wgpu_descriptors() -> Option<Arc<Descriptors>> {
|
||||||
if let Some((instance, adapter, device, queue)) = create_wgpu_device() {
|
if let Some((instance, adapter, device, queue)) = create_wgpu_device() {
|
||||||
Some(Arc::new(Descriptors::new(instance, adapter, device, queue)))
|
Some(Arc::new(Descriptors::new(instance, adapter, device, queue)))
|
||||||
|
@ -121,3 +122,8 @@ fn build_wgpu_descriptors() -> Option<Arc<Descriptors>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn descriptors() -> Option<&'static Arc<Descriptors>> {
|
||||||
|
WGPU.get_or_init(build_wgpu_descriptors).as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue