#Andromeda - Terrain/Landscape editor using Vulkan and Rust
1 messages · Page 2 of 1
needs more self
yeah 🙂
I am cooking up a new abstraction
This should answer one problem ive been having
I had a World and FutureWorld before but it was honestly not great
Much better
backend developers avoiding refactors for 3 commits challenge (impossible)
frontend devs trying to create something without 600 frameworks challenge (impossible)

But yes refactoring is some kind of drug 
just one more... i swear i can stop whenever i want...
shush 

a lot of it was &thing where thing is already a reference
#showcase message
potentially more people you could join forces with
😔
to exchange brainworms or else

😄
using some extension I presume 😄
oh wait right
there are people who pay for expensive nitro
i stick to the cheap one
I saw the preview but they locked it behind the expensive plan sadly
i didnt know there were multiple "plans" of nitro now
at some point they split it into "classic" and just regular nitro
with regular nitro getting all the cool stuff
hmm
and then not too long ago they deprecated classic and replaced it with basic
which is just emotes and stickers
i have the full thing, whatever its called
and uploads i guess
ah
emotes and bigger file uploads for 3 eur/month i can live with, but 10 is a bit much for me rn
maybe i should also just let it run out and not extend
i got too used to emotes to go without tbh lol
: )
https://github.com/weigert/SimpleHydrology ill just leave this one here, i hope the others see it too 🙂
mayhaps helpful for you(r) terrain builder builders
This is something I definitely want to implement as well
multiple types of erosion
it was already starred by me lmfao
: )
@shadow trench You forgot to make version v0.6.0 latest on gh releases
Support for storage images
All cloneable caches and vulkan types now have their Arc> internalized
Fix clippy warnings around codebase
Note that this is v0.6.1 on crates.io
Note that this is v0.6.1 on crates.io
Bruh
fixed, ty
ill change the tag to 0.6.1
you can jank 0.6.0
yeah i should do that
im thinking of just yeeting netcdf support for loading heightmap data
its what some real world data uses but loading it uses a library which takes literally 5 minutes to build somehow
geotiff seems common
might look into that
that said real world data is ass 
the resolution is kinda bad
It's hard to find good DEMs
The netherlands have good ones
0.5m res of the entire nation
too bad its almost flat 
yeah the one i found of iceland was 200m 
using real terrain as a base is neat tho
then erode, add some noise etc. erode again
my experience is you have separate datasets
due to differences in how it's measured
the one i found from iceland had depth data, was kinda cool
but bad resolution so eh
ocean data gets measured with sonar and similar mostly
which.. doesn't work anywhere else 
WHY the fuck are geotiff files completely white when you open them in gimp 
Do I really need to use a dedicated GIS program just to load and convert them
time to work through some of these
tf
I implemented pipeline query support in my vk abstraction
// Record some commands, then obtain a finished command buffer to submit
let cmd = cmd
.write_timestamp(&mut timestamps, PipelineStage::TOP_OF_PIPE)?
.begin_query(&stats, query)
.bind_compute_pipeline("compute")?
.bind_storage_buffer(0, 0, &self.buffer.view_full())?
.push_constant(vk::ShaderStageFlags::COMPUTE, 0, &multiplier)
.dispatch(1024, 1, 1)?
.end_query(&stats, query)
.write_timestamp(&mut timestamps, PipelineStage::COMPUTE_SHADER)?
.finish()?;
this little command buffer takes ~4k nanoseconds without pipeline queries and ~10k with
weird
without reading what the actual code is, but just seeing all the rustisms is odd
its basically just "self" "mut" and "pub fn" what i see 😄
kotlin is super neat, thats what java should have been from the start
hehe dont mean to start a language war 🙂
its cool but when I see private inline operator suspend fun <reified T> 
xD
but yeah, 10x better than java for sure
Im doing a group assignment in kotlin right now, its neat
that "self" thing riggers me the most i think
are those methods extensions/mixins of some kind?
its just this in C++
methods without self are like static, with is a member function
kind of :P
oki im quiet again
to answer your question though, does that happen if you have out of date builds?
ah
meanwhile new C++ convention: 
template <class Self>
auto foo(this Self&& self) {
// ...
}
dead skellingtons in the basemend
no idea why it didnt rebuild in the first place 🤷
All working nicely finally
Using timestamp queries every 60th frame to get some timing data
Wireframe is about 5 times slower 
ok i have a problem
: o
could just be a rust debug perf moment
mayhaps build as release and check if it makes any difference?
truly a rust debug performance moment indeed
ill do a quick profile in debug and see whats so slow though
frametime is just the classic this_frame - last_frame every frame
ah
the others are collected using timestamp VkQueryPools every N frames
(because they add some perf overhead so I dont do it every frame)
so, normal stuf
Yeah
actually debug perf isnt that bad without a debugger attached
like 5ms
except the occasional spike for some reason
i only profiled for 20s thats an interesting number
i guess it added the times for each thread or something
ok i should only profile the main thread for this
That is not fun to look at
I assume vkNegotiateLoaderLayerInterfaceVersion is because of the validation layers
But memcpy 
not that bad though

what is going on
ok well i wont bother looking too much at windowing stuff
what are you using to profile in Rust?
very sleepy
erminds me of ProcessExplorer for some reason
its a very simple profiler that should work for any compiled lang
if it has debug symbols
ah this one
cool, thank you 🙂
cargo flamegraph is also good but doesnt seem to work for me for some reason
ill check them both out
an open world mmo dragon jrpg fps doesnt need no gui anyway
who even makes games smh
isn't that always the case
yeah
i could do some optimizations instead of trying to bind a new descriptor set for every draw and going through the whole cache lookup
but its not a big deal yet
could do mixed retained/immediate mode
whats that
First of all are you using some gui library you have little control over
well this settles the question 
basically imagine a mix of imgui and the retained paradigm for some things
i see
it would make stuff like layouting much easier
you would be able to do "right align element" etc.
yeah
i have a hacky right align
but it requires pushing elements in the opposite order
yeah
could call it "deferred" lol

yeah adding a debugger completely kills the performance, no idea why´
maybe its just the sheer number of debug symbols and functions called in debug
e.g. all iterators
wrote my first higher ranked trait bound
pub trait PassExecutor<D: ExecutionDomain, U, A: Allocator> {
fn execute<'q>(&mut self, cmd: IncompleteCommandBuffer<'q, D>, ifc: &mut InFlightContext<A>, bindings: &PhysicalResourceBindings, user_data: &mut U) -> PassFnResult<'q, D>;
}
impl<D, U, A, F> PassExecutor<D, U, A> for F
where
D: ExecutionDomain,
A: Allocator,
F: for <'q> FnMut(IncompleteCommandBuffer<'q, D>, &mut InFlightContext<A>, &PhysicalResourceBindings, &mut U) -> PassFnResult<'q, D> {
fn execute<'q>(&mut self, cmd: IncompleteCommandBuffer<'q, D>, ifc: &mut InFlightContext<A>, bindings: &PhysicalResourceBindings, user_data: &mut U) -> PassFnResult<'q, D> {
self.call_mut((cmd, ifc, bindings, user_data))
}
}
for <'q>
awww
everything ive had to use higher ranked trait bounds for ive had to rewrite out at some point
in this case it actually simplifies things a little
because now 'q is no longer a lifetime parameter of the pass callback
true
@potent quest if youre serious about trying out rust I have a vuk-like (but much simpler atm) lib that I use myself, https://github.com/notapenguin0/phobos-rs / https://crates.io/crates/phobos
bro sneaked in a classic phobos banger

thats the goal
its a bit less fancy
im using it in my terrain project and its been working nicely so far
nah kinda like it when there's less
means when i code the stuff i need i can at least understand it 
why i waned from unreal since it was so much
Yeah game engines are overwhelming 
Hm if I add rt support I’ll have to work on adding it to the rust spirv-cross too
Oh well I had to do that anyway
Shouldn’t be too hard
Moments caught before disaster
bindgen?
Its a rust library that automatically generates rust bindings for c/c++ libraries
ash is a little different
ash just has vulkan bindings for rust
It actually loads the function pointers itself and doesn’t interface with the SDK headers
oh
its generated from vk.xml directly
so its up to you to interface with the sdk?
Also adds some builders for convenience but they’re all optional
you don’t need to include the SDK directly using rust
Depends though, if you choose the linked feature it links to vulkan.lib, otherwise it dynamically loads the pointers
(With LoadLibrary etc)
ohh i see
Its a bit like a replacement for vulkan.h
ohh winit is the glfw
i forgor that rust has no classes
structs are basically classes though
ohh impl
Yeah
i guess that gives methods
Trait?
Yep
Traits define some methods but don’t implement them
Then each struct can provide implementations for those traits in an impl block
And you can constrain generic things on a trait
trait Bar {
fn bar(&self);
}
fn foo<T: Bar>(value: T) {
value.bar();
}
foo() takes any T that implements Bar, and calls its implementation of that function
Aye
Yo pengiun if you ever add ray tracing support lmk
lmao
I was planning to add it anyway
Current version is just a very thin wrapper anyway so it’s not a lot of work
do you plan to add easter egg about gregtech in the code ?
gr-egg-tech

ill make it just as tedious
Every object needs to be created using circuit objects
I tried running from Gregtech but once again I stare at it’s endless pit
There is no escape
So rt is pretty much the vulkan api
Yeah until I figure out a good way to flexibly wrap it
But I need to actually use it for a bit to know
Obviously it’ll integrate with the rest of the lib
found a gem in my old code
Well I would do that if I still maintained this
That is an acceleration structure
API could still be cleaned up a little tbh
@potent quest https://github.com/NotAPenguin0/phobos-rs/blob/feature/raytracing/examples/03_raytracing/main.rs lmk what you think the painpoints are if you spot any obvious ones
you sure get long type names with raytracing
QueryPool<AccelerationStructureCompactedSizeQuery>
AccelerationStructureGeometryTrianglesData
oml you reminded me i need to add compacting for my blas
I found on average it uses about 40-50% less memory
i looked through it
seems way nicer to use
god i cannot wait to use this
seems like command buffer is handled at a lower level compared to vuk?
there are some quality of life abstractions of course
but in general theres no full dynamic state like in vuk
like setting the vertex format in the command buffer and all that
iirc vuks pipelines are very minimal and all other state can be set through the command buffer
i got the sample to create a BLAS with a single triangle and a TLAS with one instance
solid progress for now
I still have to add rt pipelines but apart from shader binding table nonsense that should be easy
oh yeah how tf did you get nsight to profile the as?
oh no i am doing something wrong with mines then 💀
then you can double click to show details
raytracing going well
im confused where this is coming from
since i really am adding the corresponding feature struct to my pnext chain
ok thats fixed i was missing an extension
still good old device lost though
aaaand i completely crashed my driver
oops
// walmart raygen shader
void main() {
// Apply inverse of view and projection to find camera direction
// This gives us the origin and direction of the ray
vec4 origin = inverse(pc.view) * vec4(0, 0, 0, 1);
vec4 target = inverse(pc.projection) * vec4(UV.x, UV.y, 1, 1);
vec4 direction = inverse(pc.view) * vec4(normalize(target.xyz), 0);
FragColor = vec4(intersect(direction.xyz));
}
good first comment yes?
lmfao
o7
🫡
@shadow trench Sorry for pinging, when you looked into your second AS did it also have the same mesh?
I'm running into an issue which it seems like my TLAS? isn't showing anything in nsight
Yeah it is showing
I have a big bug somewhere but I don’t know where yet
My driver crashes when calling rayQueryProceedEXT lol
lmao
my first thought was TDR but aftermath shouldn't disable those
yeah i considered that too
but it crashes pretty much instantly
like i hear a buzzing sound in my headphones even 
motherboard short via software speedrun any%

I dont get it
float intersect(vec3 origin, vec3 direction) {
rayQueryEXT ray;
origin = vec3(0, 0, 0);
direction = vec3(0, 0, 1);
rayQueryInitializeEXT(ray, as, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, 0.001, direction, 10.0);
rayQueryProceedEXT(ray);
if (rayQueryGetIntersectionTypeEXT(ray, true) != gl_RayQueryCommittedIntersectionNoneEXT) {
return 1.0;
}
else return 0.0;
}
I have an AS with a triangle at z = 1
How is this not hitting
skill issue
probably
segfault in the driver after 10 ish frames with no feedback

what in satans name is my program doing
runs sample without vkconfig on
what is vkconfig
code without the validation layer 😎
MOTHER FUCKER
how long has that been in here
and of course it still crashes randomly inside the driver

btw @potent quest if you want a stable rust api youre too early 
NOOOOO
things are somewhat settling but im still breaking stuff every now and then
its not too bad anymore I think
But no stability guarantees yet
wait why?
its setting both the build and update scratch size to the update size
idk how this ever showed up right in nsight
because im allocating like 128 bytes of scratch memory when 1500 are needed
im wondering how this hasnt blown up all this time
but yeah stability is somewhat better now but ill definitely still be breaking some stuff
yeah but you won't be able to leak 9.5tb of ram because you forgot to do some case checking
or blow up your program by reading from a moved-from struct

At this point I’ll just go to bed and hope it fixes itself tomorrow
💀
I thought game dev debugging was bad
that was when i met GPU debugging
Program crashes
Boy oh boy do I sure hope useful indicators are gi-
here is this random ahh memory address LMAOOOO have fun in the debugger doing "work"

Yeahh
I don’t have much hope for this being a driver issue sadly
I ran it on gfs pc and it crashed too, and she’s obviously not using vk beta drivers but stable ones
Its not even crashing on the first frame but on like frame 10 or so
valgrind moment
except valgrind doesnt run on windows but my program doesnt run on my linux laptop because no rt 
huh thats kinda cool
what exactly is the difference between KaTeX and LaTeX
also why does this docpage taunt me with the Fourier transform
Also @shadow trench you are hijacking your own thread with a different project? 
in a way 
the different project is my vk abstraction lib
but i also use that for the terrain thing
so it works together
fair enough
katex seems to be a small subset
kinda like mathjax
i remember
i thought my u24 struct could be borked
but i wrote some tests and i think its fine
// Provided by VK_KHR_acceleration_structure
typedef struct VkAccelerationStructureInstanceKHR {
VkTransformMatrixKHR transform;
uint32_t instanceCustomIndex:24;
uint32_t mask:8;
uint32_t instanceShaderBindingTableRecordOffset:24;
VkGeometryInstanceFlagsKHR flags:8;
uint64_t accelerationStructureReference;
} VkAccelerationStructureInstanceKHR;
this vk struct
oof
#[derive(Default, Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
#[repr(transparent)]
pub struct u24([u8; 3]);
impl u24 {
const MIN: Self = Self::from_ne_bytes([0, 0, 0]);
const MAX: Self = Self::from_ne_bytes([255, 255, 255]);
pub const fn from_ne_bytes(bytes: [u8; 3]) -> Self {
Self(bytes)
}
pub fn into_bytes(self) -> [u8; 3] {
self.0
}
}
impl From<u24> for u32 {
fn from(value: u24) -> Self {
let u24([a, b, c]) = value;
#[cfg(target_endian = "little")]
return u32::from_le_bytes([a, b, c, 0]);
#[cfg(target_endian = "big")]
return u32::from_be_bytes([0, a, b, c]);
}
}
impl TryFrom<u32> for u24 {
type Error = anyhow::Error;
fn try_from(value: u32) -> Result<Self> {
let [a, b, c, d] = value.to_ne_bytes();
#[cfg(target_endian = "little")]
return if d == 0 { Ok(u24([a, b, c])) } else { Err(anyhow!("u32 did not fit in u24")) };
#[cfg(target_endian = "big")]
return if a == 0 { Ok(u24([b, c, d])) } else { Err(anyhow!("u32 did not fit in u24")) };
}
}
not too hard to write tho
but why should you need to worry about that, aren't you using ash
ash defines it too actually I forgot about that
bruh
it does this instead
yeah this seems somewhat better
cause that layout is 999% for sure wrong
still crashes sadly
okay back to debugging
time to dig through apitrace
a 900 line trace of the acceleration structure creation
i will inspect every line
this is some serious crazy work
im going over every line in the trace and crossreferencing with the spec for possible invalid api usage
mayhaps write a tool which can analyse the stuff somehow?
i was actually thinking about that
900isms sounds like a lot
a small gui tool to examine the output of the vulkan api dump
its pretty readable luckily
oof
but stuff to easily collapse it, filter commands, maybe name addresses, etc
thatd be nice
something for my side projects list
is that written in some yml/yaml/toml variant?
yeah true
i might write something like that
could be useful
like a baby renderdoc 
would be kewl if renderdoc could replay apitrace trace files too
apitrace's ui is ass
yeah mayhaps
heh I didnt know this
When writing my old rt stuff I just immediately used a big scratch buffer and suballocated it for every AS build
Turns out thats required
TIL vkGetAccelerationStructureDeviceAddressKHR just returns the device address of the underlying buffer on my gpu
it got restored luckily
but uh, this exception im getting is usually associated with missing dlls
the fuck is going on

i have no validation layers
what
where did it go
ok reinstalling the sdk worked
unfortunately still silently crashing in the driver
@potent quest ill rewrite the sample to see if i didnt miss anything, if it still happens ill add rt pipeline support and assume driver bug for now
until i get a reply from nv
another AMD gpu runs it fine, I think ill just assume driver bug
ok the same code for building AS does not crash when using RT pipelines
theres definitely something wrong in the driver when using ray queries
Anyway @potent quest check it out
RT pipelines more or less done
I just have to fix the sample to actually face the camera the right way
ladies and gentlemen
a raytraced quad
https://github.com/NotAPenguin0/phobos-rs/releases/tag/v0.7.0 even wrote some actual release notes
what was wrong then?
nothing on my end I suspect
ray queries crash the driver, rt pipelines work fine with exactly the same code for creating the acceleration structure
also works fine on amd

i have an amd gpu and have never had to worry about driver issues
this is the second time for me
idk why people say that nvidia drivers are much better
first time was confirmed a bug, this probably is but im not totally sure
Every time I fix a sync bug in my render graph I wonder how it worked before
the urge to switch
Would be good to have a user beside myself 
I suggest reading through https://github.com/NotAPenguin0/phobos-rs/blob/master/examples/03_raytracing/main.rs to see what you think
(Also API feedback is obviously welcome)
Ok()?
Its one of the values of Result<T, E>
Basically a result either is Ok(value) or Err(error)
(Like a union)
Its the standard way of error handling in rust
If you have a function that returns a Result, and you have a x_result which is a value inside a Result, you can write ```rs
let value = x_result?;
To grab the value inside if it’s Ok(), and return the Err() case otherwise
ohh ok?
this does make sense
Confused on alignment however
How is it ensuring that the buffer is aligned 256 bytes?
it grabs the memory requirements for the buffer, which returns the required alignment
If you are storing multiple ASes in one buffer, you do need to manually align each size to 256 bytes, but get_build_sizes() already does this so it shouldnt be an issue
I pushed some fixes to a 0.7.1 release
Next up is adding missing docs for everything that i added since the last round
Ohhh i see
See I wanna learn rust so I can use this, but I’m also worried that:
- this project may just fizzle out since no one else is using it
- I should probably understand cpp first so I can have a better grasp of the basics rather than throwing it to rust
Im also using this library myself so it’s not like I’ll stop working on it
Rust has different things to learn than cpp, but the compiler helps you a bit more
yup and instead you will fight it instead of fighting yourself
imo rust is considerably easier to learn than c++
and you get an easier to work with ecosystem 🙂 no more linking errors, libraries are super easy to grab
that's only because you already knew c++ I think 
I'd never recommend anyone to learn Rust as their first language
it might be related
still though, the compiler is much more helpful in fixing your errors and theres a lot less basic things to worry about
definitely
but the concepts that rust introduces are probably a bit harsh for beginners 
yeah but if you have some c++ experience they should be fairly easy to pick up
@potent quest how do you feel about create info struct vs different constructors with different parameters
Im currently considering switching some over to create infos, especially buffers and images since they have many combinations of input parameters
wdym?
ohh create info structs
like how vulkan does it
or a function that has multiple parameter
Yeah
hmm. personally, i like multiple parameters, but doing raytracing i've come to like structs more
Mostly also many different constructor functions like new_with_alignment etc
Remember rust has no overloads so they all need unique names
Yeah for this they probably are
in rt, they def are
for few parameters I can keep it as a function
i think some yeah are better off as functions
All rt input data is structs that match the vk ones almost 1:1
with some convenience builders and stuff
tbh worse case, you can just do
func({
});
Yeah
yeah create info structs sound a lot better
Ill do this for buffers and images at least
Because it’s a bit problematic there
In rust you can write ```rs
let x = Foo {
bar: 0,
baz: "hi",
..Default::default()
};
To initialise only bar and baz and give all other fields a default value
So you can kinda optional parameters this way
i like CreateInfo structs too, like you say having default fields is akin to having default parameters
so it depends if you want them or not
so when are you switching over 
i promise your vulkan backend will be 10x more compact :)
if I ever get too frustrated I’ll see 😉
ill take it :)
just imagine how much code this actually hides 😄
i know but I want to do all that 😄
thats entirely fair yeah lmao
im like that too
thats why i wrote this instead of using vulkano or something
yeah, it's really cool but I'm enjoying NIH at the moment
the GP curse is real
yep, took me a year of messing around with Vulkan to start actually progressing a bit more with it recently
yeah ive been through a ton of refactors and iterations 
i think i started learning vk like 3 years ago now
it is nice when you look back and see how you have progressed. little do you know there's a mountain to climb right in front of you
nice, did you do much GP beforehand
that's about how far I got with OpenGL too. then I decided to become a masochist
good choice
i actually think opengl is more masochistic
its painful to use and debug and makes no sense
yeah, i tried starting a project with it myself and I hated interfacing with it, especially after I had tried Vulkan
in university our lecturer had an OpenGL framework/engine so I never made anything with it completely from scratch
most opengl frameworks also suffer from the same issues as opengl sadly
fwog is nice but its still opengl
it was still raw opengl tbh but he just abstracted EVERYTHING into classes
eh thats not great imo
so we only really had to think about the actual draw calls and stuff, he had done all the setup
the problem with classes in opengl imo comes from a lot of implicit behaviour
yeah, looking back at the repo is painful
e.g. constructing a Texture class might change some global state
without you knowing about it
A while ago I tried solving this problem with a bind_guard that would glBind... in the constructor and glBind...(previous_object) in the destructor but it really didnt work well
yep, my friend says he thinks it made it easier to learn(having everything in classes) but i think it led me to stupid designs that ignore what's actually happening
yeah
he did send me a version of his framework that was in Vulkan but he wasn't sure whether to teach it yet or not
that was also all in classes
in vk it works a bit better but theres still a lot of issues with "just use classes for every vk object lol"
yeah, it just ended up being a really brittle design
when are you gunna post more updates on the terrain editor
im going to finish off the docs for the current version of my vk thingy, add the create infos we talked about and then go back to work on it
i'm interested to see, i've done some erosion simulation in the past
nice, can't wait for the updates
erosion is definitely on the list 
@shadow trench i take it this one is the fred?
this is the fred yes
ive ran my code on some amd gpus and it worked fine there
but for some reason its crashing in a driver thread on nvidia
if I had to guess I'd say compiler bug
oh i havent considered that
this is probably also why it doesn't crash with aftermath
aftermath adding shader instrumentation -> something is different in shader -> no crash
i used glslc to compile the shader, maybe its bogus when compiling ray queries
makes sense
not necessarily
if you want to be sure run spirv-val on it
if no output then you're good and you can bonk nvidia compiler people
bonk it is then
as for why it does the compile in a bg thread, no idea, maybe a remnant from the OGLder days
possibly
ive made a fred on nvidia forums, i can wait a bit for a reply there
its a bit weird to me that it doesnt crash on frame 0
usually after like 7-8 frames
yeah, maybe they do some opts after the shader has run for a bit or similar cursed stuff
thats possible
you'd need to ask apache if there's anything like that in the driver codebase :^)
another person to go bother :)
wasn't really serious, apache has a leaked version of the driver code so not sure if you'll want to have anything to do with that
time to rust (soon i should study for exams)
but who needs exams. my midterms are through and unis only look at that 
ok now i should have 100% doc coverage if i did it right
i added a compiler check that will yell if a public item isnt documented
easy to miss things
ggez has it set so that it fails compilation lmao
ppl jumping into the rust bandwagon after seeing the funny release number
out of curiosity, how much do you use interior mutability with things like RefCell
I don’t use refcell at all
Only RwLock and Mutex
I need my lib to be thread safe my default
then you have my chad renderer which uses a global to keep track of the device and context

💀
in my defense it's threadsafe and caters to my needs also simplifies things a lot
Tbf for a regular application it’s fine
cool, just curious. i'm not sure if I'm fighting using interior mutability too much, but some of the stuff I'm doing at the moment is frustrating to design around taking exclusive references. had a look at wgpu and it seems it has a lot of just &self references when using the device
Same for the device for me
The thing is
The device is cloneable
pub struct Device(Arc<DeviceInner>) or Arc<Mutex<XXXInner>> where applicable is what I do
yeah, that's what I think would be handy for me to do. but my device does have a bit of state, so I think I need to make it interior mutable to be able to use it in an Arc as I won't be able to acces &mut references
have you had any problems with having it wrapped up like that>
I think its better
I used to return an Arc<Device> from new and pass that around but thats kind of not done usually
So I moved the arc and/or mutex inside and its much cleaner now
Cool, I might give something like that a go. Cheers 🙂
Its pretty nice :)
I think the stuff thats done like that for me is uh
Device, Allocator (but this is a bit different, the allocator trait just requires Clone), PipelineCache, DescriptorCache and ExecutionManager
@young oar engine dev is busy but
A browser interface to the Rust compiler to experiment with the language
it works
and its incredibly cursed
Normally you won't even need unsafe on line 35
Just do .as_any().downcast_ref::<R>()
Or well
That's how it's possible using normal boxes
First time hearing about thinbox
Doesnt work with regular boxes I think
Because the size of Box<T> depends on T
I tried but I couldn’t find a way to get a Box<dyn Any> from a Box<T> without UB or compiler error
ThinBox always has the same size so this isn’t a problem
And you can transmute
But it’s unstable
// assuming "value" implements "Any" (aka it's sized and 'static)
let boxed: Box<dyn Any> = Box::new(value);
That should work
Oh hm yeah maybe that’s doable
Ah but I think casting that to dyn Foo is UB
As long as T is 'static (Any is automatically implemented for all types that are 'static)
Because different vtable stuff
I ran something like that through miri
struct Registry {
dyn_items: HashMap<TypeId, ThinBox<dyn Any>>,
items: HashMap<TypeId, Box<dyn Any>>,
}
impl Registry {
pub fn new() -> Self {
Self {
dyn_items: HashMap::default(),
items: HashMap::default(),
}
}
fn put_dyn_boxed<T: ?Sized + 'static>(&mut self, item: ThinBox<T>) {
// SAFETY: ThinBox always has the same size regardless of the type inside,
// so we can transmute this to a different pointer until we cast it back to
// T in get()
let any = unsafe { std::mem::transmute::<_, ThinBox<dyn Any>>(item) };
self.dyn_items.insert(TypeId::of::<T>(), any);
}
pub fn put<T: 'static>(&mut self, item: T) {
self.items.insert(TypeId::of::<T>(), Box::new(item));
}
pub fn put_dyn<T: ?Sized + 'static>(&mut self, item: impl Unsize<T> + 'static) {
self.put_dyn_boxed::<T>(ThinBox::new_unsize(item));
}
pub fn get<T: 'static>(&self) -> Option<&T> {
let any = self.items.get(&TypeId::of::<T>());
any.map(|value| value.downcast_ref::<T>().unwrap())
}
pub fn get_dyn<T: ?Sized + 'static>(&self) -> Option<&T> {
let any = self.dyn_items.get(&TypeId::of::<T>());
any.map(|any| unsafe { std::mem::transmute::<_, &ThinBox<T>>(any) }.deref() )
}
}
alright this is kinda getting somewhere actually lmao
usage:
trait MyTrait {
fn call(&self);
}
struct Foo;
struct Bar {
pub value: String,
}
impl MyTrait for Foo {
fn call(&self) {
println!("meow");
}
}
impl MyTrait for Bar {
fn call(&self) {
println!("woof: {}", self.value);
}
}
fn main() {
let mut reg = Registry::new();
// Register an instance of Foo as our implementation of MyTrait
reg.put_dyn::<dyn MyTrait>(Foo);
// We can also put Sized types in the registry
reg.put(Bar { value: "Bar".to_owned() });
// Will call Foo::call()
reg.get_dyn::<dyn MyTrait>().unwrap().call();
// Will call Bar::call()
reg.get::<Bar>().unwrap().call();
// We did not explicitly register Foo, so this will be None
// even though there is a Foo in the registry (this is only known as dyn MyTrait)
assert!(reg.get::<Foo>().is_none())
}
would be nice to make get and get_dyn both be the same function but its tricky
Putting Sized types inside a ThinBox<dyn Any> is UB as I found out so that doesnt work
specialization could fix this
Yea honestly that's pretty much how my resource handling system gets handled kek
Other than the *_dyn methods since I never used/needed to use ThinBox
Hm that's interesting
I might use this
struct MySystem {
pub value: i32,
}
struct MyEvent;
struct OtherEvent;
impl Event for MyEvent {}
impl Event for OtherEvent {}
fn handle(state: &mut MySystem, _event: &MyEvent) -> Result<()> {
println!("Hello {}", state.value);
Ok(())
}
fn handle_other(state: &mut MySystem, _event: &OtherEvent) -> Result<()> {
println!("Other event!");
Ok(())
}
impl System for MySystem {
fn register_handlers(registry: &mut SystemRegistry, system: &StoredSystem<Self>) {
registry.register_handler(system, handle);
registry.register_handler(system, handle_other);
}
}
#[test]
fn test_system_registry() -> Result<()> {
let mut registry = SystemRegistry::new();
registry.register_system(MySystem { value: 5 });
registry.trigger(&OtherEvent)?;
registry.trigger(&MyEvent)?;
Ok(())
}
we gaming
that took a while to figure out
i really didnt want to have a fixed set of event types
Strictly speaking the event trait is not needed but for completeness I added it in
i am open to suggestions :)
EventBus or MessageBus you Subscribe to
hmm yeah that may be a better name
and EventBus/MessageBus also has Publish where you send the messages
I was considering splitting off the EventBus part to a different struct
or you split it into Subscriber and Publisher
I do like subscribe though
yeah
a lot better than register_handler
otherwise Mediator is an alternative
but thats the pattern behind all that and not really a good name for the thing
yeah
How would you call the method that does all the subscribing on init
impl System for MySystem {
fn register_handlers(registry: &mut EventBus, system: &StoredSystem<Self>) {
registry.subscribe(system, handle);
registry.subscribe(system, handle_other);
}
}
this one
initialize is fine
hmm it depends
if you inject the eventbus into where it needs to register, you can do that in the ctor already
no need to call initialize somewhere in some specific order possibly
lets say your Window class
where you use glfw
the problem is that the current system only allows subscribing from a StoredSystem<S>
// window
public Window(EventBus eventBus)
{
glfwInit
glfwSetXXXCallback(_window, handleXXX);
glfwSetYYYCallback...
}
private void handleXXX()
{
eventBus.Publish(WindowMovedEvent(x, y));
}
// somewhere else
public SomethingReactingToWindowResize(EventBus eventBus)
{
eventBus.Subscribe(WindowSizedEvent, OnWindowSized);
}
private void OnWindowSized(x, y)
{
}
pub struct StoredSystemInner<S> {
state: S,
handlers: Registry,
}
Registry is just some Vec<Any> but wrapped
I might need a better name for it
hmm
name does not tell me anything
therefore this type is not necessary
but mayhaps its the way you code in rust
this registry thing isnt part of the public api
but i use it in a few places so its still useful
it basically allows me to do type erasure in rust
registry is a bad name
probably
Registry something equivalent to ArrayList<Object> in java
from best to worst
but with some utilities to get<T> and put<T>
yeah in essence its just a list of callbacks
yeah but the generic case isnt necessarily a list of callbacks
well the event bus is already EventBus
or EventHandlerRegistry at least
this registry has nothing to do with the event system thingy
its even in a different repo :P
thought we talk about te same thing 😛
pub struct Registry {
items: HashMap<TypeId, Box<dyn Any>>,
}
impl Registry {
pub fn put<T>(&mut self, item: T) {
}
pub fn get<T>(&self) -> Option<&T> {
}
}
simplified the rust isms
but its just a way to store any type and retrieve it later
im still confused
is Registry the callback holder or not
or something different now
it stores the callbacks but it doesnt have to
it also stores the event buses for each event type
make a distinct type for the event nonsense
there is most likely no other use case for Registry in this instance
I do use it in one other place
like?
pub struct EventBus {
buses: Registry,
}
buses is wrong
Because I need a list of ```rs
pub struct TypedEventBus<E: Event> {
systems: Vec<Box<dyn Caller<E>>>,
}
type erasure in rust is tricky :P
sounds like a shit language 😛
obviously i do not know any rust, and im just talking out of my salty ass
yeah true, renaming is easy
https://www.youtube.com/watch?v=A_MsXney3EU heh just saw this one
☟☟ Upcoming Conference Workshops (Swag, links and more) ☟☟
C++ Best Practices Workshop, Sept 18, 19, Norway: https://ndctechtown.com/workshops/c-best-practices/abf61aa1ff65
T-SHIRTS AVAILABLE!
► The best C++ T-Shirts anywhere! https://my-store-d16a2f.creator-spring.com/
WANT MORE JASON?
► My Training Classes: http://emptycrate.com/training...
ooo
@young oar does splitting your source up into multiple crates have any noticable impact on build times
i checked out your repo to figure out the events thing
YES
probably worth doing then
Well if you edit the source code for like, the asset manager (a crate used by many more crates) its still pretty slow
How many seconds per clean run?
Yeaaa that's pretty long
because its compiling netcdf-sys and hdf5 which are both giant C libraries apparently
thats the biggest time sink
I should probably just yeet support for netcdf
Ahh I see
real world heightmap data sometimes uses it
but its not great to compile those giant crates
Yeaa
maybe i should write a very lightweight parser instead
I mean that's per clean run
Ouchhh
i think ill spend some time writing that parser soon
should be easy, i think the format is quite simple
yeah lol this is ridiculous
mold pls
Im already using lld I think
But that won’t help with compiling hundreds of C files
Might just yeet netcdf support altogether
I can’t find a clear spec
Just “use our c bindings lol”
I have introduced an easy way to deadlock my app
But its probably fine
fn handle(system: &mut MySystem, event: &Event, context: &mut EventContext) {
context.publish(event);
}
always fun
lmao
yeah its silly but itll deadlock lol
just publishing the same event type recursively
fn handle_input_event(
camera: &mut Camera,
event: &InputEvent,
ctx: &mut EventContext<DI>,
) -> Result<()> {
let mut state = camera.write().unwrap();
let input = ctx.read().unwrap().get::<Input>().unwrap();
state.handle_event(event, input)
}
refactoring progress
I can live with this for event handlers I think
One problem that comes with passing resources as arguments is that you can request for reading/writing to a resource afterwards
is that an issue
So material systems might be a bitch to implement since they require modular resources
my engine has a very fixed renderer because i only render terrains
Ah then you're good
so it doesnt need to be extremely flexible when it comes to material systems
yes
and thats just for primitives
so unless that devicesize is an alias, that will not compile
true
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
rust is so much cleaner wtf
also im learning rust now
im gonna phobo'ing soon
welcome 😈
LMAOO
there's no going back* (i have exams and should study but it's only 1 exam)
WHAT
rust has in-built documentation
bruh

rustdoc is great
i recommend intellij with the rust plugin if youre not using that already
thats what its like leaving C++
13 years ago c++ didnt have move semantics
because i am one
i love that rust complains if you don't handle results given
yep






