diff --git a/Cargo.lock b/Cargo.lock index e85542f..01ac62b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.7.6" @@ -49,6 +61,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + [[package]] name = "bitflags" version = "1.3.2" @@ -168,6 +186,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "copyless" version = "0.1.5" @@ -256,6 +280,59 @@ dependencies = [ "objc", ] +[[package]] +name = "crc32fast" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + [[package]] name = "cty" version = "0.2.2" @@ -268,6 +345,7 @@ version = "0.1.0" dependencies = [ "bytemuck", "glam", + "image", "pollster", "slab", "tobj", @@ -321,6 +399,24 @@ dependencies = [ "syn", ] +[[package]] +name = "deflate" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f95bf05dffba6e6cce8dfbb30def788154949ccd9aed761b472119c21e01c70" +dependencies = [ + "adler32", +] + +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -342,6 +438,117 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "exr" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4badb9489a465cb2c555af1f00f0bfd8cecd6fc12ac11da9d5b40c5dd5f0200" +dependencies = [ + "bit_field", + "deflate 1.0.0", + "flume", + "half", + "inflate", + "lebe", + "smallvec", + "threadpool", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d04dafd11240188e146b6f6476a898004cace3be31d4ec5e08e216bf4947ac0" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -363,6 +570,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + [[package]] name = "fxhash" version = "0.2.1" @@ -379,8 +598,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -440,6 +671,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.11.2" @@ -449,6 +686,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hexf-parse" version = "0.2.1" @@ -461,6 +707,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94ac3d41f882c624a82d7945952032388488681f45f9d4077999a6c85688d61" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder 0.2.1", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.8.0" @@ -471,6 +737,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "inplace_it" version = "0.3.3" @@ -495,6 +770,21 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" + +[[package]] +name = "jpeg-decoder" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcf0244f6597be39ab8d9203f574cafb529ae8c698afa2182f7b3c3205a4a9c" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.56" @@ -520,6 +810,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" + [[package]] name = "libc" version = "0.2.116" @@ -607,6 +903,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" version = "0.8.0" @@ -647,6 +953,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "nanorand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958" +dependencies = [ + "getrandom", +] + [[package]] name = "ndk" version = "0.5.0" @@ -726,6 +1041,38 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -735,6 +1082,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.6" @@ -812,12 +1169,45 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pkg-config" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "png" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c845088517daa61e8a57eee40309347cea13f273694d1385c553e7a57127763b" +dependencies = [ + "bitflags", + "crc32fast", + "deflate 0.9.1", + "encoding", + "miniz_oxide", +] + [[package]] name = "pollster" version = "0.2.5" @@ -873,6 +1263,31 @@ dependencies = [ "cty", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -900,6 +1315,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -952,6 +1373,15 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -1008,6 +1438,26 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0247608e998cb6ce39dfc8f4a16c50361ce71e5b52e6d24ea1227ea8ea8ee0b2" +dependencies = [ + "flate2", + "jpeg-decoder 0.1.22", + "weezl", +] + [[package]] name = "tobj" version = "3.2.0" @@ -1199,6 +1649,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + [[package]] name = "wgpu" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index d95325a..85e15f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] bytemuck = { version="1.7", features=["derive"] } glam = "0.20" +image = "0.24" pollster = "0.2" slab = "0.4" tobj = "3.0" diff --git a/src/commands.rs b/src/commands.rs index e9f563c..c378805 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,53 +1,66 @@ -use super::{MeshInstance, MeshPool}; +use super::{MeshHandle, MeshPool, TextureHandle, TexturePool}; use std::collections::HashMap; +#[derive(Copy, Clone, PartialEq)] +pub struct MeshInstance { + pub mesh: MeshHandle, + pub albedo: TextureHandle, + pub transform: glam::Mat4, +} + pub struct MeshCommands { transforms: Vec<[f32; 16]>, - transform_ranges: Vec<(usize, std::ops::Range)>, + draws: Vec<(usize, usize, std::ops::Range)>, } impl MeshCommands { pub fn build(instances: &Vec) -> Self { - let mut sorted_meshes = HashMap::>::new(); - for mesh in instances.iter() { - let group_id = mesh.handle.group_id; - if let Some(by_group) = sorted_meshes.get_mut(&group_id) { - by_group.push(*mesh); + let mut sorted_meshes = HashMap::<(usize, usize), Vec>::new(); + for instance in instances.iter() { + let group_id = instance.mesh.group_id; + let texture_id = instance.albedo.id; + let key = (group_id, texture_id); + if let Some(by_group) = sorted_meshes.get_mut(&key) { + by_group.push(*instance); } else { - sorted_meshes.insert(group_id, vec![*mesh]); + sorted_meshes.insert(key, vec![*instance]); } } let mut transforms = Vec::new(); - let mut transform_ranges = Vec::new(); + let mut draws = Vec::new(); // this code assumes MeshHandle only uses group_id (which it does for now) // TODO bucket by sub_id too before MeshHandle supports it - for (group_id, meshes) in sorted_meshes.iter() { + for ((group_id, texture_id), meshes) in sorted_meshes.iter() { let start_idx = transforms.len() as u32; let as_arrays = meshes.iter().map(|i| i.transform.to_cols_array()); transforms.extend(as_arrays); let end_idx = transforms.len() as u32; - transform_ranges.push((*group_id, start_idx..end_idx)); + draws.push((*group_id, *texture_id, start_idx..end_idx)); } - Self { - transforms, - transform_ranges, - } + Self { transforms, draws } } pub fn get_storage(&self) -> &[u8] { bytemuck::cast_slice(&self.transforms) } - pub fn dispatch<'a>(&self, rp: &mut wgpu::RenderPass<'a>, mesh_pool: &'a MeshPool) { + pub fn dispatch<'a>( + &self, + rp: &mut wgpu::RenderPass<'a>, + mesh_pool: &'a MeshPool, + texture_pool: &'a TexturePool, + ) { // TODO one group per mesh, still... // TODO this could be implemented without accessing private members - for (group_id, meshes_range) in self.transform_ranges.iter() { + for (group_id, texture_id, meshes_range) in self.draws.iter() { let group = mesh_pool.groups.get(*group_id).unwrap(); + let texture = texture_pool.textures.get(*texture_id).unwrap(); rp.set_vertex_buffer(0, group.vertices.slice(..)); rp.set_index_buffer(group.indices.slice(..), wgpu::IndexFormat::Uint32); + rp.set_bind_group(2, &texture.bind_group, &[]); let indices = 0..(group.index_capacity as u32); rp.draw_indexed(indices, 0, meshes_range.to_owned()); } diff --git a/src/main.rs b/src/main.rs index fd8ae4d..b7ab1e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,13 @@ mod commands; mod mesh; use camera::*; +use commands::*; use mesh::*; struct Renderer { pub device: wgpu::Device, pub mesh_pool: MeshPool, + pub texture_pool: TexturePool, pub size: winit::dpi::PhysicalSize, surface: wgpu::Surface, queue: wgpu::Queue, @@ -66,6 +68,7 @@ impl Renderer { surface.configure(&device, &config); let mesh_pool = MeshPool::default(); + let texture_pool = TexturePool::new(&device); let camera_uniform = CameraUniform::new(); @@ -133,7 +136,11 @@ impl Renderer { let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), - bind_group_layouts: &[&camera_bind_group_layout, &meshes_bind_group_layout], + bind_group_layouts: &[ + &camera_bind_group_layout, + &meshes_bind_group_layout, + &texture_pool.bind_group_layout, + ], push_constant_ranges: &[], }); @@ -181,6 +188,7 @@ impl Renderer { queue, config, mesh_pool, + texture_pool, camera_uniform, camera_buffer, camera_bind_group, @@ -249,7 +257,7 @@ impl Renderer { rp.set_pipeline(&self.render_pipeline); rp.set_bind_group(0, &self.camera_bind_group, &[]); rp.set_bind_group(1, &self.meshes_bind_group, &[]); - mesh_commands.dispatch(&mut rp, &self.mesh_pool); + mesh_commands.dispatch(&mut rp, &self.mesh_pool, &self.texture_pool); } self.queue.submit(std::iter::once(encoder.finish())); @@ -309,6 +317,141 @@ impl MeshPool { } } +pub struct TextureData { + width: u32, + height: u32, + data: Vec, +} + +pub struct Texture { + texture: wgpu::Texture, + bind_group: wgpu::BindGroup, +} + +impl Texture { + pub fn new( + device: &wgpu::Device, + queue: &wgpu::Queue, + sampler: &wgpu::Sampler, + bind_group_layout: &wgpu::BindGroupLayout, + data: &TextureData, + ) -> Self { + let size = wgpu::Extent3d { + width: data.width, + height: data.height, + depth_or_array_layers: 1, + }; + + let texture = device.create_texture(&wgpu::TextureDescriptor { + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: None, + }); + + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + + queue.write_texture( + wgpu::ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &data.data, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: std::num::NonZeroU32::new(4 * size.width), + rows_per_image: std::num::NonZeroU32::new(size.height), + }, + size, + ); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sampler), + }, + ], + label: None, + }); + + Texture { + texture, + bind_group, + } + } +} + +pub struct TexturePool { + textures: slab::Slab, + sampler: wgpu::Sampler, + bind_group_layout: wgpu::BindGroupLayout, +} + +impl TexturePool { + pub fn new(device: &wgpu::Device) -> Self { + let textures = Default::default(); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("Texture Bind Group Layout"), + }); + + Self { + textures, + sampler, + bind_group_layout, + } + } + + pub fn allocate( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + data: &TextureData, + ) -> TextureHandle { + let texture = Texture::new(device, queue, &self.sampler, &self.bind_group_layout, data); + let id = self.textures.insert(texture); + TextureHandle { id } + } +} + #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct CameraUniform { @@ -335,13 +478,14 @@ pub struct MeshHandle { sub_id: usize, } -#[derive(Copy, Clone, PartialEq)] -pub struct MeshInstance { - pub handle: MeshHandle, - pub transform: glam::Mat4, +#[repr(C)] +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub struct TextureHandle { + // only flat texture ID is used... for now + id: usize, } -fn load_model() -> MeshData { +fn load_model() -> (MeshData, TextureData) { use tobj::*; let model_data = include_bytes!("viking_room.obj").to_vec(); @@ -357,27 +501,44 @@ fn load_model() -> MeshData { let mut vertices = Vec::new(); let mut indices = Vec::new(); - for m in models { - let index_base = vertices.len() as u32; - for i in 0..m.mesh.positions.len() / 3 { - let t = i * 3; - vertices.push(Vertex { - position: [ - m.mesh.positions[t], - m.mesh.positions[t + 2], - -m.mesh.positions[t + 1], - ], - tex_coords: [ - m.mesh.texcoords[i * 2], - m.mesh.texcoords[i * 2 + 1], - ], - }); - } - indices.extend(m.mesh.indices.iter().map(|i| i + index_base)); + let m = models.first().unwrap(); + let index_base = vertices.len() as u32; + for i in 0..m.mesh.positions.len() / 3 { + let t = i * 3; + vertices.push(Vertex { + position: [ + m.mesh.positions[t], + m.mesh.positions[t + 2], + -m.mesh.positions[t + 1], + ], + tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], + }); } - MeshData { vertices, indices } + indices.extend(m.mesh.indices.iter().map(|i| i + index_base)); + + let albedo_data = include_bytes!("viking_room.png"); + let albedo = image::load_from_memory(albedo_data).unwrap(); + + use image::GenericImageView; + let dimensions = albedo.dimensions(); + + let albedo_rgb = albedo.as_rgb8().unwrap().to_vec(); + let mut albedo_rgba = Vec::::new(); + for rgb in albedo_rgb.chunks(3) { + albedo_rgba.extend_from_slice(rgb); + albedo_rgba.push(0xff); + } + + ( + MeshData { vertices, indices }, + TextureData { + width: dimensions.0, + height: dimensions.1, + data: albedo_rgba, + }, + ) } trait WorldState { @@ -391,15 +552,19 @@ struct Grid { impl Grid { fn new(ren: &mut Renderer) -> Self { - let mesh_data = load_model(); + let (mesh_data, albedo_data) = load_model(); let mesh = ren.mesh_pool.allocate(&ren.device, &mesh_data); + let albedo = ren + .texture_pool + .allocate(&ren.device, &ren.queue, &albedo_data); let mut meshes = Vec::new(); for x in -5..5 { for y in -5..5 { let translation = glam::Vec3::new(x as f32, 0.0, y as f32) * 3.0; let transform = glam::Mat4::from_translation(translation); meshes.push(MeshInstance { - handle: mesh, + mesh, + albedo, transform, }); } @@ -428,13 +593,17 @@ struct Planets { start: std::time::Instant, planets: Vec, mesh: MeshHandle, + albedo: TextureHandle, } impl Planets { fn new(ren: &mut Renderer) -> Self { let start = std::time::Instant::now(); - let mesh_data = load_model(); + let (mesh_data, albedo_data) = load_model(); let mesh = ren.mesh_pool.allocate(&ren.device, &mesh_data); + let albedo = ren + .texture_pool + .allocate(&ren.device, &ren.queue, &albedo_data); let mut planets = Vec::new(); for i in 0..10 { @@ -451,6 +620,7 @@ impl Planets { start, planets, mesh, + albedo, } } } @@ -468,7 +638,8 @@ impl WorldState for Planets { let theta = planet.speed * elapsed + planet.offset; let rotation = glam::Mat4::from_rotation_y(theta); meshes.push(MeshInstance { - handle: self.mesh, + mesh: self.mesh, + albedo: self.albedo, transform: rotation * translation, }); } diff --git a/src/shader.wgsl b/src/shader.wgsl index 95a0cc3..3507734 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -17,7 +17,7 @@ struct VertexInput { struct VertexOutput { [[builtin(position)]] clip_position: vec4; - [[location(0)]] color: vec3; + [[location(0)]] tex_coords: vec2; }; [[group(0), binding(0)]] @@ -26,6 +26,9 @@ var camera: CameraUniform; [[group(1), binding(0)]] var meshes: MeshData; +[[group(2), binding(0)]] var t_albedo: texture_2d; +[[group(2), binding(1)]] var s_albedo: sampler; + [[stage(vertex)]] fn vs_main( [[builtin(instance_index)]] mesh_idx: u32, @@ -34,7 +37,7 @@ fn vs_main( let transform = meshes.instances[mesh_idx].transform; var out: VertexOutput; out.clip_position = camera.vp * transform * vec4(vertex.position, 1.0); - out.color = vec3(vertex.tex_coords, 0.0); + out.tex_coords = vertex.tex_coords; return out; } @@ -42,5 +45,5 @@ fn vs_main( fn fs_main( frag: VertexOutput, ) -> [[location(0)]] vec4 { - return vec4(frag.color, 1.0); + return textureSample(t_albedo, s_albedo, frag.tex_coords); } diff --git a/src/viking_room.png b/src/viking_room.png new file mode 100644 index 0000000..6a879ea Binary files /dev/null and b/src/viking_room.png differ