tests: split tests into their own crates
This commit is contained in:
parent
7d6701aa4e
commit
f42328280a
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -180,7 +180,6 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"constant_time_eq",
|
||||
"digest",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1112,6 +1111,7 @@ name = "librashader"
|
|||
version = "0.1.0-rc.5"
|
||||
dependencies = [
|
||||
"ash",
|
||||
"librashader-cache",
|
||||
"librashader-common 0.1.0-rc.5",
|
||||
"librashader-preprocess",
|
||||
"librashader-presets 0.1.0-rc.5",
|
||||
|
|
|
@ -15,10 +15,10 @@ serde = { version = "1.0" }
|
|||
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" }
|
||||
platform-dirs = "0.3.0"
|
||||
blake3 = { version = "1.3.3", features = ["rayon"] }
|
||||
blake3 = { version = "1.3.3" }
|
||||
thiserror = "1.0.38"
|
||||
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"
|
||||
|
||||
|
@ -33,4 +33,10 @@ optional = true
|
|||
|
||||
|
||||
[features]
|
||||
# should be enabled at librashader crate level.
|
||||
sqlite-bundled = ["rusqlite/bundled"]
|
||||
d3d = ["windows"]
|
||||
docsrs = ["blake3/pure"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["docsrs"]
|
|
@ -1,11 +1,14 @@
|
|||
use crate::cacheable::Cacheable;
|
||||
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"))]
|
||||
pub(crate) mod internal {
|
||||
use platform_dirs::AppDirs;
|
||||
use rusqlite::{Connection, DatabaseName};
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
|
||||
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
|
||||
|
@ -18,9 +21,9 @@ pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> {
|
|||
std::fs::create_dir_all(&cache_dir)?;
|
||||
|
||||
Ok(cache_dir)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
||||
pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
||||
let cache_dir = get_cache_dir()?;
|
||||
let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
|
||||
|
||||
|
@ -37,31 +40,34 @@ pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
|||
)?;
|
||||
tx.commit()?;
|
||||
Ok(conn)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_blob(
|
||||
pub(crate) fn get_blob(
|
||||
conn: &Connection,
|
||||
index: &str,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let value = conn.query_row(
|
||||
&*format!("select value from cache where (type = (?1) and id = (?2))"),
|
||||
params![index, key],
|
||||
rusqlite::params![index, key],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
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(
|
||||
&*format!("insert or replace into cache (type, id, value) values (?1, ?2, ?3)"),
|
||||
params![index, key, value],
|
||||
rusqlite::params![index, key, value],
|
||||
) {
|
||||
Ok(_) => return,
|
||||
Err(e) => println!("err: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "docsrs"))]
|
||||
/// 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.
|
||||
|
@ -81,7 +87,7 @@ where
|
|||
return Ok(load(factory(keys)?)?);
|
||||
}
|
||||
|
||||
let cache = get_cache();
|
||||
let cache = internal::get_cache();
|
||||
|
||||
let Ok(cache) = cache else {
|
||||
return Ok(load(factory(keys)?)?);
|
||||
|
@ -97,7 +103,7 @@ where
|
|||
};
|
||||
|
||||
'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);
|
||||
|
||||
match cached {
|
||||
|
@ -111,11 +117,12 @@ where
|
|||
let blob = factory(keys)?;
|
||||
|
||||
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)?)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "docsrs"))]
|
||||
/// Cache a pipeline state object.
|
||||
///
|
||||
/// 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)?);
|
||||
}
|
||||
|
||||
let cache = get_cache();
|
||||
let cache = internal::get_cache();
|
||||
|
||||
let Ok(cache) = cache else {
|
||||
return Ok(restore_pipeline(None)?);
|
||||
|
@ -153,7 +160,7 @@ where
|
|||
};
|
||||
|
||||
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));
|
||||
match cached {
|
||||
Ok(res) => {
|
||||
|
@ -169,9 +176,15 @@ where
|
|||
// update the pso every time just in case.
|
||||
if let Ok(state) = fetch_pipeline_state(&pipeline) {
|
||||
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)
|
||||
}
|
||||
|
||||
#[cfg(feature = "docsrs")]
|
||||
pub use crate::docsrs::cache_pipeline;
|
||||
|
||||
#[cfg(feature = "docsrs")]
|
||||
pub use crate::docsrs::cache_shader_object;
|
|
@ -1,5 +1,4 @@
|
|||
//! Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV.
|
||||
use crate::cache::{get_blob, get_cache, set_blob};
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use librashader_reflect::back::targets::{DXIL, GLSL, HLSL, SPIRV};
|
||||
use librashader_reflect::back::{CompilerBackend, FromCompilation};
|
||||
|
@ -10,11 +9,12 @@ pub struct CachedCompilation<T> {
|
|||
compilation: T,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "docsrs"))]
|
||||
impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
|
||||
ShaderCompilation for CachedCompilation<T>
|
||||
{
|
||||
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||
let cache = get_cache();
|
||||
let cache = crate::cache::internal::get_cache();
|
||||
|
||||
let Ok(cache) = cache else {
|
||||
return Ok(CachedCompilation {
|
||||
|
@ -31,7 +31,7 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
|
|||
};
|
||||
|
||||
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 =
|
||||
bincode::serde::decode_from_slice(&cached, bincode::config::standard())
|
||||
.map(|(compilation, _)| CachedCompilation { compilation })
|
||||
|
@ -50,13 +50,22 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
|
|||
if let Ok(updated) =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
type Target = <DXIL as FromCompilation<GlslangCompilation>>::Target;
|
||||
type Options = <DXIL as FromCompilation<GlslangCompilation>>::Options;
|
||||
|
|
37
librashader-cache/src/docsrs.rs
Normal file
37
librashader-cache/src/docsrs.rs
Normal 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)?)?);
|
||||
}
|
|
@ -2,7 +2,10 @@
|
|||
//!
|
||||
//! This crate is exempt from semantic versioning guarantees and is an implementation
|
||||
//! detail of librashader runtimes.
|
||||
|
||||
mod cache;
|
||||
|
||||
|
||||
mod compilation;
|
||||
|
||||
mod key;
|
||||
|
@ -12,8 +15,14 @@ pub use cacheable::Cacheable;
|
|||
pub use key::CacheKey;
|
||||
|
||||
pub use compilation::CachedCompilation;
|
||||
|
||||
pub use cache::cache_pipeline;
|
||||
pub use cache::cache_shader_object;
|
||||
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "d3d"))]
|
||||
mod d3d;
|
||||
|
||||
|
||||
#[cfg(feature = "docsrs")]
|
||||
mod docsrs;
|
|
@ -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-runtime = { path = "../librashader-runtime", version = "0.1.0-rc.5" }
|
||||
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"
|
||||
rustc-hash = "1.1.0"
|
||||
|
@ -55,5 +55,9 @@ features = [
|
|||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[[test]]
|
||||
name = "triangle"
|
||||
required-features = ["librashader-cache/sqlite-bundled"]
|
||||
|
||||
[dev-dependencies]
|
||||
gfx-maths = "0.2.8"
|
||||
|
|
|
@ -6,103 +6,20 @@
|
|||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(let_chains)]
|
||||
#[cfg(test)]
|
||||
mod hello_triangle;
|
||||
|
||||
mod draw_quad;
|
||||
pub mod error;
|
||||
mod filter_chain;
|
||||
mod filter_pass;
|
||||
mod framebuffer;
|
||||
mod graphics_pipeline;
|
||||
pub mod options;
|
||||
mod parameters;
|
||||
mod samplers;
|
||||
mod texture;
|
||||
mod util;
|
||||
|
||||
pub mod error;
|
||||
pub mod options;
|
||||
|
||||
pub use filter_chain::FilterChainD3D11;
|
||||
pub use texture::D3D11InputView;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ const WIDTH: i32 = 800;
|
|||
const HEIGHT: i32 = 600;
|
||||
const TITLE: &str = "librashader DirectX 11";
|
||||
|
||||
mod texture;
|
||||
|
||||
use windows::{
|
||||
core::*, Win32::Foundation::*, Win32::Graphics::Direct3D::Fxc::*, Win32::Graphics::Direct3D::*,
|
||||
Win32::Graphics::Direct3D11::*, Win32::Graphics::Dxgi::Common::*, Win32::Graphics::Dxgi::*,
|
||||
|
@ -240,15 +242,15 @@ pub mod d3d11_hello_triangle {
|
|||
use super::*;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::filter_chain::FilterChainD3D11;
|
||||
|
||||
use crate::options::FilterChainOptionsD3D11;
|
||||
use crate::texture::{D3D11InputView, LutTexture};
|
||||
use crate::D3D11OutputView;
|
||||
use librashader_runtime_d3d11::options::FilterChainOptionsD3D11;
|
||||
use librashader_runtime_d3d11::{D3D11InputView, D3D11OutputView};
|
||||
use texture::ExampleTexture;
|
||||
use librashader_common::{FilterMode, ImageFormat, Size, Viewport, WrapMode};
|
||||
use librashader_runtime::image::Image;
|
||||
use std::slice;
|
||||
use std::time::Instant;
|
||||
use librashader_runtime_d3d11::FilterChainD3D11;
|
||||
|
||||
pub struct Sample {
|
||||
pub dxgi_factory: IDXGIFactory4,
|
||||
|
@ -292,7 +294,7 @@ pub mod d3d11_hello_triangle {
|
|||
let (dxgi_factory, device, context) = create_device()?;
|
||||
let filter = FilterChainD3D11::load_from_path(filter, &device, filter_options).unwrap();
|
||||
let lut = if let Some(image) = image {
|
||||
let lut = LutTexture::new(
|
||||
let lut = ExampleTexture::new(
|
||||
&device,
|
||||
&context,
|
||||
&image,
|
174
librashader-runtime-d3d11/tests/hello_triangle/texture.rs
Normal file
174
librashader-runtime-d3d11/tests/hello_triangle/texture.rs
Normal 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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
79
librashader-runtime-d3d11/tests/triangle.rs
Normal file
79
librashader-runtime-d3d11/tests/triangle.rs
Normal 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();
|
||||
}
|
|
@ -62,6 +62,10 @@ features = [
|
|||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
]
|
||||
|
||||
[[test]]
|
||||
name = "triangle"
|
||||
required-features = ["librashader-cache/sqlite-bundled"]
|
||||
|
||||
[dev-dependencies]
|
||||
gfx-maths = "0.2.8"
|
||||
|
||||
|
|
|
@ -6,46 +6,21 @@
|
|||
|
||||
mod buffer;
|
||||
mod descriptor_heap;
|
||||
pub mod error;
|
||||
mod filter_chain;
|
||||
mod filter_pass;
|
||||
mod framebuffer;
|
||||
mod graphics_pipeline;
|
||||
mod hello_triangle;
|
||||
mod luts;
|
||||
mod mipmap;
|
||||
pub mod options;
|
||||
mod parameters;
|
||||
mod quad_render;
|
||||
mod samplers;
|
||||
mod texture;
|
||||
mod util;
|
||||
|
||||
pub mod options;
|
||||
pub mod error;
|
||||
|
||||
pub use filter_chain::FilterChainD3D12;
|
||||
pub use texture::D3D12InputImage;
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,9 @@ use windows::{
|
|||
Win32::System::WindowsProgramming::*, Win32::UI::WindowsAndMessaging::*,
|
||||
};
|
||||
|
||||
mod descriptor_heap;
|
||||
|
||||
|
||||
static SHADER: &[u8] = b"struct PSInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
|
@ -227,12 +230,11 @@ unsafe extern "system" fn debug_log(
|
|||
|
||||
pub mod d3d12_hello_triangle {
|
||||
use super::*;
|
||||
use crate::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap};
|
||||
use crate::filter_chain::FilterChainD3D12;
|
||||
use crate::texture::{D3D12InputImage, D3D12OutputView};
|
||||
use librashader_common::{Size, Viewport};
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use librashader_runtime_d3d12::{FilterChainD3D12, D3D12InputImage, D3D12OutputView};
|
||||
use crate::hello_triangle::descriptor_heap::{CpuStagingHeap, D3D12DescriptorHeap};
|
||||
|
||||
const FRAME_COUNT: u32 = 2;
|
||||
|
||||
|
@ -480,7 +482,7 @@ pub mod d3d12_hello_triangle {
|
|||
|
||||
fn render(&mut self) {
|
||||
if let Some(resources) = &mut self.resources {
|
||||
let srv = resources.frambuffer_heap.alloc_slot().unwrap();
|
||||
let srv = resources.frambuffer_heap.alloc_slot();
|
||||
|
||||
unsafe {
|
||||
self.device.CreateShaderResourceView(
|
22
librashader-runtime-d3d12/tests/triangle.rs
Normal file
22
librashader-runtime-d3d12/tests/triangle.rs
Normal 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()
|
||||
}
|
|
@ -28,3 +28,7 @@ rayon = "1.6.1"
|
|||
|
||||
[dev-dependencies]
|
||||
glfw = "0.47.0"
|
||||
|
||||
[[test]]
|
||||
name = "triangle"
|
||||
required-features = ["librashader-cache/sqlite-bundled"]
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
mod compile_program;
|
||||
mod draw_quad;
|
||||
mod framebuffer;
|
||||
#[cfg(test)]
|
||||
pub mod hello_triangle;
|
||||
mod lut_load;
|
||||
mod texture_bind;
|
||||
mod ubo_ring;
|
||||
|
|
|
@ -5,8 +5,6 @@ mod texture_bind;
|
|||
mod ubo_ring;
|
||||
|
||||
mod compile_program;
|
||||
#[cfg(test)]
|
||||
pub mod hello_triangle;
|
||||
|
||||
use crate::gl::gl46::compile_program::Gl4CompileProgram;
|
||||
use crate::gl::GLInterface;
|
||||
|
|
|
@ -25,46 +25,3 @@ pub use crate::gl::GLFramebuffer;
|
|||
pub use filter_chain::FilterChainGL;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
|
|||
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
|
||||
use librashader_common::{Size, Viewport};
|
||||
|
||||
use crate::filter_chain::FilterChainGL;
|
||||
use crate::framebuffer::GLImage;
|
||||
|
||||
use crate::GLFramebuffer;
|
||||
use librashader_runtime_gl::{FilterChainGL, GLImage, GLFramebuffer};
|
||||
|
||||
const WIDTH: u32 = 800;
|
||||
const HEIGHT: u32 = 600;
|
|
@ -7,10 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
|
|||
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
|
||||
use librashader_common::{Size, Viewport};
|
||||
|
||||
use crate::filter_chain::FilterChainGL;
|
||||
use crate::framebuffer::GLImage;
|
||||
|
||||
use crate::GLFramebuffer;
|
||||
use librashader_runtime_gl::{FilterChainGL, GLImage, GLFramebuffer};
|
||||
|
||||
const WIDTH: u32 = 800;
|
||||
const HEIGHT: u32 = 600;
|
2
librashader-runtime-gl/tests/hello_triangle/mod.rs
Normal file
2
librashader-runtime-gl/tests/hello_triangle/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod gl46;
|
||||
pub mod gl3;
|
42
librashader-runtime-gl/tests/triangle.rs
Normal file
42
librashader-runtime-gl/tests/triangle.rs
Normal 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);
|
||||
}
|
|
@ -36,3 +36,8 @@ glfw = "0.49.0"
|
|||
winit = "0.27.5"
|
||||
raw-window-handle = "0.5"
|
||||
ash-window = "0.12.0"
|
||||
|
||||
|
||||
[[test]]
|
||||
name = "triangle"
|
||||
required-features = ["librashader-cache/sqlite-bundled"]
|
|
@ -12,8 +12,6 @@ mod filter_chain;
|
|||
mod filter_pass;
|
||||
mod framebuffer;
|
||||
mod graphics_pipeline;
|
||||
#[cfg(test)]
|
||||
mod hello_triangle;
|
||||
mod luts;
|
||||
mod memory;
|
||||
mod parameters;
|
||||
|
@ -31,51 +29,3 @@ pub mod error;
|
|||
pub mod options;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
53
librashader-runtime-vk/tests/hello_triangle/memory.rs
Normal file
53
librashader-runtime-vk/tests/hello_triangle/memory.rs
Normal 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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,26 +8,27 @@ mod pipeline;
|
|||
mod surface;
|
||||
mod swapchain;
|
||||
mod syncobjects;
|
||||
mod util;
|
||||
mod memory;
|
||||
|
||||
pub(crate) mod vulkan_base;
|
||||
|
||||
use crate::filter_chain::FilterChainVulkan;
|
||||
use crate::hello_triangle::command::VulkanCommandPool;
|
||||
use crate::hello_triangle::framebuffer::VulkanFramebuffer;
|
||||
use crate::hello_triangle::pipeline::VulkanPipeline;
|
||||
use crate::hello_triangle::surface::VulkanSurface;
|
||||
use crate::hello_triangle::swapchain::VulkanSwapchain;
|
||||
use crate::hello_triangle::syncobjects::SyncObjects;
|
||||
use crate::hello_triangle::vulkan_base::VulkanBase;
|
||||
use crate::texture::VulkanImage;
|
||||
use crate::util;
|
||||
use librashader_runtime_vk::{FilterChainVulkan, VulkanImage};
|
||||
use command::VulkanCommandPool;
|
||||
use framebuffer::VulkanFramebuffer;
|
||||
use pipeline::VulkanPipeline;
|
||||
use surface::VulkanSurface;
|
||||
use swapchain::VulkanSwapchain;
|
||||
use syncobjects::SyncObjects;
|
||||
use vulkan_base::VulkanBase;
|
||||
use ash::vk;
|
||||
|
||||
use librashader_common::Viewport;
|
||||
|
||||
use crate::options::FrameOptionsVulkan;
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
|
||||
use winit::platform::windows::EventLoopBuilderExtWindows;
|
||||
use librashader_runtime_vk::options::FrameOptionsVulkan;
|
||||
|
||||
// Constants
|
||||
const WINDOW_TITLE: &str = "librashader Vulkan";
|
|
@ -1,6 +1,6 @@
|
|||
use crate::hello_triangle::surface::VulkanSurface;
|
||||
use crate::hello_triangle::vulkan_base::VulkanBase;
|
||||
use crate::memory::VulkanImageMemory;
|
||||
use crate::hello_triangle::memory::VulkanImageMemory;
|
||||
use ash::prelude::VkResult;
|
||||
use ash::vk;
|
||||
use ash::vk::Extent3D;
|
77
librashader-runtime-vk/tests/hello_triangle/util.rs
Normal file
77
librashader-runtime-vk/tests/hello_triangle/util.rs
Normal 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))
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
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::util;
|
||||
use ash::prelude::VkResult;
|
||||
use gpu_allocator::vulkan::Allocator;
|
||||
use parking_lot::RwLock;
|
||||
use std::ffi::CStr;
|
||||
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 KHRONOS_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0";
|
||||
|
@ -66,8 +65,7 @@ impl VulkanBase {
|
|||
dbg!("got memprops");
|
||||
|
||||
let alloc =
|
||||
util::create_allocator(device.clone(), instance.clone(), physical_device.clone())
|
||||
.expect("could not create allocator");
|
||||
util::create_allocator(device.clone(), instance.clone(), physical_device.clone());
|
||||
|
||||
Ok(VulkanBase {
|
||||
entry,
|
47
librashader-runtime-vk/tests/triangle.rs
Normal file
47
librashader-runtime-vk/tests/triangle.rs
Normal 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);
|
||||
}
|
|
@ -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-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-cache = { path = "../librashader-cache", version = "0.1.0-rc.5" }
|
||||
|
||||
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"]
|
||||
|
||||
# enable all features by default
|
||||
default = [ "runtime-all", "reflect-all", "preprocess", "presets" ]
|
||||
default = [ "runtime-all", "reflect-all", "preprocess", "presets", "librashader-cache/sqlite-bundled" ]
|
||||
internal = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"]
|
||||
default-features = false
|
||||
features = [ "runtime-all", "reflect-all", "preprocess", "presets", "librashader-cache/docsrs" ]
|
||||
|
|
Loading…
Reference in a new issue