Splash screen rendering

This commit is contained in:
Luke Street 2022-02-04 21:23:15 -05:00
parent 8d01afc632
commit 509252a17f
21 changed files with 1292 additions and 163 deletions

134
Graphics/Cargo.lock generated
View File

@ -72,9 +72,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]] [[package]]
name = "ash" name = "ash"
version = "0.34.0+1.2.203" version = "0.35.1+1.2.203"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0f780da53d0063880d45554306489f09dd8d1bda47688b4a57bc579119356df" checksum = "b7fd04def1c9101b5fb488c131022d2d6f87753ef4b1b11b279e2af404fae6b9"
dependencies = [ dependencies = [
"libloading", "libloading",
] ]
@ -96,7 +96,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"android_logger", "android_logger",
"bindgen", "bindgen",
"binread", "binrw",
"bytemuck", "bytemuck",
"bytemuck_derive", "bytemuck_derive",
"cgmath", "cgmath",
@ -109,9 +109,8 @@ dependencies = [
"imgui-winit-support", "imgui-winit-support",
"log", "log",
"log-panics", "log-panics",
"mobile-entry-point",
"modular-bitfield", "modular-bitfield",
"naga", "naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=d6f8958b346676396db97053771b8d95684c47ee)",
"ndk-glue", "ndk-glue",
"num-traits", "num-traits",
"num_enum", "num_enum",
@ -120,7 +119,6 @@ dependencies = [
"smallvec", "smallvec",
"twox-hash", "twox-hash",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys",
"wgpu", "wgpu",
"wgpu-subscriber", "wgpu-subscriber",
"winit", "winit",
@ -156,23 +154,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "binread" name = "binrw"
version = "2.2.0" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" checksum = "a13d2fa8772b88ee64a0d84602c636ad2bae9c176873f6c440e2b0a1df4c8c43"
dependencies = [ dependencies = [
"array-init", "array-init",
"binread_derive", "binrw_derive",
"rustversion",
] ]
[[package]] [[package]]
name = "binread_derive" name = "binrw_derive"
version = "2.1.0" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" checksum = "193bcff2709da50edf2e9a89e0f9a2118eea70f257b1f4380ccf27d3d8578302"
dependencies = [ dependencies = [
"either", "owo-colors",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -488,9 +485,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.60" version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af5f0799eb4abbd9c46cb58dfaa781386f852386ad6681e65c66a8101b8562af" checksum = "fcd554072878dec8c16f59839336bf712d6d1f0d8d27cf9b471c10a484a3290f"
dependencies = [ dependencies = [
"cc", "cc",
"cxxbridge-flags", "cxxbridge-flags",
@ -500,13 +497,13 @@ dependencies = [
[[package]] [[package]]
name = "cxx-build" name = "cxx-build"
version = "1.0.60" version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8f82eb643890f29750b559497cd5dc8c82019e5c6ff0284a1d14e85e8c44c7" checksum = "bae954f28208a9c31eb37623d435bdfcdd28fe93ffb5a34dfe6af9a2f1a1ca66"
dependencies = [ dependencies = [
"cc", "cc",
"codespan-reporting", "codespan-reporting",
"lazy_static", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"scratch", "scratch",
@ -515,15 +512,15 @@ dependencies = [
[[package]] [[package]]
name = "cxxbridge-flags" name = "cxxbridge-flags"
version = "1.0.60" version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "394474c4844c6df346936204bda4a45e4cf524d0b7050eb70e4b81fc568ab125" checksum = "1cc629121d3f01cd7c85ba046b3bcef66d182429b495eeb1080396ba4bfb8ae4"
[[package]] [[package]]
name = "cxxbridge-macro" name = "cxxbridge-macro"
version = "1.0.60" version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05055753924c7b4ae9b038eb7823d4d9c64a045a727cb581764a4454a60d0805" checksum = "93a8fa39ee5a91d04a99fc09fc3f1dd780ebfe31817721abcaecd823e00c9f3b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -576,17 +573,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
@ -860,6 +846,7 @@ checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
dependencies = [ dependencies = [
"libc", "libc",
"libloading", "libloading",
"pkg-config",
] ]
[[package]] [[package]]
@ -971,8 +958,7 @@ dependencies = [
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.23.1" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/gfx-rs/metal-rs?rev=140c8f4#140c8f4e39001ae154f153ffc767da6c0c9d7f06"
checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"block", "block",
@ -1010,17 +996,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "mobile-entry-point"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bef5a90018326583471cccca10424d7b3e770397b02f03276543cbb9b6a1a6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "modular-bitfield" name = "modular-bitfield"
version = "0.11.2" version = "0.11.2"
@ -1045,7 +1020,7 @@ dependencies = [
[[package]] [[package]]
name = "naga" name = "naga"
version = "0.8.0" version = "0.8.0"
source = "git+https://github.com/gfx-rs/naga?rev=8df5421#8df5421e2e9d33e092c19779adfdc42e7f8a74e6" source = "git+https://github.com/gfx-rs/naga?rev=81dc674#81dc67402a743b4dc6b2d24c0d306cd18799238b"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bitflags", "bitflags",
@ -1060,6 +1035,23 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "naga"
version = "0.8.0"
source = "git+https://github.com/gfx-rs/naga?rev=d6f8958b346676396db97053771b8d95684c47ee#d6f8958b346676396db97053771b8d95684c47ee"
dependencies = [
"bit-set",
"bitflags",
"codespan-reporting",
"hexf-parse",
"indexmap",
"log",
"num-traits",
"rustc-hash",
"spirv",
"thiserror",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.5.0" version = "0.5.0"
@ -1160,19 +1152,18 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "085fe377a4b2805c0fbc09484415ec261174614b7f080b0e0d520456ac421a67" checksum = "720d3ea1055e4e4574c0c0b0f8c3fd4f24c4cdaf465948206dea090b57b526ad"
dependencies = [ dependencies = [
"derivative",
"num_enum_derive", "num_enum_derive",
] ]
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5249369707a1e07b39f78d98c8f34e00aca7dcb053812fdbb5ad7be82c1bba38" checksum = "0d992b768490d7fe0d8586d9b5745f6c49f557da6d81dc982b1d167ad4edbb21"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@ -1205,6 +1196,12 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "owo-colors"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20448fd678ec04e6ea15bbe0476874af65e98a01515d667aa49f1434dc44ebf4"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.2" version = "0.11.2"
@ -1272,9 +1269,9 @@ checksum = "bb20dcc30536a1508e75d47dd0e399bb2fe7354dcf35cda9127f2bf1ed92e30e"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
@ -1418,12 +1415,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.9" version = "1.0.9"
@ -1876,12 +1867,12 @@ dependencies = [
[[package]] [[package]]
name = "wgpu" name = "wgpu"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=39b7a8a202c21da43035ec8dd5570a58cf4e8ef1#39b7a8a202c21da43035ec8dd5570a58cf4e8ef1" source = "git+https://github.com/gfx-rs/wgpu?rev=766c6cda1917c1265fa8a5f610ef7516d4314d1b#766c6cda1917c1265fa8a5f610ef7516d4314d1b"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"js-sys", "js-sys",
"log", "log",
"naga", "naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=81dc674)",
"parking_lot", "parking_lot",
"raw-window-handle", "raw-window-handle",
"smallvec", "smallvec",
@ -1896,7 +1887,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core" name = "wgpu-core"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=39b7a8a202c21da43035ec8dd5570a58cf4e8ef1#39b7a8a202c21da43035ec8dd5570a58cf4e8ef1" source = "git+https://github.com/gfx-rs/wgpu?rev=766c6cda1917c1265fa8a5f610ef7516d4314d1b#766c6cda1917c1265fa8a5f610ef7516d4314d1b"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bitflags", "bitflags",
@ -1905,7 +1896,7 @@ dependencies = [
"copyless", "copyless",
"fxhash", "fxhash",
"log", "log",
"naga", "naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=81dc674)",
"parking_lot", "parking_lot",
"profiling", "profiling",
"raw-window-handle", "raw-window-handle",
@ -1918,7 +1909,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-hal" name = "wgpu-hal"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=39b7a8a202c21da43035ec8dd5570a58cf4e8ef1#39b7a8a202c21da43035ec8dd5570a58cf4e8ef1" source = "git+https://github.com/gfx-rs/wgpu?rev=766c6cda1917c1265fa8a5f610ef7516d4314d1b#766c6cda1917c1265fa8a5f610ef7516d4314d1b"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"ash", "ash",
@ -1938,7 +1929,7 @@ dependencies = [
"libloading", "libloading",
"log", "log",
"metal", "metal",
"naga", "naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=81dc674)",
"objc", "objc",
"parking_lot", "parking_lot",
"profiling", "profiling",
@ -1968,7 +1959,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-types" name = "wgpu-types"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=39b7a8a202c21da43035ec8dd5570a58cf4e8ef1#39b7a8a202c21da43035ec8dd5570a58cf4e8ef1" source = "git+https://github.com/gfx-rs/wgpu?rev=766c6cda1917c1265fa8a5f610ef7516d4314d1b#766c6cda1917c1265fa8a5f610ef7516d4314d1b"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@ -2017,12 +2008,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "winit" name = "winit"
version = "0.26.0" version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70466a5f4825cc88c92963591b06dbc255420bffe19d847bfcda475e82d079c0" checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"block",
"cocoa", "cocoa",
"core-foundation 0.9.2", "core-foundation 0.9.2",
"core-graphics 0.22.3", "core-graphics 0.22.3",

View File

@ -13,15 +13,13 @@ crate-type = ["staticlib"]
#path = "src/main.rs" #path = "src/main.rs"
[dependencies] [dependencies]
cxx = "1.0.60" cxx = "1.0.64"
web-sys = "0.3.55"
env_logger = "0.9.0" env_logger = "0.9.0"
mobile-entry-point = "0.1.1"
pollster = "0.2.4" pollster = "0.2.4"
binread = { version = "2.2.0", features = ['const_generics'] } binrw = "0.8.4"
modular-bitfield = "0.11.2" modular-bitfield = "0.11.2"
num-traits = "0.2.14" num-traits = "0.2.14"
num_enum = "0.5.5" num_enum = "0.5.6"
bytemuck = "1.7.3" bytemuck = "1.7.3"
bytemuck_derive = "1.0.1" bytemuck_derive = "1.0.1"
log = "0.4.14" log = "0.4.14"
@ -30,7 +28,7 @@ cgmath = "0.18.0"
smallvec = "1.7.0" smallvec = "1.7.0"
scopeguard = "1.1.0" scopeguard = "1.1.0"
twox-hash = "1.6.2" twox-hash = "1.6.2"
winit = "0.26.0" winit = "0.26.1"
[dependencies.imgui] [dependencies.imgui]
git = "https://github.com/imgui-rs/imgui-rs" git = "https://github.com/imgui-rs/imgui-rs"
@ -44,11 +42,12 @@ features = ["winit-26"]
imgui-sys = { path = "../imgui-sys" } imgui-sys = { path = "../imgui-sys" }
[patch.crates-io] [patch.crates-io]
imgui-sys = { path = "../imgui-sys" } imgui-sys = { path = "../imgui-sys" }
naga = { git = "https://github.com/gfx-rs/naga", rev = "d6f8958b346676396db97053771b8d95684c47ee" }
[dependencies.wgpu] [dependencies.wgpu]
#path = "../../wgpu/wgpu" #path = "../../wgpu/wgpu"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "39b7a8a202c21da43035ec8dd5570a58cf4e8ef1" rev = "766c6cda1917c1265fa8a5f610ef7516d4314d1b"
features = ["spirv"] features = ["spirv"]
#[patch.'https://github.com/gfx-rs/naga'] #[patch.'https://github.com/gfx-rs/naga']
@ -56,9 +55,9 @@ features = ["spirv"]
[build-dependencies] [build-dependencies]
#naga = { path = "../../naga", features = ['spv-out', 'wgsl-in'] } #naga = { path = "../../naga", features = ['spv-out', 'wgsl-in'] }
naga = { git = "https://github.com/gfx-rs/naga", rev = "8df5421", features = ['spv-out', 'wgsl-in'] } naga = { git = "https://github.com/gfx-rs/naga", rev = "d6f8958b346676396db97053771b8d95684c47ee", features = ['spv-out', 'wgsl-in'] }
bytemuck = "1.7.3" bytemuck = "1.7.3"
cxx-build = "1.0.60" cxx-build = "1.0.64"
bindgen = "0.59.2" bindgen = "0.59.2"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]

View File

@ -59,6 +59,7 @@ pub(crate) fn initialize_imgui(window: &winit::window::Window, gpu: &DeviceHolde
state state
} }
#[allow(non_snake_case)]
fn ImGuiEngine_AddTexture( fn ImGuiEngine_AddTexture(
state: &mut ImGuiState, state: &mut ImGuiState,
gpu: &DeviceHolder, gpu: &DeviceHolder,

View File

@ -3,21 +3,21 @@ struct Uniforms {
}; };
struct VertexInput { struct VertexInput {
[[location(0)]] a_Pos: vec2<f32>; @location(0) a_Pos: vec2<f32>;
[[location(1)]] a_UV: vec2<f32>; @location(1) a_UV: vec2<f32>;
[[location(2)]] a_Color: vec4<f32>; @location(2) a_Color: vec4<f32>;
}; };
struct VertexOutput { struct VertexOutput {
[[location(0)]] v_UV: vec2<f32>; @location(0) v_UV: vec2<f32>;
[[location(1)]] v_Color: vec4<f32>; @location(1) v_Color: vec4<f32>;
[[builtin(position)]] v_Position: vec4<f32>; @builtin(position) v_Position: vec4<f32>;
}; };
[[group(0), binding(0)]] @group(0) @binding(0)
var<uniform> uniforms: Uniforms; var<uniform> uniforms: Uniforms;
[[stage(vertex)]] @stage(vertex)
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.v_UV = in.a_UV; out.v_UV = in.a_UV;
@ -26,9 +26,9 @@ fn vs_main(in: VertexInput) -> VertexOutput {
return out; return out;
} }
[[group(1), binding(0)]] @group(1) @binding(0)
var u_Texture: texture_2d<f32>; var u_Texture: texture_2d<f32>;
[[group(1), binding(1)]] @group(1) @binding(1)
var u_Sampler: sampler; var u_Sampler: sampler;
fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> { fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> {
@ -40,14 +40,14 @@ fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> {
return vec4<f32>(result, srgb.a); return vec4<f32>(result, srgb.a);
} }
[[stage(fragment)]] @stage(fragment)
fn fs_main_linear(in: VertexOutput) -> [[location(0)]] vec4<f32> { fn fs_main_linear(in: VertexOutput) -> @location(0) vec4<f32> {
let color = srgb_to_linear(in.v_Color); let color = srgb_to_linear(in.v_Color);
return color * textureSample(u_Texture, u_Sampler, in.v_UV); return color * textureSample(u_Texture, u_Sampler, in.v_UV);
} }
[[stage(fragment)]] @stage(fragment)
fn fs_main_srgb(in: VertexOutput) -> [[location(0)]] vec4<f32> { fn fs_main_srgb(in: VertexOutput) -> @location(0) vec4<f32> {
let color = in.v_Color; let color = in.v_Color;
return color * textureSample(u_Texture, u_Sampler, in.v_UV); return color * textureSample(u_Texture, u_Sampler, in.v_UV);
} }

View File

@ -22,6 +22,7 @@ mod imgui;
mod imgui_backend; mod imgui_backend;
mod shaders; mod shaders;
mod zeus; mod zeus;
mod util;
#[cxx::bridge(namespace = "aurora")] #[cxx::bridge(namespace = "aurora")]
mod ffi { mod ffi {
@ -143,6 +144,8 @@ fn app_run(mut delegate: cxx::UniquePtr<ffi::AppDelegate>) {
APP.replace(app); APP.replace(app);
ffi::App_onAppLaunched(delegate.as_mut().unwrap()); ffi::App_onAppLaunched(delegate.as_mut().unwrap());
}; };
let mut last_frame: Option<Instant> = None;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
// Have the closure take ownership of the resources. // Have the closure take ownership of the resources.
// `event_loop.run` never returns, therefore we must do this to ensure // `event_loop.run` never returns, therefore we must do this to ensure
@ -153,7 +156,6 @@ fn app_run(mut delegate: cxx::UniquePtr<ffi::AppDelegate>) {
let imgui = &mut app.imgui; let imgui = &mut app.imgui;
let window_ctx = get_window_context(); let window_ctx = get_window_context();
let gpu = &mut app.gpu; let gpu = &mut app.gpu;
let mut last_frame: Option<Instant> = None;
*control_flow = ControlFlow::Poll; *control_flow = ControlFlow::Poll;
match event { match event {
@ -183,7 +185,7 @@ fn app_run(mut delegate: cxx::UniquePtr<ffi::AppDelegate>) {
Event::Suspended => {} Event::Suspended => {}
Event::Resumed => {} Event::Resumed => {}
Event::MainEventsCleared => { Event::MainEventsCleared => {
log::info!("Requesting redraw"); log::trace!("Requesting redraw");
window_ctx.window.request_redraw(); window_ctx.window.request_redraw();
} }
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
@ -208,7 +210,7 @@ fn app_run(mut delegate: cxx::UniquePtr<ffi::AppDelegate>) {
return; return;
} }
log::info!("Redrawing"); log::trace!("Redrawing");
let frame_result = gpu.surface.get_current_texture(); let frame_result = gpu.surface.get_current_texture();
if let Err(err) = frame_result { if let Err(err) = frame_result {
log::warn!("Failed to acquire frame {}", err); log::warn!("Failed to acquire frame {}", err);

View File

@ -1,16 +0,0 @@
struct Uniform {
xf: mat4x4<f32>;
color: vec4<f32>;
};
[[group(0), binding(0)]]
var<uniform> ubuf: Uniform;
[[stage(vertex)]]
fn vs_main([[location(0)]] in_pos: vec3<f32>) -> [[builtin(position)]] vec4<f32> {
return ubuf.xf * vec4<f32>(in_pos, 1.0);
}
[[stage(fragment)]]
fn fs_main() -> [[location(0)]] vec4<f32> {
return ubuf.color;
}

View File

@ -24,8 +24,10 @@ pub(crate) struct DrawData {
pub(crate) struct PipelineConfig { pub(crate) struct PipelineConfig {
z_only: bool, z_only: bool,
} }
pub(crate) const INITIAL_PIPELINES: &[PipelineConfig] = pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] = &[
&[PipelineConfig { z_only: false }, PipelineConfig { z_only: true }]; PipelineCreateCommand::Aabb(PipelineConfig { z_only: false }),
PipelineCreateCommand::Aabb(PipelineConfig { z_only: true }),
];
pub(crate) struct State { pub(crate) struct State {
shader: wgpu::ShaderModule, shader: wgpu::ShaderModule,
@ -39,7 +41,7 @@ pub(crate) fn construct_state(
_queue: &wgpu::Queue, _queue: &wgpu::Queue,
buffers: &BuiltBuffers, buffers: &BuiltBuffers,
) -> State { ) -> State {
let shader = device.create_shader_module(&include_wgsl!("aabb.wgsl")); let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
let uniform_size = wgpu::BufferSize::new(std::mem::size_of::<Uniform>() as u64); let uniform_size = wgpu::BufferSize::new(std::mem::size_of::<Uniform>() as u64);
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("AABB Bind Group Layout"), label: Some("AABB Bind Group Layout"),
@ -145,7 +147,7 @@ struct Uniform {
} }
pub(crate) fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool) { pub(crate) fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool) {
let pipeline = pipeline_ref(PipelineCreateCommand::Aabb(PipelineConfig { z_only })); let pipeline = pipeline_ref(&PipelineCreateCommand::Aabb(PipelineConfig { z_only }));
let vert_range = push_verts(&[ let vert_range = push_verts(&[
CVector3f::new(aabb.max.x, aabb.max.y, aabb.min.z), CVector3f::new(aabb.max.x, aabb.max.y, aabb.min.z),
CVector3f::new(aabb.max.x, aabb.min.y, aabb.min.z), CVector3f::new(aabb.max.x, aabb.min.y, aabb.min.z),

View File

@ -0,0 +1,16 @@
struct Uniform {
xf: mat4x4<f32>;
color: vec4<f32>;
};
@group(0) @binding(0)
var<uniform> ubuf: Uniform;
@stage(vertex)
fn vs_main(@location(0) in_pos: vec3<f32>) -> @builtin(position) vec4<f32> {
return ubuf.xf * vec4<f32>(in_pos, 1.0);
}
@stage(fragment)
fn fs_main() -> @location(0) vec4<f32> {
return ubuf.color;
}

View File

@ -1,7 +1,6 @@
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
num::NonZeroU8,
ops::Range, ops::Range,
sync::Arc, sync::Arc,
}; };
@ -10,10 +9,12 @@ use aabb::queue_aabb;
use bytemuck::Pod; use bytemuck::Pod;
use bytemuck_derive::{Pod, Zeroable}; use bytemuck_derive::{Pod, Zeroable};
use cxx::{type_id, ExternType}; use cxx::{type_id, ExternType};
use cxx::private::hash;
use fog_volume_filter::queue_fog_volume_filter; use fog_volume_filter::queue_fog_volume_filter;
use fog_volume_plane::queue_fog_volume_plane; use fog_volume_plane::queue_fog_volume_plane;
use model::{add_material_set, add_model}; use model::{add_material_set, add_model};
use texture::{create_render_texture, create_static_texture_2d, drop_texture}; use texture::{create_render_texture, create_static_texture_2d, drop_texture};
use textured_quad::queue_textured_quad;
use twox_hash::Xxh3Hash64; use twox_hash::Xxh3Hash64;
use wgpu::RenderPipeline; use wgpu::RenderPipeline;
@ -21,11 +22,14 @@ use crate::{
gpu::GraphicsConfig, gpu::GraphicsConfig,
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, IDENTITY_MATRIX4F}, zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, IDENTITY_MATRIX4F},
}; };
use crate::shaders::ffi::{TextureFormat, TextureRef};
use crate::shaders::texture::{RenderTexture, TextureWithView};
mod aabb; mod aabb;
mod fog_volume_filter; mod fog_volume_filter;
mod fog_volume_plane; mod fog_volume_plane;
mod model; mod model;
mod textured_quad;
mod texture; mod texture;
#[cxx::bridge] #[cxx::bridge]
@ -55,7 +59,7 @@ mod ffi {
} }
#[namespace = "aurora::shaders"] #[namespace = "aurora::shaders"]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Hash)]
pub(crate) enum CameraFilterType { pub(crate) enum CameraFilterType {
Passthru, Passthru,
Multiply, Multiply,
@ -69,7 +73,7 @@ mod ffi {
InvDstMultiply, InvDstMultiply,
} }
#[namespace = "aurora::shaders"] #[namespace = "aurora::shaders"]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Hash)]
pub(crate) enum ZTest { pub(crate) enum ZTest {
None, None,
LEqual, LEqual,
@ -96,9 +100,10 @@ mod ffi {
ClampToBlack, ClampToBlack,
} }
#[namespace = "aurora::shaders"] #[namespace = "aurora::shaders"]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Hash)]
pub(crate) struct TextureRef { pub(crate) struct TextureRef {
pub(crate) id: u32, pub(crate) id: u32,
pub(crate) render: bool,
} }
#[namespace = "aurora::shaders"] #[namespace = "aurora::shaders"]
@ -180,6 +185,15 @@ mod ffi {
fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool); fn queue_aabb(aabb: CAABox, color: CColor, z_only: bool);
fn queue_fog_volume_plane(verts: &CxxVector<CVector4f>, pass: u8); fn queue_fog_volume_plane(verts: &CxxVector<CVector4f>, pass: u8);
fn queue_fog_volume_filter(color: CColor, two_way: bool); fn queue_fog_volume_filter(color: CColor, two_way: bool);
fn queue_textured_quad(
filter_type: CameraFilterType,
texture: TextureRef,
z_test: ZTest,
color: CColor,
uv_scale: f32,
rect: CRectangle,
z: f32,
);
fn create_static_texture_2d( fn create_static_texture_2d(
width: u32, width: u32,
@ -210,6 +224,21 @@ impl Default for ffi::FogState {
Self { color: Default::default(), a: 0.0, b: 0.5, c: 0.0, mode: ffi::FogMode::None } Self { color: Default::default(), a: 0.0, b: 0.5, c: 0.0, mode: ffi::FogMode::None }
} }
} }
impl Into<u32> for ffi::TextureFormat {
// noinspection RsUnreachablePatterns
fn into(self) -> u32 {
match self {
TextureFormat::RGBA8 => 1,
TextureFormat::R8 => 2,
TextureFormat::R32Float => 3,
TextureFormat::DXT1 => 4,
TextureFormat::DXT3 => 5,
TextureFormat::DXT5 => 6,
TextureFormat::BPTC => 7,
_ => panic!("Invalid texture format {:?}", self),
}
}
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum ColoredStripMode { enum ColoredStripMode {
@ -280,12 +309,7 @@ enum ShaderDrawCommand {
RandomStaticFilter {/* TODO */}, RandomStaticFilter {/* TODO */},
ScanLinesFilter {/* TODO */}, ScanLinesFilter {/* TODO */},
TextSupport {/* TODO */}, TextSupport {/* TODO */},
TexturedQuad { TexturedQuad(textured_quad::DrawData),
filter_type: ffi::CameraFilterType,
z_test: ffi::ZTest,
tex: u32, /* TODO */
/* draw, cropped, verts, filter? */
},
ThermalCold, ThermalCold,
ThermalHot, ThermalHot,
WorldShadow { WorldShadow {
@ -309,8 +333,11 @@ struct RenderState {
storage_alignment: usize, storage_alignment: usize,
buffers: BuiltBuffers, buffers: BuiltBuffers,
commands: VecDeque<Command>, commands: VecDeque<Command>,
textures: HashMap<u32, TextureWithView>,
render_textures: HashMap<u32, RenderTexture>,
// Shader states // Shader states
aabb: aabb::State, aabb: aabb::State,
textured_quad: textured_quad::State,
} }
pub(crate) fn construct_state( pub(crate) fn construct_state(
device: Arc<wgpu::Device>, device: Arc<wgpu::Device>,
@ -318,7 +345,7 @@ pub(crate) fn construct_state(
graphics_config: &GraphicsConfig, graphics_config: &GraphicsConfig,
) { ) {
let limits = device.limits(); let limits = device.limits();
let mut buffers = BuiltBuffers { let buffers = BuiltBuffers {
uniform_buffer: device.create_buffer(&wgpu::BufferDescriptor { uniform_buffer: device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shared Uniform Buffer"), label: Some("Shared Uniform Buffer"),
size: 134_217_728, // 128mb size: 134_217_728, // 128mb
@ -333,6 +360,7 @@ pub(crate) fn construct_state(
}), }),
}; };
let aabb = aabb::construct_state(&device, &queue, &buffers); let aabb = aabb::construct_state(&device, &queue, &buffers);
let textured_quad = textured_quad::construct_state(&device, &queue, &buffers, graphics_config);
let mut state = RenderState { let mut state = RenderState {
device: device.clone(), device: device.clone(),
queue: queue.clone(), queue: queue.clone(),
@ -343,12 +371,16 @@ pub(crate) fn construct_state(
storage_alignment: limits.min_storage_buffer_offset_alignment as usize, storage_alignment: limits.min_storage_buffer_offset_alignment as usize,
buffers, buffers,
commands: Default::default(), commands: Default::default(),
textures: Default::default(),
render_textures: Default::default(),
aabb, aabb,
textured_quad,
}; };
for config in aabb::INITIAL_PIPELINES { for config in aabb::INITIAL_PIPELINES {
let hash = hash_with_seed(config, 0xAABB); construct_pipeline(&mut state, config);
let pipeline = aabb::construct_pipeline(&device, graphics_config, &state.aabb, config); }
state.pipelines.insert(hash, pipeline); for config in textured_quad::INITIAL_PIPELINES {
construct_pipeline(&mut state, config);
} }
unsafe { unsafe {
STATE = Some(state); STATE = Some(state);
@ -442,8 +474,9 @@ struct PipelineRef {
id: u64, id: u64,
} }
enum PipelineCreateCommand { pub(crate) enum PipelineCreateCommand {
Aabb(aabb::PipelineConfig), Aabb(aabb::PipelineConfig),
TexturedQuad(textured_quad::PipelineConfig),
} }
#[inline(always)] #[inline(always)]
fn hash_with_seed<T: Hash>(value: &T, seed: u64) -> u64 { fn hash_with_seed<T: Hash>(value: &T, seed: u64) -> u64 {
@ -451,12 +484,11 @@ fn hash_with_seed<T: Hash>(value: &T, seed: u64) -> u64 {
value.hash(&mut state); value.hash(&mut state);
state.finish() state.finish()
} }
fn pipeline_ref(cmd: PipelineCreateCommand) -> PipelineRef { fn construct_pipeline(state: &mut RenderState, cmd: &PipelineCreateCommand) -> u64 {
let state = unsafe { STATE.as_mut().unwrap() };
let id = match cmd { let id = match cmd {
PipelineCreateCommand::Aabb(ref config) => hash_with_seed(config, 0xAABB), PipelineCreateCommand::Aabb(ref config) => hash_with_seed(config, 0xAABB),
PipelineCreateCommand::TexturedQuad(ref config) => hash_with_seed(config, 0xEEAD),
}; };
// TODO queue for creation if not found
if !state.pipelines.contains_key(&id) { if !state.pipelines.contains_key(&id) {
let pipeline = match cmd { let pipeline = match cmd {
PipelineCreateCommand::Aabb(ref config) => aabb::construct_pipeline( PipelineCreateCommand::Aabb(ref config) => aabb::construct_pipeline(
@ -465,14 +497,26 @@ fn pipeline_ref(cmd: PipelineCreateCommand) -> PipelineRef {
&state.aabb, &state.aabb,
config, config,
), ),
PipelineCreateCommand::TexturedQuad(ref config) => textured_quad::construct_pipeline(
state.device.as_ref(),
&state.graphics_config,
&state.textured_quad,
config,
),
}; };
state.pipelines.insert(id, pipeline); state.pipelines.insert(id, pipeline);
} }
id
}
fn pipeline_ref(cmd: &PipelineCreateCommand) -> PipelineRef {
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
// TODO queue for creation if not found
let id = construct_pipeline(state, cmd);
PipelineRef { id } PipelineRef { id }
} }
fn bind_pipeline(pipeline_ref: PipelineRef, pass: &mut wgpu::RenderPass) -> bool { fn bind_pipeline(pipeline_ref: PipelineRef, pass: &mut wgpu::RenderPass) -> bool {
let state = unsafe { STATE.as_ref().unwrap() }; let state = unsafe { STATE.as_ref().unwrap_unchecked() };
if pipeline_ref.id == state.current_pipeline { if pipeline_ref.id == state.current_pipeline {
return true; return true;
} }
@ -484,7 +528,7 @@ fn bind_pipeline(pipeline_ref: PipelineRef, pass: &mut wgpu::RenderPass) -> bool
} }
pub(crate) fn render_into_pass(pass: &mut wgpu::RenderPass) { pub(crate) fn render_into_pass(pass: &mut wgpu::RenderPass) {
let state = unsafe { STATE.as_mut().unwrap() }; let state = unsafe { STATE.as_mut().unwrap_unchecked() };
{ {
let global_buffers = unsafe { &mut GLOBAL_BUFFERS }; let global_buffers = unsafe { &mut GLOBAL_BUFFERS };
state.queue.write_buffer(&state.buffers.vertex_buffer, 0, &global_buffers.verts); state.queue.write_buffer(&state.buffers.vertex_buffer, 0, &global_buffers.verts);
@ -512,6 +556,9 @@ pub(crate) fn render_into_pass(pass: &mut wgpu::RenderPass) {
ShaderDrawCommand::Aabb(data) => { ShaderDrawCommand::Aabb(data) => {
aabb::draw_aabb(data, &state.aabb, pass, &state.buffers); aabb::draw_aabb(data, &state.aabb, pass, &state.buffers);
} }
ShaderDrawCommand::TexturedQuad(data) => {
textured_quad::draw_textured_quad(data, &state.textured_quad, pass, &state.buffers);
}
_ => todo!(), _ => todo!(),
}, },
} }
@ -554,15 +601,15 @@ fn finalize_global_uniform() -> Range<u64> {
} }
fn push_draw_command(cmd: ShaderDrawCommand) { fn push_draw_command(cmd: ShaderDrawCommand) {
let state = unsafe { STATE.as_mut().unwrap() }; let state = unsafe { STATE.as_mut().unwrap_unchecked() };
state.commands.push_back(Command::Draw(cmd)); state.commands.push_back(Command::Draw(cmd));
} }
fn set_viewport(rect: CRectangle, znear: f32, zfar: f32) { fn set_viewport(rect: CRectangle, znear: f32, zfar: f32) {
let state = unsafe { STATE.as_mut().unwrap() }; let state = unsafe { STATE.as_mut().unwrap_unchecked() };
state.commands.push_back(Command::SetViewport(rect, znear, zfar)); state.commands.push_back(Command::SetViewport(rect, znear, zfar));
} }
fn set_scissor(x: u32, y: u32, w: u32, h: u32) { fn set_scissor(x: u32, y: u32, w: u32, h: u32) {
let state = unsafe { STATE.as_mut().unwrap() }; let state = unsafe { STATE.as_mut().unwrap_unchecked() };
state.commands.push_back(Command::SetScissor(x, y, w, h)); state.commands.push_back(Command::SetScissor(x, y, w, h));
} }

View File

@ -0,0 +1,349 @@
struct PassTrait {
constant_color: vec4<f32>;
tex_idx: i32;
uv_source: i32;
tcg_mtx_idx: i32;
normalize: u32;
sample_alpha: u32;
has_constant_color: u32;
};
struct TCGUniform {
mode: i32;
f0: f32;
f1: f32;
f2: f32;
f3: f32;
f4: f32;
f5: f32;
f6: f32;
f7: f32;
f8: f32;
pad1: u32;
pad2: u32;
};
struct ConstShaderTraits {
shader_type: u32;
world_shadow: u32;
short_uvs: u32;
alpha_discard: u32;
samus_reflection: u32;
@align(16) tcgs: @stride(48) array<TCGUniform, 4>;
};
struct MutShaderTraits {
post_type: u32;
game_blend_mode: u32;
model_flags: u32;
};
struct ConstTraits {
shader: ConstShaderTraits;
lightmap: PassTrait;
diffuse: PassTrait;
diffuse_mod: PassTrait;
emissive: PassTrait;
specular: PassTrait;
extended_specular: PassTrait;
reflection: PassTrait;
alpha: PassTrait;
alpha_mod: PassTrait;
};
@group(2) @binding(10)
var<uniform> c_traits: ConstTraits;
struct Light {
pos: vec3<f32>;
dir: vec3<f32>;
color: vec4<f32>;
lin_att: vec3<f32>;
ang_att: vec3<f32>;
};
struct FogState {
color: vec4<f32>;
a: f32;
b: f32;
c: f32;
mode: u32;
};
struct ModelUniform {
position: vec3<f32>;
orientation: vec3<f32>;
scale: vec3<f32>;
ambient_color: vec4<f32>;
lights: array<Light, 8>;
};
struct GlobalUniform {
view: mat4x4<f32>;
proj: mat4x4<f32>;
ambient: vec4<f32>;
lightmap_mul: vec4<f32>;
fog: FogState;
seconds: f32;
};
@group(1) @binding(0)
var<uniform> u_model: ModelUniform;
@group(1) @binding(1)
var<uniform> u_global: GlobalUniform;
@group(1) @binding(2)
var<uniform> u_traits: MutShaderTraits;
@group(2) @binding(0)
var t_lightmap: texture_2d<f32>;
@group(2) @binding(1)
var t_diffuse: texture_2d<f32>;
@group(2) @binding(2)
var t_diffuse_mod: texture_2d<f32>;
@group(2) @binding(3)
var t_emissive: texture_2d<f32>;
@group(2) @binding(4)
var t_specular: texture_2d<f32>;
@group(2) @binding(5)
var t_extended_specular: texture_2d<f32>;
@group(2) @binding(6)
var t_reflection: texture_2d<f32>;
@group(2) @binding(7)
var t_alpha: texture_2d<f32>;
@group(2) @binding(8)
var t_alpha_mod: texture_2d<f32>;
@group(2) @binding(9)
var t_material: texture_2d<f32>;
@group(3) @binding(0)
var s_repeat: sampler;
@group(3) @binding(1)
var s_clamp: sampler;
@group(3) @binding(2)
var s_reflect: sampler;
@group(3) @binding(3)
var s_clamp_edge: sampler;
struct VertexOutput {
@builtin(position) position: vec4<f32>;
@location(0) lighting: vec3<f32>;
@location(1) uv_lightmap: vec2<f32>;
@location(2) uv_diffuse: vec2<f32>;
@location(3) uv_diffuse_mod: vec2<f32>;
@location(4) uv_emissive: vec2<f32>;
@location(5) uv_specular: vec2<f32>;
@location(6) uv_extended_specular: vec2<f32>;
@location(7) uv_reflection: vec2<f32>;
@location(8) uv_alpha: vec2<f32>;
@location(9) uv_alpha_mod: vec2<f32>;
};
fn make_rotate_x(r: f32) -> mat4x4<f32> {
var s = sin(r);
var c = cos(r);
return mat4x4<f32>(
vec4<f32>(1.0, 0.0, 0.0, 0.0),
vec4<f32>(0.0, c, s, 0.0),
vec4<f32>(0.0, -s, c, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0)
);
}
fn make_rotate_y(r: f32) -> mat4x4<f32> {
var s = sin(r);
var c = cos(r);
return mat4x4<f32>(
vec4<f32>(c, 0.0, -s, 0.0),
vec4<f32>(0.0, 1.0, 0.0, 0.0),
vec4<f32>(s, 0.0, c, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0)
);
}
fn make_rotate_z(r: f32) -> mat4x4<f32> {
var s = sin(r);
var c = cos(r);
return mat4x4<f32>(
vec4<f32>(c, s, 0.0, 0.0),
vec4<f32>(-s, c, 0.0, 0.0),
vec4<f32>(0.0, 0.0, 1.0, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0)
);
}
fn make_rotate(r: vec3<f32>) -> mat4x4<f32> {
return make_rotate_z(r.z)
* make_rotate_y(r.y)
* make_rotate_x(r.x);
}
fn make_scale(s: vec3<f32>) -> mat4x4<f32> {
return mat4x4<f32>(
vec4<f32>(s.x, 0.0, 0.0, 0.0),
vec4<f32>(0.0, s.y, 0.0, 0.0),
vec4<f32>(0.0, 0.0, s.z, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0)
);
}
fn make_translate(translate: vec3<f32>) -> mat4x4<f32> {
return mat4x4<f32>(
vec4<f32>(1.0, 0.0, 0.0, 0.0),
vec4<f32>(0.0, 1.0, 0.0, 0.0),
vec4<f32>(0.0, 0.0, 1.0, 0.0),
vec4<f32>(translate, 1.0)
);
}
struct TCGMatrix {
mtx: mat4x4<f32>;
post_mtx: mat4x4<f32>;
};
let identity_mtx: mat4x4<f32> = mat4x4<f32>(
vec4<f32>(1.0, 0.0, 0.0, 0.0),
vec4<f32>(0.0, 1.0, 0.0, 0.0),
vec4<f32>(0.0, 0.0, 1.0, 0.0),
vec4<f32>(0.0, 0.0, 0.0, 1.0),
);
fn process_uv_anim(
model_matrix: ptr<function, mat4x4<f32>>,
mv_inv: ptr<function, mat4x4<f32>>,
traits: PassTrait,
) -> TCGMatrix {
var out = TCGMatrix(identity_mtx, identity_mtx);
var tu = c_traits.shader.tcgs[traits.tcg_mtx_idx];
switch (tu.mode) {
case 0: {
out.mtx = *mv_inv;
out.mtx[3][3] = 1.0;
out.post_mtx[0][0] = 0.5;
out.post_mtx[1][1] = 0.5;
out.post_mtx[3][0] = 0.5;
out.post_mtx[3][1] = 0.5;
}
case 1: {
out.mtx = *mv_inv;
out.mtx[3] = u_global.view * vec4<f32>(u_model.position, 1.0);
out.mtx[3][3] = 1.0;
out.post_mtx[0][0] = 0.5;
out.post_mtx[1][1] = 0.5;
out.post_mtx[3][0] = 0.5;
out.post_mtx[3][1] = 0.5;
}
case 2: {
out.mtx[3][0] = u_global.seconds * tu.f2 + tu.f0;
out.mtx[3][1] = u_global.seconds * tu.f3 + tu.f1;
}
case 3: {
var angle = u_global.seconds * tu.f1 + tu.f0;
var acos = cos(angle);
var asin = sin(angle);
out.mtx[0][0] = acos;
out.mtx[0][1] = asin;
out.mtx[1][0] = -asin;
out.mtx[1][1] = acos;
out.mtx[3][0] = (1.0 - (acos - asin)) * 0.5;
out.mtx[3][1] = (1.0 - (asin + acos)) * 0.5;
}
case 4: {
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
out.mtx[3][0] = trunc(tu.f1 * fract(n)) * tu.f2;
}
case 5: {
var n: f32 = tu.f2 * tu.f0 * (tu.f3 + u_global.seconds);
out.mtx[3][1] = trunc(tu.f1 * fract(n)) * tu.f2;
}
case 6: {
let pos = u_model.position;
out.mtx = *model_matrix;
out.mtx[3] = vec4<f32>(0.0, 0.0, 0.0, 1.0);
out.post_mtx[0][0] = 0.5;
out.post_mtx[1][1] = 0.0;
out.post_mtx[2][1] = 0.5;
out.post_mtx[3][0] = pos.x * 0.05;
out.post_mtx[3][1] = pos.y * 0.05;
}
default: {}
}
return out;
}
fn t_sample(
traits: PassTrait,
tex: texture_2d<f32>,
uv: vec2<f32>,
) -> vec3<f32> {
// For control flow uniformity, we always have to sample
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
if (traits.has_constant_color == 1u) {
color = traits.constant_color;
}
if (traits.sample_alpha == 1u) {
return vec3<f32>(color.w);
}
return color.xyz;
}
let kRGBToYPrime: vec3<f32> = vec3<f32>(0.299, 0.587, 0.114);
fn t_sample_alpha(
traits: PassTrait,
tex: texture_2d<f32>,
uv: vec2<f32>,
) -> f32 {
// For control flow uniformity, we always have to sample
var color: vec4<f32> = textureSample(tex, s_repeat, uv);
if (traits.has_constant_color == 1u) {
color = traits.constant_color;
}
if (traits.sample_alpha == 1u) {
return color.w;
}
return dot(color.xyz, kRGBToYPrime);
}
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
var color: vec4<f32>;
let mat_sample = textureSample(t_material, s_repeat, in.uv_diffuse);
if (c_traits.shader.shader_type == 0u) {
// Invalid
color = vec4<f32>(
t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse),
t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
);
} else if (c_traits.shader.shader_type == 1u) {
// RetroShader
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
* u_global.lightmap_mul.xyz + in.lighting;
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
* in.lighting;
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
// TODO DynReflectionSample
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
} else if (c_traits.shader.shader_type == 2u) {
// RetroDynamicShader
var rgb = t_sample(c_traits.lightmap, t_lightmap, in.uv_lightmap)
* u_global.lightmap_mul.xyz + in.lighting;
rgb = rgb * t_sample(c_traits.diffuse, t_diffuse, in.uv_diffuse)
+ t_sample(c_traits.emissive, t_emissive, in.uv_emissive);
let specular = t_sample(c_traits.specular, t_specular, in.uv_specular)
+ t_sample(c_traits.extended_specular, t_extended_specular, in.uv_extended_specular)
* in.lighting;
rgb = rgb + specular * t_sample(c_traits.reflection, t_reflection, in.uv_reflection);
// TODO DynReflectionSample
color = vec4<f32>(rgb, t_sample_alpha(c_traits.alpha, t_alpha, in.uv_alpha)
* t_sample_alpha(c_traits.alpha_mod, t_alpha_mod, in.uv_alpha_mod));
} else {
// TODO RetroDynamicAlphaShader + RetroDynamicCharacterShader
}
// TODO fog shader
// TODO post type
if (c_traits.shader.alpha_discard == 1u && color.w < 0.25) {
discard;
}
return color;
}

View File

@ -0,0 +1,141 @@
struct Vec3Block {
data: @stride(16) array<vec3<f32>>;
};
struct Vec2Block {
data: @stride(8) array<vec2<f32>>;
};
struct TCGUniforms {
data: array<TCGUniform>;
};
@group(0) @binding(0)
var<storage, read> v_verts: Vec3Block;
@group(0) @binding(1)
var<storage, read> v_norms: Vec3Block;
@group(0) @binding(2)
var<storage, read> v_uvs: Vec2Block;
@group(0) @binding(3)
var<storage, read> v_short_uvs: Vec2Block;
//@group(0) @binding(4)
//var<uniform> v_tcg: TCGUniforms;
fn calculate_tcg(
traits: PassTrait,
obj_pos: ptr<function, vec4<f32>>,
obj_norm: ptr<function, vec4<f32>>,
model_matrix: ptr<function, mat4x4<f32>>,
mv_inv: ptr<function, mat4x4<f32>>,
uv_0_4_idx: vec4<i32>,
uv_4_7_idx: vec4<i32>,
) -> vec2<f32> {
if (traits.uv_source == -1) {
return vec2<f32>(0.0);
}
if (traits.tcg_mtx_idx >= 0) {
var src: vec4<f32>;
switch (traits.uv_source) {
case 0: { src = vec4<f32>((*obj_pos).xyz, 1.0); }
case 1: { src = vec4<f32>((*obj_norm).xyz, 1.0); }
case 2: {
if (c_traits.shader.short_uvs == 1u) {
src = vec4<f32>(v_short_uvs.data[uv_0_4_idx.x], 0.0, 1.0);
} else {
src = vec4<f32>(v_uvs.data[uv_0_4_idx.x], 0.0, 1.0);
}
}
case 3: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.y], 0.0, 1.0); }
case 4: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.z], 0.0, 1.0); }
case 5: { src = vec4<f32>(v_uvs.data[uv_0_4_idx.w], 0.0, 1.0); }
case 6: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.x], 0.0, 1.0); }
case 7: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.y], 0.0, 1.0); }
case 8: { src = vec4<f32>(v_uvs.data[uv_4_7_idx.z], 0.0, 1.0); }
default: {}
}
let tcgm = process_uv_anim(model_matrix, mv_inv, traits);
var tmp = (tcgm.mtx * src).xyz;
if (traits.normalize == 1u) {
tmp = normalize(tmp);
}
let tmp_proj = tcgm.post_mtx * vec4<f32>(tmp, 1.0);
return (tmp_proj / tmp_proj.w).xy;
} else {
switch (traits.uv_source) {
case 0: { return (*obj_pos).xy; }
case 1: { return (*obj_norm).xy; }
case 2: {
if (c_traits.shader.short_uvs == 1u) {
return v_short_uvs.data[uv_0_4_idx.x];
} else {
return v_uvs.data[uv_0_4_idx.x];
}
}
case 3: { return v_uvs.data[uv_0_4_idx.y]; }
case 4: { return v_uvs.data[uv_0_4_idx.z]; }
case 5: { return v_uvs.data[uv_0_4_idx.w]; }
case 6: { return v_uvs.data[uv_4_7_idx.x]; }
case 7: { return v_uvs.data[uv_4_7_idx.y]; }
case 8: { return v_uvs.data[uv_4_7_idx.z]; }
default: {}
}
}
return vec2<f32>(0.0);
}
@stage(vertex)
fn vs_main(
@location(0) pos_norm_idx: vec2<i32>,
@location(1) uv_0_4_idx: vec4<i32>,
@location(2) uv_4_7_idx: vec4<i32>,
) -> VertexOutput {
var out: VertexOutput;
var obj_pos = vec4<f32>(v_verts.data[pos_norm_idx.x], 1.0);
var obj_norm = vec4<f32>(v_norms.data[pos_norm_idx.y], 0.0);
var model_matrix_no_trans = make_rotate(radians(u_model.orientation)) * make_scale(u_model.scale);
var model_matrix = make_translate(u_model.position) * model_matrix_no_trans;
var mv = u_global.view * model_matrix;
var mv_inv = transpose(u_global.view * model_matrix_no_trans);
var mv_pos = mv * obj_pos;
var mv_norm = mv_inv * obj_norm;
out.position = u_global.proj * mv_pos;
out.uv_lightmap = calculate_tcg(c_traits.lightmap, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_diffuse = calculate_tcg(c_traits.diffuse, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_diffuse_mod = calculate_tcg(c_traits.diffuse_mod, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_emissive = calculate_tcg(c_traits.emissive, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_specular = calculate_tcg(c_traits.specular, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_extended_specular = calculate_tcg(c_traits.extended_specular, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_reflection = calculate_tcg(c_traits.reflection, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_alpha = calculate_tcg(c_traits.alpha, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
out.uv_alpha_mod = calculate_tcg(c_traits.alpha_mod, &obj_pos, &obj_norm, &model_matrix, &mv_inv, uv_0_4_idx, uv_4_7_idx);
if (c_traits.shader.samus_reflection == 1u) {
// TODO dyn reflection
}
if (u_traits.post_type > 0u && u_traits.post_type < 5u) {
out.lighting = vec3<f32>(1.0);
} else {
var lighting = u_global.ambient.xyz + u_model.ambient_color.xyz;
for (var i = 0; i < 8; i = i + 1) {
var light = u_model.lights[i];
var delta = mv_pos.xyz - light.pos;
var dist = length(delta);
var delta_norm = delta / dist;
var ang_dot = max(dot(delta_norm, light.dir), 0.0);
var lin_att = light.lin_att;
var att = 1.0 / (lin_att.z * dist * dist * lin_att.y * dist + lin_att.x);
var ang_att = light.ang_att;
var ang_att_d = ang_att.z * ang_dot * ang_dot * ang_att.y * ang_dot + ang_att.x;
var this_color = light.color.xyz * ang_att_d * att * max(dot(-delta_norm, mv_norm.xyz), 0.0);
if (i == 0 && c_traits.shader.world_shadow == 1u) {
// TODO ExtTex0 sample
}
lighting = lighting + this_color;
}
out.lighting = clamp(lighting, vec3<f32>(0.0), vec3<f32>(1.0));
}
// TODO dyn reflection sample
return out;
}

View File

@ -0,0 +1,87 @@
fn calculate_tcg(
traits: PassTrait,
uv_in: vec3<f32>,
obj_pos: ptr<function, vec4<f32>>,
obj_norm: ptr<function, vec4<f32>>,
model_matrix: ptr<function, mat4x4<f32>>,
mv_inv: ptr<function, mat4x4<f32>>,
) -> vec2<f32> {
var uv: vec4<f32> = vec4<f32>(uv_in, 1.0);
if (traits.tcg_mtx_idx >= 0) {
let tcgm = process_uv_anim(model_matrix, mv_inv, traits);
var tmp = (tcgm.mtx * uv).xyz;
if (traits.normalize == 1u) {
tmp = normalize(tmp);
}
let tmp_proj = tcgm.post_mtx * vec4<f32>(tmp, 1.0);
return (tmp_proj / tmp_proj.w).xy;
}
return uv.xy;
}
@stage(vertex)
fn vs_main(
@location(0) pos: vec3<f32>,
@location(1) norm: vec3<f32>,
@location(2) in_uv_lightmap: vec3<f32>,
@location(3) in_uv_diffuse: vec2<f32>,
@location(4) in_uv_diffuse_mod: vec2<f32>,
@location(5) in_uv_emissive: vec2<f32>,
@location(6) in_uv_specular: vec2<f32>,
@location(7) in_uv_extended_specular: vec2<f32>,
@location(8) in_uv_reflection: vec2<f32>,
@location(9) in_uv_alpha: vec2<f32>,
@location(10) in_uv_alpha_mod: vec2<f32>,
) -> VertexOutput {
var out: VertexOutput;
var obj_pos = vec4<f32>(pos, 1.0);
var obj_norm = vec4<f32>(norm, 0.0);
var model_matrix_no_trans = make_rotate(radians(u_model.orientation)) * make_scale(u_model.scale);
var model_matrix = make_translate(u_model.position) * model_matrix_no_trans;
var mv = u_global.view * model_matrix;
var mv_inv = transpose(u_global.view * model_matrix_no_trans);
var mv_pos = mv * obj_pos;
var mv_norm = mv_inv * obj_norm;
out.position = u_global.proj * mv_pos;
out.uv_lightmap = calculate_tcg(c_traits.lightmap, in_uv_lightmap, &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_diffuse = calculate_tcg(c_traits.diffuse, vec3<f32>(in_uv_diffuse, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_diffuse_mod = calculate_tcg(c_traits.diffuse_mod, vec3<f32>(in_uv_diffuse_mod, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_emissive = calculate_tcg(c_traits.emissive, vec3<f32>(in_uv_emissive, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_specular = calculate_tcg(c_traits.specular, vec3<f32>(in_uv_specular, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_extended_specular = calculate_tcg(c_traits.extended_specular, vec3<f32>(in_uv_extended_specular, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_reflection = calculate_tcg(c_traits.reflection, vec3<f32>(in_uv_reflection, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_alpha = calculate_tcg(c_traits.alpha, vec3<f32>(in_uv_alpha, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
out.uv_alpha_mod = calculate_tcg(c_traits.alpha_mod, vec3<f32>(in_uv_alpha_mod, 0.0), &obj_pos, &obj_norm, &model_matrix, &mv_inv);
if (c_traits.shader.samus_reflection == 1u) {
// TODO dyn reflection
}
if (u_traits.post_type > 0u && u_traits.post_type < 5u) {
out.lighting = vec3<f32>(1.0);
} else {
var lighting = u_global.ambient.xyz + u_model.ambient_color.xyz;
for (var i = 0; i < 8; i = i + 1) {
var light = u_model.lights[i];
var delta = mv_pos.xyz - light.pos;
var dist = length(delta);
var delta_norm = delta / dist;
var ang_dot = max(dot(delta_norm, light.dir), 0.0);
var lin_att = light.lin_att;
var att = 1.0 / (lin_att.z * dist * dist * lin_att.y * dist + lin_att.x);
var ang_att = light.ang_att;
var ang_att_d = ang_att.z * ang_dot * ang_dot * ang_att.y * ang_dot + ang_att.x;
var this_color = light.color.xyz * ang_att_d * att * max(dot(-delta_norm, mv_norm.xyz), 0.0);
if (i == 0 && c_traits.shader.world_shadow == 1u) {
// TODO ExtTex0 sample
}
lighting = lighting + this_color;
}
out.lighting = clamp(lighting, vec3<f32>(0.0), vec3<f32>(1.0));
}
// TODO dyn reflection sample
return out;
}

View File

@ -1,17 +1,20 @@
use std::ptr::null; use std::hash::{Hash, Hasher};
use std::num::NonZeroU8;
use cxx::SharedPtr; use twox_hash::XxHash32;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use crate::{ use crate::{
get_app, get_app,
gpu::TextureWithSampler, shaders::{
shaders::ffi::{TextureClampMode, TextureFormat, TextureRef}, ffi::{TextureFormat, TextureRef},
STATE,
},
}; };
pub(crate) struct TextureWithView { pub(crate) struct TextureWithView {
texture: wgpu::Texture, pub(crate) texture: wgpu::Texture,
view: wgpu::TextureView, pub(crate) view: wgpu::TextureView,
} }
impl TextureWithView { impl TextureWithView {
fn new(texture: wgpu::Texture) -> Self { fn new(texture: wgpu::Texture) -> Self {
@ -21,12 +24,8 @@ impl TextureWithView {
} }
pub(crate) struct RenderTexture { pub(crate) struct RenderTexture {
color_texture: Option<TextureWithView>, pub(crate) color_texture: Option<TextureWithView>,
depth_texture: Option<TextureWithView>, pub(crate) depth_texture: Option<TextureWithView>,
}
pub(crate) struct Texture {
texture: wgpu::Texture,
} }
pub(crate) fn create_static_texture_2d( pub(crate) fn create_static_texture_2d(
@ -60,7 +59,20 @@ pub(crate) fn create_static_texture_2d(
}, },
data, data,
); );
TextureRef { id: u32::MAX }
// Generate texture hash as ID
let mut hasher = XxHash32::with_seed(format.into());
width.hash(&mut hasher);
height.hash(&mut hasher);
mips.hash(&mut hasher);
data.hash(&mut hasher);
label.hash(&mut hasher);
let id = hasher.finish() as u32;
// Store texture and return reference
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
state.textures.insert(id, TextureWithView::new(texture));
TextureRef { id, render: false }
} }
pub(crate) fn create_render_texture( pub(crate) fn create_render_texture(
@ -122,8 +134,54 @@ pub(crate) fn create_render_texture(
// anisotropy_clamp: None, // anisotropy_clamp: None,
// border_color, // border_color,
// }); // });
RenderTexture { color_texture, depth_texture };
TextureRef { id: u32::MAX } // Generate texture hash as ID
let mut hasher = XxHash32::default();
width.hash(&mut hasher);
height.hash(&mut hasher);
color_bind_count.hash(&mut hasher);
depth_bind_count.hash(&mut hasher);
label.hash(&mut hasher);
let id = hasher.finish() as u32;
// Store texture and return reference
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
state.render_textures.insert(id, RenderTexture { color_texture, depth_texture });
TextureRef { id, render: true }
} }
pub(crate) fn drop_texture(handle: TextureRef) {} pub(crate) fn drop_texture(handle: TextureRef) {
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
if handle.render {
state.render_textures.remove(&handle.id).expect("Render texture already dropped");
} else {
state.textures.remove(&handle.id).expect("Texture already dropped");
}
}
pub(crate) fn create_sampler(
device: &wgpu::Device,
mut address_mode: wgpu::AddressMode,
mut border_color: Option<wgpu::SamplerBorderColor>,
) -> wgpu::Sampler {
if address_mode == wgpu::AddressMode::ClampToBorder
&& !device.features().contains(wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER)
{
address_mode = wgpu::AddressMode::ClampToEdge;
border_color = None;
}
device.create_sampler(&wgpu::SamplerDescriptor {
label: None,
address_mode_u: address_mode,
address_mode_v: address_mode,
address_mode_w: address_mode,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: NonZeroU8::new(16),
border_color,
})
}

View File

@ -0,0 +1,317 @@
use std::{collections::HashMap, hash::Hash, ops::Range};
use bytemuck_derive::{Pod, Zeroable};
use wgpu::{include_wgsl, vertex_attr_array};
use crate::{
get_app,
gpu::GraphicsConfig,
shaders::{
bind_pipeline,
BuiltBuffers,
ffi::{CameraFilterType, TextureRef, ZTest}, pipeline_ref, PipelineCreateCommand, PipelineHolder, PipelineRef,
push_draw_command, push_uniform, push_verts, ShaderDrawCommand, STATE,
},
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, CVector4f},
};
use crate::shaders::texture::create_sampler;
use crate::util::{align, Vec2, Vec3};
#[derive(Debug, Clone)]
pub(crate) struct DrawData {
pipeline: PipelineRef,
vert_range: Range<u64>,
uniform_range: Range<u64>,
texture: TextureRef,
}
#[derive(Hash)]
pub(crate) struct PipelineConfig {
filter_type: CameraFilterType,
z_test: ZTest,
}
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] = &[
// PipelineCreateCommand::TexturedQuad(PipelineConfig { z_only: false }),
// PipelineCreateCommand::TexturedQuad(PipelineConfig { z_only: true }),
];
pub(crate) struct State {
shader: wgpu::ShaderModule,
uniform_layout: wgpu::BindGroupLayout,
uniform_bind_group: wgpu::BindGroup,
texture_layout: wgpu::BindGroupLayout,
sampler: wgpu::Sampler,
pipeline_layout: wgpu::PipelineLayout,
// Transient state
texture_bind_groups: HashMap<u32, wgpu::BindGroup>,
frame_used_textures: Vec<u32>, // TODO use to clear bind groups
}
pub(crate) fn construct_state(
device: &wgpu::Device,
_queue: &wgpu::Queue,
buffers: &BuiltBuffers,
graphics_config: &GraphicsConfig,
) -> State {
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
let uniform_size = wgpu::BufferSize::new(align(std::mem::size_of::<Uniform>() as u64, uniform_alignment as u64));
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Textured Quad Uniform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: uniform_size,
},
count: None,
}, wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
}],
});
let sampler = create_sampler(device, wgpu::AddressMode::Repeat, None);
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Textured Quad Uniform Bind Group"),
layout: &uniform_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &buffers.uniform_buffer,
offset: 0, // dynamic
size: uniform_size,
}),
}, wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
}],
});
let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Textured Quad Texture Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Textured Quad Pipeline Layout"),
bind_group_layouts: &[&uniform_layout, &texture_layout],
push_constant_ranges: &[],
});
State {
shader,
uniform_layout,
uniform_bind_group,
texture_layout,
sampler,
pipeline_layout,
texture_bind_groups: Default::default(),
frame_used_textures: vec![],
}
}
pub(crate) fn construct_pipeline(
device: &wgpu::Device,
graphics: &GraphicsConfig,
state: &State,
config: &PipelineConfig,
) -> PipelineHolder {
let (blend_component, alpha_write) = match config.filter_type {
CameraFilterType::Multiply => (
wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::Zero,
dst_factor: wgpu::BlendFactor::Src,
operation: wgpu::BlendOperation::Add,
},
true,
),
CameraFilterType::Add => (
wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
false,
),
CameraFilterType::Subtract => (
wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Subtract,
},
false,
),
CameraFilterType::Blend => (
wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
false,
),
CameraFilterType::InvDstMultiply => (
wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::Zero,
dst_factor: wgpu::BlendFactor::OneMinusSrc,
operation: wgpu::BlendOperation::Add,
},
true,
),
_ => todo!(),
};
log::warn!("VERT SIZE: {}", std::mem::size_of::<Vert>() as u64);
PipelineHolder {
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Textured Quad Pipeline"),
layout: Some(&state.pipeline_layout),
vertex: wgpu::VertexState {
module: &state.shader,
entry_point: "vs_main",
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vert>() as u64,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x2],
}],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: graphics.depth_format,
depth_write_enabled: config.z_test == ZTest::GEqualZWrite,
depth_compare: match config.z_test {
ZTest::None => wgpu::CompareFunction::Always,
ZTest::LEqual => wgpu::CompareFunction::LessEqual,
ZTest::GEqual | ZTest::GEqualZWrite => wgpu::CompareFunction::GreaterEqual,
_ => todo!(),
},
stencil: Default::default(),
bias: Default::default(),
}),
multisample: wgpu::MultisampleState {
count: graphics.msaa_samples,
..Default::default()
},
fragment: Some(wgpu::FragmentState {
module: &state.shader,
entry_point: "fs_main",
targets: &[wgpu::ColorTargetState {
format: graphics.color_format,
blend: Some(wgpu::BlendState {
color: blend_component,
alpha: blend_component,
}),
write_mask: if alpha_write {
wgpu::ColorWrites::ALL
} else {
wgpu::ColorWrites::COLOR
},
}],
}),
multiview: None,
}),
}
}
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
#[repr(C)]
struct Uniform {
xf: CMatrix4f,
color: CColor,
lod: f32,
}
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
#[repr(C)]
struct Vert {
pos: Vec3<f32>,
uv: Vec2<f32>,
}
pub(crate) fn queue_textured_quad(
filter_type: CameraFilterType,
texture: TextureRef,
z_test: ZTest,
color: CColor,
uv_scale: f32,
rect: CRectangle,
z: f32,
) {
let pipeline =
pipeline_ref(&PipelineCreateCommand::TexturedQuad(PipelineConfig { filter_type, z_test }));
let vert_range = push_verts(&[
Vert { pos: Vec3::new(0.0, 0.0, z), uv: Vec2::new(0.0, 0.0) },
Vert { pos: Vec3::new(0.0, 1.0, z), uv: Vec2::new(0.0, uv_scale) },
Vert { pos: Vec3::new(1.0, 0.0, z), uv: Vec2::new(uv_scale, 0.0) },
Vert { pos: Vec3::new(1.0, 1.0, z), uv: Vec2::new(uv_scale, uv_scale) },
]);
let uniform_range = push_uniform(&Uniform {
xf: CMatrix4f::new(
CVector4f::new(rect.size.x * 2.0, 0.0, 0.0, 0.0),
CVector4f::new(0.0, rect.size.y * 2.0, 0.0, 0.0),
CVector4f::new(0.0, 0.0, 1.0, 0.0),
CVector4f::new(rect.position.x * 2.0 - 1.0, rect.position.y * 2.0 - 1.0, 0.0, 1.0),
),
color,
lod: 0.0,
});
// TODO defer bind group creation to draw time or another thread?
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
let groups = &mut state.textured_quad.texture_bind_groups;
if !groups.contains_key(&texture.id) {
let tex = state.textures.get(&texture.id).unwrap();
let bind_group = get_app().gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &state.textured_quad.texture_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&tex.view),
}],
});
groups.insert(texture.id, bind_group);
}
push_draw_command(ShaderDrawCommand::TexturedQuad(DrawData {
pipeline,
vert_range,
uniform_range,
texture,
}));
}
pub(crate) fn draw_textured_quad<'a>(
data: &DrawData,
state: &'a State,
pass: &mut wgpu::RenderPass<'a>,
buffers: &'a BuiltBuffers,
) {
if !bind_pipeline(data.pipeline, pass) {
return;
}
// Uniform bind group
pass.set_bind_group(0, &state.uniform_bind_group, &[
data.uniform_range.start as wgpu::DynamicOffset
]);
// Texture bind group
let texture_bind_group =
state.texture_bind_groups.get(&data.texture.id).expect("Failed to find texture bind group");
pass.set_bind_group(1, texture_bind_group, &[]);
// Vertex buffer
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
pass.draw(0..4, 0..1);
}

View File

@ -0,0 +1,29 @@
struct Uniform {
xf: mat4x4<f32>;
color: vec4<f32>;
lod: f32;
};
@group(0) @binding(0)
var<uniform> ubuf: Uniform;
@group(0) @binding(1)
var texture_sampler: sampler;
@group(1) @binding(0)
var texture: texture_2d<f32>;
struct VertexOutput {
@builtin(position) pos: vec4<f32>;
@location(0) uv: vec2<f32>;
};
@stage(vertex)
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
var out: VertexOutput;
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
out.uv = in_uv;
return out;
}
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return ubuf.color * textureSampleBias(texture, texture_sampler, in.uv, ubuf.lod);
}

99
Graphics/src/util.rs Normal file
View File

@ -0,0 +1,99 @@
//! This contains regular vector types for use in packed buffers (i.e. vertex).
//! zeus types are all aligned to f32x4
use binrw::BinRead;
use bytemuck::{Pod, Zeroable};
#[derive(Copy, Clone, Debug, PartialEq, BinRead, Default)]
#[repr(C)]
pub(crate) struct Vec3<T: 'static + BinRead<Args = ()> + Pod> {
pub(crate) x: T,
pub(crate) y: T,
pub(crate) z: T,
}
impl<T: 'static + BinRead<Args = ()> + Pod> Vec3<T> {
// TODO const
pub fn new(x: T, y: T, z: T) -> Self { Self { x, y, z } }
}
unsafe impl<T: BinRead<Args = ()> + Pod> Zeroable for Vec3<T> {}
unsafe impl<T: BinRead<Args = ()> + Pod> Pod for Vec3<T> {}
impl<T: BinRead<Args = ()> + Pod> From<T> for Vec3<T> {
fn from(v: T) -> Self { Vec3::<T> { x: v, y: v, z: v } }
}
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Vector3<T>> for Vec3<T> {
fn from(v: cgmath::Vector3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
}
impl<T: BinRead<Args = ()> + Pod> From<Vec3<T>> for cgmath::Vector3<T> {
fn from(v: Vec3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
}
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Point3<T>> for Vec3<T> {
fn from(v: cgmath::Point3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
}
impl<T: BinRead<Args = ()> + Pod> From<Vec3<T>> for cgmath::Point3<T> {
fn from(v: Vec3<T>) -> Self { Self { x: v.x, y: v.y, z: v.z } }
}
// pub(crate) const fn vec3_splat<T: BinRead<Args = ()> + Pod>(v: T) -> Vec3<T> {
// Vec3::<T> { x: v, y: v, z: v }
// }
#[derive(Copy, Clone, Debug, PartialEq, BinRead, Default)]
#[repr(C)]
pub(crate) struct Vec2<T: 'static + BinRead<Args = ()> + Pod> {
pub(crate) x: T,
pub(crate) y: T,
}
impl<T: 'static + BinRead<Args = ()> + Pod> Vec2<T> {
// TODO const
pub fn new(x: T, y: T) -> Self { Self { x, y } }
}
unsafe impl<T: BinRead<Args = ()> + Pod> Zeroable for Vec2<T> {}
unsafe impl<T: BinRead<Args = ()> + Pod> Pod for Vec2<T> {}
impl<T: BinRead<Args = ()> + Pod> From<T> for Vec2<T> {
fn from(v: T) -> Self { Self { x: v, y: v } }
}
impl<T: BinRead<Args = ()> + Pod> From<cgmath::Vector2<T>> for Vec2<T> {
fn from(v: cgmath::Vector2<T>) -> Self { Self { x: v.x, y: v.y } }
}
impl<T: BinRead<Args = ()> + Pod> From<Vec2<T>> for cgmath::Vector2<T> {
fn from(v: Vec2<T>) -> Self { Self { x: v.x, y: v.y } }
}
impl From<Vec2<i16>> for Vec2<f32> {
fn from(v: Vec2<i16>) -> Self { Self { x: v.x as f32 / 32768.0, y: v.y as f32 / 32768.0 } }
}
// pub(crate) const fn vec2_splat<T: BinRead<Args = ()> + Pod>(v: T) -> Vec2<T> {
// Vec2::<T> { x: v, y: v }
// }
#[inline(always)]
pub(crate) fn align<
T: Copy
+ std::ops::Sub<Output = T>
+ std::ops::Add<Output = T>
+ std::ops::Not<Output = T>
+ std::ops::BitAnd<Output = T>
+ num_traits::One
+ num_traits::Zero
+ std::cmp::PartialEq,
>(
n: T,
a: T,
) -> T {
if a == num_traits::Zero::zero() {
return n;
}
(n + (a - num_traits::One::one())) & !(a - num_traits::One::one())
}

View File

@ -15,13 +15,13 @@ constexpr std::array SplashTextures{"TXTR_NintendoLogo"sv, "TXTR_RetroLogo"sv, "
CSplashScreen::CSplashScreen(ESplashScreen which) CSplashScreen::CSplashScreen(ESplashScreen which)
: CIOWin("SplashScreen") : CIOWin("SplashScreen")
, x14_which(which) , x14_which(which)
, m_quad(EFilterType::Blend, g_SimplePool->GetObj(SplashTextures[size_t(which)])) {} , m_texture(g_SimplePool->GetObj(SplashTextures[size_t(which)])) {}
CIOWin::EMessageReturn CSplashScreen::OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue) { CIOWin::EMessageReturn CSplashScreen::OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue) {
switch (msg.GetType()) { switch (msg.GetType()) {
case EArchMsgType::TimerTick: { case EArchMsgType::TimerTick: {
if (!x25_textureLoaded) { if (!x25_textureLoaded) {
if (!m_quad.GetTex().IsLoaded()) if (!m_texture.IsLoaded())
return EMessageReturn::Exit; return EMessageReturn::Exit;
x25_textureLoaded = true; x25_textureLoaded = true;
} }
@ -68,12 +68,20 @@ void CSplashScreen::Draw() {
} }
zeus::CRectangle rect; zeus::CRectangle rect;
rect.size.x() = m_quad.GetTex()->GetWidth() / (480.f * g_Viewport.aspect); rect.size.x() = m_texture->GetWidth() / (480.f * g_Viewport.aspect);
rect.size.y() = m_quad.GetTex()->GetHeight() / 480.f; rect.size.y() = m_texture->GetHeight() / 480.f;
rect.position.x() = 0.5f - rect.size.x() / 2.f; rect.position.x() = 0.5f - rect.size.x() / 2.f;
rect.position.y() = 0.5f - rect.size.y() / 2.f; rect.position.y() = 0.5f - rect.size.y() / 2.f;
m_quad.draw(color, 1.f, rect); aurora::shaders::queue_textured_quad(
aurora::shaders::CameraFilterType::Blend,
m_texture->GetTexture()->ref,
aurora::shaders::ZTest::None,
color,
1.f,
rect,
0.f
);
} }
} // namespace metaforce } // namespace metaforce

View File

@ -19,7 +19,7 @@ private:
// EProgressivePhase x20_progressivePhase = EProgressivePhase::Before; // EProgressivePhase x20_progressivePhase = EProgressivePhase::Before;
// bool x24_progressiveSelection = true; // bool x24_progressiveSelection = true;
bool x25_textureLoaded = false; bool x25_textureLoaded = false;
CTexturedQuadFilterAlpha m_quad; TLockedToken<CTexture> m_texture;
public: public:
explicit CSplashScreen(ESplashScreen); explicit CSplashScreen(ESplashScreen);