0r@ngE
> Это NIH синдром, это нормально.
Не в этом случае.
Там вроде бы нет поддержки нескольких девайсов. Нет атрибута [[nodiscard]] и весьма неудобно когда у тебя хэдеры одной версии, а лоадер другой.
/A\
> Там вроде бы нет поддержки нескольких девайсов.
void volkLoadInstance(VkInstance instance); void volkLoadDevice( VkDevice device);
а так же:
> For applications that use multiple VkDevice objects, load device-related Vulkan entrypoints into a table:
void volkLoadDeviceTable(struct VolkDeviceTable* table, VkDevice device);
Но я нисколько не агитирую, если что.
0r@ngE
Но девасовые функции все равно видимы глобально, нет защиты от того что ты обратишься к ним, а там может быть либо нулевой указатель, либо указатель на функции другого девайса.
/A\
> Но девасовые функции все равно видимы глобально
я же показал для этого выше volkLoadDeviceTable, функции сидят потом в таблице, для каждого девайса своя таблица, потом вместо
vkDoSomethingCool();
table->vkDoSomethingCool();
0r@ngE
Ну вот пишешь
dev1->vkDoSomethingCool();
dev2->vkDoSomethingCool();
а в другом месте забыл и написал
vkDoSomethingCool()
и оно скомпилится, а потом упадет.
/A\
А еще можно выйти за пределы массива, обратиться к нулевому указателю, забыть обнулить удаленную память и т.д. Куча способов написать плохой код.
Я тебе ответил на
> вроде бы нет поддержки нескольких девайсов
и все, не нравится - не юзай. Не зачем мне доказывать что в теории, при кривых руках, все может упасть.
Отправная точка получена - 9 фпс на трейсе дума. На обычном трейсе 100фпс, а вообще в думе должно быть около 200фпс.
/A\
прикольно
Suslik
На самом деле не очень прикольно, у меня 50% времени цпу уходит на размещение барьеров и 70% времени гпу на обновление буферов. Без этого легко было бы 100фпс и более.
Но пока делал не мало ошибок пофиксил.
/A\
какие имено буферы так долго обновляются? как твоё обновление буферов на стороне гпу отличается от их?
что именно включено в трейс и за чем тебе нужно следить самому?
Suslik
Я конвертирую vktrace в вызовы моего фреймграфа, поэтому всю работу делает он.
Проблема с обновлением буферов в том, что я точно не знаю какие данные находятся на гпу, я могу только узнать что записано в staging buffer перед рисованием кадра, видимо где-то тут ошибка и получается слишком много обновлений, там несколько тысяч вызовов на кадр.
С барьерами чуть сложнее, там надо делать все ресурсы readonly и только те, что меняются захватывать для изменения. Это было в планах уже давно, но раньше не было заметно просадки производительности.
Оказалось в дум юниформ буфер был host visible, а я при конвертации сделал заливку данных в device local память и на этом получил до 9к вызовов апи и жуткую загрузку цп при расстановке барьеров.
Еще сделал что если у бефера в usage нет TransferDst или Storage значит он ридонли и барьеры для него не нужны. Для image такое сработает только если будет 1 лейаут, но пока вообще не кретично.
В итоге я получил 30-40 фпс.
40% времени цпу уходит на memcpy, 20% на фреймграф, остальное на present и чтение трейса.
Пока сойдет, пойду чем-то более веселым займусь, например рейтерсом)
вот здесь: https://gpuopen.com/presentation-porting-engine-to-vulkan-dx12/
рекомендуют frame graph делать зависимости не через какой тас зависит от какого, а какой таск использует какие ресурсы. например, если какой-то таск принимает ресурс A, а выдаёт B и C, то таски, использующие эти ресурсы, могут стартовать, как только он закончится. ресурсы — рендертаргеты, например.
Suslik
На ГПУ таски выполняются так как решит планировщик задач. Барьеры и ивенты как раз нужны, чтоб определенные задачи не выполнялись параллельно.
У меня зависимость тасков определяет в какой последовательности они будут обрабатываться, от этого зависит в каком порядке они отправятся на ГПУ, а барьеры расставятся наиболее оптимальным образом.
Кстати, в CUDA добавили графы https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cuda-graphs
Добавил дебаггер шейдеров в фреймграф, раньше было отдельной демкой, теперь доработал.
Пока поддерживаются пиксельный и компьют шейдеры, не поддерживаются структуры и матрицы, но постепенно все добавлю.
пример
Пишем такой шейдер:
uint index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x; uint size = gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupSize.x * gl_WorkGroupSize.y; float value = sin(float( index) / size ); imageStore( un_OutImage, ivec2( gl_GlobalInvocationID.xy), vec4( value) );
// gl_GlobalInvocationID: uint3 {8, 8, 0} no source // index: uint {136} // gl_GlobalInvocationID: uint3 {8, 8, 0} 10. index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x; // size: uint {256} 11. size = gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupSize.x * gl_WorkGroupSize.y; // value: float {0.506611} // index: uint {136} // size: uint {256} 12. value = sin(float( index) / size );