#Andromeda - Terrain/Landscape editor using Vulkan and Rust
1 messages · Page 3 of 1
i dont use any
i dont use it a whole lot but when I do its a lot nicer than the alternative
Im heading to bed @potent quest, if you have any questions you can just leave them here and I’ll answer in the morning
moving everything into subcrates instead of submodules
also switching to the event bus thingy where possible
but one thing at a time
send repo when you push those changes
sure
It all compiles
I fully expect it to come crashing down when I run it
actually not that bad
one deadlock on the input system
https://github.com/NotAPenguin0/andromeda-rs/tree/master still need to switch some stuff to using the new event system ofc
Wha subcrates?
yeah
better compile time and code separation
this is a nasty deadlock though
let input = input.write();
input.process_event(event) {
bus.publish(event) {
camera_handler(event) {
let input = input.read(); // DEADLOCK
}
}
}
i fixed it by pushing events to a buffer and flushing them after
i have another issue to fix now :P
not really related to my project
but my little embedded journey for school is making progress
its a memory in pixel display thingy
its kinda cool, if you cut off the power it keeps the image for a good 20 seconds
at this point idk what this post is about anymore 
just my various ramblings 
Oh yea I had deadlocks happen in those types of situations before
At least we got fallible functions for that (I think?)
Bad apple when
funnily enough, the site where they sell these shows bad apple running as a demo

Penguin sorry to turn this channel in rust, but
did learning rust make you a "better" c++ developer?
Im not penguin but it made me a better c++ dev
I agree yeah
I mean, the more language you know the better you become at you favourite
step 2: how to borrow checker
You do what the compiler says
But it comes down to
- don’t use after free (I mean, obviously)
- never have more than one mutable reference to the same thing
ok so brain dead moment
I'm not getting the difference between move vs copy
so move is taking the pointer, length, and etc. and copying it and invalidating the pervious pointer
but a copy copies both and redirects the pointer to the new copy
oh ownership is pretty easy it's unique_ptrs
a move is like std::move
There’s not necessarily a pointer
Also, a move in rust is a bit more than in C++
It leaves the old object destroyed and fully relocates it somewhere else
Then there’s Clone which makes a deep copy (for example cloning a Vec allocates a new vector and clones each element into the new vec)
Then Copy is a trait that allows a type to do a bitwise copy and still be valid
A Vec or obviously not Copy, but u32 is
Copy types are not moved either
mfw all the rust code ive seen using vulkan wraps vk functions with unsafe
Thats because vulkan functions are inherently unsafe
The safety of the call is determined by whether the input parameters were valid
You could pass in an invalid pNext pointer for example
In theory any unsafe block could cause UB
But using unsafe for vk calls is no big deal
ok so we're back to: i dont know cpp
so rust tutorial is saying data races can also be caused by
2+ pointers accessing the data at the same time
but they proceed to do
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{}, {}, and {}", r1, r2);
oh wait
is it because r1 and r2 are accessed non-concurrently of each other
or whatever it's called i.e. println!() accesses r1 first then r2 after it finished r1
This isn’t an issue because both r1 and r2 are immutable references
But suppose you would send r2 to another thread and then proceed to try to modify s
This won’t compile at all
This should say two mutable references really
many immutable shared references is fine
But from the moment you want mutability, it’s a data race
note to self: make my pointer faster to win the race 😄

In rust thread safety is done using two traits
Send and Sync
They’re automatically implemented where applicable
Send means: “you can send this to another thread and the data it accesses will still be valid”
Sync means multiple threads can safely use this concurrently
Mutex<T> is Sync
&T is not Send because the thing it references could be dropped before the thread exits
&'static T is Send because of the static lifetime
fun fact: i have yet to learn multithreading 💀
most ive done was slap openmp onto my math project
lifetime?
eh rust tutorial said that was a later me problem
so ill leave it like that

yes you can
you can make cyclic references with ref countes ptrs in rust like arc or rc
then cut off all access to them except the refs they have to each other
that just leaks
its considered safe to leak in rust
yea it is
Yeah dw about it too much yet
i dont think this ever happened to me
ok so rust slices are like memory safe buffer slices
they’re like a std::span
They refer to some contiguous range of items, for example a subslice of a vec
Except the validity of what they point to is checked at compile time
ohh ic
hmhmh
so i finished
learning move semantics
should i yolo
and jump into phobos
the answer: yes
error[E0554]: `#![feature]` may not be used on the stable release channel
uh penguin do i have to switch over to the unstable?
kinda would like to just stay in stable
also i think
the example given
Fast, powerful Vulkan abstraction library
does not compile?
use winit::window::WindowBuilder;
use winit::event_loop::EventLoopBuilder;
use phobos::prelude::*;
fn main() {
let event_loop = EventLoopBuilder::new().build();
let window = WindowBuilder::new()
.with_title("Hello, Phobos!")
.build(&event_loop)
.unwrap();
let settings = AppBuilder::new()
.version((1, 0, 0))
.name("Phobos demo app")
.validation(true)
.window(&window)
.present_mode(vk::PresentModeKHR::MAILBOX)
.scratch_size(1 * 1024u64) // 1 KiB scratch memory per buffer type per frame
.gpu(GPURequirements {
dedicated: true,
min_video_memory: 1 * 1024 * 1024 * 1024, // 1 GiB.
min_dedicated_video_memory: 1 * 1024 * 1024 * 1024,
queues: vec![
QueueRequest { dedicated: false, queue_type: QueueType::Graphics },
QueueRequest { dedicated: true, queue_type: QueueType::Transfer },
QueueRequest { dedicated: true, queue_type: QueueType::Compute }
],
..Default::default()
})
.build();
let (
instance,
physical_device,
surface,
allocator,
exec,
frame,
Some(debug_messenger)
) = WindowedContext::init(&settings)? else {
panic!("Asked for debug messenger but didn't get one.")
};
println!("Hello, world!");
}
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src\main.rs:40:41
|
5 | fn main() {
| --------- this function should return `Result` or `Option` to accept `?`
...
40 | ) = WindowedContext::init(&settings)? else {
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<std::result::Result<Infallible, anyhow::Error>>` is not implemented for `()`
error[E0308]: mismatched types
--> src\main.rs:32:9
|
32 | let (
| _________^
33 | | instance,
34 | | physical_device,
35 | | surface,
... |
39 | | Some(debug_messenger)
40 | | ) = WindowedContext::init(&settings)? else {
| | ^ --------------------------------- this expression has type `(VkInstance, phobos::PhysicalDevice, Surface, phobos::Device, DefaultAllocator, ExecutionManager, FrameManager, Option<DebugMessenger>)`
| |_____|
| expected a tuple with 8 elements, found one with 7 elements
|
= note: expected tuple `(VkInstance, phobos::PhysicalDevice, Surface, phobos::Device, DefaultAllocator, ExecutionManager, FrameManager, Option<DebugMessenger>)`
found tuple `(_, _, _, _, _, _, _)`
Yea it requires nightly unfortunately
Make your main return a Result<()> (anyhow::Result is good)
But generally nightly isn’t that “unstable”
I can maybe cut down on the features used though, I’ll try to remove them
Yeah its just I wanna code and not accidentally end up getting used to some experimental feature
The feature flags are not transitive luckily
So it doesn’t apply to your app, they’re only used when compiling the phobos dependency
And all unstable features require such a flag to be added
Ohh okay I see
If you want to switch in the meantime, it’s as simple as rustup default nightly (or stable to go back)
Ah, the example was also missing one tuple element for the device
i got rid of all but one unstable feature
i cant seem to get around using min_specialization for the Fence<T> Future<T> implementation
I recommend doing ```toml
phobos = { git = "https://github.com/NotAPenguin0/phobos-rs", rev = "bd0ea43" }
btw
this way you can easily stay on the git branch instead of waiting for releases, but the commit is fixed
ohh alright i see
Ohh what is the right version then?
its fixed in latest
you need to add a device after the surface
ohh that's it
yeah i just forgot one element in the example
some example code i tend to forget to update
most up to date one youll find is the raytracing sample
ohh yeah seems like there's an issue with how you're also handling
WindowContext::init()
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src\main.rs:43:41
|
7 | fn main() {
| --------- this function should return `Result` or `Option` to accept `?`
...
43 | ) = WindowedContext::init(&settings)? else {
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<std::result::Result<Infallible, anyhow::Error>>` is not implemented for `()`
you can only use ? in a function that returns a result
do cargo add anyhow, use anyhow::Result; and make main fn main() -> Result<()>
all good
anyhow is a crate that implements an error type that works for any library's error type
So it converts between them
ohh a compat layer
kind of
its really convenient for applications
i might move ph over to its own error type so it doesnt depend on anyhow
we'll see
Ok(())
() is the unit type, its the same as void really
except you can construct it
i got a window to open for 0.01 seconds before the program closed
BUT LETS GOOOOOO
no i literally just took the code from the examples
to make sure my pc is chill it vuk
i meant
phobos*
shhh
it does the boilerplate for the provided examples
03_raytracing is
02_headless_compute maybe
ohh okay. maybe add like test cases for them?
it has everything 
bro i feel like a cavemen exiting into the modern world
c++ developer discovers rust
ok so pub makes stuff public
yep
wait what is
.scratch_size(1 * 1024u64) // 1 KiB scratch memory per buffer type per frame
?
wait you allocate scratch for rasterization too?
currently you get a scratch allocator provided yeah
you can use it for quick uniform data for example
but its a bit different from staging buffers
iirc scratch memory is temp memory of sorts?
like it's the spot where if you need to work on some uniform or data on the gpu, you put it there
yeah
uniform buffers, a storage buffer thats updated each frame
or a small vertex buffer for gui for example
ohh alright thank you
you get this InFlightContext each frame in the new_frame() callback
oh yeah dumb question
I never understood the differnece between CpuToGpu and GPUOnly
Don't you either way have to transfer memory from the cpu to gpu? i.e. uploading vertex data from gltf
CpuToGpu is ideally DEVICE_LOCAL| HOST_VISIBLE, GpuOnly is HOST_VISIBLE
CpuToGpu is perfect for uploading
the name CpuToGpu comes from the fact that you can easily write to it from the Cpu (its mappable), and the gpu can easily read from it
OHHH
Make buffer for CpuToGpu then have a the GPU read it and make a GpuOnly buffer
? iirc that was in vkguide or smth
Thats for uploads with a staging buffer
These scratch buffers for example are also CpuToGpu
what do you have rn
im talking about my vuk rt project i kinda just assumd everything was GpuOnly unless vuk's examples told me otherwise
ahh
i mean, GpuOnly is best for performance
Stuff that you dont need to constantly write from the cpu is best to have GpuOnly
yeah then gpuonly is best
my dev folder is 90 gb
nah all projects
makes sense
Mine was around 200gb bc it includes projects I cloned but have forgotten to try out
each >25gb 
cleaning out build folders always helps
Doesn't help either if said projects have assets
yeah
that's actually much less than i expected
that is not a lot
taking into account it uses vk, winit and whatnot
yeah
what about andromeda
lets see
cargo tree lists 405 lines of output but theres a lot of duplicates, since its split up in multiple crates
using cargo-deps-list
not even rust is safe
i forgot that my math exams
are soon
so gotta study
tbh i just need to past 💀 since universities only look at your mid terms for admission
damn so this "exam" has zero problem solving whatsoever
the calculator part is so broken too 💀 You can use the graphing calculator to more or less get one line solutions and say GCD
I've only had one exam where a calculator was allowed
and there really wasn't anything you needed a calculator for in the first place
lmfao
ohh nono im prepping for my last high school exam
and yeah nothing reall
yits just that theyll make you calculate something stupid like normalcdf but it also means when they ask you to ingrate anything it's one liners 💀 or solve
Yo dumb question penguin
I hesrd that zig is good for unsafe messiness
Why hasn’t gp clung on that much to zig compared to rust?
zig is not safe
or rather does not prevent any unsafe operations
also meme lang too
dont tell herald
LMAO I thought it was serious 
funny part is they think they are too
heh i broke camera rotation
but not movement

hmm probably has to do with the way input events are triggering new input events
heh
ah
i do not register the input system lmao
as an added bonus fixing that once again deadlocked my program
cant publish the same event type from within its own handler
these chains can get pretty long but i do think its a bit nicer than passing everything around all the time
i broke my framebuffer scaling
Especially when adding multithreading into the mix later
I just fixed that 😄
That's rust yeah
I could maybe use ? here actually
The first unwrap() is needed because the lock could be poisoned
The second is needed because the dependency might not exist in the registry, and theres no meaningful way to continue if it isnt (its just an initialization error)
And I have to handle these errors to access the value so unwrap it is 🤷
In a lot of cases you can just let value = may_fail()?;
: )
I know unwrap sucks
But this TryLockResult doesnt implement Send so I can't use ? on it
Maybe I should fix that
how are you gonna fix it
Im not sure actually
Maybe I can make the inner T Send safely but I don’t even know if that would be sound and if it would help
changing the compiler?
if the result is not send there must be a good reason for it
or something else
Probably
its private by default if you don't put pub on it
pub enum AssetRef<'a, A: Send + 'static> {
Pending,
Invalid,
Failed(anyhow::Error),
Ready(&'a A),
}
I hope this is not too annoying to work with if I return this from my asset get() function
Maybe if I add some helper functions
Ok one thing I am always struggling with is getting around the following problem
I need some struct like the following
struct RefToInnerLock<'a, T> {
lock: RwReadLockGuard<'a, T>,
value: &'a SomeTypeInT
}
Now this won’t ever compile because it’s a self-referential type which would never work
Currently I’m trying to make something like ```rs
pub struct AssetReadLock<'a, A: Send + 'static> {
lock: RwLockReadGuard<'a, AssetContainer<A>>,
asset: RefCell<Option<AssetRef<'a, A>>>,
}
Where the option inside the `RefCell` is lazily filled in `Deref`
But it stinks and I don’t even know if it’ll work
seems like this isnt possible either
Can't you create a RwReadLockGuard from the member value?
Or wait
i dont think so
Why are you passing the lock around in the first place
because if i drop the lock the reference to the inner value is invalidated
i may have an unsafe workaround
Yes, Rust takes care of that
the public interface does
i have a RwLock<HashMap<K, V>> and I need a &V
not really
So you have something like
member fn {
let lock = self.get_map();
return &lock.get(key);
}```
Yeah
the cleanest way to handle this is by passing a lambda
Yeah I know thats possible but it gets really messy with many asset lookups
Also I feel like since I only need immutable access I can do this cleaner
No I don't think having it immutable makes anything much easier
The issue is that the lock must stay inside
So you can only use it inside
So
member fn(do_fn: impl FnOnce(&V) -> T) -> T {
let lock = self.get_map();
do_fn(&lock.get(key))
}```
a lot of crates do this btw
including std
enum AssetPtr<A> {
Invalid,
Failed(*const anyhow::Error),
Pending,
Ready(*const A),
}
impl<A> AssetPtr<A> {
unsafe fn as_ref<'a>(&self) -> AssetRef<'a, A> {
match self {
AssetPtr::Invalid => AssetRef::Invalid,
AssetPtr::Failed(err) => AssetRef::Failed(&**err),
AssetPtr::Pending => AssetRef::Pending,
AssetPtr::Ready(ptr) => AssetRef::Ready(&**ptr),
}
}
}
pub enum AssetRef<'a, A> {
Invalid,
Failed(&'a anyhow::Error),
Pending,
Ready(&'a A),
}
pub struct AssetReadLock<'a, A: Send + 'static> {
lock: RwLockReadGuard<'a, AssetContainer<A>>,
asset: AssetRef<'a, A>,
}
impl<'a, A: Send + 'static> AssetReadLock<'a, A> {
pub(crate) fn new(lock: RwLockReadGuard<'a, AssetContainer<A>>, ptr: AssetPtr<A>) -> Self {
Self {
lock,
// SAFETY: We immediately dereference the pointer we just got
asset: unsafe { ptr.as_ref() },
}
}
}
This is my current (unsafe) approach

Yeah I dont mind this for a lot of things but I feel like this would get really annoying with many assets
// SAFETY: We immediately dereference the pointer we just got
this does not say much
yeh true
in fact i have no idea whether that is ub or not
idk either
i wouldn't risk it, but if you think it's really really really worth it
normally when i reach situations like this i step back and see what i've done to get to there instead of forcing the decision
yeah
it feels absolutely overengineered what you guys do here for some reason
getting lost in rustisms 😄
pub fn with<A, R, F>(&self, handle: Handle<A>, f: F) -> Option<R>
where
A: Asset + Send + 'static,
F: FnOnce(AssetRef<A>) -> R, {
let lock = self.inner.read().unwrap();
lock.with_container(|container| {
let entry = container.items.get(handle);
entry.map(|entry| {
let asset = Self::poll_entry(entry);
f(asset)
})
})
}
let assets = di.get::<AssetStorage>();
assets.with(my_handle, |asset| {
// do something with asset if it existed
});
ARF 🙂

what is R and what is F?
ill also add with_if_ready that only calls f if the asset was in the ready state
i get A is Asset
F is a function that is called with the asset if it was found
R is the return value of that function
can you not name those things like that then? 🙂
I suppose
WhenAssetFound instead of F
ReturnValue instead of R or TReturn at least dunno if rust peeps use T... for generic paramaters
I tend to use single letter generics too much 
there is one caveat with this
if you call with before loading an asset of that type there wont be a container for that asset
Which right now results in a panic
return a NullAsset/Unit then
since this is functional inspired stuff it seems
or rather dysfunctional 😛
i want to but i dont know how 
I mean, I know a way but it involves a lot of redundant locking
get read lock, check if container exists, if not release read lock and get write lock, insert it, release write lock, get new read lock, continue as if nothing happened
hmm
: D
fn with_container<'a, A, F, R>(&'a self, f: F) -> R
where
A: Send + 'static,
F: FnOnce(RwLockReadGuard<'a, AssetContainer<A>>) -> R, {
let lock = self.inner.read().unwrap();
let maybe_container = lock.containers.read_sync::<AssetContainer<A>>();
match maybe_container {
None => {
drop(maybe_container);
drop(lock);
let mut lock = self.inner.write().unwrap();
lock.with_new_container(f)
}
Some(container) => {
f(container)
}
}
}
unfortunately this pattern doesnt compile
The read lock is on the container list yeah
i can read other languages usually, but this code is 100% unreadable

and it is 0 clear what its intent is or w🇹🇫 it is supposed to do
its mostly internal but i should go around my codebase clarifying things for future me
because i will forget
dont get me wrong
Well this doesnt work anyway
its mostly just me being an idiot especially when it comes to rust
I mean I was going to do that anyway tbh
Though you are right its not very obvious
im not 17 anymore
oh it compiles if i remove the lifetime lol
that shouldnt have been there
okay that fixes the load before get issue
Okay heres a design question
Querying an asset gives this
pub enum AssetRef<'a, A> {
Failed(&'a anyhow::Error),
Pending,
Ready(&'a A),
}
Basically either a failure during load, its still loading, or a ready asset
with(handle, callback) lets you handle these cases yourself
Now I probably want to add a with_if_ready that only calls the callback if the asset is ready
But I wonder if that should also call the callback if it is ready but with an error status
So basically
with_if_ready(handle, |asset| {
// can assume asset loaded successfully
});
vs
with_if_ready(handle, |asset| {
// must handle error case
});
I like the former because its simple, but this error must be reported at some point so maybe the latter is better
Alternatively I use the first and periodically check all assets for errors and report/remove failed ones
hmm so you basically want your whole api to be "fluent"
Yeah ideally
thats quite the brainworm
it really is 😄
i get fluid api for builder pattern
I cant just ignore the error indefinitely but handling it on each access is wasteful and annoying
or unit tests
or otherwise smaller things where you need/want to - well - build something
but keep the rest "ordinary"
ifs and elses and otherwise conditional blocks
unless, its yet another thing amongst rustaceans 🙂
to make everything fluent
The thing is this needs to be a callback because of rust isms 
in monke words: do not return reference to local variable
other way to say shrimple
monke was a gorilla who killed someone?
no no thats a different monkey
I think some sort of "asset garbage collector" might not be awful
which is more or less just a List<IDisposable> (a list of disposables, a thing which has a Dispose method)
yeah it would just go over all assets, check which ones failed to load, report their errors and remove them
yeah
and do that every now and then
😛
and im going to need another one of those tasks to resolve pending promises when they are ready
I was going to do that in with originally but that wont work because it needs mutable access
I guess that also means its a good idea to use a HopSlotMap over SlotMap, if I'm going to be iterating over all assets somewhat frequently
i read HopSlob 😄
what is it a reference to? freya's bot is also called that
i have no idea
also you usin latex or typst?
i recommend it fully
it does look pretty good tbh
the most useful feature it has is that you can code in it
but the new syntax is appreciated too (although it takes a while to get used to)
a lot of whitespace
the columns are intentional but uh
I added some text above and now the whitespace is all messed up
maybe it's trying to place something but it don't fit
Yeah we have some very bad code to put a table somewhere
this table
spans both columns
ah, making some random image a bit smaller works 
bruh literally microsoft word
literally
tokio::time::sleep(Duration::from_secs(RESOLVE_INTERVAL_MS)).await;
oops
Accidentally running the asset gc every 500 seconds instead of ms
i may come to regret doing this
pub struct Texture<F: TextureFormat> {
pub image: PairedImageView,
marker: PhantomData<F>,
}
making textures typed over their format
wtf why did you do that
actually there's a fix
introduce a dynamically formatted texture type and add a conversion method from the typed one
im just working on it to see if its viable
generally you dont want random dynamic image types: a heightmap is always expected to have a single-channel f16 texture for example
contributed to rust: ✅
yes but for e.g. assets you do not know the image format
cause not everything is hardcoded
yeah but every asset needs to be in a finalized consistent format after loading
color textures must be srgba8
so the impl can do a conversion to that on load
you don't allow other formats other than srgb8 for color textures?
i dont plan to without a conversion
the conversion happens automatically anyway
like you can load anything from disk
I ditched the asset GC
It was smelly
Now the asset task removes itself from the storage if it fails
I have a minor issue with the way my asset system works but its not big deal
pub struct Heightmap {
pub image: Handle<Texture<HeightmapFormat>>,
}
I have this asset type for example
If I want to do something when this is ready, I need to write
let handle: Handle<Heightmap> = ... ;
assets.with_if_ready(handle, |heightmap| {
assets.with_if_ready(heigtmap.image, |image| {
// done
});
});
Though maybe in this case I can store a texture directly actually
but this wont work when I have multiple sub-assets inside
pub type HeightmapFormat = Grayscale<f16>;
#[derive(Debug)]
pub struct Heightmap {
pub image: Texture<HeightmapFormat>,
}
pub struct HeightmapLoadInfo {
pub path: PathBuf,
}
impl Asset for Heightmap {
type LoadInfo = HeightmapLoadInfo;
fn load(info: Self::LoadInfo, bus: EventBus<DI>) -> Result<Self>
where
Self: Sized, {
load_from_image(info, bus)
}
}
// Normalizes height values in the height map to [-1, 1] based on the most extreme value
fn normalize_height(width: u32, height: u32, data: &mut [LumaPixel<f16>]) -> Result<()> {
trace!("Normalizing heightmap data");
// Find the largest absolute value in the dataset, and take the absolute value of it.
let extreme_val = data
.par_iter()
.max_by(|lhs, rhs| lhs.to_f32().abs().total_cmp(&rhs.to_f32().abs()))
.unwrap();
let extreme_val = f16::from_f32(extreme_val.to_f32().abs());
let extreme_val_inverse = f16::ONE / extreme_val;
// Now divide every height value by this extreme value
data.par_iter_mut().for_each(|value| {
**value *= extreme_val_inverse;
});
Ok(())
}
fn load_from_image(info: HeightmapLoadInfo, bus: EventBus<DI>) -> Result<Heightmap> {
let tex_info = TextureLoadInfo::FromPath {
path: info.path,
cpu_postprocess: Some(normalize_height),
};
// Because we only load one image, we can get away with not doing this in another
// async task through the asset system. This also makes it a bit more ergonomic to
// access the image inside the heightmap because we don't need to go through two layers of
// handles.
let image = Texture::load(tex_info, bus)?;
Ok(Heightmap {
image,
})
}
This entire file got so much simpler now after the asset system rework, it's so nice
assets.assets.assets.assets 😛

It is a lot less code than before though, I used to handle all the task spawning and joining inside each asset load function which was really messy
now I just ask for a handle and its done
THandle GetHandle<THandle>() why not 😛
cant you do this stuff with async instead?
im not sure, its kinda hard because of the locking involved
thats how I ended up with this callback approach
ngl compiler errors on using a wrong color space is kinda nice to have
I, too, should do structured rambling on the internet
although you were always able to do that before
just host with github 
9 eur for the first year
that's actually fair
30/year or so after that
i need to figure out how to set up https
but its not that big of a deal
they always get you with the renewals
true
https://www.notapenguin.blog/ got an empty jekyll site running now
Welcome to my page, where I ramble about various things, usually related to computer graphics or Rust.
i quite like this theme
i still need a better name tho, this is boring
I wonder if my asset system is interesting enough to write something about
There's a single result for your name
There could be another person with your exact same name lol
that theme is nice
It says 5th place in some regional coding contest when I was in hs
i think mine is still default theme
But I got 2nd the year after 
only 5th??? smh smh smh
Yeah I know
The second time I got 2nd without my team
It was like a cp contest except you have 3 people on 1 laptop
Or 4 i think
Except my teammates didn’t rly do anything and I did everything that year

like a what contest
Competitive programming
Ahhh
@shadow trench Nice copypaste of martty's blog 
is it the same theme?
ye
Intro GPUs are complex beasts - and certainly more mysterious in some ways than CPUs which come with ample amounts of documentation and manuals. Aspiring graphics programmers (too insignificant to have a devrel contact to give insight) are sometimes left to scrounge old GDC presentations on performance tips, with very little known about the inne...
dammit now I need a different theme
its on the front page of the jekyll theme list
also i hadn't seen this lmfao
its not the only theme by far, there are lots, i'm sure you can find one that you like more
but otherwise you can ofc stay with this one
yeah ill look a little further
i should post something on my blog again, i posted a single article and nothing more since 2021
mewby hardware stuff
why not post about the thingy thats secret
thats actually kinda secret
ahh
:p
lmao
pRoJeCt vExEd
This isnt too bad but I really dont have a good image to put there
most screenshots from renders just arent vertical like that
Cant you change your renderer resolution?
Yeah I can but it would look kinda weird probably
literally pick any jekyll theme, fork it and call the repo githubname.github.io
https://sfreytag.github.io/friday-theme/ is the theme I am using. Need to modify the scss though for dark mode
Yeah it's super simple to set up
sneak peak time
bruhhhhw hat you doin
transmuting a dyn T to an impl T
that doesn't work like that, dyn objects are completely different types of pointers
they're fat pointers
you cannot cast that at all
you can downcast tho
I know that doesn’t work 
It does work with ThinBox
That’s the point I’m trying to make
dyn Any to dyn T
ah i see
Its UB because the vtables don’t match
But with ThinBox the vtable is on the heap so you can “safely” transmute
when is this rustism over and we can finally see some terrainisms again? 🙂
Soon ™️
Sometimes I have to set up some infrastructure to continue
While writing the post I realized theres an easy way to allow for recursively publishing the same event type without deadlocking
Easy fix
Actually it might still deadlock, just later down the line
Oh well
so thinbox is like regular c++ oop?
yeye
ive written a first draft of a post https://www.notapenguin.blog/posts/rust-event-systems/
this one is very rust-focused i suppose
if you only suppose it is rust focused 
said no one ever
W
the design is somewhat inspired by yours actually
Yea lol I noticed some similarities here and there
Yours seems tk be more focused on actual proper events though
Yeah I wanted no hardcoded event types
Yeap
I wanted to implement such a thing when writing my event system but I found it not too rewarding
So in your game/game engine you can have custom user events?
I'm not making a game engine, but yeah that's definitely possible
I liked this because it allows me to add new event types easily
Yea
@shadow trench considering using phobos for rex
maintaining a nih graphics backend is hard work
it do be
and i kinda need just something done for the editor
i have an egui backend too
I'm also thinking of using egui for my game engine editor
Idk how to handle reflection though
Probably through bevy_reflect
or a simple handrolled proc macro
tho i assume thats pretty much exactly what bevy_reflect does
yeah except good
use bevy_reflect, don't bother nihing your own solution, it ain't worth it
didnt you try that at some point

that is why i say that its not worth it
maybe, but it looks like a fun challenge to solve 
debugging deadlocks 
time to add a wrapper around RwLock that logs everything it does
Wrap<RwLock<Wrap<RwLock<X>>>>
I'll just name it RwLock too and make sure to import the right one
They have the same API anyway
The other name I came up with was InstrumentedRwLock<T> but that feels a bit verbose
Instrumented suggests it contains instrumentation
Yeah I don't really know what else to call it
It should log its acquires, possibly throw a warn if its held for too long, stuff like that
things i can toggle off for release builds ofc
intrumentation is more like collecting shrimples
true
hm alright I fixed my deadlock
im not sure I like the fix a lot tbh
The problem is I have this with_when_ready() method on my asset system which blocks the caller until an asset load is completed
But its implemented as a dumb polling loop
make it async orsomethin
I need to think about it yeah
await await it();
I think I can maybe use a channel instead and yeet a message through it when load completes
Then I can just wait on the channel instead which doesn’t need a polling loop
I assume it uses some unsafe state internally to wake the future on send() no?
Idk honestly
But it would hide that loop into the runtime
And I can use something like a broadcast channel to only have to lock the asset system shortly to retrieve a copy of the receiver and await it after
That might work
/// Calls the provided callback with the given asset, blocking the calling thread until it is ready.
pub fn with_when_ready<A, R, F>(&self, handle: Handle<A>, f: F) -> Option<R>
where
A: Asset + Send + 'static,
F: FnOnce(&A) -> R, {
// Poll the asset system once for the current status of the asset.
// * If the asset doesn't exist, don't call `f`
// * If the asset load completed, call `f` if it was successful
// * If the asset load is still pending, we get a broadcast channel that we can wait on
let poll = self.poll_asset(handle);
match poll {
// Wait for the broadcast message and call `f` if we receive a `Success` message.
PollResult::Pending(mut rx) => {
let message = tokio::runtime::Handle::current().block_on(rx.recv()).ok()?;
match message {
AssetLoadMessage::Success => self.with_if_ready(handle, f),
AssetLoadMessage::Fail => None,
}
}
PollResult::Failed => None,
PollResult::Ready => {
// We know the asset is ready, so this will always call `f`.
self.with_if_ready(handle, f)
}
}
}
this works yeah, and it holds the lock only for a short time
And it's not a dumb polling loop, I get to use whatever smart tricks tokio uses to optimize scheduling broadcast channels
uh oh
somewhere down the line half my mesh disapeared
Well, not destroying staging buffers before uploading would be helpful

lets check out release mode first
much better
Even better, my program keeps running fine even if an asset loader panics due to a library bug
Looks either distorted or maybe half is being culled
this 
oh lmao
I had this code
struct BufferCopyResult<'a, D: ExecutionDomain> {
pub cmd: IncompleteCommandBuffer<'a, D>,
pub buffer: Buffer,
pub staging: StagingBuffer,
}
...
let BufferCopyResult {
cmd,
buffer: vertices,
..
} = copy_buffer( ... );
which apparently drops the staging buffer
yeah I fixed it, I thought it would just keep it floating around somehow 
which i realize now doesnt make sense
for a quick api fix you could make it private and introduce a getter
thats just a hack tho
I just gave it a name for now
I was thinking of writing my own reflect macro as well lol but then realized that bevy reflect isn't so bad
looks like a star destroyer
i kinda see it
@shadow trench sorry for this OT question, but do you remember the gl validation layer nonsense jaker you and tried to toy with?
i cant find it in the projects posts or did we remove it somehow or was that just a thread somewhere?
Yeah that project
@hot yarrow you thinking of reviving it?
maybe we used the fwog thread?
#opengl message
I found this message which has a link I don't have access to
i was trying to remember what and where it was for LVSTRI to perhaps have a go at it 🙂 since he just started with gl and is neck deep into MDI/compute/hiZ and whatnot already and complains about opengl not providing proper yelling at him hehe
yes
What mysterious lands does it lead to
gets me here
Yeah I meant the one in that message
ah
Though considering it says Unknown for you too I guess it maybe got deleted
when we recreated the forum channel
maybe i can resurrect the messages from it somehow into a new post in #1019722539116802068
Mayhaps
will take a looksie over the weekend

new warnings added
these should help track down deadlocks
I also added a similar one that checks if a lock is blocking for over 100ms
Quick test shows it working nicely for a simple deadlock
let _lock = lock.read();
let lock = lock.write();
Or you can try and use a try_read try_write system
That would return an error of the lock was already locked
That's how I do things (though instead of using RwLock I use a refcell)
Yeah that's possible but it's a lot more annoying to use
Also inside like a renderer hot path there's really not much room to keep retrying
but these logs are mostly just for debugging in case of deadlocks
the last one was a huge pain to track down so
Ok I think it's time to work on the first major terrain editor feature
@hot yarrow you're going to like this
I'm going to make the first brush tools
That said I don't really know where to begin
Perhaps getting mouse click positions in worldspace isn't a bad start
I made a little list of tasks
Well it's not very easy
I need to figure out how to insert arbitrary graphics work but not every frame
: ) no rush

Happens like every other week here
oof
Most of the time we can stay inside but this time we had to evacuate
skool? or home?
ah
lmao
someone opened an issue on my repo
once again another reason to never use vec3s on the gpu
i frogor the alignment rules again
yeah it does
maybe you could just host the assets elsewhere and download them at startup
thats what i was thinking too yeah
tho eventually you provide your own assets to use
andromeda launcher lol

that could work
if needed yeah
im trying to figure out why debug builds are so slow and this is the top function 
wat it doin
it creating backtraces everywhere

should it not only do that on error
oh wtf i get it
I have a bunch of ok_or(anyhow!(...)) calls
which always constructs the error case
and captures a backtrace and everything
i assume thats optimized out in release but not in debug
bruh!!!!
ok_or_else
that is a good idea
clippy should notify you of that iirc
not in its default config I think
theres a lint to use ok_or_else for anything that requires computation
maybe its getting confused by the macro
Yeah maybe
Yeah I didn't know that was such a huge deal lol
holy shit thats so much better lmao
its a heap alloc + backtrace if enabled vs a branch predicted jump
you should search for other ok_or usages
frametime with debugger went from 70ms to 20ms lmao
Yeah I did project-wide already
ill check phobos too
now time to figure out how to map world position to uv
should not be too difficult to figure out
Step 1 complete
why cant i find motivation to learn any gp 💀 i'll phobos soon
What'd you do
The err thing lol
@potent quest im reworking frame submissions a little so its more in line with out of frame submits and also allows submitting multiple command buffers
I've had this SubmitBatch thingy for a while to get multiple command buffers and semaphores between them
Im going to try to make it so that to submit frame commands you just return that struct instead of a single command buffer
Seems to work nicely
let cmd = cmd.finish()?;
let mut batch = ctx.exec.start_submit_batch()?;
batch.submit_for_present(cmd, &ifc)?;
Ok(batch)
Should also be able to submit any amount of command buffers in a frame, and have sync applied nicely
(and its all batched in one VkQueueSubmit
The only drawback is they must be on the same queue for now
It might be possible to get around that later
alright I can now add a command buffer to the next running frame from anywhere on any thread
this is mostly useful for commands that have to be synchronized to resources that are also used in the frame
something is happening
im only updating one pixel in the heightmap but
it very delayed for some reason
im guessing the brush event arrives way after the click for some reason
no thats not it
hmmm
time to pull out the lock timers again and see if its not stuck waiting on one
i think the delay is fixed, its just kinda putting the pillars not at the right place
terrain editing 101
looks fine to me
ogldev is working on terrain as well and his current series is quite great on geomipmapping+lod isms
theyre slightly offset from where im clicking 
ah those are the locations where you clicked?
hmm is this raytracing the position?
ah
It lags one frame behind but that shouldnt matter
are you including some non client area isms perhaps
not sure
ill log everything and see what comes up
fn height_uv_at(world_pos: Vec3, options: &TerrainOptions) -> Vec2 {
// First compute outer bounds of the terrain mesh
let min_x = options.min_x();
let min_y = options.min_y();
let max_x = options.max_x();
let max_y = options.max_y();
// Then we get the length of the terrain in each dimension
let dx = (max_x - min_x).abs();
let dy = (max_y - min_y).abs();
// Now we can simple calculate the ratio between world_pos and the length in each dimension
// to get the uvs.
// Note that we use the z coordinate since y is up, and our terrain is in the flat plane.
// We will assume our terrain is properly centered. In this case, the uvs we get are
// in the [-0.5, 0.5] range, so we need to remap them to [0, 1]. Since this range is
// of the same size, we can just add 0.5
let uv = Vec2::new(world_pos.x / dx, world_pos.z / dy);
uv + 0.5
}
This is what im doing to get the uv from the position
Clicking a corner seems about right though
Same with the opposite corner 🤷
implicit return
sheesh
i think that would drive me crazy lol
not when remapping position to uv
fuck you discord for not scrolling me to the bottom
but im sorry, i didnt mean to start a language war now
float calculate_weight(float distance) {
float max_distance = sqrt(2) * pc.size / 2.0;
float distance_ratio = distance / max_distance;
return exp(-distance_ratio);
}
[numthreads(16, 16, 1)]
void main(uint3 GlobalInvocationID : SV_DispatchThreadID) {
uint w, h;
heights.GetDimensions(w, h);
int2 texels = int2(float2(w, h) * pc.uv);
int2 offset = int2(GlobalInvocationID.xy) - int(pc.size / 2);
texels = texels + offset;
if (texels.x < 0 || texels.y < 0 || texels.x >= w || texels.y >= h)
return;
float dist = length(float2(offset));
float weight = calculate_weight(dist);
float height = heights.Load(int3(texels, 0)) + 0.1 * weight;
heights[texels] = height;
}
this is my komput shader for editing the heightmap
its supposed to grab a size range around the clicked pixel and update the heights around it
It may be bugged but it's still fun to play around with
i like it anyway, although your fov looks fucked
adapted stolen from lvutner
and that rough/powderesqe finish on that terrain
just phong?
I think @wispy ore did something with that to stitch them together
but im not doing that yet
ah I have an idea to debug this
I can try to color the area around where it thinks the mouse is
good idea
that should be fairly easy to do too
actually I want to do that eventually anyway to have some kind of decal that shows where youre brushing
i think almost exactly 20 years ago was the last time i fiddled with terrain :C
:o
had a little world editor for maps for Ultima Online freeshards
anytime
