wgpu: Cache compiled naga-agal shader module
We use an `lru::LruCache` inside `ShaderModuleAgal`. This automatically gives us the proper garbage-collection behavior (when the Flash Program3D instance is garbage collected, we'll drop the `ShaderModuleAgal` and the cache). The cache is keyed on the data needed to compile the shader (vertex attributes and sampler overrides). This lets us avoid shader recompilations when a Stage3D program repeatedly uses the same Program3D with different sampler overrides / vertex attribute formats.
This commit is contained in:
parent
455b96710e
commit
d44c9cceb1
|
@ -2572,6 +2572,15 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e"
|
||||
dependencies = [
|
||||
"hashbrown 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lyon"
|
||||
version = "1.0.1"
|
||||
|
@ -3775,6 +3784,7 @@ dependencies = [
|
|||
"gif",
|
||||
"h263-rs-yuv",
|
||||
"jpeg-decoder",
|
||||
"lru",
|
||||
"lyon",
|
||||
"png",
|
||||
"ruffle_wstr",
|
||||
|
@ -3832,6 +3842,7 @@ dependencies = [
|
|||
"futures",
|
||||
"gc-arena",
|
||||
"image",
|
||||
"lru",
|
||||
"naga",
|
||||
"naga-agal",
|
||||
"naga_oil",
|
||||
|
|
|
@ -24,6 +24,7 @@ enum-map = "2.5.0"
|
|||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
clap = { version = "4.3.1", features = ["derive"], optional = true }
|
||||
h263-rs-yuv = { git = "https://github.com/ruffle-rs/h263-rs", rev = "d5d78eb251c1ce1f1da57c63db14f0fdc77a4b36"}
|
||||
lru = "0.10.0"
|
||||
|
||||
[dependencies.jpeg-decoder]
|
||||
version = "0.3.0"
|
||||
|
|
|
@ -10,7 +10,7 @@ pub const SHADER_ENTRY_POINT: &str = "main";
|
|||
pub const MAX_VERTEX_ATTRIBUTES: usize = 8;
|
||||
pub const MAX_TEXTURES: usize = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum VertexAttributeFormat {
|
||||
Float1,
|
||||
Float2,
|
||||
|
|
|
@ -127,20 +127,20 @@ impl SourceField {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Filter {
|
||||
Nearest = 0,
|
||||
Linear = 1,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Mipmap {
|
||||
Disable = 0,
|
||||
Nearest = 1,
|
||||
Linear = 2,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Wrapping {
|
||||
Clamp = 0,
|
||||
Repeat = 1,
|
||||
|
@ -181,7 +181,7 @@ pub struct SamplerField {
|
|||
pub reg_type: RegisterType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SamplerOverride {
|
||||
pub wrapping: Wrapping,
|
||||
pub filter: Filter,
|
||||
|
|
|
@ -26,6 +26,7 @@ naga-agal = { path = "../naga-agal" }
|
|||
downcast-rs = "1.2.0"
|
||||
profiling = { version = "1.0", default-features = false, optional = true }
|
||||
naga = { version = "0.12.2", features = ["validate", "wgsl-out"] }
|
||||
lru = "0.10.0"
|
||||
|
||||
# desktop
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
|
||||
|
|
|
@ -12,12 +12,11 @@ use wgpu::{
|
|||
use wgpu::{Buffer, DepthStencilState, StencilFaceState};
|
||||
use wgpu::{ColorTargetState, RenderPipelineDescriptor, TextureFormat, VertexState};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::num::NonZeroU64;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::context3d::VertexBufferWrapper;
|
||||
use crate::context3d::{ShaderCompileData, VertexBufferWrapper};
|
||||
use crate::descriptors::Descriptors;
|
||||
|
||||
use super::{ShaderModuleAgal, VertexAttributeInfo, MAX_VERTEX_ATTRIBUTES};
|
||||
|
@ -413,44 +412,31 @@ impl CurrentPipeline {
|
|||
})
|
||||
});
|
||||
|
||||
let sampler_overrides = &self.sampler_override;
|
||||
let vertex_module = self
|
||||
.vertex_shader
|
||||
.as_ref()
|
||||
.expect("Missing vertex shader!")
|
||||
.compile(
|
||||
descriptors,
|
||||
ShaderCompileData {
|
||||
vertex_attributes: agal_attributes,
|
||||
// Vertex shaders do not use sampler overrides
|
||||
sampler_overrides: [None; 8],
|
||||
},
|
||||
);
|
||||
|
||||
let vertex_naga = naga_agal::agal_to_naga(
|
||||
&self
|
||||
.vertex_shader
|
||||
.as_ref()
|
||||
.expect("Missing vertex shader!")
|
||||
.0,
|
||||
&agal_attributes,
|
||||
sampler_overrides,
|
||||
)
|
||||
.expect("Vertex shader failed to compile");
|
||||
|
||||
let fragment_naga = naga_agal::agal_to_naga(
|
||||
&self
|
||||
.fragment_shader
|
||||
.as_ref()
|
||||
.expect("Missing fragment shader")
|
||||
.0,
|
||||
&[None; 8],
|
||||
sampler_overrides,
|
||||
)
|
||||
.expect("Fragment shader failed to compile");
|
||||
|
||||
let vertex_module = descriptors
|
||||
.device
|
||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Vertex shader"),
|
||||
source: wgpu::ShaderSource::Naga(Cow::Owned(vertex_naga)),
|
||||
});
|
||||
|
||||
let fragment_module =
|
||||
descriptors
|
||||
.device
|
||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Fragment shader"),
|
||||
source: wgpu::ShaderSource::Naga(Cow::Owned(fragment_naga)),
|
||||
});
|
||||
let fragment_module = self
|
||||
.fragment_shader
|
||||
.as_ref()
|
||||
.expect("Missing fragment shader!")
|
||||
.compile(
|
||||
descriptors,
|
||||
ShaderCompileData {
|
||||
// Fragment shaders do not use vertex attributes
|
||||
vertex_attributes: [None; 8],
|
||||
sampler_overrides: self.sampler_override,
|
||||
},
|
||||
);
|
||||
|
||||
struct BufferData {
|
||||
buffer: Rc<VertexBufferWrapper>,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use lru::LruCache;
|
||||
use naga_agal::{SamplerOverride, VertexAttributeFormat};
|
||||
use ruffle_render::backend::{
|
||||
Context3D, Context3DBlendFactor, Context3DCommand, Context3DCompareMode,
|
||||
Context3DTextureFormat, Context3DVertexBufferFormat, IndexBuffer, ProgramType, ShaderModule,
|
||||
|
@ -6,7 +8,7 @@ use ruffle_render::backend::{
|
|||
use ruffle_render::bitmap::{BitmapFormat, BitmapHandle};
|
||||
use ruffle_render::error::Error;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell, RefMut};
|
||||
use swf::{Rectangle, Twips};
|
||||
|
||||
use wgpu::util::StagingBelt;
|
||||
|
@ -21,7 +23,7 @@ use crate::descriptors::Descriptors;
|
|||
use crate::Texture;
|
||||
use gc_arena::{Collect, MutationContext};
|
||||
|
||||
use std::num::NonZeroU64;
|
||||
use std::num::{NonZeroU64, NonZeroUsize};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -353,7 +355,53 @@ pub struct VertexBufferWrapper {
|
|||
|
||||
#[derive(Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct ShaderModuleAgal(Vec<u8>);
|
||||
pub struct ShaderModuleAgal {
|
||||
bytecode: Vec<u8>,
|
||||
// Caches compiled wgpu shader modules. The cache key represents all of the data
|
||||
// that we need to pass to `naga_agal::agal_to_naga` to compile a shader.
|
||||
compiled: RefCell<LruCache<ShaderCompileData, wgpu::ShaderModule>>,
|
||||
}
|
||||
|
||||
impl ShaderModuleAgal {
|
||||
pub fn new(bytecode: Vec<u8>) -> Self {
|
||||
Self {
|
||||
bytecode,
|
||||
// TODO - figure out a good size for this cache.
|
||||
compiled: RefCell::new(LruCache::new(NonZeroUsize::new(2).unwrap())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(
|
||||
&self,
|
||||
descriptors: &Descriptors,
|
||||
data: ShaderCompileData,
|
||||
) -> RefMut<'_, wgpu::ShaderModule> {
|
||||
let compiled = self.compiled.borrow_mut();
|
||||
RefMut::map(compiled, |compiled| {
|
||||
// TODO: Figure out a way to avoid the clone when we have a cache hit
|
||||
compiled.get_or_insert_mut(data.clone(), || {
|
||||
let naga_module = naga_agal::agal_to_naga(
|
||||
&self.bytecode,
|
||||
&data.vertex_attributes,
|
||||
&data.sampler_overrides,
|
||||
)
|
||||
.unwrap();
|
||||
descriptors
|
||||
.device
|
||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("AGAL shader"),
|
||||
source: wgpu::ShaderSource::Naga(Cow::Owned(naga_module)),
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Clone)]
|
||||
pub struct ShaderCompileData {
|
||||
sampler_overrides: [Option<SamplerOverride>; 8],
|
||||
vertex_attributes: [Option<VertexAttributeFormat>; MAX_VERTEX_ATTRIBUTES],
|
||||
}
|
||||
|
||||
#[derive(Collect)]
|
||||
#[collect(require_static)]
|
||||
|
@ -933,8 +981,9 @@ impl Context3D for WgpuContext3D {
|
|||
fragment_shader,
|
||||
fragment_shader_agal,
|
||||
} => {
|
||||
*vertex_shader.write(mc) = Some(Rc::new(ShaderModuleAgal(vertex_shader_agal)));
|
||||
*fragment_shader.write(mc) = Some(Rc::new(ShaderModuleAgal(fragment_shader_agal)));
|
||||
*vertex_shader.write(mc) = Some(Rc::new(ShaderModuleAgal::new(vertex_shader_agal)));
|
||||
*fragment_shader.write(mc) =
|
||||
Some(Rc::new(ShaderModuleAgal::new(fragment_shader_agal)));
|
||||
}
|
||||
|
||||
Context3DCommand::SetShaders {
|
||||
|
|
Loading…
Reference in New Issue