#Andromeda - Terrain/Landscape editor using Vulkan and Rust

1 messages · Page 3 of 1

potent quest
#

seems like my gltf extension died thanks to the update

#

you rec some for clion?

shadow trench
#

i dont use any

potent quest
#

🗿

#

alr thank you

livid geode
#

its incredibly useful tho

#

rebind without changing mutability

shadow trench
#

i dont use it a whole lot but when I do its a lot nicer than the alternative

shadow trench
#

Im heading to bed @potent quest, if you have any questions you can just leave them here and I’ll answer in the morning

shadow trench
#

3 left to fix compilation of

#

app, renderer and gui are all broken

livid geode
#

why

#

what yu doin

shadow trench
#

moving everything into subcrates instead of submodules

#

also switching to the event bus thingy where possible

#

but one thing at a time

livid geode
#

send repo when you push those changes

shadow trench
#

sure

shadow trench
#

the renderer compiles

#

now to glue it all together again

shadow trench
#

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

potent quest
#

Wha subcrates?

shadow trench
#

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
    }
  }
}
potent quest
#

What why

#

Uh

shadow trench
#

i fixed it by pushing events to a buffer and flushing them after

#

i have another issue to fix now :P

shadow trench
#

not really related to my project

#

but my little embedded journey for school is making progress

livid geode
#

thats awesome

#

@shadow trench is that a regular lcd w a reflective panel?

shadow trench
#

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

wispy ore
shadow trench
#

just my various ramblings KEKW

potent quest
#

holy shit

#

they werent kidding when they said

#

the rust compiler teaches you rust

young oar
#

At least we got fallible functions for that (I think?)

shadow trench
#

scrolling and keyboard input is working too now :)

#

all in rust btw

potent quest
#

Bad apple when

shadow trench
#

funnily enough, the site where they sell these shows bad apple running as a demo

potent quest
#

💀 LMAOOO

#

bad apple is the universal bench mark

shrewd lynx
#

sure a good way to entice weebs to buy it

#

good marketing strategy

shadow trench
potent quest
#

Penguin sorry to turn this channel in rust, but

#

did learning rust make you a "better" c++ developer?

wise viper
#

Im not penguin but it made me a better c++ dev

shadow trench
#

I agree yeah

spare bay
#

I mean, the more language you know the better you become at you favourite

potent quest
#

step 2: how to borrow checker

shadow trench
#

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
potent quest
#

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

shadow trench
#

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

potent quest
#

ok i think i get it

#

thank you!

shadow trench
#

habibi_pray anytime

#

Typically Clone is an “expensive copy” while Copy is just a memcpy

potent quest
#

they literally forced you to follow RAII

shadow trench
#

Yes

#

Without unsafe you cannot possibly leak memory

potent quest
#

mfw all the rust code ive seen using vulkan wraps vk functions with unsafe

shadow trench
#

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

potent quest
#

ohh i see

#

but the returned data is safe from the call*

shadow trench
#

In theory any unsafe block could cause UB

#

But using unsafe for vk calls is no big deal

potent quest
#

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

shadow trench
#

But suppose you would send r2 to another thread and then proceed to try to modify s

#

This won’t compile at all

shadow trench
#

many immutable shared references is fine

#

But from the moment you want mutability, it’s a data race

potent quest
#

note to self: make my pointer faster to win the race 😄

shadow trench
#

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

potent quest
#

fun fact: i have yet to learn multithreading 💀

#

most ive done was slap openmp onto my math project

potent quest
#

eh rust tutorial said that was a later me problem

#

so ill leave it like that

wise viper
#

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

shadow trench
#

Ooh yeah true I guess

#

Seems unlikely to actually run into tbh

wise viper
#

yea it is

shadow trench
wise viper
#

i dont think this ever happened to me

potent quest
#

ok so rust slices are like memory safe buffer slices

shadow trench
#

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

potent quest
#

ohh ic

potent quest
#

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

potent quest
#

also i think

#

the example given

#

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 `(_, _, _, _, _, _, _)`
shadow trench
shadow trench
#

But generally nightly isn’t that “unstable”

#

I can maybe cut down on the features used though, I’ll try to remove them

potent quest
#

Yeah its just I wanna code and not accidentally end up getting used to some experimental feature

shadow trench
#

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

potent quest
#

Ohh okay I see

shadow trench
#

If you want to switch in the meantime, it’s as simple as rustup default nightly (or stable to go back)

shadow trench
#

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

#

this way you can easily stay on the git branch instead of waiting for releases, but the commit is fixed

potent quest
#

ohh alright i see

potent quest
shadow trench
#

its fixed in latest

potent quest
#

huh

#

it's still complaining about the tuples

shadow trench
#

you need to add a device after the surface

potent quest
#

ohh that's it

shadow trench
#

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

potent quest
#

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 `()`
shadow trench
#

you can only use ? in a function that returns a result

#

do cargo add anyhow, use anyhow::Result; and make main fn main() -> Result<()>

potent quest
#

ohh alright

#

im so sorry for making you teach me rust 💀

shadow trench
#

all good

#

anyhow is a crate that implements an error type that works for any library's error type

#

So it converts between them

potent quest
#

ohh a compat layer

shadow trench
#

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

potent quest
#

oh wait

#

what do i return in main?

shadow trench
#

Ok(())

#

() is the unit type, its the same as void really

#

except you can construct it

potent quest
#

i got a window to open for 0.01 seconds before the program closed
BUT LETS GOOOOOO

shadow trench
#

Do you have a main loop

potent quest
#

no i literally just took the code from the examples

#

to make sure my pc is chill it vuk

#

i meant

#

phobos*

#

shhh

potent quest
#

oooh alright

#

so i gotta guess the examples are reliable*

shadow trench
#

it does the boilerplate for the provided examples

#

03_raytracing is

#

02_headless_compute maybe

potent quest
#

ohh okay. maybe add like test cases for them?

shadow trench
#

the basic one i gotta fix

#

Yeah

potent quest
#

wtf cargo has testing tooling

#

bro what

shadow trench
#

it has everything kek

potent quest
#

bro i feel like a cavemen exiting into the modern world

shadow trench
#

c++ developer discovers rust

potent quest
#

i should probably finish learning rust

#

especially lifetimes

shadow trench
#

eh lifetimes you learn as you go

#

in the end they do make a lot of sense tbh

potent quest
#

ok so pub makes stuff public

shadow trench
#

yep

potent quest
#

struct is like a bluepritn

#

then impl is like the .cpp file to the .h

shadow trench
#

sure

#

in the end structs are like classes

potent quest
#

wait what is
.scratch_size(1 * 1024u64) // 1 KiB scratch memory per buffer type per frame
?

#

wait you allocate scratch for rasterization too?

shadow trench
#

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

potent quest
#

iirc scratch memory is temp memory of sorts?

shadow trench
#

yeah

#

in this case its cleared every frame

potent quest
#

like it's the spot where if you need to work on some uniform or data on the gpu, you put it there

shadow trench
#

yeah

potent quest
#

ohh alright

#

thank you

shadow trench
#

uniform buffers, a storage buffer thats updated each frame

#

or a small vertex buffer for gui for example

potent quest
#

ohh alright thank you

shadow trench
#

you get this InFlightContext each frame in the new_frame() callback

potent quest
#

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

shadow trench
#

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

potent quest
#

OHHH
Make buffer for CpuToGpu then have a the GPU read it and make a GpuOnly buffer

#

? iirc that was in vkguide or smth

shadow trench
#

Thats for uploads with a staging buffer

#

These scratch buffers for example are also CpuToGpu

potent quest
#

ohh\

#

wait i might need to redo my buffers then KEKW

shadow trench
#

what do you have rn

potent quest
#

im talking about my vuk rt project i kinda just assumd everything was GpuOnly unless vuk's examples told me otherwise

shadow trench
#

ahh

#

i mean, GpuOnly is best for performance

#

Stuff that you dont need to constantly write from the cpu is best to have GpuOnly

potent quest
#

ohh okay

#

i might be fine then since i only write my blas and tlas once

shadow trench
#

yeah then gpuonly is best

shadow trench
#

MonkaS my dev folder is 90 gb

haughty valley
#

rookie numbers

#

193GB

shadow trench
#

I deleted a couple gb of random assets and .vs folders

haughty valley
#

ah just that one project

#

that is quite big

#

or all projects?

shadow trench
#

nah all projects

haughty valley
#

ah

#

yeah my biggest 3 are kernel, CTS and the amdvlk driver 🐸

shadow trench
#

froge makes sense

shrewd lynx
#

Mine was around 200gb bc it includes projects I cloned but have forgotten to try out

haughty valley
#

each >25gb frog_sweat

shadow trench
#

cleaning out build folders always helps

shrewd lynx
#

Doesn't help either if said projects have assets

shadow trench
#

yeah

livid geode
#

you know you're using rust when your project's folder is 13gb

shadow trench
#

300 dependencies

livid geode
#

that's actually much less than i expected

shadow trench
#

that is not a lot

livid geode
#

taking into account it uses vk, winit and whatnot

shadow trench
#

yeah

livid geode
#

what about andromeda

shadow trench
#

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

shadow trench
#

oh no

#

im only crashing in release

potent quest
#

not even rust is safe

shadow trench
#

happens

#

hows progress on your end

potent quest
#

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

shadow trench
#

happens

#

gl

potent quest
#

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

wispy ore
#

and there really wasn't anything you needed a calculator for in the first place

#

lmfao

potent quest
#

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

potent quest
#

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?

shadow trench
#

Because zig is a meme lang KEKW

#

Its a lot closer to C really

livid geode
#

zig is not safe

#

or rather does not prevent any unsafe operations

#

also meme lang too

hot yarrow
#

dont tell herald

potent quest
#

LMAO I thought it was serious KEKW

shadow trench
#

funny part is they think they are too

shadow trench
#

heh i broke camera rotation

#

but not movement

#

hmm probably has to do with the way input events are triggering new input events

hot yarrow
#

heh

shadow trench
#

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

hot yarrow
#

i broke my framebuffer scaling

shadow trench
#

Especially when adding multithreading into the mix later

shadow trench
hot yarrow
#

this .unwrap()...unwrap() looks super cursed

#

but its what its in rust i guess

shadow trench
#

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()?;

hot yarrow
#

: )

livid geode
#

use ? 😭

#

unwrap is nasty

shadow trench
#

I know unwrap sucks

#

But this TryLockResult doesnt implement Send so I can't use ? on it

#

Maybe I should fix that

livid geode
#

how are you gonna fix it

shadow trench
#

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

livid geode
#

changing the compiler?

#

if the result is not send there must be a good reason for it

#

or something else

shadow trench
#

Probably

potent quest
#

yo penguin

#

how would you uh make a private member in a struct

livid geode
#

its private by default if you don't put pub on it

shadow trench
#
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

shadow trench
#

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

shadow trench
#

seems like this isnt possible either

livid geode
#

Or wait

shadow trench
#

i dont think so

livid geode
#

Why are you passing the lock around in the first place

shadow trench
#

because if i drop the lock the reference to the inner value is invalidated

#

i may have an unsafe workaround

livid geode
livid geode
#

Use the power of lifetimes

shadow trench
#

the public interface does

livid geode
#

*lock is valid for 'a

#

or at least you can force it to be afaik

shadow trench
#

i have a RwLock<HashMap<K, V>> and I need a &V

livid geode
#

okay

#

How about (*lock).get(key)

shadow trench
#

returning that drops the lock

#

so i need to also return the lock somehow

livid geode
#

Can you pass the lock from outside the fn?

#

May I have some context

shadow trench
#

not really

livid geode
#

So you have something like

member fn {
  let lock = self.get_map();
  return &lock.get(key);
}```
shadow trench
#

Yeah

livid geode
#

the cleanest way to handle this is by passing a lambda

shadow trench
#

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

livid geode
#

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

shadow trench
#
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

livid geode
shadow trench
livid geode
#

// SAFETY: We immediately dereference the pointer we just got
this does not say much

shadow trench
#

yeh true

livid geode
#

in fact i have no idea whether that is ub or not

shadow trench
#

idk either

livid geode
#

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

shadow trench
#

yeah probably not a bad idea

#

maybe the callback approach is fine

livid geode
#

callbacks are annoying

#

i understand you

#

but i wouldn't risk ub

shadow trench
#

yeah

hot yarrow
#

it feels absolutely overengineered what you guys do here for some reason

#

getting lost in rustisms 😄

shadow trench
#

well this is kinda not amazing

#

ill rework it to callback isms

shadow trench
#
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
});
hot yarrow
#

ARF 🙂

shadow trench
#

should try to get a B in there

hot yarrow
#

yeah Handle Aff is also not bad

#

FnOnce reads as Fucking Once

#

hehe

shadow trench
hot yarrow
#

what is R and what is F?

shadow trench
#

ill also add with_if_ready that only calls f if the asset was in the ready state

hot yarrow
#

i get A is Asset

shadow trench
#

F is a function that is called with the asset if it was found

#

R is the return value of that function

hot yarrow
#

can you not name those things like that then? 🙂

shadow trench
#

I suppose

hot yarrow
#

WhenAssetFound instead of F

#

ReturnValue instead of R or TReturn at least dunno if rust peeps use T... for generic paramaters

shadow trench
#

I tend to use single letter generics too much kekw

#

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

hot yarrow
#

return a NullAsset/Unit then

#

since this is functional inspired stuff it seems

#

or rather dysfunctional 😛

shadow trench
#

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

hot yarrow
#

: D

shadow trench
#
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

hot yarrow
#

why not check if container exists before

#

before locking it for reading

shadow trench
#

The read lock is on the container list yeah

hot yarrow
#

i can read other languages usually, but this code is 100% unreadable

shadow trench
hot yarrow
#

and it is 0 clear what its intent is or w🇹🇫 it is supposed to do

shadow trench
#

its mostly internal but i should go around my codebase clarifying things for future me

#

because i will forget

hot yarrow
#

dont get me wrong

shadow trench
#

Well this doesnt work anyway

hot yarrow
#

its mostly just me being an idiot especially when it comes to rust

shadow trench
#

I mean I was going to do that anyway tbh

#

Though you are right its not very obvious

hot yarrow
#

im not 17 anymore

shadow trench
#

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

hot yarrow
#

hmm so you basically want your whole api to be "fluent"

shadow trench
#

Yeah ideally

hot yarrow
#

thats quite the brainworm

shadow trench
#

it really is 😄

hot yarrow
#

i get fluid api for builder pattern

shadow trench
#

I cant just ignore the error indefinitely but handling it on each access is wasteful and annoying

hot yarrow
#

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

shadow trench
#

The thing is this needs to be a callback because of rust isms kek

#

in monke words: do not return reference to local variable

hot yarrow
#

callback itself is probably not a bad thing

#

idk what that means "in monke words"

shadow trench
#

other way to say shrimple

hot yarrow
#

monke was a gorilla who killed someone?

shadow trench
#

no no thats a different monkey

hot yarrow
#

then i dont get that monke nonsense at all 😄

#

anyway

shadow trench
#

I think some sort of "asset garbage collector" might not be awful

hot yarrow
#

which is more or less just a List<IDisposable> (a list of disposables, a thing which has a Dispose method)

shadow trench
#

yeah it would just go over all assets, check which ones failed to load, report their errors and remove them

hot yarrow
#

yeah

shadow trench
#

and do that every now and then

hot yarrow
#

reasonable

#

like an AssetCache

shadow trench
#

Yeah

#

I mean this thing is named AssetStorage for a reason :P

hot yarrow
#

😛

shadow trench
#

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

shadow trench
#

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

hot yarrow
#

i read HopSlob 😄

shadow trench
#

im sneaking an el_rubio in my paper

livid geode
shadow trench
#

i have no idea

livid geode
#

also you usin latex or typst?

shadow trench
#

latex

#

i havent really checked out typst yet

livid geode
#

i recommend it fully

shadow trench
#

it does look pretty good tbh

livid geode
#

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)

shadow trench
#

I mean latex syntax kinda sucks ngl

#

also this just happened

livid geode
#

What happened

#

Columns?

shadow trench
#

a lot of whitespace

livid geode
#

Or that your whole paper got turned into gibberish

#

Ah

shadow trench
#

the columns are intentional but uh

#

I added some text above and now the whitespace is all messed up

livid geode
#

maybe it's trying to place something but it don't fit

shadow trench
#

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 kek

livid geode
#

bruh literally microsoft word

shadow trench
#

literally

shadow trench
#
tokio::time::sleep(Duration::from_secs(RESOLVE_INTERVAL_MS)).await;
#

oops

#

Accidentally running the asset gc every 500 seconds instead of ms

shadow trench
#

i may come to regret doing this

#
pub struct Texture<F: TextureFormat> {
    pub image: PairedImageView,
    marker: PhantomData<F>,
}

making textures typed over their format

livid geode
#

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

shadow trench
#

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

shadow trench
#

contributed to rust: ✅

livid geode
#

cause not everything is hardcoded

shadow trench
#

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

livid geode
#

you don't allow other formats other than srgb8 for color textures?

shadow trench
#

i dont plan to without a conversion

#

the conversion happens automatically anyway

#

like you can load anything from disk

shadow trench
#

I ditched the asset GC

#

It was smelly

#

Now the asset task removes itself from the storage if it fails

shadow trench
#

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

shadow trench
#

I guess if its in wrapper its not so bad

hot yarrow
#

assets.assets.assets.assets 😛

shadow trench
#

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

hot yarrow
#

THandle GetHandle<THandle>() why not 😛

livid geode
shadow trench
#

im not sure, its kinda hard because of the locking involved

#

thats how I ended up with this callback approach

shadow trench
shadow trench
#

i may have impulsively bought a domain name

#

it was cheap

livid geode
#

what lol

#

which one

shadow trench
#

notapenguin.blog

#

i can do structured rambling on the internet now

wispy ore
#

I, too, should do structured rambling on the internet

#

although you were always able to do that before

#

just host with github KEKW

shadow trench
#

im still going to host with github

#

but a custom domain is kinda nice

wispy ore
#

yeah can set a custom domain

#

how much was it / is it

shadow trench
#

9 eur for the first year

wispy ore
#

that's actually fair

shadow trench
#

30/year or so after that

#

i need to figure out how to set up https

#

but its not that big of a deal

short surge
#

they always get you with the renewals

shadow trench
#

true

#

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

livid geode
#

doxxed

#

sending a linkedin invite your way right now

#

NOOOOOO

shadow trench
#

I don’t think I have a linkedin profile

#

Do I??

livid geode
#

There's a single result for your name

#

There could be another person with your exact same name lol

shadow trench
#

No I think that’s me

#

Wtf when did I make that

short surge
#

that theme is nice

shadow trench
#

It says 5th place in some regional coding contest when I was in hs

short surge
#

i think mine is still default theme

shadow trench
#

But I got 2nd the year after KEKW

livid geode
#

only 5th??? smh smh smh

shadow trench
#

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

livid geode
#

like a what contest

shadow trench
#

Competitive programming

livid geode
#

Ahhh

livid geode
#

@shadow trench Nice copypaste of martty's blog KEKW

shadow trench
#

is it the same theme?

livid geode
#

ye

shadow trench
#

oh lmfao

#

that was not intentional kekw

livid geode
#
shadow trench
#

dammit now I need a different theme

#

its on the front page of the jekyll theme list

livid geode
#

also i hadn't seen this lmfao

shadow trench
#

well its fine, im sure there are hundreds of blogs with this theme

livid geode
#

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

shadow trench
#

yeah ill look a little further

livid geode
#

i should post something on my blog again, i posted a single article and nothing more since 2021

#

mewby hardware stuff

shadow trench
#

why not post about the thingy thats secret

livid geode
#

thats actually kinda secret

shadow trench
#

ahh

livid geode
#

:p

shadow trench
#

my bad

#

incredible

livid geode
#

lmao

haughty valley
#

pRoJeCt vExEd

shadow trench
#

This isnt too bad but I really dont have a good image to put there

#

most screenshots from renders just arent vertical like that

livid geode
#

Cant you change your renderer resolution?

shadow trench
#

Yeah I can but it would look kinda weird probably

shrewd lynx
#

i want to be able to do structured rambling aswell

#

damn cool though

wispy ore
shadow trench
#

Yeah it's super simple to set up

shadow trench
#

sneak peak time

livid geode
#

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

shadow trench
#

I know that doesn’t work kekw

#

It does work with ThinBox

#

That’s the point I’m trying to make

shadow trench
livid geode
#

ah i see

shadow trench
#

Its UB because the vtables don’t match

#

But with ThinBox the vtable is on the heap so you can “safely” transmute

hot yarrow
#

when is this rustism over and we can finally see some terrainisms again? 🙂

shadow trench
#

Soon ™️

shadow trench
#

Sometimes I have to set up some infrastructure to continue

shadow trench
#

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

livid geode
shadow trench
#

yea

#

but its unstable

livid geode
#

yeye

shadow trench
#

also comes with the Unsize trait

#

T: Unsize<dyn Foo> if T: Foo

shadow trench
#

this one is very rust-focused i suppose

wispy ore
shadow trench
#

It won’t always be like that

hot yarrow
#

said no one ever

potent quest
#

exams are finally over

#

so what didi miss

shadow trench
#

I wrote a blog post

#

That’s about it from me

shadow trench
#

the design is somewhat inspired by yours actually

young oar
#

Yea lol I noticed some similarities here and there

#

Yours seems tk be more focused on actual proper events though

shadow trench
#

Yeah I wanted no hardcoded event types

young oar
#

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?

shadow trench
#

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

young oar
#

Yea

livid geode
#

@shadow trench considering using phobos for rex

#

maintaining a nih graphics backend is hard work

shadow trench
#

it do be

livid geode
#

and i kinda need just something done for the editor

shadow trench
#

i have an egui backend too

livid geode
#

yup i do need that too

#

was it trivial to make?

shadow trench
#

yeah it was pretty easy

#

just porting the existing one

young oar
#

I'm also thinking of using egui for my game engine editor

#

Idk how to handle reflection though

#

Probably through bevy_reflect

shadow trench
#

or a simple handrolled proc macro

#

tho i assume thats pretty much exactly what bevy_reflect does

livid geode
#

yeah except good

#

use bevy_reflect, don't bother nihing your own solution, it ain't worth it

shadow trench
#

didnt you try that at some point

livid geode
#

(says the person that did a nihed solution)

#

yes

shadow trench
livid geode
#

that is why i say that its not worth it

shadow trench
#

maybe, but it looks like a fun challenge to solve kek

shadow trench
#

debugging deadlocks dread_cat

shadow trench
#

time to add a wrapper around RwLock that logs everything it does

hot yarrow
#

Wrap<RwLock<Wrap<RwLock<X>>>>

shadow trench
#

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

hot yarrow
#

Instrumented suggests it contains instrumentation

shadow trench
#

Yeah I don't really know what else to call it

hot yarrow
#

i mean why not

#

LoggedRwLock perhaps

#

to be more specific

shadow trench
#

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

hot yarrow
#

intrumentation is more like collecting shrimples

shadow trench
#

true

shadow trench
#

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

livid geode
#

make it async orsomethin

shadow trench
#

I need to think about it yeah

hot yarrow
#

await await it();

shadow trench
#

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

livid geode
#

it doesn't?

#

you can recv but i think that just busyloops inside

shadow trench
#

I assume it uses some unsafe state internally to wake the future on send() no?

livid geode
#

how

#

no i dont think so

shadow trench
#

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

shadow trench
#
/// 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

shadow trench
#

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

livid geode
livid geode
#

oh lmao

shadow trench
#

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

livid geode
#

oh ofc lol

#

you're not binding it

#

that's actually a problem there

shadow trench
#

yeah I fixed it, I thought it would just keep it floating around somehow kek

#

which i realize now doesnt make sense

livid geode
#

for a quick api fix you could make it private and introduce a getter

#

thats just a hack tho

shadow trench
#

I just gave it a name for now

young oar
hot yarrow
shadow trench
#

i kinda see it

young oar
#

Oh yep lol

#

"It's not a bug it's a feature"

hot yarrow
#

@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?

shadow trench
#

I don’t know if we had a fred

#

Not sure

hot yarrow
#

yeah, i dont rember either

#

but you know what i mean, neh?

shadow trench
#

Yeah that project

shadow trench
#

@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

hot yarrow
#

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

shadow trench
#

Does that message link work for you?

hot yarrow
#

yes

shadow trench
#

What mysterious lands does it lead to

hot yarrow
#

gets me here

shadow trench
#

Yeah I meant the one in that message

hot yarrow
#

ah

shadow trench
#

Though considering it says Unknown for you too I guess it maybe got deleted

hot yarrow
#

ah

#

yes

#

thats from the retired yourprojects channel

shadow trench
#

Ahh I see

#

Unfortunate

hot yarrow
#

when we recreated the forum channel

#

maybe i can resurrect the messages from it somehow into a new post in #1019722539116802068

shadow trench
#

Mayhaps

hot yarrow
#

will take a looksie over the weekend

shadow trench
shadow trench
#

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();
young oar
#

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)

shadow trench
#

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

shadow trench
#

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

shadow trench
#

I made a little list of tasks

shadow trench
#

Well it's not very easy

#

I need to figure out how to insert arbitrary graphics work but not every frame

hot yarrow
#

: ) no rush

shadow trench
#

I have an idea but it got interrupted by the fire alarm

hot yarrow
shadow trench
#

Happens like every other week here

hot yarrow
#

oof

shadow trench
#

Most of the time we can stay inside but this time we had to evacuate

hot yarrow
#

skool? or home?

shadow trench
#

Student dorm thingy

#

Probably someone messed up while cooking again

hot yarrow
#

ah

livid geode
#

lmao

shadow trench
#

NanaStare someone opened an issue on my repo

shadow trench
#

kinda?

#

im not sure why it reports zero every other frame

shadow trench
#

once again another reason to never use vec3s on the gpu

#

i frogor the alignment rules again

livid geode
#

you got to use lfs @shadow trench

#

ppl are complainin

shadow trench
#

yeah i saw lol

#

for now i fixed

#

but lfs sucks too

livid geode
#

yeah it does

#

maybe you could just host the assets elsewhere and download them at startup

shadow trench
#

thats what i was thinking too yeah

#

tho eventually you provide your own assets to use

livid geode
#

andromeda launcher lol

shadow trench
livid geode
#

that could work

shadow trench
#

if needed yeah

shadow trench
#

im trying to figure out why debug builds are so slow and this is the top function catHUH

#

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

livid geode
#

bruh!!!!

livid geode
shadow trench
#

that is a good idea

livid geode
#

clippy should notify you of that iirc

shadow trench
#

not in its default config I think

livid geode
#

theres a lint to use ok_or_else for anything that requires computation

livid geode
shadow trench
#

Yeah maybe

#

Yeah I didn't know that was such a huge deal lol

#

holy shit thats so much better lmao

livid geode
#

its a heap alloc + backtrace if enabled vs a branch predicted jump

#

you should search for other ok_or usages

shadow trench
#

frametime with debugger went from 70ms to 20ms lmao

#

Yeah I did project-wide already

#

ill check phobos too

livid geode
#

lmao

#

rip

shadow trench
#

now time to figure out how to map world position to uv

#

should not be too difficult to figure out

shadow trench
#

Step 1 complete

potent quest
#

why cant i find motivation to learn any gp 💀 i'll phobos soon

shadow trench
#

GP is hard

#

I just improved perf by a lot lmao

livid geode
#

What'd you do

shadow trench
#

The err thing lol

shadow trench
#

@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

shadow trench
#

wtf my frame sync was overly complicated lol

#

i still blame vulkan-tutorial

potent quest
#

Yoooooi

#

that’s awesome

shadow trench
#

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

shadow trench
#

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

shadow trench
#

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

livid geode
shadow trench
#

time to pull out the lock timers again and see if its not stuck waiting on one

shadow trench
#

i think the delay is fixed, its just kinda putting the pillars not at the right place

#

terrain editing 101

hot yarrow
#

looks fine to me

#

ogldev is working on terrain as well and his current series is quite great on geomipmapping+lod isms

shadow trench
hot yarrow
#

ah those are the locations where you clicked?

shadow trench
#

Should be

#

It always appears a little shifted in some direction

hot yarrow
#

hmm is this raytracing the position?

shadow trench
#

I get the position from the depth boofer

#

unproject isms

hot yarrow
#

ah

shadow trench
#

It lags one frame behind but that shouldnt matter

hot yarrow
#

are you including some non client area isms perhaps

shadow trench
#

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 🤷

hot yarrow
#

no "return"?

#

that looks cursed

shadow trench
#

implicit return

hot yarrow
#

sheesh

shadow trench
#

you get used to it kek

#

Its really nice for expression blocks and stuff

hot yarrow
#

i think that would drive me crazy lol

shadow trench
#

not when remapping position to uv

livid geode
#

fuck you discord for not scrolling me to the bottom

hot yarrow
#

but im sorry, i didnt mean to start a language war now

shadow trench
#
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

hot yarrow
#

i like it anyway, although your fov looks fucked

shadow trench
#

thats just the screenshot

#

Looks fine if I take a shot of the entire program

hot yarrow
#

ah

#

i like the sky 🙂

shadow trench
#

adapted stolen from lvutner

hot yarrow
#

and that rough/powderesqe finish on that terrain

shadow trench
#

its uh

hot yarrow
#

just phong?

shadow trench
#

very bad shading kekw

#

Yeah

hot yarrow
#

lol

#

must be the many looking tringles then

shadow trench
#

there are quite a lot yeah

#

32x32x128x128 points

#

so like 32 million tris

hot yarrow
#

are terrains not some special kind of kind with uneven dimensions

#

129x129 etc

shadow trench
#

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

hot yarrow
#

good idea

shadow trench
#

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

hot yarrow
#

i think almost exactly 20 years ago was the last time i fiddled with terrain :C

shadow trench
#

:o

hot yarrow
#

had a little world editor for maps for Ultima Online freeshards