#DARE (Danny's Awesome Rendering Engine)
1 messages Β· Page 6 of 1

better question
how do we abstract semaphores in a nice way
such that i dont wanna die
oh fuck*
barriers
uh
what the fuck do i do
i don't have subpasses since i am dynamic rendering
you know what
that is tomorrow me problem
i'll worry about how to transition images tomorrow
and vulkan sychronization

how tf did i even get dynamic rendering to work last time
Throughout the devices and commands, I alone am the synchronized one
Stand proud, binary Semaphore, youβre strong
Are you visible memory because *_WRITE_BIT_KHR or are you *_WRITE_BIT_KHR because you are visible?
For the next 4ms and 11mbs, the acceleration structure would be built on the device
lvstri really said skill issue to me in srgb
my triangle is also raytraced

@tropic vigil sorry for ponging but how do you manage the lifetimes of gpu-allocator? Wrapping in Arc doesn't reallly work since literally all allocations produced some references (???)
hmm im not sure what u mean by that last bit
the allocations returned own the memory
buffers hold their Allocation (which is parametrized over a trait because yes)
when you drop the buffer you drop the allocation
keeping the buffer alive is your responsibility
if you want delayed deletion you need to make a deletion queue for them and push them in there
that stuff is mostly on the user side in phobos atm
im not doing a deletion queue im gonna ref count it all
because afaik theres no good generic way to do it
you cant just refcount
since you may also have references on the gpu in flight
while all actual references are gone
wait wdym?
if you submit come commands that use a buffer
and then drop all references to that buffer
even if you refcount itll be deleted while its in use on the gpu

Has drop submit the commands to a deletion queue which itself is dropped when refcount is zero
43 lines of rust
and you have a deletion queue
oh you use deferred
im more used to flushing the queue
ah
A shrimpler way is to keep track of cpu and gpu timeline
and only delete when the gpu timeline reached the cpu timeline
vkDeviceWaitIdle π (this single menovour will take us 50 years)
you use timeline semaphores for this btw
hmhmhmh
so like when i drop my lifetime
I put it into a deletion queue then I wait until the gpu timeline for that resource reaches the cpu?
then I can destroy it?
isn't that expensive however? Since you need to constantly query everything in that deletion queue to see when the timelines reach
you don't wait
wat
you send the cpu timeline value and the object to destroy
every frame you get the gpu's timeline counter
if it is > then you can safely delete
ohh
i forgot timeline semaphores can have multiple listeners
and signalers
so you just have one universal timeline semaphore you just use across everything?
you can just vkGetSemaphoreCounter yknow
no need to signal or wait
the only thing that signals is the device
uhhh okay
I'll show exshrimple when I get home
okok thank you
but the easy way is what penguin did
OH i understand why penguin does it per frame since you'll know you won't have those resources in flight

i guess yours is similar but allows for more granular control in the frames itself...?
How tf do you integrate egui into your ash renderer
theres an integration for ash i think
Bro i need to overhaul my entire engine 
vkguide2 was not built for this one
Do i just bite the bullet and use the imgui rs wrapper
what is the legality
of including vulkan in my project's name
π€
Danny's Awesome Vulkan Layer
@devout mesa can you uh explain how your lifetime management system works im so confused 
uint32 hostTimelineValue = 0;
TimelineSemaphore deviceTimelineSemaphore = {};
uint32 maxTimelineDifference = FRAMES_IN_FLIGHT;
while (window.IsOpen()) {
hostTimelineValue = max(hostTimelineValue - maxTimelineDifference + 1, 0);
vkQueueSubmit2(
Signal = { deviceTimelineSemaphore, hostTimelineSemaphore };
);
}```
is this clear
if it is then I can explain how the deletion queue works
if not, read how timeline semaphores work
yeah it is
ok
DeletionQueue::push(T object) {
_vector.push(object, currentHostTimelineValue);
}
DeletionQueue::tick() {
uint32 deviceCounter = deviceTimelineSemaphore.GetCounter();
for each item in _vector {
if (deviceCounter > item.timelineValue) {
delete(item);
}
}
}```
no, here _vector.push(object, currentHostTimelineValue);
OH
i think it makes sense
wait no currentHostTimelineValue? Is this assigned based off of hostTimelineValue? i.e. hostTimelineValue + maxTimelineDifference
here, have another exshrimple
uint32 hostTimelineValue = 0;
TimelineSemaphore deviceTimelineSemaphore = {};
uint32 maxTimelineDifference = FRAMES_IN_FLIGHT;
while (window.IsOpen()) {
hostTimelineValue = max(hostTimelineValue - maxTimelineDifference + 1, 0);
deletionQueue.Push(someRandomObject, hostTimelineValue);
vkQueueSubmit2(
Signal = { deviceTimelineSemaphore, hostTimelineSemaphore };
);
deletionQueue.Tick();
}```
Why do you subtract the maxTimelineDifference?
I did omit the vkWaitSemaphores
That's when you wait for hostTimelineValue
the difference is subtracted such that you wait for nothing the first N frames
Oh you have the same sema for swapchain and deletion?
ye
I see I see
wait wtf phobos has a pipelien cache
oh wiat i forgot phobos removes thme from the deletion queue
then i assume that causes it to drop then cleanup
@tropic vigil Does phobos use the deletion queue to manage its lifetimes?
yea
oh ok now it starts to make more sense
how does it ensure that stuff gets destroyed in the proper order however?
then drops them
or do you just rely on first in first out
dependency injection?
if X needs Y to be destroyed first
store Y inside X
and destruction order handles the rest
using language constructs for this sort of stuff is usually best
because its mostly intuitive to someone who doesnt know about the gpu internals
still debating if i should hit my abstraction layer with strong reference counting
π€
waaiai im dumb how tf does it ensure stuff that is used in frames in flight doesn't get destroyed?
since it iterates every frame but you do FIF so some resources may still be used in the next frame in flight
you see that max_ttl parameter
every resource is kept in the queue for that many frames
i set it to frames_in_flight + 1 or so
its pretty much just a "time until death" for the resource
in a world where a penguin gives everyone a time to death 
yes and no
going through the code base, it doesn't seem to be?
it supports bindless just fine
there was some stuff in the works to make it easier but thats kind of on hold for now
huh alright im just trying to figure out how to make a good descriptor abstraction π
its a bit complicated
bit
What I did (stole from daxa) when recently hacking together quick thingy for uni is just create a single descriptor set with four bindings (one for each buffers, sampled images, storage images and samplers). Then whenever user creates a resource I assign it a slot in the binding (basically a hashmap) and update the descriptor set
I then can use the key to the slot to index this resource on both the GPU and CPU
Do you want me to describe in more detail?
uh yeah
(this system is the same me, darian, potrick and literally every other
boy in this server has
)
It is very shriple and works perfectly
Ill explain better when I'm at my PC (tag me in an hour or so if I forget π )
I'll try explaining it in more detail myself then
alright look at this
First: I create a huge descriptor pool with 4 bindings (don't mind the storage buffer one, you technically don't need it) descriptor pool
Then I create a descriptor layout that has one descriptor type per binding, totaling 4 bindings
so binding 0 is samplers, binding 1 is sampled images etc..
you still with me?
yeah i think so

I think they just describe metadata around the descriptors themselves, no?
no, they describe the descriptors themselves

if you see this shader
layout (set = 0, binding = 0) uniform image2D image;
void main() {}```
What is the descriptor layout of this shader
it has a single binding of I believe a sampled image?
storage image, but yes
at dstBinding = 0

Uh it's the same but with a .count indicating there are more than 1 descriptors in that binding or maybe just 1
good, with an unbounded array you are telling GLSL that you don't to define any upper bound (yet, you'll define one when creating the descriptor layout using count)
so when I set 65536 as the count, I'm saying, "I want this binding to have 65536 MAX descriptors"
okok
now
here comes the interesting part
what happens if I do this
layout (set = 0, binding = 0) uniform image1D[] image1Ds;
layout (set = 0, binding = 0) uniform image2D[] image2Ds;
layout (set = 0, binding = 0) uniform image3D[] image3Ds;
...```
nope this is valid GLSL
and a core concept for implementing this thingy
Think about it
How many different descriptor types am I describing here
you have 3 of those bindings all set to binding = 0, but with different descriptor types
remember when I refer to descriptor type I strictly refer to VkDescriptorType
yes, that's not a typo
Alright, what's the descriptor type for the first one
Give me the VkDescriptorType entry
STORAGE_IMAGE?
correct, what about the second one
STORAGE_IMAGE again
yep
so here's the thing
you can alias multiple GLSL types to the same descriptor type
anything that has *image* in it is a storage image, no exceptions
so image2DArray, image2DMSArray, etc..
they're all storage images
you got this?
(take your time)
hmm ok
if you don't fully get this, we can't proceed, re-read everything again, ask me questions and then we can continue
so in other words i can alias multiple types to a binding?
alright, now
I only make one descriptor set
from now on, let's erase the name "descriptor set" which means nothing
and let's replace that with "table of descriptors"
because that's what we're gonna be treating this as
wait why is descriptor set meainginless?
a big ass array of descriptors
it's not, but it's better to call this an array of descriptors, in this case
okok
so
do you know how a free list allocator works
a simple one
not one with offsets and shit
just one with IDs
the simplest of them all
what 
alright
let's focus on the storage image descriptor table
we have an big ass array of storage image descriptors
but it's empty now
we want to put stuff in the array in a way that is simple and works for us
that's our goal
now we're gonna use two vectors to achieve this goal
std::vector<Resource> resources;
std::vector<uint64> freeIds;```
the first vector is a descriptor table, but a concrete one, we store our storage images in there
it will map directly to the array in our shader
these ones
do you understand that resources is the C++ version of uniform *image*[] images;
yes
good
now what happens when we allocate a resource
we want to get back a unique identifier to locate said resource in the shader
which better unique identifier than the index of the resource within the array
so our allocation function is literally just this
uint64 allocate(...) {
const auto id = resources.size();
resources.emplace_back(...);
return id;
}```
since we know resources maps directly to the image table in the shader, we can just pass this ID as a push constant to access our resource in the shader
you got this?
good
This has a problem though
what happens when we want to destroy a resource
we would do this right?
void deallocate(uint64 id) {
resources[id] = {};
}```
yes
can you think of a way to change allocate and deallocate to fix this issue using the second vector we declared before?
std::vector<uint64> freeIds; this one
take your time
it's fine if you don't get it right, I've done this a million times and I still had a bug regarding my free list when did this
i would've make a stack where i push free_ids into it then pop ids as they're used
uint64 allocate(...) {
const auto id = freeIds.pop();
resources.emplace_back(...);
return id;
}
right
void deallocate(uint64 id) {
resources[id] = {};
freeIds.push(id);
}
yeah, the idea is exactly it, but it's not quite there yet
(also a stack and a vector are functionally equivalent in C++
)

So, here's allocate
uint64 allocate(...) {
if (freeIds.empty()) {
const auto id = resources.size();
resources.emplace_back(...);
return id;
}
const auto id = freeIds.back();
freeIds.pop_back();
resources[id] = ...;
return id;
}```
and deallocate is this
alright now we got Ids and a resource array
what's missing?
shouldn't we initialize freeIds with all the slots first? I'm confused what
if (freeIds.empty()) {
const auto id = resources.size();
resources.emplace_back(...);
return id;
}
is doing 
like why not an error instead since no ids are avaliable?
why though
why throw away this perfectly reasonable assumption
"if free list is empty then we can allocate with no worries of leaving gaps"
yes
LMAOO alright this makes sense now
alright so
what are we missing
my CDescriptorTable<D>::AllocateResource has one more line in it
uhh allocating the resources themselves i..e updating the descriptor set?
yes
so we need a third vector
std::vector<Descriptor> writes;
where do we use this vector? in allocate?
wdym allocate?
here
add the missing line
oh kekw
uint64 allocate(...) {
if (freeIds.empty()) {
const auto id = resources.size();
resources.emplace_back(...);
return id;
}
const auto id = freeIds.back();
freeIds.pop_back();
resources[id] = ...;
writes[id] = ...->Descriptors;
return id;
}
?
nope, we don't want to traverse the whole writes array to get to a good descriptor
we shrimply emplace every time we allocate
and clear vector once we have confirmed the updates using vkUpdateDescriptorSets
so, here's the final impl
uh
Wait how would you enforce the ordering of resources in
vkUpdateDescriptorSets?
I'm just uh lost how writes ensures that order?
look under dstBinding
yeah
what would you set dstArrayElement to?
set the starting element to start writing descriptors to
they're not gonna be necessarily in sequence though
now, I merge writes into one big write where possible, but it's not necessary
you can shrimply have one write per Allocate call you do
since you're gonna do that once and that's it

I think i get it
the index of the resource in resources?
it returns my version of VkDescriptorBufferInfo/VkDescriptorImageInfo/...
One set and multiple "array" bindings
hmhmh okay
p_immutable_samplers literally exists if you want to initalize samplers?
i got stuck on this so long
Uh oh I want really keeping up
Where have you gotten?
Danny what do you understand?
Prof LVSTRI did a good job?
yes someone give him model-view-projection
Very good very good
Well the resource is stored in the vector and I use an Id to refer to it
So when you create an image you get an id back (which is essential just an index to the resource vector)
yeah i get that im talking more to
nah wait nvm
ill just accept my bindless descriptor
will have ownership of my resources
I do that for some fields, so for example to extract the create info of any resource
But yeah the resources are owned by the resource table (it's really just the info, the vkhandle and VMA allocation)
yo uh
for writes im a bit lost
when iterating through it, right?
im just kinda lost how tf
wait nvm
no i need help
if writes contains Descriptor...Info about what is going to be written
how would we know the indices that would need to be written?
do we keep an IndiceUpdate<u32> vector or smth?
it's literally this for me
C++ moment
wtf
Do you mean resource type?
Yeah each resource type
uh
i might have a dropping error?
is it normal for image view to be negative...?
u64
either your debugger is smoking crack or you did something very very bad
im smoking crack
yes there's that possibility too 
nah my debugger is smoking crack this time
no iam
@tropic vigil sorry for ponging, but is it normal for ash to return negative pointers...?
uhh
(no)

why unbind
what if i need to remove/delete resources
why unbind
You can bind the slot with some default resource to easier spot mistakes
Or just leave as is
uh i would assume i don't want a descriptor to be binded to a destroyed resource?
Well if you never access it it's okay
eh i guess so worse case i get some weird visual error
You can still do this if you are really worried about it, have like a pink 1x1 image or smth
So you might not crash if you access dead descriptor

i rewritten it
and it still crashes with a forbidden access error
also what are sane defaults for a sampler
no wait
[Debug][Error][Validation] "Validation Error: [ VUID-VkWriteDescriptorSet-descriptorType-02996 ] Object 0: handle = 0x26759546a90, type = VK_OBJECT_TYPE_INSTANCE; | MessageID = 0x1cc3d039 | vkUpdateDescriptorSets(): Invalid VkImageView Object 0xb9181f0000000029. The Vulkan spec states: If descriptorType i
s VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, or VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, the imageView member of each element of pImageInfo must be either a valid VkImageView handle or VK_NULL_HANDLE (https://vulkan.lunarg.com/doc/view/1.3.268.0/windows/1.3-extensions/vkspec.html#VUID-VkWriteDescriptorSet-descriptorType-02996)"
NO SHOT
im gonna kms
this would break it
DescriptorInfo::Image(vk::DescriptorImageInfo {
image_view: self.get_view(),
image_layout: vk::ImageLayout::GENERAL,
..Default::default()
})
but this would'nt
DescriptorInfo::Image(vk::DescriptorImageInfo {
image_view: self.get_view(),
image_layout: vk::ImageLayout::GENERAL,
sampler: vk::Sampler::null()
})
it was trying to access a non-existent sampler and i never indicated that the sampler is null
uhhh
who wants to help me name
My vulkan thingy is split into two parts rn:
The abstraction layer which literally only abstracts and does nothing else
And the "Utility" layer i.e. lifetime management + gpu resource table + rendergraph + ...
There has to be a better name of utility layer π
[Debug][Error][Validation] "Validation Error: [ VUID-vkCmdDispatch-None-08114 ] Object 0: handle = 0x2e2941000000001f, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0x30b6e267 | vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] the descriptor (VkDescriptorSet 0x2e2941000000001f[], binding 0, index
- is being used in draw but has never been updated via vkUpdateDescriptorSets() or a similar call. The Vulkan spec states: Descriptors in each bound descriptor set, specified via vkCmdBindDescriptorSets, must be valid if they are statically used by the VkPipeline bound to the pipeline bind point used by this command and the bound VkPipeline was not created with VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT (https://vulkan.lunarg.com/doc/view/1.3.268.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDispatch-None-08114)"
huh
π I thought I enabled partial bounding
huh
//descriptor bindings for the pipeline
layout(rgba16f,set = 0, binding = 0) uniform image2D image;
unsolved mysteries
AHHH
WAIT
I NEED TO TURN THIS INTO AN ARRAY
LMAO
Ah right
no that didn't fix it
Did you create all of them with partially bound?
You need to gib array to the pnext when creating them iirc
let descriptor_flags = [
vk::DescriptorBindingFlags::UPDATE_AFTER_BIND,
vk::DescriptorBindingFlags::PARTIALLY_BOUND,
vk::DescriptorBindingFlags::UPDATE_UNUSED_WHILE_PENDING,
];
let binding_flags = vk::DescriptorSetLayoutBindingFlagsCreateInfo {
s_type: vk::DescriptorSetLayoutBindingFlagsCreateInfo::STRUCTURE_TYPE,
p_next: ptr::null(),
binding_count: types.len() as u32,
p_binding_flags: descriptor_flags.as_ptr(),
};
let layout_ci = vk::DescriptorSetLayoutCreateInfo {
s_type: vk::DescriptorSetLayoutCreateInfo::STRUCTURE_TYPE,
p_next: &binding_flags as *const _ as *const c_void,
binding_count: descriptor_bindings.len() as u32,
p_bindings: descriptor_bindings.as_ptr(),
..Default::default()
};
Uh oh Rust π½
maybe i ask penguin
but DescriptorSetLayoutBindingFlagsCreateInfo go into the pnext of DescriptorSetLayoutCreateInfo yeah?
yeah
std::array<VkDescriptorBindingFlags, 4> const descriptor_binding_flags = {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
};
VkDescriptorSetLayoutBindingFlagsCreateInfo descriptor_set_layout_binding_flags_create_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.pNext = nullptr,
.bindingCount = static_cast<u32>(descriptor_binding_flags.size()),
.pBindingFlags = descriptor_binding_flags.data(),
};
VkDescriptorSetLayoutCreateInfo const descriptor_set_layout_create_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &descriptor_set_layout_binding_flags_create_info,
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.bindingCount = static_cast<u32>(descriptor_set_layout_bindings.size()),
.pBindings = descriptor_set_layout_bindings.data(),
};
CHECK_VK_RESULT(vkCreateDescriptorSetLayout(vulkan_device, &descriptor_set_layout_create_info, nullptr, &descriptor_set_layout));
does this actually give the pNext array of four PARTIALLY_BOUND | UPDATE_AFTER_BIND | UNUSED...?
Yeah one for each descriptor
waiaia i need to pnext chain them?
like chain 4 of them
rn im just testing with just 1 descriptor binding
Well you give the pnext array of bitsmasks
But if you are only testing with one it should be fine
whats going on
bindless indexing is not recognizing that i had set the layout of my descriptors to be partially bounded
array of flags?
each descriptor needs its own set of flags
[flag, flag, flag].as_ptr?
more like uh
| the flags together
i think
basically the flags apply to individual descriptors in the set
or something
uhhh
its been a while
you should check out my code for it lol
lmao okok
sorry i forgot so much stuff π
you know
dumb question
but what the fuck is "reflection"? I hear it like shader reflection or like code reflection
Inspecting source code to get information from it at runtime
Shader reflection usually involves reading the shader code to generate pipeline layouts from it
huh alright
time to make cursed code reflection
.rs
#[align(glsl)]
struct Ray {
hit: glm::Vec3,
direction: glm::Vec3
}
.glsl
struct Ray {
vec3 hit,
vec3 direction
}

can't be that bad
with the trusty GRTβ’οΈ (patent pending) you won't need reflection ever
except for push constants maybe
GRT? 
oh 
that's what we call it
i got the gpu resource table working btw

it has higher meme value
course it is
geh
i don't like using deferred deletion to manage my lifetimes :(
i can't do dependency injection
or else i risk the chance of dropping my object π
I could in theory wrap it in Arc but that ruins the point
you know
how does daxa do it
maybe i can copy their system
oh
it's the same
you know
what if i combined and uses ref counting for some stuff and non-refcounting
all vulkan enjoyers use deletion queues
daxa does too
(not for resource handles though)
okok deletion queue
well deferred deletion queue
wait no that doesn't solve my problem
how do i know when to put it in the deletion queue
do i just ref count it π
I mean i could put all my dependents in my dependencies, but dependency injection becomes impossible π
when you want to destroy an object
then you lied
when you put something in the deletion queue you say "imma not need this anymore"
if you do need it though, you have lied
New idea babe for bikeshedding my lifetime mansger
No more queues, just ref counting any any resources bounded at command submission is automatically refcounted up then dropped upon the submission fence ending
Domain Expansion: 25% overhead
No that is swuare one afain
FUCK IT ARC + DEFERRED DELETION USING TIMELINE SEMAPHORE
200% OVERHEAD
egui integration wtf
i basically need to rewrite my entire thing
nah im not doing this π What the hell I literally just destroy half of my work since the egui-vulkan integration just simplifies the rest
nah im sorry penguin im gonna do imgui-rs
(nvm i realized it doesn't even support vulkan
)
@tropic vigil Uh question, I can't find your code for egui-winit-phobos? I need some example code to reference while trying to figure how tf to implement egui
Could you link it please?
thanks penguin
np
you know
This is the egui integration crate for winit and ash. The default GPU allocator is gpu_allocator, but you can also implement AllocatorTrait.
my only issue is im relying so much on this crate
i ported mine from that
huh
so my push constants are just entirely wrong?
#[repr(C)]
struct ComputePushConstants {
data1: [f32; 4],
data2: [f32; 4],
data3: [f32; 4],
data4: [f32; 4],
}
let pc = ComputePushConstants {
data1: glam::Vec4::new(1.0, 0.0, 0.0, 1.0).to_array(),
data2: glam::Vec4::new(0.0, 0.0, 1.0, 1.0).to_array(),
data3: glam::Vec4::ZERO.to_array(),
data4: glam::Vec4::ZERO.to_array(),
};
self.logical_device.handle.cmd_push_constants(
command_buffer,
self.gradient_pipeline.get_layout(),
vk::ShaderStageFlags::COMPUTE,
0,
std::slice::from_raw_parts(
&pc as *const _ as *const u8,
std::mem::size_of::<PushConstantRange>(),
),
);
repr(C) should make it compatiable, no?

is it normal for the range to be zero...?
why should i 
actually my experience with imgui and egui might make me also rust has barely any gp support beyond the initial bindings of ash 
right so when are you coming back

it seems to randomly only decide to render 1 or 2 color channels
wtfff
okay now my traignel no longer appears

I should do a very simple n-body simulator :)
I think that would allow me to apply my knowledge beyond abstraction hell
thats fine lol but you dont have to delete it
HUSTON
WE HAVE A PROBLEM
AHHHHHHHHH
HOW DID I MAKE 32765 QUEUES
@tropic vigil sorry for ponging but does ash-rs working in crago tests?
i think so
try cargo test --tests
maybe doctest is failing
I fixed it
Tears in my eyes
But also why tf does amdβs integrated gpus not support vk 1.3
Lvstri is right, sleep is for the weak
real
@tropic vigil Yo uh penguin i notice in phobos you just different logging crates, wtf do they all do?
i realized it's gonna be insanely useful to log when all objects are made/destroyed
I only use one
itβs called log
it doesnβt do any logging
Emm
Well
It doesnβt print logs
Just forwards them to a log handler that can be set by the user of the library
Maybe i just bite the bullet and use cpp. Windowing + gui in rust is miserable if you arenβt using wgpu or smth
Cuz like why tf is imgui implementation on cpp is so much easier than rust. Rust you need to destroy your entire render loop and refactor it for egui to even work
Nah quitting is for losers
Nah fuck it is might just return to the standard since cpp is suoported way more and egui ash integration is horrendous
I implemented a wgpu backend for egui and it honestly wasn't that bad ngl
Seems like it is? I have to use painter or smth to upload my triangles
Problem is I have a ray tracing pipelineβ¦
Not a rasterizationβ¦
Do I just first render to the image using rt
Then render over that with egui + rasterization?
Well yea that most definitely won't work lol
Yea I'd assume so
Unless you want to pain yourself trying to implement UI with raytracing which would most probably be wasteful and worse for performance anyways
Pretty sure it's not even possible to even begin with lol
I mean you couldβ¦
At the end of the day they just give you triangles + a texture
More ui => more performance since rt will not ray tracing through the ui
True but UI layered rasterization is relatively fast
Idk I haven't touched rt pipelines at all so I don't even know how they would work, but in a normal case I'd implement UI rendering using rasterization that's for sure
dawg
how tf do i make a deletion queue in rust π
i mean it's not hard literally a stack
my only issue is that i cannot pass by references into my deletion functions i push onto the stack
since borrow checkers get angry. i mean i could in theory just duplicate every fucking vulkan object since they're at the end of the day pointers
but that seems like a bad idea
@feral trellis asking for help here nodle men
Uhhh my naive approach would be to do some sort of IDing system
Idk how well that would work or if there any major pitfalls with that
Just a really simple unique id for each resource that you can use to delete them afterwards
Could simply be an Arc reference

arc for gc
it's probably fine for commonly used stuff but like having to deal with that overhead over and over seems kinda painful
phobos uses arc mainly for very commonly used stuff like device
but iirc it's raii from there
Ahh fair fair
still doesn't deal with gc'ing it though maybe i just do dependency injection over and over but that makes a lot of "coupling"?? like i would have to add too many abstractions then since like:
Device {
pools: {...}
asdas: { { surface: { .... } } }
}
so like change seems to be annoying
Hmm right
holy fuck
struct Buffer {
asd asdadasdfsadfsadf
}
impl Buffer {
fn new() -> Weak<Self> {
}
}
like hear me out
i have an arc pointer but it realistically is only used once
that is it's only rc'd once for the deletion queue
but everything else interacts with the actual struct as a weak pointer
no reference counting overhead
i think
Or you can make it so whenever the value gets dropped it adds it to the deletion queue automatically
So there'd be no need for an arc to even begin with
Unless I'm misunderstanding what a deletion queue is
well you wanna use a deletion queue
to ensure you delete your objects in order
having it push on drop means that you just moved the problem down the road to you
like:
let mut deletion_stack = DeletionStack::new();
deletion_stack.push({
let surface_extension = surface_extension.clone();
move || unsafe {
surface_extension.destroy_surface(surface, None);
}
});
deletion_stack.push({
let logical_device = logical_device.clone();
move || unsafe {
logical_device.destroy_device(None);
}
});
bottom 2 blocks ig
ut ensures the surface is destroyed before the loigcal device
Ohh icic
I mean if it's nothing dynamic then I'd just call them directly without a stack but I assume that's not the case lol
Hmmmmm
Weak Pointer Dereference
time: [3.1106 ns 3.1220 ns 3.1327 ns]
Arc Pointer Dereference time: [187.87 ps 188.68 ps 189.56 ps]
Found 8 outliers among 100 measurements (8.00%)
2 (2.00%) high mild
6 (6.00%) high severe
Direct Reference time: [186.97 ps 187.63 ps 188.35 ps]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
it so joever
maybe i need to start c++ maxxing
@tropic vigil sorry for pinging but
https://github.com/NotAPenguin0/phobos-rs/blob/2a1e539611bb3ede5c2d7978300353630c7c553b/src/wsi/frame.rs#L359-L361
Do you deal with the borrow checker by literally just cloning the swapchain>
Let me see
Thatβs not cloning
It swaps the new one with the old one
Then pushes the old one to the deletion queue
ohh
swap is really useful for things like this
aghhh i just want a deletion queue without having to literally clone everything 
interior mutability is kinda being doodoo rn since it doesn't ensure lifetimes
@tropic vigil sorry for pinging, but how does phobos ensure it deletes each allocation? A bit lost how the image.rs resource is managing to free all allocations
good evening
Well when the struct is dropped its contents are dropped
memory is an Allocation
So when Allocation is dropped, gpu_alloactor frees it implicitly?
impl traits::Allocation for Allocation {
unsafe fn memory(&self) -> DeviceMemory {
self.allocation.as_ref().unwrap().memory()
}
fn offset(&self) -> DeviceSize {
self.allocation.as_ref().unwrap().offset()
}
fn mapped_ptr(&self) -> Option<NonNull<c_void>> {
self.allocation.as_ref().unwrap().mapped_ptr()
}
}
impl Drop for Allocation {
fn drop(&mut self) {
let mut allocator = self.allocator.clone().unwrap();
allocator.free_impl(self).unwrap();
}
}
I made it do that
I wrap around the gpu_allocator allocation
impl DefaultAllocator {
fn free_impl(&mut self, allocation: &mut <Self as Allocator>::Allocation) -> Result<()> {
let mut alloc = self.alloc.lock().map_err(|_| Error::PoisonError)?;
match allocation.allocation.take() {
None => {}
Some(allocation) => {
alloc.free(allocation)?;
}
}
Ok(())
}
}
But Allocation as a trait has docs specifycing that Drop must be implemented properly
Okok thank you
Roses are red, violets are violent, how the fuck do i task graph
maybe i just live with manually syncing it all until i figure out async rust
where tf does phobos actually implement the validation layer
like the function that is ran
Vulkan abstraction library for Rust. Contribute to NotAPenguin0/phobos-rs development by creating an account on GitHub.
found it
@tropic vigil sorry to ping again but uh could you give me advice on this strategy for command buffers?
/// A command buffer which is actively recording commands
struct CommandBufferRecording {
...
}
/// A command buffer which has been ended.
///
/// No guarantees if the inactive command buffer has been properly
/// reset.
struct CommandBufferInactive {
...
}
impl CommandBufferRecording {
pub fn end_recording(self) -> CommandBufferInactive;
}
impl CommandBufferInactive {
pub fn start_recording(self) -> CommandBufferRecording
pub fn reset(&self);
}
I do something like that yeah
But end gives a Finished command buffer that has to be submitted
struct CommandBuffer {
}
impl CommandBuffer {
fn submit(queue: &Queue) -> Result<()> {}
}
trait CmdBuffer {
fn finish() -> CommandBuffer;
}
Something like this
I give up on RAII. It scares me
Deletion stack π«‘
LVSTRI was right
I can clone my resources and not have to worry about cleaning them up since itβs already on the stack. Now I just need to figure out synchronization which doesnβt require me to bring a fucking task graph
Should combine memory allocation implicitly? Like
Image::new_allocate(device, image_data_to_be_uploaded)
or do
let image = Image::new();
let buffer = Buffer::new().upload(yapyapyap);
image.bind_buffer(buffer);
Have both
let image = Image::new_uninitialized(...); // Empty image
let image = Image::new_with_data(..., buffer, context); // fill it
@dawn wave
ahhh alright
AHHH how do i enable extensions again

let handle = Self::new_empty(device.clone(), image_ci)?;
let allocation = allocator.allocate(name, &unsafe {
device.get_handle().get_image_memory_requirements(handle.handle)
}, memory_type)?;
unsafe {
device.get_handle().bind_image_memory(handle.handle.clone(), allocation.memory(), allocation.offset())?
}
Ok(handle)
I geniunely have no clue how to nicely implement an allocator trait
since vk-mem-rs uses using allocator functions depending on what you're allocating
while gpu-allocator hasn't updating to ash 0.30.0
no way this is right since im getitng offset errors that the memory occupied isn't aligned to 65536
https://github.com/NotAPenguin0/phobos-rs/blob/master/src/allocator/traits.rs Would this not work for you?
I don't think an allocator trait should be too complicated
It wasn't and most of my allocator was copied from your stuff 
But it seems like vk-mem-rs needed some extra work to ensure the offsets are padded to alignment
But thank you penguin once again
i did not mean to click

Oh yeah sure, but you can just wrap around it and implement the allocator trait for your wrapper
Yeah that worked
[VALIDATION]: VUID-vkDestroyDevice-device-05137 (1215490720): Validation Error: [ VUID-vkDestroyDevice-device-05137 ] Object 0: handle = 0x980f360000000011, type = VK_OBJECT_TYPE_DEVICE_MEMORY; | MessageID = 0x4872eaa0 | vkCreateDevice(): OBJ ERROR : For VkDevice 0x1817b053160[], VkDeviceMemory 0x980f360000000011[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.280.0/windows/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-05137)
stack backtrace:
Destroying memory allocation 0x980f360000000011

I-
how?
okay now im convinced vk-mem-rs is broken
I havent used it
yeah for whatever reason freeing the memory does not actually free it 
yeah but no clue how to then tell it "bro please no one wants to use this memory like dead ass destroy it frfrfr"
It'll do that when it feels like it, do you really need it destroyed?
You can just free it through the lib and forget about it
Probably

Speaker: Grigory Dzhavadyan, Independent
This talk was presented at Vulkanised 2024 which took place on Feb 5-7 in Sunnyvale, California, USA. Vulkanised is organized by the Khronos Group and is the largest event dedicated to 3D developers using the Vulkan API, and provides a unique opportunity to bring the Vulkan developer community together ...
This seems like a viable synchronization method
I really don't wanna make a render graph π
Track global sync state and instead of issuing errors, I just issue out barriers
AHHHH
descriptor bindless
how do i uh
do that free slot allocator
lvstri if you can hear me please save me
on another note
plus resizing
istg there is probably some really nice abstraction using slices or borrowing from rust and images and subresources
just fill out the create structs
and picture renderdoc in front of view
when you are on the pipeline view
oh ok
@tropic vigil Sorry for pinigng so late, but do you know if you can just wrap glam structs with repr(C) and it will automatically convert them?
it's for shaders
you know maybe bevy-rs is the way
i don't have to think about architecture or event systems
NAHH
sort of
repr(C) has a few rules
make sure to check the docs
what the fuck
why does validation vulkan crash in nsight
but perfectly outside of it
what the fuck nvidia
why the fuck does ash
never provide good defaults π
is it that much effort to set the default s_type and p_next
wait phobos uses a cache hashmap because it needs to be able to refer to resources via name
AHHH
@tropic vigil Uh sorry for pinging, but do you know where/if phobos has immediate command submission?
What do you mean by that
let cmd = record_command_buffer();
let batch = SubmitBatch::new(device, exec, &pool);
batch.submit(cmd);
let fence = batch.finish()?;
fence.await;
ahh okay thank you penguin
You can submit batches anytime with phobos
for andromeda I made a system that stores such a batch per frame
which allows you to add command buffers to this frame's submission from anywhere
same
mmm squares
tasty
wtf does cleanup chain do in phobos
oh it's a linked list
of functions which are continiously executed
hmmmm
why
AHHH
SO YOU CAN ATTACH CLEAN UP FUNCTIONS
AHHHHHHHHHHHHHH
i'll just use a deletion queue instead
ah, the duality
old code
they only exist on fences
so you can do something like
fn upload(data) -> Fence<Buffer> {
let cmd = exec.on_domain::<Transfer>();
let buffer = Buffer::new();
let staging = Buffer::new(data);
cmd.copy_buffer(&staging, &buffer);
fence = submit(cmd);
fence.attach_value(buffer)
.with_cleanup(move || drop(staging));
return fence;
}
let fence = upload([1, 2, 3]);
let buffer = fence.wait()?;
this will make sure the staging buffer is deleted after the fence completes
Hmm ok
Also π€ the lifetime of staging is not ensured you akshually must use move ||
rust my beloved π¦
now im back into gp
does nsight have a way to view what you're drawing?
iirc renderdoc had a similar feature
I think so
True but minecraft mods arent written in rust
And neither are my assignments
rust bytecode when 

But it's not practical
it'd be so fun to deal with the java gc
Don't remind me
To be fair it has been fun just dealing with high level gameplay code for once
I joined the dev team of the GTNH modpack

couldve been faster but I've spent a lot of time just building
woahh
Looks better in game with shaders but I cba to start it now 
WHY IS IT A BLACK SCREEN AHHHHHH
FUYCK
FUCK
FUCK YOU LINEAR ALGEBRA
WHEN I CATCH YOU GAUSS WHEN I CATCH YOU JORDAN
LETS FUCKING GOOOO
now the question is
why is glam not working
what is the glam equivalent to glm perspective 
i...
i mixed up z_near and z_far...
no that was wrong
vkguide does switch them
wtffff
uh
i think
i got a read write issue
i might be wrong however
@devout mesa sorry for pinging, but where was your shader resource table code at again
thanks lvstri
Holy shit!!!
Fellow modded minecraft enjoyers, rejoice
i have become blocc
alpha blending is the killer of joy

