Медленная выборка текстуры в фрагментном шейдере с использованием Vulkan
Я делаю шейдер SSAO с размером ядра 64.
Фрагмент шейдера SSAO:
const int kernelSize = 64;
for (int i = 0; i < kernelSize; i++) {
//Get sample position
vec3 s = tbn * ubo.kernel[i].xyz;
s = s * radius + origin;
vec4 offset = vec4(s, 1.0);
offset = ubo.projection * offset;
offset.xy /= offset.w;
offset.xy = offset.xy * 0.5 + 0.5;
float sampleDepth = texture(samplerposition, offset.xy).z;
float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0;
occlusion += (sampleDepth >= s.z ? 1.0 : 0.0) * rangeCheck;
}
Текстура сэмплера имеет формат VK_FORMAT_R16G16B16A16_SFLOAT
и загружен с флагом VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
,
Я использую ноутбук с графической картой nvidia K1100M. Если я запускаю код в renderdoc, этот шейдер занимает 114 мс. И если я изменю kernelSize
до 1, это занимает 1 мс.
Это время выборки текстуры нормально? Или может быть, я где-то настроил что-то не так?
Вроде раскладка перехода не прошла, поэтому текстура в VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
вместо VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
,
2 ответа
Память GPU зависит от интенсивного использования кэша, которое очень ограничено, если фрагменты, расположенные близко друг к другу, не образуют тексели, находящиеся рядом друг с другом, что также известно как отсутствие пространственной когерентности. Я ожидал бы около 10-кратного замедления или более при произвольном доступе к текстуре по сравнению с линейным, когерентным доступом. SSAO очень склонна к этому при использовании с большими радиусами.
Я рекомендую использовать меньшие радиусы и оптимизировать доступ к текстуре. Вы выбираете 4 16-битных числа, но вы используете только одно. Разрезание глубины до отдельного 16-битного изображения, обеспечивающего только глубину, должно ускорить 4-кратное ускорение.
Вы вычисляете координаты текстуры на фрагментном шейдере, что означает, что вы не разрешаете графическому процессору предварительно выбирать текстуры. Лучше рассчитать все текстурные координаты в вершинном шейдере и передать их как изменяющиеся.
Обновлено: я бы предложил добавить некоторые продвинутые трюки в SSAO, чем просто пытаться вычислить карту AO. 1. Вы можете визуализировать карту AO намного меньшего размера и увеличить ее, добавив фильтр размытия. Это даст гораздо лучшие результаты. 2. Если вы пытаетесь сделать рендеринг в реальном времени, тогда AO Map не нужно рассчитывать каждый кадр. Вы можете подделать его на основе ваших настроек.
Отказ от ответственности: я делаю много шейдеров на основе OpenGL ES, и мои знания в основном ограничены мобильными платформами.