tests: split tests into their own crates

This commit is contained in:
chyyran 2023-02-16 00:39:36 -05:00
parent 7d6701aa4e
commit f42328280a
40 changed files with 874 additions and 309 deletions

2
Cargo.lock generated
View file

@ -180,7 +180,6 @@ dependencies = [
"cfg-if", "cfg-if",
"constant_time_eq", "constant_time_eq",
"digest", "digest",
"rayon",
] ]
[[package]] [[package]]
@ -1112,6 +1111,7 @@ name = "librashader"
version = "0.1.0-rc.5" version = "0.1.0-rc.5"
dependencies = [ dependencies = [
"ash", "ash",
"librashader-cache",
"librashader-common 0.1.0-rc.5", "librashader-common 0.1.0-rc.5",
"librashader-preprocess", "librashader-preprocess",
"librashader-presets 0.1.0-rc.5", "librashader-presets 0.1.0-rc.5",

View file

@ -15,10 +15,10 @@ serde = { version = "1.0" }
librashader-reflect = { path = "../librashader-reflect", version = "0.1.0-rc.5", features = ["serialize", "dxil"] } librashader-reflect = { path = "../librashader-reflect", version = "0.1.0-rc.5", features = ["serialize", "dxil"] }
librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.0-rc.5" } librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.0-rc.5" }
platform-dirs = "0.3.0" platform-dirs = "0.3.0"
blake3 = { version = "1.3.3", features = ["rayon"] } blake3 = { version = "1.3.3" }
thiserror = "1.0.38" thiserror = "1.0.38"
bincode = { version = "2.0.0-rc.2", features = ["serde"] } bincode = { version = "2.0.0-rc.2", features = ["serde"] }
rusqlite = { version = "0.28.0", features = ["bundled"] } rusqlite = { version = "0.28.0" }
bytemuck = "1.13.0" bytemuck = "1.13.0"
@ -33,4 +33,10 @@ optional = true
[features] [features]
# should be enabled at librashader crate level.
sqlite-bundled = ["rusqlite/bundled"]
d3d = ["windows"] d3d = ["windows"]
docsrs = ["blake3/pure"]
[package.metadata.docs.rs]
features = ["docsrs"]

View file

@ -1,67 +1,73 @@
use crate::cacheable::Cacheable; use crate::cacheable::Cacheable;
use crate::key::CacheKey; use crate::key::CacheKey;
use platform_dirs::AppDirs;
use rusqlite::{params, Connection, DatabaseName};
use std::error::Error;
use std::path::PathBuf;
pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> { #[cfg(not(feature = "docsrs"))]
let cache_dir = pub(crate) mod internal {
if let Some(cache_dir) = AppDirs::new(Some("librashader"), false).map(|a| a.cache_dir) { use platform_dirs::AppDirs;
cache_dir use rusqlite::{Connection, DatabaseName};
} else { use std::path::PathBuf;
let mut current_dir = std::env::current_dir()?; use std::error::Error;
current_dir.push("librashader");
current_dir
};
std::fs::create_dir_all(&cache_dir)?; pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> {
let cache_dir =
if let Some(cache_dir) = AppDirs::new(Some("librashader"), false).map(|a| a.cache_dir) {
cache_dir
} else {
let mut current_dir = std::env::current_dir()?;
current_dir.push("librashader");
current_dir
};
Ok(cache_dir) std::fs::create_dir_all(&cache_dir)?;
}
pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> { Ok(cache_dir)
let cache_dir = get_cache_dir()?; }
let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
let tx = conn.transaction()?; pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
tx.pragma_update(Some(DatabaseName::Main), "journal_mode", "wal2")?; let cache_dir = get_cache_dir()?;
tx.execute( let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
r#"create table if not exists cache (
let tx = conn.transaction()?;
tx.pragma_update(Some(DatabaseName::Main), "journal_mode", "wal2")?;
tx.execute(
r#"create table if not exists cache (
type text not null, type text not null,
id blob not null, id blob not null,
value blob not null unique, value blob not null unique,
primary key (id, type) primary key (id, type)
)"#, )"#,
[], [],
)?; )?;
tx.commit()?; tx.commit()?;
Ok(conn) Ok(conn)
} }
pub(crate) fn get_blob( pub(crate) fn get_blob(
conn: &Connection, conn: &Connection,
index: &str, index: &str,
key: &[u8], key: &[u8],
) -> Result<Vec<u8>, Box<dyn Error>> { ) -> Result<Vec<u8>, Box<dyn Error>> {
let value = conn.query_row( let value = conn.query_row(
&*format!("select value from cache where (type = (?1) and id = (?2))"), &*format!("select value from cache where (type = (?1) and id = (?2))"),
params![index, key], rusqlite::params![index, key],
|row| row.get(0), |row| row.get(0),
)?; )?;
Ok(value) Ok(value)
} }
pub(crate) fn set_blob(conn: &Connection, index: &str, key: &[u8], value: &[u8]) { pub(crate) fn set_blob(conn: &Connection, index: &str, key: &[u8], value: &[u8]) {
match conn.execute( match conn.execute(
&*format!("insert or replace into cache (type, id, value) values (?1, ?2, ?3)"), &*format!("insert or replace into cache (type, id, value) values (?1, ?2, ?3)"),
params![index, key, value], rusqlite::params![index, key, value],
) { ) {
Ok(_) => return, Ok(_) => return,
Err(e) => println!("err: {:?}", e), Err(e) => println!("err: {:?}", e),
}
} }
} }
#[cfg(not(feature = "docsrs"))]
/// Cache a shader object (usually bytecode) created by the keyed objects. /// Cache a shader object (usually bytecode) created by the keyed objects.
/// ///
/// - `factory` is the function that compiles the values passed as keys to a shader object. /// - `factory` is the function that compiles the values passed as keys to a shader object.
@ -81,7 +87,7 @@ where
return Ok(load(factory(keys)?)?); return Ok(load(factory(keys)?)?);
} }
let cache = get_cache(); let cache = internal::get_cache();
let Ok(cache) = cache else { let Ok(cache) = cache else {
return Ok(load(factory(keys)?)?); return Ok(load(factory(keys)?)?);
@ -97,7 +103,7 @@ where
}; };
'attempt: { 'attempt: {
if let Ok(blob) = get_blob(&cache, index, hashkey.as_bytes()) { if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
let cached = T::from_bytes(&blob).map(&load); let cached = T::from_bytes(&blob).map(&load);
match cached { match cached {
@ -111,11 +117,12 @@ where
let blob = factory(keys)?; let blob = factory(keys)?;
if let Some(slice) = T::to_bytes(&blob) { if let Some(slice) = T::to_bytes(&blob) {
set_blob(&cache, index, hashkey.as_bytes(), &slice); internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
} }
Ok(load(blob)?) Ok(load(blob)?)
} }
#[cfg(not(feature = "docsrs"))]
/// Cache a pipeline state object. /// Cache a pipeline state object.
/// ///
/// Keys are not used to create the object and are only used to uniquely identify the pipeline state. /// Keys are not used to create the object and are only used to uniquely identify the pipeline state.
@ -137,7 +144,7 @@ where
return Ok(restore_pipeline(None)?); return Ok(restore_pipeline(None)?);
} }
let cache = get_cache(); let cache = internal::get_cache();
let Ok(cache) = cache else { let Ok(cache) = cache else {
return Ok(restore_pipeline(None)?); return Ok(restore_pipeline(None)?);
@ -153,7 +160,7 @@ where
}; };
let pipeline = 'attempt: { let pipeline = 'attempt: {
if let Ok(blob) = get_blob(&cache, index, hashkey.as_bytes()) { if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
let cached = restore_pipeline(Some(blob)); let cached = restore_pipeline(Some(blob));
match cached { match cached {
Ok(res) => { Ok(res) => {
@ -169,9 +176,15 @@ where
// update the pso every time just in case. // update the pso every time just in case.
if let Ok(state) = fetch_pipeline_state(&pipeline) { if let Ok(state) = fetch_pipeline_state(&pipeline) {
if let Some(slice) = T::to_bytes(&state) { if let Some(slice) = T::to_bytes(&state) {
set_blob(&cache, index, hashkey.as_bytes(), &slice); internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
} }
} }
Ok(pipeline) Ok(pipeline)
} }
#[cfg(feature = "docsrs")]
pub use crate::docsrs::cache_pipeline;
#[cfg(feature = "docsrs")]
pub use crate::docsrs::cache_shader_object;

View file

@ -1,5 +1,4 @@
//! Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV. //! Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV.
use crate::cache::{get_blob, get_cache, set_blob};
use librashader_preprocess::ShaderSource; use librashader_preprocess::ShaderSource;
use librashader_reflect::back::targets::{DXIL, GLSL, HLSL, SPIRV}; use librashader_reflect::back::targets::{DXIL, GLSL, HLSL, SPIRV};
use librashader_reflect::back::{CompilerBackend, FromCompilation}; use librashader_reflect::back::{CompilerBackend, FromCompilation};
@ -10,11 +9,12 @@ pub struct CachedCompilation<T> {
compilation: T, compilation: T,
} }
#[cfg(not(feature = "docsrs"))]
impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone> impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
ShaderCompilation for CachedCompilation<T> ShaderCompilation for CachedCompilation<T>
{ {
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> { fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
let cache = get_cache(); let cache = crate::cache::internal::get_cache();
let Ok(cache) = cache else { let Ok(cache) = cache else {
return Ok(CachedCompilation { return Ok(CachedCompilation {
@ -31,7 +31,7 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
}; };
let compilation = 'cached: { let compilation = 'cached: {
if let Ok(cached) = get_blob(&cache, "spirv", key.as_bytes()) { if let Ok(cached) = crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes()) {
let decoded = let decoded =
bincode::serde::decode_from_slice(&cached, bincode::config::standard()) bincode::serde::decode_from_slice(&cached, bincode::config::standard())
.map(|(compilation, _)| CachedCompilation { compilation }) .map(|(compilation, _)| CachedCompilation { compilation })
@ -50,13 +50,22 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
if let Ok(updated) = if let Ok(updated) =
bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard()) bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard())
{ {
set_blob(&cache, "spirv", key.as_bytes(), &updated) crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
} }
Ok(compilation) Ok(compilation)
} }
} }
#[cfg(feature = "docsrs")]
impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
ShaderCompilation for CachedCompilation<T>
{
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
T::compile(source)
}
}
impl FromCompilation<CachedCompilation<GlslangCompilation>> for DXIL { impl FromCompilation<CachedCompilation<GlslangCompilation>> for DXIL {
type Target = <DXIL as FromCompilation<GlslangCompilation>>::Target; type Target = <DXIL as FromCompilation<GlslangCompilation>>::Target;
type Options = <DXIL as FromCompilation<GlslangCompilation>>::Options; type Options = <DXIL as FromCompilation<GlslangCompilation>>::Options;

View file

@ -0,0 +1,37 @@
/// Cache a pipeline state object.
///
/// Keys are not used to create the object and are only used to uniquely identify the pipeline state.
///
/// - `restore_pipeline` tries to restore the pipeline with either a cached binary pipeline state
/// cache, or create a new pipeline if no cached value is available.
/// - `fetch_pipeline_state` fetches the new pipeline state cache after the pipeline was created.
pub fn cache_pipeline<E, T, R, const KEY_SIZE: usize>(
index: &str,
keys: &[&dyn CacheKey; KEY_SIZE],
restore_pipeline: impl Fn(Option<Vec<u8>>) -> Result<R, E>,
fetch_pipeline_state: impl FnOnce(&R) -> Result<T, E>,
bypass_cache: bool,
) -> Result<R, E>
where
T: Cacheable,
{
return Ok(restore_pipeline(None)?);
}
/// Cache a shader object (usually bytecode) created by the keyed objects.
///
/// - `factory` is the function that compiles the values passed as keys to a shader object.
/// - `load` tries to load a compiled shader object to a driver-specialized result.
pub fn cache_shader_object<E, T, R, H, const KEY_SIZE: usize>(
index: &str,
keys: &[H; KEY_SIZE],
factory: impl FnOnce(&[H; KEY_SIZE]) -> Result<T, E>,
load: impl Fn(T) -> Result<R, E>,
bypass_cache: bool,
) -> Result<R, E>
where
H: CacheKey,
T: Cacheable,
{
return Ok(load(factory(keys)?)?);
}

View file

@ -2,7 +2,10 @@
//! //!
//! This crate is exempt from semantic versioning guarantees and is an implementation //! This crate is exempt from semantic versioning guarantees and is an implementation
//! detail of librashader runtimes. //! detail of librashader runtimes.
mod cache; mod cache;
mod compilation; mod compilation;
mod key; mod key;
@ -12,8 +15,14 @@ pub use cacheable::Cacheable;
pub use key::CacheKey; pub use key::CacheKey;
pub use compilation::CachedCompilation; pub use compilation::CachedCompilation;
pub use cache::cache_pipeline; pub use cache::cache_pipeline;
pub use cache::cache_shader_object; pub use cache::cache_shader_object;
#[cfg(all(target_os = "windows", feature = "d3d"))] #[cfg(all(target_os = "windows", feature = "d3d"))]
mod d3d; mod d3d;
#[cfg(feature = "docsrs")]
mod docsrs;

View file

@ -18,7 +18,7 @@ librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.0-
librashader-reflect = { path = "../librashader-reflect", version = "0.1.0-rc.5", features = ["standalone"] } librashader-reflect = { path = "../librashader-reflect", version = "0.1.0-rc.5", features = ["standalone"] }
librashader-runtime = { path = "../librashader-runtime", version = "0.1.0-rc.5" } librashader-runtime = { path = "../librashader-runtime", version = "0.1.0-rc.5" }
spirv_cross = { package = "librashader-spirv-cross", version = "0.23" } spirv_cross = { package = "librashader-spirv-cross", version = "0.23" }
librashader-cache = { path = "../librashader-cache", version = "0.1.0-rc.5", features = ["d3d"]} librashader-cache = { path = "../librashader-cache", version = "0.1.0-rc.5", features = ["d3d"] }
thiserror = "1.0.37" thiserror = "1.0.37"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
@ -55,5 +55,9 @@ features = [
"Win32_UI_WindowsAndMessaging", "Win32_UI_WindowsAndMessaging",
] ]
[[test]]
name = "triangle"
required-features = ["librashader-cache/sqlite-bundled"]
[dev-dependencies] [dev-dependencies]
gfx-maths = "0.2.8" gfx-maths = "0.2.8"

View file

@ -6,103 +6,20 @@
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#![feature(let_chains)] #![feature(let_chains)]
#[cfg(test)]
mod hello_triangle;
mod draw_quad; mod draw_quad;
pub mod error;
mod filter_chain; mod filter_chain;
mod filter_pass; mod filter_pass;
mod framebuffer; mod framebuffer;
mod graphics_pipeline; mod graphics_pipeline;
pub mod options;
mod parameters; mod parameters;
mod samplers; mod samplers;
mod texture; mod texture;
mod util; mod util;
pub mod error;
pub mod options;
pub use filter_chain::FilterChainD3D11; pub use filter_chain::FilterChainD3D11;
pub use texture::D3D11InputView; pub use texture::D3D11InputView;
pub use texture::D3D11OutputView; pub use texture::D3D11OutputView;
#[cfg(test)]
mod tests {
use super::*;
use crate::options::FilterChainOptionsD3D11;
use librashader_runtime::image::{Image, UVDirection};
// "../test/slang-shaders/scalefx/scalefx-9x.slangp",
// "../test/slang-shaders/bezel/koko-aio/monitor-bloom.slangp",
// "../test/slang-shaders/presets/crt-geom-ntsc-upscale-sharp.slangp",
// const FILTER_PATH: &str =
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp";
// "../test/null.slangp",
const FILTER_PATH: &str =
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID].slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/test/history.slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/test/feedback.slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp";
const IMAGE_PATH: &str = "../triangle.png";
#[test]
fn triangle_d3d11_args() {
let mut args = std::env::args();
let _ = args.next();
let _ = args.next();
let filter = args.next();
let image = args
.next()
.and_then(|f| Image::load(f, UVDirection::TopLeft).ok())
.or_else(|| Some(Image::load(IMAGE_PATH, UVDirection::TopLeft).unwrap()))
.unwrap();
let sample = hello_triangle::d3d11_hello_triangle::Sample::new(
filter.as_deref().unwrap_or(FILTER_PATH),
Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false,
disable_cache: false,
}),
// replace below with 'None' for the triangle
Some(image),
)
.unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new(
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// Some(&FilterChainOptions {
// use_deferred_context: true,
// })
// )
// .unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap();
hello_triangle::main(sample).unwrap();
}
#[test]
fn triangle_d3d11() {
let sample = hello_triangle::d3d11_hello_triangle::Sample::new(
FILTER_PATH,
Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false,
disable_cache: false,
}),
// replace below with 'None' for the triangle
// None,
Some(Image::load(IMAGE_PATH, UVDirection::TopLeft).unwrap()),
)
.unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new(
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// Some(&FilterChainOptions {
// use_deferred_context: true,
// })
// )
// .unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap();
hello_triangle::main(sample).unwrap();
}
}

View file

@ -2,6 +2,8 @@ const WIDTH: i32 = 800;
const HEIGHT: i32 = 600; const HEIGHT: i32 = 600;
const TITLE: &str = "librashader DirectX 11"; const TITLE: &str = "librashader DirectX 11";
mod texture;
use windows::{ use windows::{
core::*, Win32::Foundation::*, Win32::Graphics::Direct3D::Fxc::*, Win32::Graphics::Direct3D::*, core::*, Win32::Foundation::*, Win32::Graphics::Direct3D::Fxc::*, Win32::Graphics::Direct3D::*,
Win32::Graphics::Direct3D11::*, Win32::Graphics::Dxgi::Common::*, Win32::Graphics::Dxgi::*, Win32::Graphics::Direct3D11::*, Win32::Graphics::Dxgi::Common::*, Win32::Graphics::Dxgi::*,
@ -240,15 +242,15 @@ pub mod d3d11_hello_triangle {
use super::*; use super::*;
use std::path::Path; use std::path::Path;
use crate::filter_chain::FilterChainD3D11;
use crate::options::FilterChainOptionsD3D11; use librashader_runtime_d3d11::options::FilterChainOptionsD3D11;
use crate::texture::{D3D11InputView, LutTexture}; use librashader_runtime_d3d11::{D3D11InputView, D3D11OutputView};
use crate::D3D11OutputView; use texture::ExampleTexture;
use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode}; use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode};
use librashader_runtime::image::Image; use librashader_runtime::image::Image;
use std::slice; use std::slice;
use std::time::Instant; use std::time::Instant;
use librashader_runtime_d3d11::FilterChainD3D11;
pub struct Sample { pub struct Sample {
pub dxgi_factory: IDXGIFactory4, pub dxgi_factory: IDXGIFactory4,
@ -292,7 +294,7 @@ pub mod d3d11_hello_triangle {
let (dxgi_factory, device, context) = create_device()?; let (dxgi_factory, device, context) = create_device()?;
let filter = FilterChainD3D11::load_from_path(filter, &device, filter_options).unwrap(); let filter = FilterChainD3D11::load_from_path(filter, &device, filter_options).unwrap();
let lut = if let Some(image) = image { let lut = if let Some(image) = image {
let lut = LutTexture::new( let lut = ExampleTexture::new(
&device, &device,
&context, &context,
&image, &image,

View file

@ -0,0 +1,174 @@
use librashader_common::{FilterMode, Size, WrapMode};
use librashader_runtime::image::Image;
use librashader_runtime::scaling::MipmapSize;
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
use windows::Win32::Graphics::Direct3D11::{
ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView,
ID3D11Texture2D, D3D11_BIND_FLAG, D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE,
D3D11_BOX, D3D11_CPU_ACCESS_FLAG, D3D11_CPU_ACCESS_WRITE, D3D11_RESOURCE_MISC_FLAG,
D3D11_RESOURCE_MISC_GENERATE_MIPS, D3D11_SHADER_RESOURCE_VIEW_DESC,
D3D11_SHADER_RESOURCE_VIEW_DESC_0, D3D11_SUBRESOURCE_DATA, D3D11_TEX2D_SRV,
D3D11_TEXTURE2D_DESC, D3D11_USAGE_DYNAMIC, D3D11_USAGE_STAGING,
};
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
/// An image view for use as a shader resource.
///
/// Contains an `ID3D11ShaderResourceView`, and a size.
#[derive(Debug, Clone)]
pub struct D3D11InputView {
/// A handle to the shader resource view.
pub handle: ID3D11ShaderResourceView,
/// The size of the image.
pub size: Size<u32>,
}
#[derive(Debug, Clone)]
pub struct InputTexture {
pub view: D3D11InputView,
pub filter: FilterMode,
pub wrap_mode: WrapMode,
}
impl AsRef<InputTexture> for InputTexture {
fn as_ref(&self) -> &InputTexture {
self
}
}
#[derive(Debug, Clone)]
pub(crate) struct ExampleTexture {
// The handle to the Texture2D must be kept alive.
#[allow(dead_code)]
pub handle: ID3D11Texture2D,
#[allow(dead_code)]
pub desc: D3D11_TEXTURE2D_DESC,
pub image: InputTexture,
}
impl AsRef<InputTexture> for ExampleTexture {
fn as_ref(&self) -> &InputTexture {
&self.image
}
}
impl ExampleTexture {
pub fn new(
device: &ID3D11Device,
context: &ID3D11DeviceContext,
source: &Image,
desc: D3D11_TEXTURE2D_DESC,
filter: FilterMode,
wrap_mode: WrapMode,
) -> Result<ExampleTexture, windows::core::Error> {
let mut desc = D3D11_TEXTURE2D_DESC {
Width: source.size.width,
Height: source.size.height,
// todo: set this to 0
MipLevels: if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
0
} else {
1
},
ArraySize: 1,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
CPUAccessFlags: if desc.Usage == D3D11_USAGE_DYNAMIC {
D3D11_CPU_ACCESS_WRITE
} else {
D3D11_CPU_ACCESS_FLAG(0)
},
..desc
};
desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
// determine number of mipmaps required
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
desc.MipLevels = source.size.calculate_miplevels();
}
// Don't need to determine format support because LUTs are always DXGI_FORMAT_R8G8B8A8_UNORM
// since we load them with the Image module.
unsafe {
let mut handle = None;
device.CreateTexture2D(&desc, None, Some(&mut handle))?;
let handle = handle.unwrap();
// need a staging texture to defer mipmap generation
let mut staging = None;
device.CreateTexture2D(
&D3D11_TEXTURE2D_DESC {
MipLevels: 1,
BindFlags: D3D11_BIND_FLAG(0),
MiscFlags: D3D11_RESOURCE_MISC_FLAG(0),
Usage: D3D11_USAGE_STAGING,
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE,
..desc
},
Some(&D3D11_SUBRESOURCE_DATA {
pSysMem: source.bytes.as_ptr().cast(),
SysMemPitch: source.pitch as u32,
SysMemSlicePitch: 0,
}),
Some(&mut staging),
)?;
let staging = staging.unwrap();
context.CopySubresourceRegion(
&handle,
0,
0,
0,
0,
&staging,
0,
Some(&D3D11_BOX {
left: 0,
top: 0,
front: 0,
right: source.size.width,
bottom: source.size.height,
back: 1,
}),
);
let mut srv = None;
device.CreateShaderResourceView(
&handle,
Some(&D3D11_SHADER_RESOURCE_VIEW_DESC {
Format: desc.Format,
ViewDimension: D3D_SRV_DIMENSION_TEXTURE2D,
Anonymous: D3D11_SHADER_RESOURCE_VIEW_DESC_0 {
Texture2D: D3D11_TEX2D_SRV {
MostDetailedMip: 0,
MipLevels: u32::MAX,
},
},
}),
Some(&mut srv),
)?;
let srv = srv.unwrap();
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
context.GenerateMips(&srv)
}
Ok(ExampleTexture {
handle,
desc,
image: InputTexture {
view: D3D11InputView {
handle: srv,
size: source.size,
},
filter,
wrap_mode,
},
})
}
}
}

View file

@ -0,0 +1,79 @@
mod hello_triangle;
use librashader_runtime::image::{Image, UVDirection};
use librashader_runtime_d3d11::options::FilterChainOptionsD3D11;
// "../test/slang-shaders/scalefx/scalefx-9x.slangp",
// "../test/slang-shaders/bezel/koko-aio/monitor-bloom.slangp",
// "../test/slang-shaders/presets/crt-geom-ntsc-upscale-sharp.slangp",
// const FILTER_PATH: &str =
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp";
// "../test/null.slangp",
const FILTER_PATH: &str =
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID].slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/test/history.slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/test/feedback.slangp";
// const FILTER_PATH: &str = "../test/slang-shaders/crt/crt-royale.slangp";
const IMAGE_PATH: &str = "../triangle.png";
#[test]
fn triangle_d3d11_args() {
let mut args = std::env::args();
let _ = args.next();
let _ = args.next();
let filter = args.next();
let image = args
.next()
.and_then(|f| Image::load(f, UVDirection::TopLeft).ok())
.or_else(|| Some(Image::load(IMAGE_PATH, UVDirection::TopLeft).unwrap()))
.unwrap();
let sample = hello_triangle::d3d11_hello_triangle::Sample::new(
filter.as_deref().unwrap_or(FILTER_PATH),
Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false,
disable_cache: false,
}),
// replace below with 'None' for the triangle
Some(image),
)
.unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new(
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// Some(&FilterChainOptions {
// use_deferred_context: true,
// })
// )
// .unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap();
hello_triangle::main(sample).unwrap();
}
#[test]
fn triangle_d3d11() {
let sample = hello_triangle::d3d11_hello_triangle::Sample::new(
FILTER_PATH,
Some(&FilterChainOptionsD3D11 {
force_no_mipmaps: false,
disable_cache: false,
}),
// replace below with 'None' for the triangle
// None,
Some(Image::load(IMAGE_PATH, UVDirection::TopLeft).unwrap()),
)
.unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new(
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// Some(&FilterChainOptions {
// use_deferred_context: true,
// })
// )
// .unwrap();
// let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap();
hello_triangle::main(sample).unwrap();
}

View file

@ -62,6 +62,10 @@ features = [
"Win32_System_Threading", "Win32_System_Threading",
"Win32_System_WindowsProgramming", "Win32_System_WindowsProgramming",
] ]
[[test]]
name = "triangle"
required-features = ["librashader-cache/sqlite-bundled"]
[dev-dependencies] [dev-dependencies]
gfx-maths = "0.2.8" gfx-maths = "0.2.8"

View file

@ -6,46 +6,21 @@
mod buffer; mod buffer;
mod descriptor_heap; mod descriptor_heap;
pub mod error;
mod filter_chain; mod filter_chain;
mod filter_pass; mod filter_pass;
mod framebuffer; mod framebuffer;
mod graphics_pipeline; mod graphics_pipeline;
mod hello_triangle;
mod luts; mod luts;
mod mipmap; mod mipmap;
pub mod options;
mod parameters; mod parameters;
mod quad_render; mod quad_render;
mod samplers; mod samplers;
mod texture; mod texture;
mod util; mod util;
pub mod options;
pub mod error;
pub use filter_chain::FilterChainD3D12; pub use filter_chain::FilterChainD3D12;
pub use texture::D3D12InputImage; pub use texture::D3D12InputImage;
pub use texture::D3D12OutputView; pub use texture::D3D12OutputView;
#[cfg(test)]
mod tests {
use super::*;
use crate::hello_triangle::{DXSample, SampleCommandLine};
#[test]
fn triangle_d3d12() {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
// "../test/slang-shaders/crt/crt-lottes.slangp",
// "../test/basic.slangp",
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp",
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",
// "../test/slang-shaders/test/feedback.slangp",
// "../test/slang-shaders/test/history.slangp",
// "../test/slang-shaders/crt/crt-royale.slangp",
// "../test/slang-shaders/vhs/VHSPro.slangp",
&SampleCommandLine {
use_warp_device: false,
},
)
.unwrap();
hello_triangle::main(sample).unwrap()
}
}

View file

@ -0,0 +1,183 @@
use bitvec::bitvec;
use bitvec::boxed::BitBox;
use parking_lot::RwLock;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use windows::Win32::Graphics::Direct3D12::{
ID3D12DescriptorHeap, ID3D12Device, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_DESC,
D3D12_DESCRIPTOR_HEAP_FLAG_NONE, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
D3D12_DESCRIPTOR_HEAP_TYPE, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
D3D12_GPU_DESCRIPTOR_HANDLE,
};
pub trait D3D12HeapType {
fn get_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC;
}
pub trait D3D12ShaderVisibleHeapType: D3D12HeapType {}
#[derive(Clone)]
pub struct CpuStagingHeap;
impl D3D12HeapType for CpuStagingHeap {
// Lut texture heaps are CPU only and get bound to the descriptor heap of the shader.
fn get_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC {
D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
NumDescriptors: size as u32,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
}
}
}
pub type D3D12DescriptorHeapSlot<T> = Arc<D3D12DescriptorHeapSlotInner<T>>;
pub struct D3D12DescriptorHeapSlotInner<T> {
cpu_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
gpu_handle: Option<D3D12_GPU_DESCRIPTOR_HANDLE>,
heap: Arc<RwLock<D3D12DescriptorHeapInner>>,
slot: usize,
_pd: PhantomData<T>,
}
impl<T> D3D12DescriptorHeapSlotInner<T> {
/// Get the index of the resource within the heap.
pub fn index(&self) -> usize {
self.slot
}
/// unsafe because type must match
pub unsafe fn copy_descriptor(&self, source: D3D12_CPU_DESCRIPTOR_HANDLE) {
unsafe {
let heap = self.heap.deref().read();
heap.device
.CopyDescriptorsSimple(1, self.cpu_handle, source, heap.ty)
}
}
}
impl<T> AsRef<D3D12_CPU_DESCRIPTOR_HANDLE> for D3D12DescriptorHeapSlotInner<T> {
fn as_ref(&self) -> &D3D12_CPU_DESCRIPTOR_HANDLE {
&self.cpu_handle
}
}
impl<T: D3D12ShaderVisibleHeapType> AsRef<D3D12_GPU_DESCRIPTOR_HANDLE>
for D3D12DescriptorHeapSlotInner<T>
{
fn as_ref(&self) -> &D3D12_GPU_DESCRIPTOR_HANDLE {
// SAFETY: D3D12ShaderVisibleHeapType must have a GPU handle.
self.gpu_handle.as_ref().unwrap()
}
}
impl<T: D3D12ShaderVisibleHeapType> From<&D3D12DescriptorHeap<T>> for ID3D12DescriptorHeap {
fn from(value: &D3D12DescriptorHeap<T>) -> Self {
value.0.read().heap.clone()
}
}
#[derive(Debug)]
struct D3D12DescriptorHeapInner {
device: ID3D12Device,
heap: ID3D12DescriptorHeap,
ty: D3D12_DESCRIPTOR_HEAP_TYPE,
cpu_start: D3D12_CPU_DESCRIPTOR_HANDLE,
gpu_start: Option<D3D12_GPU_DESCRIPTOR_HANDLE>,
handle_size: usize,
start: usize,
num_descriptors: usize,
map: BitBox,
}
pub struct D3D12DescriptorHeap<T>(Arc<RwLock<D3D12DescriptorHeapInner>>, PhantomData<T>);
impl<T: D3D12HeapType> D3D12DescriptorHeap<T> {
pub fn new(device: &ID3D12Device, size: usize) -> Result<D3D12DescriptorHeap<T>, windows::core::Error> {
let desc = T::get_desc(size);
unsafe { D3D12DescriptorHeap::new_with_desc(device, desc) }
}
}
impl<T> D3D12DescriptorHeap<T> {
/// Gets a cloned handle to the inner heap
pub fn handle(&self) -> ID3D12DescriptorHeap {
let inner = self.0.read();
inner.heap.clone()
}
pub unsafe fn new_with_desc(
device: &ID3D12Device,
desc: D3D12_DESCRIPTOR_HEAP_DESC,
) -> Result<D3D12DescriptorHeap<T>, windows::core::Error> {
unsafe {
let heap: ID3D12DescriptorHeap = device.CreateDescriptorHeap(&desc)?;
let cpu_start = heap.GetCPUDescriptorHandleForHeapStart();
let gpu_start = if (desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).0 != 0 {
Some(heap.GetGPUDescriptorHandleForHeapStart())
} else {
None
};
Ok(D3D12DescriptorHeap(
Arc::new(RwLock::new(D3D12DescriptorHeapInner {
device: device.clone(),
heap,
ty: desc.Type,
cpu_start,
gpu_start,
handle_size: device.GetDescriptorHandleIncrementSize(desc.Type) as usize,
start: 0,
num_descriptors: desc.NumDescriptors as usize,
map: bitvec![0; desc.NumDescriptors as usize].into_boxed_bitslice(),
})),
PhantomData::default(),
))
}
}
pub fn alloc_slot(&mut self) -> D3D12DescriptorHeapSlot<T> {
let mut handle = D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 };
let mut inner = self.0.write();
for i in inner.start..inner.num_descriptors {
if !inner.map[i] {
inner.map.set(i, true);
handle.ptr = inner.cpu_start.ptr + (i * inner.handle_size);
inner.start = i + 1;
let gpu_handle = inner
.gpu_start
.map(|gpu_start| D3D12_GPU_DESCRIPTOR_HANDLE {
ptr: (handle.ptr as u64 - inner.cpu_start.ptr as u64) + gpu_start.ptr,
});
return Arc::new(D3D12DescriptorHeapSlotInner {
cpu_handle: handle,
slot: i,
heap: Arc::clone(&self.0),
gpu_handle,
_pd: Default::default(),
});
}
}
panic!("overflow")
}
}
impl<T> Drop for D3D12DescriptorHeapSlotInner<T> {
fn drop(&mut self) {
let mut inner = self.heap.write();
inner.map.set(self.slot, false);
if inner.start > self.slot {
inner.start = self.slot
}
}
}

View file

@ -7,6 +7,9 @@ use windows::{
Win32::System::WindowsProgramming::*, Win32::UI::WindowsAndMessaging::*, Win32::System::WindowsProgramming::*, Win32::UI::WindowsAndMessaging::*,
}; };
mod descriptor_heap;
static SHADER: &[u8] = b"struct PSInput static SHADER: &[u8] = b"struct PSInput
{ {
float4 position : SV_POSITION; float4 position : SV_POSITION;
@ -227,12 +230,11 @@ unsafe extern "system" fn debug_log(
pub mod d3d12_hello_triangle { pub mod d3d12_hello_triangle {
use super::*; use super::*;
use crate::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap};
use crate::filter_chain::FilterChainD3D12;
use crate::texture::{D3D12InputImage, D3D12OutputView};
use librashader_common::{Size, Viewport}; use librashader_common::{Size, Viewport};
use std::ops::Deref; use std::ops::Deref;
use std::path::Path; use std::path::Path;
use librashader_runtime_d3d12::{FilterChainD3D12, D3D12InputImage, D3D12OutputView};
use crate::hello_triangle::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap};
const FRAME_COUNT: u32 = 2; const FRAME_COUNT: u32 = 2;
@ -480,7 +482,7 @@ pub mod d3d12_hello_triangle {
fn render(&mut self) { fn render(&mut self) {
if let Some(resources) = &mut self.resources { if let Some(resources) = &mut self.resources {
let srv = resources.frambuffer_heap.alloc_slot().unwrap(); let srv = resources.frambuffer_heap.alloc_slot();
unsafe { unsafe {
self.device.CreateShaderResourceView( self.device.CreateShaderResourceView(

View file

@ -0,0 +1,22 @@
mod hello_triangle;
use crate::hello_triangle::{DXSample, SampleCommandLine};
#[test]
fn triangle_d3d12() {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
// "../test/slang-shaders/crt/crt-lottes.slangp",
// "../test/basic.slangp",
// "../test/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp",
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",
// "../test/slang-shaders/test/feedback.slangp",
// "../test/slang-shaders/test/history.slangp",
// "../test/slang-shaders/crt/crt-royale.slangp",
// "../test/slang-shaders/vhs/VHSPro.slangp",
&SampleCommandLine {
use_warp_device: false,
},
)
.unwrap();
hello_triangle::main(sample).unwrap()
}

View file

@ -28,3 +28,7 @@ rayon = "1.6.1"
[dev-dependencies] [dev-dependencies]
glfw = "0.47.0" glfw = "0.47.0"
[[test]]
name = "triangle"
required-features = ["librashader-cache/sqlite-bundled"]

View file

@ -1,8 +1,6 @@
mod compile_program; mod compile_program;
mod draw_quad; mod draw_quad;
mod framebuffer; mod framebuffer;
#[cfg(test)]
pub mod hello_triangle;
mod lut_load; mod lut_load;
mod texture_bind; mod texture_bind;
mod ubo_ring; mod ubo_ring;

View file

@ -5,8 +5,6 @@ mod texture_bind;
mod ubo_ring; mod ubo_ring;
mod compile_program; mod compile_program;
#[cfg(test)]
pub mod hello_triangle;
use crate::gl::gl46::compile_program::Gl4CompileProgram; use crate::gl::gl46::compile_program::Gl4CompileProgram;
use crate::gl::GLInterface; use crate::gl::GLInterface;

View file

@ -25,46 +25,3 @@ pub use crate::gl::GLFramebuffer;
pub use filter_chain::FilterChainGL; pub use filter_chain::FilterChainGL;
pub use framebuffer::GLImage; pub use framebuffer::GLImage;
#[cfg(test)]
mod tests {
use super::*;
use crate::filter_chain::FilterChainGL;
use crate::options::FilterChainOptionsGL;
#[test]
fn triangle_gl() {
let (glfw, window, events, shader, vao) = gl::gl3::hello_triangle::setup();
let mut filter = FilterChainGL::load_from_path(
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
use_dsa: 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)
.unwrap();
gl::gl3::hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter);
}
#[test]
fn triangle_gl46() {
let (glfw, window, events, shader, vao) = gl::gl46::hello_triangle::setup();
let mut filter = FilterChainGL::load_from_path(
// "../test/slang-shaders/vhs/VHSPro.slangp",
// "../test/slang-shaders/test/history.slangp",
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
use_dsa: true,
force_no_mipmaps: false,
disable_cache: false,
}),
)
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
.unwrap();
gl::gl46::hello_triangle::do_loop(glfw, window, events, shader, vao, &mut filter);
}
}

View file

@ -7,10 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use librashader_common::{Size, Viewport}; use librashader_common::{Size, Viewport};
use crate::filter_chain::FilterChainGL; use librashader_runtime_gl::{FilterChainGL, GLImage, GLFramebuffer};
use crate::framebuffer::GLImage;
use crate::GLFramebuffer;
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;

View file

@ -7,10 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use librashader_common::{Size, Viewport}; use librashader_common::{Size, Viewport};
use crate::filter_chain::FilterChainGL; use librashader_runtime_gl::{FilterChainGL, GLImage, GLFramebuffer};
use crate::framebuffer::GLImage;
use crate::GLFramebuffer;
const WIDTH: u32 = 800; const WIDTH: u32 = 800;
const HEIGHT: u32 = 600; const HEIGHT: u32 = 600;

View file

@ -0,0 +1,2 @@
pub mod gl46;
pub mod gl3;

View file

@ -0,0 +1,42 @@
mod hello_triangle;
use librashader_runtime_gl::FilterChainGL;
use librashader_runtime_gl::options::FilterChainOptionsGL;
#[test]
fn triangle_gl() {
let (glfw, window, events, shader, vao) = hello_triangle::gl3::setup();
let mut filter = FilterChainGL::load_from_path(
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
use_dsa: 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)
.unwrap();
hello_triangle::gl3::do_loop(glfw, window, events, shader, vao, &mut filter);
}
#[test]
fn triangle_gl46() {
let (glfw, window, events, shader, vao) = hello_triangle::gl46::setup();
let mut filter = FilterChainGL::load_from_path(
// "../test/slang-shaders/vhs/VHSPro.slangp",
// "../test/slang-shaders/test/history.slangp",
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
Some(&FilterChainOptionsGL {
glsl_version: 0,
use_dsa: true,
force_no_mipmaps: false,
disable_cache: false,
}),
)
// FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
.unwrap();
hello_triangle::gl46::do_loop(glfw, window, events, shader, vao, &mut filter);
}

View file

@ -36,3 +36,8 @@ glfw = "0.49.0"
winit = "0.27.5" winit = "0.27.5"
raw-window-handle = "0.5" raw-window-handle = "0.5"
ash-window = "0.12.0" ash-window = "0.12.0"
[[test]]
name = "triangle"
required-features = ["librashader-cache/sqlite-bundled"]

View file

@ -12,8 +12,6 @@ mod filter_chain;
mod filter_pass; mod filter_pass;
mod framebuffer; mod framebuffer;
mod graphics_pipeline; mod graphics_pipeline;
#[cfg(test)]
mod hello_triangle;
mod luts; mod luts;
mod memory; mod memory;
mod parameters; mod parameters;
@ -31,51 +29,3 @@ pub mod error;
pub mod options; pub mod options;
mod render_pass; mod render_pass;
#[cfg(test)]
mod tests {
use crate::filter_chain::FilterChainVulkan;
use crate::hello_triangle::vulkan_base::VulkanBase;
use crate::options::FilterChainOptionsVulkan;
#[test]
fn triangle_vk() {
let entry = unsafe { ash::Entry::load().unwrap() };
let base = VulkanBase::new(entry).unwrap();
let filter = FilterChainVulkan::load_from_path(
// "../test/slang-shaders/crt/crt-royale.slangp",
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",
&base,
// "../test/slang-shaders/test/feedback.slancargogp",
// "../test/basic.slangp",
Some(&FilterChainOptionsVulkan {
frames_in_flight: 3,
force_no_mipmaps: false,
use_render_pass: true,
disable_cache: false,
}),
)
.unwrap();
crate::hello_triangle::main(base, filter)
// let base = hello_triangle_old::ExampleBase::new(900, 600);
// // let mut filter = FilterChainVulkan::load_from_path(
// // (base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()),
// // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
// // None
// // )
//
// let mut filter = FilterChainVulkan::load_from_path(
// (
// base.device.clone(),
// base.present_queue.clone(),
// base.device_memory_properties.clone(),
// ),
// "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
// None,
// )
// // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
// .unwrap();
// hello_triangle_old::main(base, filter);
}
}

View file

@ -0,0 +1,53 @@
use ash::vk;
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
use gpu_allocator::MemoryLocation;
use librashader_runtime::uniforms::UniformStorageAccess;
use parking_lot::RwLock;
use std::ffi::c_void;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use std::sync::Arc;
use ash::prelude::VkResult;
pub struct VulkanImageMemory {
allocation: Option<Allocation>,
allocator: Arc<RwLock<Allocator>>,
}
impl VulkanImageMemory {
pub fn new(
device: &Arc<ash::Device>,
allocator: &Arc<RwLock<Allocator>>,
requirements: vk::MemoryRequirements,
image: &vk::Image,
) -> VkResult<VulkanImageMemory> {
let allocation = allocator.write().allocate(&AllocationCreateDesc {
name: "imagemem",
requirements,
location: MemoryLocation::GpuOnly,
linear: false,
allocation_scheme: AllocationScheme::DedicatedImage(*image),
}).unwrap();
unsafe {
device.bind_image_memory(*image, allocation.memory(), 0)?;
Ok(VulkanImageMemory {
allocation: Some(allocation),
allocator: Arc::clone(allocator),
})
}
}
}
impl Drop for VulkanImageMemory {
fn drop(&mut self) {
let allocation = self.allocation.take();
if let Some(allocation) = allocation {
if let Err(e) = self.allocator.write().free(allocation) {
println!("librashader-runtime-vk: [warn] failed to deallocate image buffer {e}")
}
}
}
}

View file

@ -8,26 +8,27 @@ mod pipeline;
mod surface; mod surface;
mod swapchain; mod swapchain;
mod syncobjects; mod syncobjects;
mod util;
mod memory;
pub(crate) mod vulkan_base; pub(crate) mod vulkan_base;
use crate::filter_chain::FilterChainVulkan; use librashader_runtime_vk::{FilterChainVulkan, VulkanImage};
use crate::hello_triangle::command::VulkanCommandPool; use command::VulkanCommandPool;
use crate::hello_triangle::framebuffer::VulkanFramebuffer; use framebuffer::VulkanFramebuffer;
use crate::hello_triangle::pipeline::VulkanPipeline; use pipeline::VulkanPipeline;
use crate::hello_triangle::surface::VulkanSurface; use surface::VulkanSurface;
use crate::hello_triangle::swapchain::VulkanSwapchain; use swapchain::VulkanSwapchain;
use crate::hello_triangle::syncobjects::SyncObjects; use syncobjects::SyncObjects;
use crate::hello_triangle::vulkan_base::VulkanBase; use vulkan_base::VulkanBase;
use crate::texture::VulkanImage;
use crate::util;
use ash::vk; use ash::vk;
use librashader_common::Viewport; use librashader_common::Viewport;
use crate::options::FrameOptionsVulkan;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder}; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
use winit::platform::windows::EventLoopBuilderExtWindows; use winit::platform::windows::EventLoopBuilderExtWindows;
use librashader_runtime_vk::options::FrameOptionsVulkan;
// Constants // Constants
const WINDOW_TITLE: &str = "librashader Vulkan"; const WINDOW_TITLE: &str = "librashader Vulkan";

View file

@ -1,6 +1,6 @@
use crate::hello_triangle::surface::VulkanSurface; use crate::hello_triangle::surface::VulkanSurface;
use crate::hello_triangle::vulkan_base::VulkanBase; use crate::hello_triangle::vulkan_base::VulkanBase;
use crate::memory::VulkanImageMemory; use crate::hello_triangle::memory::VulkanImageMemory;
use ash::prelude::VkResult; use ash::prelude::VkResult;
use ash::vk; use ash::vk;
use ash::vk::Extent3D; use ash::vk::Extent3D;

View file

@ -0,0 +1,77 @@
use ash::vk;
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
use parking_lot::RwLock;
use std::sync::Arc;
use librashader_reflect::reflect::semantics::BindingStage;
pub fn binding_stage_to_vulkan_stage(stage_mask: BindingStage) -> vk::ShaderStageFlags {
let mut mask = vk::ShaderStageFlags::default();
if stage_mask.contains(BindingStage::VERTEX) {
mask |= vk::ShaderStageFlags::VERTEX;
}
if stage_mask.contains(BindingStage::FRAGMENT) {
mask |= vk::ShaderStageFlags::FRAGMENT;
}
mask
}
#[inline(always)]
pub unsafe fn vulkan_image_layout_transition_levels(
device: &ash::Device,
cmd: vk::CommandBuffer,
image: vk::Image,
levels: u32,
old_layout: vk::ImageLayout,
new_layout: vk::ImageLayout,
src_access: vk::AccessFlags,
dst_access: vk::AccessFlags,
src_stage: vk::PipelineStageFlags,
dst_stage: vk::PipelineStageFlags,
src_queue_family_index: u32,
dst_queue_family_index: u32,
) {
let mut barrier = vk::ImageMemoryBarrier::default();
barrier.s_type = vk::StructureType::IMAGE_MEMORY_BARRIER;
barrier.p_next = std::ptr::null();
barrier.src_access_mask = src_access;
barrier.dst_access_mask = dst_access;
barrier.old_layout = old_layout;
barrier.new_layout = new_layout;
barrier.src_queue_family_index = src_queue_family_index;
barrier.dst_queue_family_index = dst_queue_family_index;
barrier.image = image;
barrier.subresource_range.aspect_mask = vk::ImageAspectFlags::COLOR;
barrier.subresource_range.base_array_layer = 0;
barrier.subresource_range.level_count = levels;
barrier.subresource_range.layer_count = vk::REMAINING_ARRAY_LAYERS;
device.cmd_pipeline_barrier(
cmd,
src_stage,
dst_stage,
vk::DependencyFlags::empty(),
&[],
&[],
&[barrier],
)
}
pub fn create_allocator(
device: ash::Device,
instance: ash::Instance,
physical_device: vk::PhysicalDevice,
) -> Arc<RwLock<Allocator>> {
let alloc = Allocator::new(&AllocatorCreateDesc {
instance,
device,
physical_device,
debug_settings: Default::default(),
buffer_device_address: false,
}).unwrap();
Arc::new(RwLock::new(alloc))
}

View file

@ -1,16 +1,15 @@
use ash::vk; use ash::vk;
use crate::error::FilterChainError;
use crate::filter_chain::VulkanObjects;
use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device}; use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device};
use crate::util;
use ash::prelude::VkResult; use ash::prelude::VkResult;
use gpu_allocator::vulkan::Allocator; use gpu_allocator::vulkan::Allocator;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::ffi::CStr; use std::ffi::CStr;
use std::sync::Arc; use std::sync::Arc;
use librashader_runtime_vk::error::FilterChainError;
use librashader_runtime_vk::VulkanObjects;
use crate::hello_triangle::util;
const WINDOW_TITLE: &[u8] = b"librashader Vulkan\0"; const WINDOW_TITLE: &[u8] = b"librashader Vulkan\0";
const KHRONOS_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0"; const KHRONOS_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0";
@ -66,8 +65,7 @@ impl VulkanBase {
dbg!("got memprops"); dbg!("got memprops");
let alloc = let alloc =
util::create_allocator(device.clone(), instance.clone(), physical_device.clone()) util::create_allocator(device.clone(), instance.clone(), physical_device.clone());
.expect("could not create allocator");
Ok(VulkanBase { Ok(VulkanBase {
entry, entry,

View file

@ -0,0 +1,47 @@
mod hello_triangle;
use librashader_runtime_vk::FilterChainVulkan;
use hello_triangle::vulkan_base::VulkanBase;
use librashader_runtime_vk::options::FilterChainOptionsVulkan;
#[test]
fn triangle_vk() {
let entry = unsafe { ash::Entry::load().unwrap() };
let base = VulkanBase::new(entry).unwrap();
let filter = FilterChainVulkan::load_from_path(
// "../test/slang-shaders/crt/crt-royale.slangp",
"../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_GBA_SP/GBA_SP-[ADV]-[LCD-GRID]-[Night].slangp",
&base,
// "../test/slang-shaders/test/feedback.slancargogp",
// "../test/basic.slangp",
Some(&FilterChainOptionsVulkan {
frames_in_flight: 3,
force_no_mipmaps: false,
use_render_pass: true,
disable_cache: false,
}),
)
.unwrap();
hello_triangle::main(base, filter)
// let base = hello_triangle_old::ExampleBase::new(900, 600);
// // let mut filter = FilterChainVulkan::load_from_path(
// // (base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()),
// // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
// // None
// // )
//
// let mut filter = FilterChainVulkan::load_from_path(
// (
// base.device.clone(),
// base.present_queue.clone(),
// base.device_memory_properties.clone(),
// ),
// "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
// None,
// )
// // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None)
// .unwrap();
// hello_triangle_old::main(base, filter);
}

View file

@ -22,6 +22,7 @@ librashader-runtime-d3d11 = { path = "../librashader-runtime-d3d11", version =
librashader-runtime-d3d12 = { path = "../librashader-runtime-d3d12", version = "0.1.0-rc.5", optional = true } librashader-runtime-d3d12 = { path = "../librashader-runtime-d3d12", version = "0.1.0-rc.5", optional = true }
librashader-runtime-gl = { path = "../librashader-runtime-gl", version = "0.1.0-rc.5", optional = true } librashader-runtime-gl = { path = "../librashader-runtime-gl", version = "0.1.0-rc.5", optional = true }
librashader-runtime-vk = { path = "../librashader-runtime-vk", version = "0.1.0-rc.5", optional = true } librashader-runtime-vk = { path = "../librashader-runtime-vk", version = "0.1.0-rc.5", optional = true }
librashader-cache = { path = "../librashader-cache", version = "0.1.0-rc.5" }
ash = { version = "0.37.1+1.3.235", optional = true } ash = { version = "0.37.1+1.3.235", optional = true }
@ -50,8 +51,10 @@ runtime-all = ["runtime-gl", "runtime-d3d11", "runtime-d3d12", "runtime-vk"]
reflect-all = ["reflect-cross", "reflect-dxil"] reflect-all = ["reflect-cross", "reflect-dxil"]
# enable all features by default # enable all features by default
default = [ "runtime-all", "reflect-all", "preprocess", "presets" ] default = [ "runtime-all", "reflect-all", "preprocess", "presets", "librashader-cache/sqlite-bundled" ]
internal = [] internal = []
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"] targets = ["x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"]
default-features = false
features = [ "runtime-all", "reflect-all", "preprocess", "presets", "librashader-cache/docsrs" ]