Skip to content

Commit bd05c9e

Browse files
committed
Add smallvec and use it to try and reduce indirection; Add indirection to avoid exploding my stack in debug; Add more tracing
1 parent bba276b commit bd05c9e

File tree

9 files changed

+91
-72
lines changed

9 files changed

+91
-72
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ glutin = "0.32"
2727
glutin-winit = "0.5"
2828
raw-window-handle = "0.6"
2929

30+
smallvec = "1.13.2"
31+
3032
common = { git = "https://github.com/manpat/common-rs.git", version = ">=0.5.0", features=["interop", "serde"] }
3133
toy = { git = "https://github.com/manpat/toy-rs.git" }
3234

toybox-gfx/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ anyhow.workspace = true
99
common.workspace = true
1010
log.workspace = true
1111
tracing.workspace = true
12+
smallvec.workspace = true
1213

1314
toybox-host.workspace = true
1415
toybox-vfs.workspace = true

toybox-gfx/src/bindings.rs

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ pub struct ImageBindDesc {
5656
#[derive(Debug, Default)]
5757
pub struct BindingDescription {
5858
// TODO(pat.m): store unresolved named targets separately to resolved/explicit targets to simplify usage
59-
pub buffer_bindings: Vec<BufferBindDesc>,
60-
pub image_bindings: Vec<ImageBindDesc>,
59+
pub buffer_bindings: SmallVec<[BufferBindDesc; 4]>,
60+
pub image_bindings: SmallVec<[ImageBindDesc; 4]>,
6161

6262
pub framebuffer: Option<FramebufferArgument>,
6363
}
@@ -171,79 +171,92 @@ impl BindingDescription {
171171
pub fn bind(&self, core: &mut Core, resource_manager: &mut ResourceManager) {
172172
let mut barrier_tracker = core.barrier_tracker();
173173

174-
for BufferBindDesc{target, source} in self.buffer_bindings.iter() {
175-
let BufferArgument::Name{name, range} = *source
176-
else { panic!("Unresolved buffer bind source") };
174+
{
175+
let _span = tracing::info_span!("bind buffers").entered();
177176

178-
let Some((index, indexed_target)) = target.to_raw_index().zip(target.to_indexed_buffer_target())
179-
else { panic!("Unresolved buffer target") };
177+
for BufferBindDesc{target, source} in self.buffer_bindings.iter() {
178+
let BufferArgument::Name{name, range} = *source
179+
else { panic!("Unresolved buffer bind source") };
180180

181-
match indexed_target {
182-
// TODO(pat.m): this is pessimistic - but we need shader reflection to guarantee that an ssbo is bound
183-
// as readonly.
184-
IndexedBufferTarget::ShaderStorage => barrier_tracker.write_buffer(name, gl::SHADER_STORAGE_BARRIER_BIT),
185-
IndexedBufferTarget::Uniform => barrier_tracker.read_buffer(name, gl::UNIFORM_BARRIER_BIT),
186-
}
181+
let Some((index, indexed_target)) = target.to_raw_index().zip(target.to_indexed_buffer_target())
182+
else { panic!("Unresolved buffer target") };
183+
184+
match indexed_target {
185+
// TODO(pat.m): this is pessimistic - but we need shader reflection to guarantee that an ssbo is bound
186+
// as readonly.
187+
IndexedBufferTarget::ShaderStorage => barrier_tracker.write_buffer(name, gl::SHADER_STORAGE_BARRIER_BIT),
188+
IndexedBufferTarget::Uniform => barrier_tracker.read_buffer(name, gl::UNIFORM_BARRIER_BIT),
189+
}
187190

188-
core.bind_indexed_buffer(indexed_target, index, name, range);
191+
core.bind_indexed_buffer(indexed_target, index, name, range);
192+
}
189193
}
190194

191-
for ImageBindDesc{target, source, sampler} in self.image_bindings.iter() {
192-
let ImageArgument::Name(image_name) = *source
193-
else { panic!("Unresolved image bind source") };
195+
{
196+
let _span = tracing::info_span!("bind images").entered();
194197

195-
match *target {
196-
ImageBindTarget::Sampled(unit) => {
197-
barrier_tracker.read_image(image_name, gl::TEXTURE_FETCH_BARRIER_BIT);
198+
for ImageBindDesc{target, source, sampler} in self.image_bindings.iter() {
199+
let ImageArgument::Name(image_name) = *source
200+
else { panic!("Unresolved image bind source") };
198201

199-
// TODO(pat.m): use default instead of panicking
200-
let sampler_name = match sampler.expect("Sampled bind target missing sampler") {
201-
SamplerArgument::Name(name) => name,
202-
SamplerArgument::Common(sampler) => resource_manager.get_common_sampler(sampler),
203-
};
202+
match *target {
203+
ImageBindTarget::Sampled(unit) => {
204+
barrier_tracker.read_image(image_name, gl::TEXTURE_FETCH_BARRIER_BIT);
204205

205-
core.bind_sampler(unit, sampler_name);
206-
core.bind_sampled_image(unit, image_name);
207-
}
206+
// TODO(pat.m): use default instead of panicking
207+
let sampler_name = match sampler.expect("Sampled bind target missing sampler") {
208+
SamplerArgument::Name(name) => name,
209+
SamplerArgument::Common(sampler) => resource_manager.get_common_sampler(sampler),
210+
};
208211

209-
ImageBindTarget::ReadonlyImage(unit) => {
210-
barrier_tracker.read_image(image_name, gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
211-
core.bind_image(unit, image_name);
212-
}
212+
core.bind_sampler(unit, sampler_name);
213+
core.bind_sampled_image(unit, image_name);
214+
}
213215

214-
ImageBindTarget::ReadWriteImage(unit) => {
215-
barrier_tracker.write_image(image_name, gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
216-
core.bind_image_rw(unit, image_name);
217-
}
216+
ImageBindTarget::ReadonlyImage(unit) => {
217+
barrier_tracker.read_image(image_name, gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
218+
core.bind_image(unit, image_name);
219+
}
220+
221+
ImageBindTarget::ReadWriteImage(unit) => {
222+
barrier_tracker.write_image(image_name, gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
223+
core.bind_image_rw(unit, image_name);
224+
}
218225

219-
_ => panic!("Unresolved image bind target"),
226+
_ => panic!("Unresolved image bind target"),
227+
}
220228
}
221229
}
222230

223-
// TODO(pat.m): the following should only be done for the Draw command really.
224-
// it doesn't make sense to bind or emit barriers for e.g., Compute.
225-
226-
// Framebuffer should _ALWAYS_ be defined by this point.
227-
// The global BindingDescription should specify Default
228-
let framebuffer = self.framebuffer.as_ref()
229-
.expect("Unresolved framebuffer")
230-
.resolve_name(core, resource_manager);
231-
232-
if let Some(framebuffer_name) = framebuffer {
233-
let framebuffer_info = core.get_framebuffer_info(framebuffer_name);
234-
for attachment_image in framebuffer_info.attachments.values() {
235-
// NOTE: only a read barrier since framebuffer writes are implicitly synchronised with later draw calls.
236-
// We only need to make sure that if an image is _modified_ that a barrier is inserted before rendering to it.
237-
barrier_tracker.read_image(*attachment_image, gl::FRAMEBUFFER_BARRIER_BIT);
231+
232+
{
233+
let _span = tracing::info_span!("bind framebuffer").entered();
234+
235+
// TODO(pat.m): the following should only be done for the Draw command really.
236+
// it doesn't make sense to bind or emit barriers for e.g., Compute.
237+
238+
// Framebuffer should _ALWAYS_ be defined by this point.
239+
// The global BindingDescription should specify Default
240+
let framebuffer = self.framebuffer.as_ref()
241+
.expect("Unresolved framebuffer")
242+
.resolve_name(core, resource_manager);
243+
244+
if let Some(framebuffer_name) = framebuffer {
245+
let framebuffer_info = core.get_framebuffer_info(framebuffer_name);
246+
for attachment_image in framebuffer_info.attachments.values() {
247+
// NOTE: only a read barrier since framebuffer writes are implicitly synchronised with later draw calls.
248+
// We only need to make sure that if an image is _modified_ that a barrier is inserted before rendering to it.
249+
barrier_tracker.read_image(*attachment_image, gl::FRAMEBUFFER_BARRIER_BIT);
250+
}
251+
252+
let framebuffer_size = core.get_framebuffer_size(framebuffer_name);
253+
core.set_viewport(framebuffer_size);
254+
} else {
255+
core.set_viewport(core.backbuffer_size());
238256
}
239257

240-
let framebuffer_size = core.get_framebuffer_size(framebuffer_name);
241-
core.set_viewport(framebuffer_size);
242-
} else {
243-
core.set_viewport(core.backbuffer_size());
258+
core.bind_framebuffer(framebuffer);
244259
}
245-
246-
core.bind_framebuffer(framebuffer);
247260
}
248261
}
249262

toybox-gfx/src/command_group.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::prelude::*;
12
use crate::bindings::*;
23
use crate::command::{Command, compute, draw};
34
use crate::resource_manager::{ShaderHandle, arguments::*};
@@ -33,7 +34,7 @@ pub enum FrameStage {
3334
pub struct CommandGroup {
3435
pub stage: FrameStage,
3536

36-
pub commands: Vec<Command>,
37+
pub commands: SmallVec<[Command; 16]>,
3738

3839
pub shared_bindings: BindingDescription,
3940
}
@@ -42,7 +43,7 @@ impl CommandGroup {
4243
pub(crate) fn new(stage: FrameStage) -> CommandGroup {
4344
CommandGroup {
4445
stage,
45-
commands: Vec::new(),
46+
commands: SmallVec::new(),
4647
shared_bindings: BindingDescription::new(),
4748
}
4849
}

toybox-gfx/src/core/image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub enum ImageType {
3333
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3434
pub(in crate::core) struct ImageInfoInternal {
3535
info: ImageInfo,
36-
views: Vec<(ImageFormat, u32)>,
36+
views: SmallVec<[(ImageFormat, u32); 2]>,
3737
}
3838

3939
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

toybox-gfx/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub mod prelude {
2424
pub use crate::host::gl;
2525
pub use crate::{ResourceName, BufferRangeExt};
2626

27+
pub use smallvec::SmallVec;
28+
2729
pub use toybox_vfs as vfs;
2830
pub use common::math::*;
2931
}
@@ -49,7 +51,7 @@ impl System {
4951

5052
impl System {
5153
#[instrument(skip_all, name="gfxsys System::new")]
52-
pub fn new(mut core: core::Core) -> anyhow::Result<System> {
54+
pub fn new(mut core: core::Core) -> anyhow::Result<Box<System>> {
5355
core.register_debug_hook();
5456

5557
let resource_manager = resource_manager::ResourceManager::new(&mut core)?;
@@ -63,11 +65,11 @@ impl System {
6365
core.gl.Enable(gl::FRAMEBUFFER_SRGB);
6466
}
6567

66-
Ok(System {
68+
Ok(Box::new(System {
6769
core,
6870
resource_manager,
6971
frame_encoder,
70-
})
72+
}))
7173
}
7274

7375
pub fn resize(&mut self, new_size: common::Vec2i) {

toybox-host/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub type GlContext = glutin::context::PossiblyCurrentContext;
4444

4545

4646
pub fn start<F, H>(settings: Settings<'_>, start_hostee: F) -> anyhow::Result<()>
47-
where F: FnOnce(&Host) -> anyhow::Result<H>
47+
where F: FnOnce(&Host) -> anyhow::Result<Box<H>>
4848
, H: HostedApp + 'static
4949
{
5050
let _span = tracing::info_span!("host start").entered();
@@ -78,8 +78,8 @@ pub fn start<F, H>(settings: Settings<'_>, start_hostee: F) -> anyhow::Result<()
7878
_span,
7979
};
8080

81-
let mut app_host = ApplicationHost::Bootstrap(bootstrap_state, start_hostee);
82-
81+
// NOTE: Box to avoid issues with stack sizes if hostee is too large.
82+
let mut app_host = Box::new(ApplicationHost::Bootstrap(bootstrap_state, start_hostee));
8383
event_loop.run_app(&mut app_host)?;
8484

8585
Ok(())
@@ -92,11 +92,11 @@ enum ApplicationHost<F, H> {
9292
#[default]
9393
Empty,
9494
Bootstrap(BootstrapState, F),
95-
Hosting(Host, H),
95+
Hosting(Host, Box<H>),
9696
}
9797

9898
impl<F, H> ApplicationHandler for ApplicationHost<F, H>
99-
where F: FnOnce(&Host) -> anyhow::Result<H>
99+
where F: FnOnce(&Host) -> anyhow::Result<Box<H>>
100100
, H: HostedApp + 'static
101101
{
102102
fn resumed(&mut self, event_loop: &ActiveEventLoop) {

toybox/src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::prelude::*;
22

33
pub struct Context {
4-
pub gfx: gfx::System,
4+
pub gfx: Box<gfx::System>,
55
pub audio: audio::System,
66
pub input: input::System,
77
pub egui: egui::Context,

toybox/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ pub fn run_with_settings<F, A>(settings: host::Settings<'_>, start_app: F) -> an
8080

8181
let app = tracing::info_span!("app start").in_scope(|| start_app(&mut context))?;
8282

83-
Ok(HostedApp {
83+
Ok(Box::new(HostedApp {
8484
context,
8585
debug_menu_state: debug::MenuState::default(),
8686
app,
87-
})
87+
}))
8888
})
8989
}
9090

0 commit comments

Comments
 (0)