rt: expose cache disable option in filter options

This commit is contained in:
chyyran 2023-02-15 03:06:34 -05:00 committed by Ronny Chan
parent 604edfb78f
commit e7645a5592
15 changed files with 124 additions and 39 deletions

View file

@ -1,5 +1,5 @@
use crate::key::CacheKey;
use crate::cacheable::Cacheable; use crate::cacheable::Cacheable;
use crate::key::CacheKey;
use platform_dirs::AppDirs; use platform_dirs::AppDirs;
use rusqlite::{params, Connection, DatabaseName}; use rusqlite::{params, Connection, DatabaseName};
use std::error::Error; use std::error::Error;

View file

@ -1,7 +1,7 @@
//! Cache implementations for D3D blob types that need to live //! Cache implementations for D3D blob types that need to live
//! here because of the orphan rule. //! here because of the orphan rule.
use crate::{Cacheable, CacheKey}; use crate::{CacheKey, Cacheable};
impl CacheKey for windows::Win32::Graphics::Direct3D::ID3DBlob { impl CacheKey for windows::Win32::Graphics::Direct3D::ID3DBlob {
fn hash_bytes(&self) -> &[u8] { fn hash_bytes(&self) -> &[u8] {
@ -15,7 +15,6 @@ impl CacheKey for windows::Win32::Graphics::Direct3D::Dxc::IDxcBlob {
} }
} }
impl Cacheable for windows::Win32::Graphics::Direct3D::ID3DBlob { impl Cacheable for windows::Win32::Graphics::Direct3D::ID3DBlob {
fn from_bytes(bytes: &[u8]) -> Option<Self> { fn from_bytes(bytes: &[u8]) -> Option<Self> {
let Some(blob) = (unsafe { windows::Win32::Graphics::Direct3D::Fxc::D3DCreateBlob(bytes.len()).ok() }) else { let Some(blob) = (unsafe { windows::Win32::Graphics::Direct3D::Fxc::D3DCreateBlob(bytes.len()).ok() }) else {

View file

@ -118,15 +118,24 @@ impl FilterChainD3D11 {
ctx: &ID3D11DeviceContext, ctx: &ID3D11DeviceContext,
options: Option<&FilterChainOptionsD3D11>, options: Option<&FilterChainOptionsD3D11>,
) -> error::Result<FilterChainD3D11> { ) -> error::Result<FilterChainD3D11> {
let (passes, semantics) = HLSL::compile_preset_passes::< let disable_cache = options.map_or(false, |o| o.disable_cache);
CachedCompilation<GlslangCompilation>,
FilterChainError, let (passes, semantics) = if !disable_cache {
>(preset.shaders, &preset.textures)?; HLSL::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
preset.shaders,
&preset.textures,
)?
} else {
HLSL::compile_preset_passes::<GlslangCompilation, FilterChainError>(
preset.shaders,
&preset.textures,
)?
};
let samplers = SamplerSet::new(device)?; let samplers = SamplerSet::new(device)?;
// initialize passes // initialize passes
let filters = FilterChainD3D11::init_passes(device, passes, &semantics)?; let filters = FilterChainD3D11::init_passes(device, passes, &semantics, disable_cache)?;
println!("passes loded"); println!("passes loded");
let immediate_context = unsafe { device.GetImmediateContext()? }; let immediate_context = unsafe { device.GetImmediateContext()? };
@ -212,6 +221,7 @@ impl FilterChainD3D11 {
device: &ID3D11Device, device: &ID3D11Device,
passes: Vec<ShaderPassMeta>, passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
disable_cache: bool,
) -> error::Result<Vec<FilterPass>> { ) -> error::Result<Vec<FilterPass>> {
let device_is_singlethreaded = let device_is_singlethreaded =
unsafe { (device.GetCreationFlags() & D3D11_CREATE_DEVICE_SINGLETHREADED.0) == 1 }; unsafe { (device.GetCreationFlags() & D3D11_CREATE_DEVICE_SINGLETHREADED.0) == 1 };
@ -235,7 +245,7 @@ impl FilterChainD3D11 {
blob, blob,
)) ))
}, },
true, !disable_cache,
)?; )?;
let ia_desc = DrawQuad::get_spirv_cross_vbo_desc(); let ia_desc = DrawQuad::get_spirv_cross_vbo_desc();
@ -248,7 +258,7 @@ impl FilterChainD3D11 {
|blob| { |blob| {
d3d11_compile_bound_shader(device, &blob, None, ID3D11Device::CreatePixelShader) d3d11_compile_bound_shader(device, &blob, None, ID3D11Device::CreatePixelShader)
}, },
true, !disable_cache,
)?; )?;
let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 { let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 {

View file

@ -61,6 +61,7 @@ mod tests {
filter.as_deref().unwrap_or(FILTER_PATH), filter.as_deref().unwrap_or(FILTER_PATH),
Some(&FilterChainOptionsD3D11 { Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false, force_no_mipmaps: false,
disable_cache: false,
}), }),
// replace below with 'None' for the triangle // replace below with 'None' for the triangle
Some(image), Some(image),
@ -85,6 +86,7 @@ mod tests {
FILTER_PATH, FILTER_PATH,
Some(&FilterChainOptionsD3D11 { Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false, force_no_mipmaps: false,
disable_cache: false,
}), }),
// replace below with 'None' for the triangle // replace below with 'None' for the triangle
// None, // None,

View file

@ -18,4 +18,7 @@ pub struct FilterChainOptionsD3D11 {
/// Whether or not to explicitly disable mipmap /// Whether or not to explicitly disable mipmap
/// generation regardless of shader preset settings. /// generation regardless of shader preset settings.
pub force_no_mipmaps: bool, pub force_no_mipmaps: bool,
/// Disable the shader object cache. Shaders will be
/// recompiled rather than loaded from the cache.
pub disable_cache: bool,
} }

View file

@ -207,16 +207,31 @@ impl FilterChainD3D12 {
let lut_count = preset.textures.len(); let lut_count = preset.textures.len();
let shader_copy = preset.shaders.clone(); let shader_copy = preset.shaders.clone();
let disable_cache = options.map_or(false, |o| o.disable_cache);
let (passes, semantics) = DXIL::compile_preset_passes::< let (passes, semantics) = if !disable_cache {
CachedCompilation<GlslangCompilation>, DXIL::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
FilterChainError, preset.shaders,
>(preset.shaders, &preset.textures)?; &preset.textures,
)?
} else {
DXIL::compile_preset_passes::<GlslangCompilation, FilterChainError>(
preset.shaders,
&preset.textures,
)?
};
let (hlsl_passes, _) = HLSL::compile_preset_passes::< let (hlsl_passes, _) = if !disable_cache {
CachedCompilation<GlslangCompilation>, HLSL::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
FilterChainError, shader_copy,
>(shader_copy, &preset.textures)?; &preset.textures,
)?
} else {
HLSL::compile_preset_passes::<GlslangCompilation, FilterChainError>(
shader_copy,
&preset.textures,
)?
};
let samplers = SamplerSet::new(device)?; let samplers = SamplerSet::new(device)?;
let mipmap_gen = D3D12MipmapGen::new(device, false)?; let mipmap_gen = D3D12MipmapGen::new(device, false)?;
@ -244,6 +259,7 @@ impl FilterChainD3D12 {
hlsl_passes, hlsl_passes,
&semantics, &semantics,
options.map_or(false, |o| o.force_hlsl_pipeline), options.map_or(false, |o| o.force_hlsl_pipeline),
disable_cache,
)?; )?;
let mut residuals = FrameResiduals::new(); let mut residuals = FrameResiduals::new();
@ -367,6 +383,7 @@ impl FilterChainD3D12 {
hlsl_passes: Vec<HlslShaderPassMeta>, hlsl_passes: Vec<HlslShaderPassMeta>,
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
force_hlsl: bool, force_hlsl: bool,
disable_cache: bool,
) -> error::Result<( ) -> error::Result<(
ID3D12DescriptorHeap, ID3D12DescriptorHeap,
ID3D12DescriptorHeap, ID3D12DescriptorHeap,
@ -432,7 +449,8 @@ impl FilterChainD3D12 {
validator, validator,
&dxil, &dxil,
root_signature, root_signature,
render_format render_format,
disable_cache
) { ) {
(dxil_reflection, graphics_pipeline) (dxil_reflection, graphics_pipeline)
} else { } else {
@ -446,6 +464,7 @@ impl FilterChainD3D12 {
&hlsl, &hlsl,
root_signature, root_signature,
render_format, render_format,
disable_cache
)?; )?;
(hlsl_reflection, graphics_pipeline) (hlsl_reflection, graphics_pipeline)
}; };

View file

@ -36,6 +36,7 @@ pub struct D3D12GraphicsPipeline {
pub(crate) format: DXGI_FORMAT, pub(crate) format: DXGI_FORMAT,
vertex: Vec<u8>, vertex: Vec<u8>,
fragment: Vec<u8>, fragment: Vec<u8>,
cache_disabled: bool,
} }
const D3D12_SLANG_ROOT_PARAMETERS: &[D3D12_ROOT_PARAMETER1; 4] = &[ const D3D12_SLANG_ROOT_PARAMETERS: &[D3D12_ROOT_PARAMETER1; 4] = &[
@ -152,6 +153,7 @@ impl D3D12GraphicsPipeline {
fragment_dxil: IDxcBlob, fragment_dxil: IDxcBlob,
root_signature: &D3D12RootSignature, root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT, render_format: DXGI_FORMAT,
disable_cache: bool,
) -> error::Result<D3D12GraphicsPipeline> { ) -> error::Result<D3D12GraphicsPipeline> {
let input_element = DrawQuad::get_spirv_cross_vbo_desc(); let input_element = DrawQuad::get_spirv_cross_vbo_desc();
@ -246,7 +248,7 @@ impl D3D12GraphicsPipeline {
let cached_pso = pso.GetCachedBlob()?; let cached_pso = pso.GetCachedBlob()?;
Ok(cached_pso) Ok(cached_pso)
}, },
true, !disable_cache,
)? )?
}; };
@ -264,6 +266,7 @@ impl D3D12GraphicsPipeline {
format: render_format, format: render_format,
vertex, vertex,
fragment, fragment,
cache_disabled: disable_cache,
}) })
} }
} }
@ -288,8 +291,14 @@ impl D3D12GraphicsPipeline {
)?; )?;
(vertex, fragment) (vertex, fragment)
}; };
let mut new_pipeline = let mut new_pipeline = Self::new_from_blobs(
Self::new_from_blobs(device, vertex.into(), fragment.into(), root_sig, format)?; device,
vertex.into(),
fragment.into(),
root_sig,
format,
self.cache_disabled,
)?;
std::mem::swap(self, &mut new_pipeline); std::mem::swap(self, &mut new_pipeline);
Ok(()) Ok(())
@ -302,6 +311,7 @@ impl D3D12GraphicsPipeline {
shader_assembly: &ShaderCompilerOutput<DxilObject, ()>, shader_assembly: &ShaderCompilerOutput<DxilObject, ()>,
root_signature: &D3D12RootSignature, root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT, render_format: DXGI_FORMAT,
disable_cache: bool,
) -> error::Result<D3D12GraphicsPipeline> { ) -> error::Result<D3D12GraphicsPipeline> {
if shader_assembly.vertex.requires_runtime_data() { if shader_assembly.vertex.requires_runtime_data() {
return Err(Direct3DOperationError( return Err(Direct3DOperationError(
@ -319,7 +329,7 @@ impl D3D12GraphicsPipeline {
&[shader_assembly.vertex.deref()], &[shader_assembly.vertex.deref()],
|&[source]| util::dxc_validate_shader(library, validator, source), |&[source]| util::dxc_validate_shader(library, validator, source),
|f| Ok(f), |f| Ok(f),
true, !disable_cache,
)?; )?;
let fragment_dxil = cache_object( let fragment_dxil = cache_object(
@ -327,7 +337,7 @@ impl D3D12GraphicsPipeline {
&[shader_assembly.fragment.deref()], &[shader_assembly.fragment.deref()],
|&[source]| util::dxc_validate_shader(library, validator, source), |&[source]| util::dxc_validate_shader(library, validator, source),
|f| Ok(f), |f| Ok(f),
true, !disable_cache,
)?; )?;
Self::new_from_blobs( Self::new_from_blobs(
@ -336,6 +346,7 @@ impl D3D12GraphicsPipeline {
fragment_dxil, fragment_dxil,
root_signature, root_signature,
render_format, render_format,
disable_cache,
) )
} }
@ -346,13 +357,14 @@ impl D3D12GraphicsPipeline {
shader_assembly: &ShaderCompilerOutput<String, CrossHlslContext>, shader_assembly: &ShaderCompilerOutput<String, CrossHlslContext>,
root_signature: &D3D12RootSignature, root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT, render_format: DXGI_FORMAT,
disable_cache: bool,
) -> error::Result<D3D12GraphicsPipeline> { ) -> error::Result<D3D12GraphicsPipeline> {
let vertex_dxil = cache_object( let vertex_dxil = cache_object(
"dxil", "dxil",
&[shader_assembly.vertex.as_bytes()], &[shader_assembly.vertex.as_bytes()],
|&[source]| util::dxc_compile_shader(library, dxc, source, u16cstr!("vs_6_0")), |&[source]| util::dxc_compile_shader(library, dxc, source, u16cstr!("vs_6_0")),
|f| Ok(f), |f| Ok(f),
true, !disable_cache,
)?; )?;
let fragment_dxil = cache_object( let fragment_dxil = cache_object(
@ -360,7 +372,7 @@ impl D3D12GraphicsPipeline {
&[shader_assembly.fragment.as_bytes()], &[shader_assembly.fragment.as_bytes()],
|&[source]| util::dxc_compile_shader(library, dxc, source, u16cstr!("ps_6_0")), |&[source]| util::dxc_compile_shader(library, dxc, source, u16cstr!("ps_6_0")),
|f| Ok(f), |f| Ok(f),
true, !disable_cache,
)?; )?;
Self::new_from_blobs( Self::new_from_blobs(
@ -369,6 +381,7 @@ impl D3D12GraphicsPipeline {
fragment_dxil, fragment_dxil,
root_signature, root_signature,
render_format, render_format,
disable_cache,
) )
} }
} }

View file

@ -22,4 +22,8 @@ pub struct FilterChainOptionsD3D12 {
/// generation for intermediate passes regardless /// generation for intermediate passes regardless
/// of shader preset settings. /// of shader preset settings.
pub force_no_mipmaps: bool, pub force_no_mipmaps: bool,
/// Disable the shader object cache. Shaders will be
/// recompiled rather than loaded from the cache.
pub disable_cache: bool,
} }

View file

@ -19,6 +19,7 @@ use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::front::GlslangCompilation; use librashader_reflect::front::GlslangCompilation;
use librashader_reflect::reflect::semantics::{ShaderSemantics, UniformMeta}; use librashader_reflect::reflect::semantics::{ShaderSemantics, UniformMeta};
use librashader_cache::compilation::CachedCompilation;
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_reflect::reflect::ReflectShader; use librashader_reflect::reflect::ReflectShader;
use librashader_runtime::binding::BindingUtil; use librashader_runtime::binding::BindingUtil;
@ -104,15 +105,24 @@ impl<T: GLInterface> FilterChainImpl<T> {
preset: ShaderPreset, preset: ShaderPreset,
options: Option<&FilterChainOptionsGL>, options: Option<&FilterChainOptionsGL>,
) -> error::Result<Self> { ) -> error::Result<Self> {
let (passes, semantics) = GLSL::compile_preset_passes::< let disable_cache = options.map_or(false, |o| o.disable_cache);
GlslangCompilation,
FilterChainError, let (passes, semantics) = if !disable_cache {
>(preset.shaders, &preset.textures)?; GLSL::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
preset.shaders,
&preset.textures,
)?
} else {
GLSL::compile_preset_passes::<GlslangCompilation, FilterChainError>(
preset.shaders,
&preset.textures,
)?
};
let version = options.map_or_else(gl_get_version, |o| gl_u16_to_version(o.glsl_version)); let version = options.map_or_else(gl_get_version, |o| gl_u16_to_version(o.glsl_version));
// initialize passes // initialize passes
let filters = Self::init_passes(version, passes, &semantics)?; let filters = Self::init_passes(version, passes, &semantics, disable_cache)?;
let default_filter = filters.first().map(|f| f.config.filter).unwrap_or_default(); let default_filter = filters.first().map(|f| f.config.filter).unwrap_or_default();
let default_wrap = filters let default_wrap = filters
@ -181,6 +191,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
version: GlslVersion, version: GlslVersion,
passes: Vec<ShaderPassMeta>, passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
disable_cache: bool,
) -> error::Result<Box<[FilterPass<T>]>> { ) -> error::Result<Box<[FilterPass<T>]>> {
let mut filters = Vec::new(); let mut filters = Vec::new();
@ -189,7 +200,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
let reflection = reflect.reflect(index, semantics)?; let reflection = reflect.reflect(index, semantics)?;
let glsl = reflect.compile(version)?; let glsl = reflect.compile(version)?;
let (program, ubo_location) = T::CompileShader::compile_program(glsl, true)?; let (program, ubo_location) = T::CompileShader::compile_program(glsl, !disable_cache)?;
let ubo_ring = if let Some(ubo) = &reflection.ubo { let ubo_ring = if let Some(ubo) = &reflection.ubo {
let ring = UboRing::new(ubo.size); let ring = UboRing::new(ubo.size);

View file

@ -40,6 +40,7 @@ mod tests {
glsl_version: 0, glsl_version: 0,
use_dsa: false, use_dsa: false,
force_no_mipmaps: false, force_no_mipmaps: false,
disable_cache: false,
}), }),
) )
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
@ -59,6 +60,7 @@ mod tests {
glsl_version: 0, glsl_version: 0,
use_dsa: true, use_dsa: true,
force_no_mipmaps: false, force_no_mipmaps: false,
disable_cache: false,
}), }),
) )
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)

View file

@ -18,7 +18,11 @@ pub struct FilterChainOptionsGL {
/// The GLSL version. Should be at least `330`. /// The GLSL version. Should be at least `330`.
pub glsl_version: u16, pub glsl_version: u16,
/// Whether or not to use the Direct State Access APIs. Only available on OpenGL 4.5+. /// Whether or not to use the Direct State Access APIs. Only available on OpenGL 4.5+.
/// This is required to use shader caching.
pub use_dsa: bool, pub use_dsa: bool,
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings. /// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
pub force_no_mipmaps: bool, pub force_no_mipmaps: bool,
/// Disable the shader object cache. Shaders will be
/// recompiled rather than loaded from the cache.
pub disable_cache: bool,
} }

View file

@ -307,10 +307,20 @@ impl FilterChainVulkan {
V: TryInto<VulkanObjects, Error = E>, V: TryInto<VulkanObjects, Error = E>,
FilterChainError: From<E>, FilterChainError: From<E>,
{ {
let (passes, semantics) = SPIRV::compile_preset_passes::< let disable_cache = options.map_or(false, |o| o.disable_cache);
CachedCompilation<GlslangCompilation>,
FilterChainError, let (passes, semantics) = if !disable_cache {
>(preset.shaders, &preset.textures)?; SPIRV::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
preset.shaders,
&preset.textures,
)?
} else {
SPIRV::compile_preset_passes::<GlslangCompilation, FilterChainError>(
preset.shaders,
&preset.textures,
)?
};
let device = vulkan.try_into().map_err(From::from)?; let device = vulkan.try_into().map_err(From::from)?;
let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight); let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight);
@ -325,6 +335,7 @@ impl FilterChainVulkan {
&semantics, &semantics,
frames_in_flight, frames_in_flight,
options.map_or(false, |o| o.use_render_pass), options.map_or(false, |o| o.use_render_pass),
disable_cache,
)?; )?;
let luts = FilterChainVulkan::load_luts(&device, cmd, &preset.textures)?; let luts = FilterChainVulkan::load_luts(&device, cmd, &preset.textures)?;
@ -388,6 +399,7 @@ impl FilterChainVulkan {
semantics: &ShaderSemantics, semantics: &ShaderSemantics,
frames_in_flight: u32, frames_in_flight: u32,
use_render_pass: bool, use_render_pass: bool,
disable_cache: bool,
) -> error::Result<Box<[FilterPass]>> { ) -> error::Result<Box<[FilterPass]>> {
let frames_in_flight = std::cmp::max(1, frames_in_flight); let frames_in_flight = std::cmp::max(1, frames_in_flight);
@ -430,6 +442,7 @@ impl FilterChainVulkan {
&reflection, &reflection,
frames_in_flight, frames_in_flight,
render_pass_format, render_pass_format,
!disable_cache,
)?; )?;
Ok(FilterPass { Ok(FilterPass {

View file

@ -314,6 +314,7 @@ impl VulkanGraphicsPipeline {
reflection: &ShaderReflection, reflection: &ShaderReflection,
replicas: u32, replicas: u32,
render_pass_format: vk::Format, render_pass_format: vk::Format,
cache_objects: bool,
) -> error::Result<VulkanGraphicsPipeline> { ) -> error::Result<VulkanGraphicsPipeline> {
let pipeline_layout = PipelineLayoutObjects::new(reflection, replicas, device)?; let pipeline_layout = PipelineLayoutObjects::new(reflection, replicas, device)?;
@ -358,7 +359,7 @@ impl VulkanGraphicsPipeline {
Ok::<_, FilterChainError>((pipeline, pipeline_cache)) Ok::<_, FilterChainError>((pipeline, pipeline_cache))
}, },
|(_pipeline, cache)| unsafe { Ok(device.get_pipeline_cache_data(*cache)?) }, |(_pipeline, cache)| unsafe { Ok(device.get_pipeline_cache_data(*cache)?) },
true, cache_objects,
)?; )?;
Ok(VulkanGraphicsPipeline { Ok(VulkanGraphicsPipeline {

View file

@ -51,6 +51,7 @@ mod tests {
frames_in_flight: 3, frames_in_flight: 3,
force_no_mipmaps: false, force_no_mipmaps: false,
use_render_pass: true, use_render_pass: true,
disable_cache: false,
}), }),
) )
.unwrap(); .unwrap();

View file

@ -22,4 +22,7 @@ pub struct FilterChainOptionsVulkan {
/// Use explicit render pass objects It is recommended if possible to use dynamic rendering, /// Use explicit render pass objects It is recommended if possible to use dynamic rendering,
/// because render-pass mode will create new framebuffers per pass. /// because render-pass mode will create new framebuffers per pass.
pub use_render_pass: bool, pub use_render_pass: bool,
/// Disable the shader object cache. Shaders will be
/// recompiled rather than loaded from the cache.
pub disable_cache: bool,
} }