Мне нужно извлечь все текстуры из приложения на уровне драйвера, аналогично GFXR и RenderDocs, но мне неясна основная методология. В Vulkan данные текстур передаются в память графического процессора приложением, а не драйвером. Основываясь на моем понимании и тестировании, типичный процесс выглядит следующим образом:
Создайте промежуточный буфер.
Приложение копирует данные пикселей в промежуточный буфер:
void* hostData;
vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); // Map GPU memory to host and copy data
memcpy(data, srcPixels, static_cast<size_t>(imageSize));
vkUnmapMemory(device, stagingBufferMemory);
Создайте VkImage (еще один VkDeviceMemory).
Переведите макет VkImage в оптимальный макет для графического процессора.
Скопируйте промежуточный буфер (линейные данные) в VkImage, используя:
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, VkImage, etc...)
Один из подходов к получению данных текстуры — сохранить их перед вызовом vkUnmapMemory. Однако нет никакой гарантии, что приложение вызовет vkUnmapMemory, поскольку оно может сохранять сопоставление бесконечно. Другой потенциальный метод — перехватить vkCmdCopyBufferToImage, но если приложение не использует промежуточный буфер и имеет пиксели в формате, удобном для графического процессора, этот подход также не сработает, что приведет к искажению данных пикселей, которые трудно восстановить.
Может ли кто-нибудь, знакомый с GFXR или RenderDoc, объяснить их общую методологию этого процесса?
Спасибо за ваше время.
С наилучшими пожеланиями,
@NicolBolas Я не понимаю твоего комментария. Большинство приложений будут вызывать vkUnmapMemory сразу после копирования буфера. Почему вы говорите иначе?
Потому что они не должны. Сопоставленная отображаемая память ничего не делает. Это не мешает графическому процессору получить к нему доступ (Vulkan — это не OpenGL, и даже GL теперь может получить доступ к отображаемой памяти). Это не вызывает проблем с производительностью. Единственное, что он делает, это занимает место в вашем 64-битном адресном пространстве, которого вам никогда не будет не хватать в реальных приложениях. Таким образом, нет причин отключать отображаемую память. Просто держите его на карте все время. Это то, что делает большинство приложений.
Кроме того, я очень сомневаюсь, что программы Vulkan, которые не являются учебными пособиями, демонстрациями или другими тривиальными вещами, будут отключать память после каждой модификации.
Поскольку «формат, дружественный к графическому процессору», представляет собой прямую загрузку в часть этого макета, это невозможно. ЦП не может напрямую отображать макеты TILED_OPTIMAL, поскольку макет определяется реализацией. Это вариант использования, в котором всегда необходимо использовать промежуточное хранение, чтобы обеспечить доступность процессора.
Другие вещи, о которых стоит подумать: * Вас волнуют визуализированные текстуры? (Возможно, это вообще не API на стороне ЦП, кроме буфера команд) * Заботитесь ли вы о правильной обработке форматов с псевдонимами? (Вычислительный шейдер записывает данные INT32, которые волшебным образом превращаются, например, в сжатые изображения BC7 при доступе через другой ImageView).
@solidpixel Я хотел бы знать основную идею RenderDoc или GFXR, поскольку они это делают. Тем не менее, копаться в тысячах строк кода и, возможно, десятилетие разработки — это не то, что нужно, чтобы понять, что алгоритм тоже не является чем-то простым.





Все взаимодействие RenderDoc с программами, имеющими доступ к Vulkan, осуществляется через слои Vulkan. Уровень — это фрагмент кода, который может находиться между реализацией и приложением. Слои часто используются во время отладки, но неявные слои могут регистрироваться таким образом, что они всегда подключаются.
То, как это работает, зависит от ОС, но хуки всегда присутствуют.
Если у вас есть слой, вы можете прослушивать все взаимодействия между хостом и реализацией Vulkan. Это дает вам всю информацию, необходимую для извлечения любой информации, которая вам нужна.
Но это не значит, что это легко.
Как вы видели, приложениям не обязательно отключать память сразу после копирования в нее. Действительно, это может привести к снижению производительности, поскольку отображение — нетривиальная операция, а сохранение отображения памяти почти не имеет недостатков. Таким образом, большинство реальных приложений, выделяющих отображаемую память, оставляют ее подключенной. И если эта память выделяется как когерентная, им даже не нужно вызывать функцию, чтобы сделать записанные ими значения видимыми для Vulkan.
Но это не значит, что невозможно узнать, когда произошли изменения.
Поскольку операции Vulkan выполняются асинхронно относительно операций хоста, если хост записывает данные в часть памяти, должна быть некоторая синхронизация между записью в эту память и любым чтением из нее. vkQueueSubmit может справиться с этим неявно, при условии, что записи хоста сами синхронизируются с вызовом хоста vkQueueSubmit. Но это не обязательно; пакет работы может ожидать события, установленного хостом. Это событие будет иметь барьер памяти, чтобы сделать байты, записанные хостом, доступными для графического процессора.
Дело в том, что это хотя и осуществимо, но и очень сложный процесс. RenderDoc имеет открытый исходный код, поэтому один из способов узнать все детали — посмотреть на его реализацию.
«нет никакой гарантии, что приложение вызовет vkUnmapMemory, поскольку оно может отображать ее бесконечно». Приложения должны сохранять отображение такой памяти неопределенно долго.