Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
fc6fc48
fix: mac renderer start
sp0lsh Oct 13, 2025
3c6d256
fix: launcher, create new project
sp0lsh Oct 13, 2025
64bcc21
fix: template res names
sp0lsh Oct 13, 2025
e4832ac
feat: renderer design
sp0lsh Oct 13, 2025
8ee28c4
docs
sp0lsh Oct 13, 2025
8164662
m1: black window
sp0lsh Oct 13, 2025
455f7ca
feat: m3, render bunch of pills
sp0lsh Oct 13, 2025
73553f1
feat: M3, refactor culling
sp0lsh Oct 13, 2025
212bee5
feat: M3, group draw calls by meterial and mesh
sp0lsh Oct 13, 2025
5bbbc97
feat: M4 bring back the material
sp0lsh Oct 13, 2025
c0b57bc
feat: M4 update time measure
sp0lsh Oct 13, 2025
f943910
fix: build warning
sp0lsh Oct 14, 2025
ffbf786
feat: inlining pipeline
sp0lsh Oct 14, 2025
2d159e5
feat: M4 prebuilt PSO
sp0lsh Oct 14, 2025
9cb7de5
feat: timers
sp0lsh Oct 14, 2025
c36fd74
feat: marix update 15ms -> 0.5ms with SIMD
sp0lsh Oct 14, 2025
fb6de9e
feat: M5 per frame ring buffer to avoid dynamic uniforms per frame al…
sp0lsh Oct 14, 2025
c5a1dfc
feat: mac retina preferred window setup
sp0lsh Oct 14, 2025
8f29d33
feat: stop log spam
sp0lsh Oct 14, 2025
2a3ca06
feat: egui, show resolution
sp0lsh Oct 14, 2025
344b94e
feat: demo project
sp0lsh Oct 14, 2025
0252abb
feat: remove double sorting queue
sp0lsh Oct 14, 2025
b40a988
feat: comments vs rendering talk
sp0lsh Oct 14, 2025
813a29b
feat: instance batching +5-10 fps
sp0lsh Oct 14, 2025
3cca425
feat: add counters
sp0lsh Oct 14, 2025
0241ce5
feat: ignore due to file change each build
sp0lsh Oct 14, 2025
959e969
feat: M6 render to offscreen RT and compose to framebuffer
sp0lsh Oct 14, 2025
eec95f3
feat: M6 HDR, tonnemap
sp0lsh Oct 14, 2025
01832a7
feat: M6 overlay quad pass
sp0lsh Oct 14, 2025
41f547f
feat: M6 overlay bookkeeping
sp0lsh Oct 14, 2025
1f274af
feat: M6 texture overlay
sp0lsh Oct 14, 2025
f6b9d8c
feat: M6 depth overlay
sp0lsh Oct 14, 2025
4cec7b8
feat: align bind groups with talk
sp0lsh Oct 14, 2025
9c93c97
docs
sp0lsh Oct 14, 2025
c5760d0
draft renderer pass API
sp0lsh Oct 14, 2025
d9ea8be
feat: add renderer system init
sp0lsh Oct 15, 2025
e20962c
remove State logic nesting
sp0lsh Oct 16, 2025
177b8cb
feat: renderer factory method, pipeline v2
sp0lsh Oct 17, 2025
59f54e4
feat: pass interface
sp0lsh Oct 18, 2025
c0b9955
fixme: mutability workadround until happy with passes
sp0lsh Oct 18, 2025
e4ab060
feat: pass example
sp0lsh Oct 18, 2025
d12dfc7
feat: simplify pass with no resource storage
sp0lsh Oct 18, 2025
557dcad
feat: pass overlay logo
sp0lsh Oct 18, 2025
ef4998c
feat: pass overlay depth
sp0lsh Oct 18, 2025
676cd3d
feat: move compose to a pass
sp0lsh Oct 19, 2025
24a8cd5
fix: remove model matrix, per draw 144 -> 80 bytes
sp0lsh Oct 19, 2025
325566f
feat: preallocate render() containers
sp0lsh Oct 20, 2025
4a9568d
feat: use glam with SIMD for culling
sp0lsh Oct 20, 2025
e4076b1
feat: preallocate system dirty entities
sp0lsh Oct 20, 2025
12d3c78
feat: resource pools with minimal handles and vector lookup
sp0lsh Nov 2, 2025
4a619f5
wip: pass scene
sp0lsh Nov 2, 2025
32e389d
wip: pass scene, local camera
sp0lsh Nov 2, 2025
300de4e
wip: pass scene, pipeline
sp0lsh Nov 3, 2025
f2eaae8
wip: pass scene, materialfull
sp0lsh Nov 10, 2025
efdc248
feat: pbr basic
sp0lsh Nov 10, 2025
394922d
feat: gltf basic (.glb)
sp0lsh Nov 10, 2025
1eb2392
feat: gltf model sponza
sp0lsh Nov 10, 2025
20acb09
move passes creation to engine
sp0lsh Nov 15, 2025
40bb2f3
consolidated renderer in engine with wgpu rhi
sp0lsh Nov 15, 2025
4a5e8b8
fix path
sp0lsh Nov 16, 2025
be47968
cleanup resource manager
sp0lsh Nov 16, 2025
51a6a77
cleanup resource manager
sp0lsh Nov 16, 2025
2dd2e91
feat: PassEgui
sp0lsh Nov 16, 2025
8c54158
cleanup warnings
sp0lsh Nov 16, 2025
594c6a3
feat: ibl, TODO: cleanup
sp0lsh Nov 17, 2025
c1d7636
feat: linear depth
sp0lsh Nov 17, 2025
b2aa66f
feat: depth of field pass, fixed depth projection
sp0lsh Dec 14, 2025
4500973
feat: velocity buffer + per object motion blur
sp0lsh Dec 14, 2025
b324f33
feat: lastest demo
sp0lsh Dec 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ build/

crates/pill_standalone/Cargo.toml
examples/*/Cargo.toml

engine/Cargo.toml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know why you are doing that, though this is probably not the best solution 😓

The launcher will constantly update the paths in engine/Cargo and show this file as changed. Honestly did not find a good solution for that except calling git add engine/pill_* when staging files.

16 changes: 16 additions & 0 deletions engine/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[build]
# Enable SIMD optimizations for math operations
# This will auto-vectorize cgmath operations when possible

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase on glam - we probably get auto-vectorization from the get-go

rustflags = ["-C", "target-cpu=native"]

# Alternative: More conservative SIMD flags
# rustflags = ["-C", "target-feature=+avx2,+fma"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sometimes we might want avx512 etc


[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-cpu=native"]

[target.x86_64-apple-darwin]
rustflags = ["-C", "target-cpu=native"]

[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-cpu=native"]
12 changes: 10 additions & 2 deletions engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ resolver = "2"
members = [
"pill_core",
"pill_engine",
"pill_renderer",
"pill_standalone",
"NO_PATH", ### Game project crate (This will be changed by Pill Launcher on build to allow proper compilation of game project)
"/Users/mk/dev/demo/pill_demo", ### Game project crate (This will be changed by Pill Launcher on build to allow proper compilation of game project)
]

exclude = [
"pill_launcher",
]

[profile.dev]
opt-level = 3
incremental = true

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
116 changes: 116 additions & 0 deletions engine/pill_core/src/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;

#[repr(transparent)]
pub struct Handle<T> {
raw: u64,
_marker: PhantomData<T>,
}

impl<T> Handle<T> {
#[inline]
pub const fn from_parts(index: u32, generation: u32) -> Self {
Self {
raw: ((generation as u64) << 32) | (index as u64),
_marker: PhantomData,
}
}

#[inline]
pub const fn index(&self) -> u32 { self.raw as u32 }

#[inline]
pub const fn generation(&self) -> u32 { (self.raw >> 32) as u32 }

pub const INVALID: Self = Self { raw: u64::MAX, _marker: PhantomData };

#[inline]
pub const fn is_valid(&self) -> bool { self.raw != u64::MAX }
}

impl<T> Copy for Handle<T> {}
impl<T> Clone for Handle<T> { fn clone(&self) -> Self { *self } }
impl<T> PartialEq for Handle<T> { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } }
impl<T> Eq for Handle<T> {}
impl<T> Hash for Handle<T> { fn hash<H: Hasher>(&self, state: &mut H) { self.raw.hash(state) } }
impl<T> fmt::Debug for Handle<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({:#x})", self.raw) } }

struct Entry<V> {
generation: u32,
resource: Option<V>,
}

pub struct ResourcePool<K, V> {
entries: Vec<Entry<V>>,
free_list: Vec<u32>,
_marker: PhantomData<K>,
}

impl<K, V> ResourcePool<K, V> {
pub fn new() -> Self {
Self {
entries: Vec::new(),
free_list: Vec::new(),
_marker: PhantomData,
}
}

#[inline]
pub fn insert(&mut self, resource: V) -> Handle<K> {
if let Some(idx) = self.free_list.pop() {
let e = &mut self.entries[idx as usize];
e.generation = e.generation.wrapping_add(1);
e.resource = Some(resource);
Handle::from_parts(idx, e.generation)
} else {
let idx = self.entries.len() as u32;
self.entries.push(Entry { generation: 0, resource: Some(resource) });
Handle::from_parts(idx, 0)
}
}

#[inline]
pub fn remove(&mut self, h: Handle<K>) -> Option<V> {
let idx = h.index() as usize;
if idx >= self.entries.len() {
return None;
}
let e = &mut self.entries[idx];
if e.generation != h.generation() {
return None;
}
let res = e.resource.take();
e.generation = e.generation.wrapping_add(1);
self.free_list.push(h.index());
res
}

#[inline]
pub fn get(&self, h: Handle<K>) -> Option<&V> {
let idx = h.index() as usize;
if idx >= self.entries.len() {
return None;
}
let e = &self.entries[idx];
if e.generation != h.generation() {
return None;
}
e.resource.as_ref()
}

#[inline]
pub fn get_mut(&mut self, h: Handle<K>) -> Option<&mut V> {
let idx = h.index() as usize;
if idx >= self.entries.len() {
return None;
}
let e = &mut self.entries[idx];
if e.generation != h.generation() {
return None;
}
e.resource.as_mut()
}
}


17 changes: 17 additions & 0 deletions engine/pill_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ mod pill_twinmap;
mod pill_typemap;
mod bitmask_utils;
mod timer;
mod handle;
mod tags;

// --- Use ---

Expand Down Expand Up @@ -62,4 +64,19 @@ pub use utils::{
pub use timer::{
Timer,
TimerRecord
};

pub use handle::{
Handle,
ResourcePool,
};

pub use tags::{
RendererBufferTag,
RendererCameraTag,
RendererMaterialTag,
RendererMeshTag,
RendererPipelineTag,
RendererPipelineV2Tag,
RendererTextureTag,
};
47 changes: 30 additions & 17 deletions engine/pill_core/src/pill_slotmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

// 3. This notice may not be removed or altered from any source distribution.


// --- PillSlotMap
// This is slotmap crate modified by changing names of types, adding public key variables, removing iterators and adding possibility to restrict version limit
// Original crate: https://crates.io/crates/SlotMap
Expand All @@ -30,14 +29,14 @@

//! Contains the slot map implementation.
use core::fmt;
use core::fmt::Debug;
use core::marker::PhantomData;
#[allow(unused_imports)] // MaybeUninit is only used on nightly at the moment.
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::{Index, IndexMut};
use std::fmt::Formatter;
use core::fmt::Debug;
use std::iter::Enumerate;
use std::num::{ NonZeroU32};
use std::num::NonZeroU32;

// Storage inside a slot or metadata for the freelist when vacant.
union SlotUnion<T> {
Expand Down Expand Up @@ -73,7 +72,7 @@ impl<T> Slot<T> {
self.version % 2 > 0
}

pub fn get(&self) -> SlotContent<T> {
pub fn get(&self) -> SlotContent<'_, T> {
unsafe {
if self.occupied() {
Occupied(&*self.u.value)
Expand All @@ -83,7 +82,7 @@ impl<T> Slot<T> {
}
}

pub fn get_mut(&mut self) -> SlotContentMut<T> {
pub fn get_mut(&mut self) -> SlotContentMut<'_, T> {
unsafe {
if self.occupied() {
OccupiedMut(&mut *self.u.value)
Expand Down Expand Up @@ -142,7 +141,6 @@ pub struct PillSlotMap<K: PillSlotMapKey, V> {
}

impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {

pub fn with_key() -> Self {
Self::with_capacity_and_key(0)
}
Expand All @@ -152,11 +150,14 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
}

// Warning: Version limit has to be odd value
pub fn with_capacity_and_key_and_version_limit(capacity: usize, version_limit: u32) -> Result<Self, ()> {
pub fn with_capacity_and_key_and_version_limit(
capacity: usize,
version_limit: u32,
) -> Result<Self, ()> {
if version_limit % 2 == 0 {
return Err(())
return Err(());
}

let mut slots = Vec::with_capacity(capacity + 1);
slots.push(Slot {
u: SlotUnion { next_free: 0 },
Expand All @@ -170,7 +171,7 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
version_limit,
_k: PhantomData,
};

Ok(slotmap)
}

Expand Down Expand Up @@ -201,7 +202,6 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
}

pub fn get_next_free_slot_handle(&self) -> K {

let new_num_elems = self.num_elems + 1;
if new_num_elems == core::u32::MAX {
panic!("PillSlotMap number of elements overflow");
Expand Down Expand Up @@ -290,8 +290,11 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
self.free_head = index as u32;
self.num_elems -= 1;


slot.version = if slot.version >= self.version_limit { 0 } else { slot.version + 1 };
slot.version = if slot.version >= self.version_limit {
0
} else {
slot.version + 1
};

value
}
Expand Down Expand Up @@ -366,7 +369,7 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
.value
}

pub fn iter(&self) -> Iter<K, V> {
pub fn iter(&self) -> Iter<'_, K, V> {
let mut it = self.slots.iter().enumerate();
it.next(); // Skip sentinel.
Iter {
Expand All @@ -376,7 +379,7 @@ impl<K: PillSlotMapKey, V> PillSlotMap<K, V> {
}
}

pub fn iter_mut(&mut self) -> IterMut<K, V> {
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
let len = self.len();
let mut it = self.slots.iter_mut().enumerate();
it.next(); // Skip sentinel.
Expand Down Expand Up @@ -528,8 +531,18 @@ impl Default for PillSlotMapKeyData {
}
}

pub unsafe trait PillSlotMapKey:
From<PillSlotMapKeyData> + Copy + Clone + Default + Eq + PartialEq + Ord + PartialOrd + core::hash::Hash + core::fmt::Debug {
pub unsafe trait PillSlotMapKey:
From<PillSlotMapKeyData>
+ Copy
+ Clone
+ Default
+ Eq
+ PartialEq
+ Ord
+ PartialOrd
+ core::hash::Hash
+ core::fmt::Debug
{
fn null() -> Self {
PillSlotMapKeyData::null().into()
}
Expand Down
Loading