Комментарии 6
Генерировать мипы за один вызов хорошо, но есть опенсорсные проверенные решения:
https://gpuopen.com/manuals/fidelityfx_sdk/techniques/single-pass-downsampler/
Bank conflicts, Morton z code, gather, все это когда-то было важно но на современном железе это все можно выкинуть и написать влоб и будет быстрее. Проверте перфу прежде чем это все использовать, это все могло быть написанно во времена динозавров.
Округление до степени двойки - тоже очень сомнительно, этот буфер можно будет использовать только для hzb. А например Ssr, Ao вы тоже скорее скорее всего захотите делать не в полном разрешниии и там тоже потребуется downsampled depth только нормальная, не степени двойки. В итоге придется готовить отдельный downsampled depth mip chain для остальных эффектов что двойная работа.
Для SSR и AO такой HZB тоже можно использовать. В Unreal, например, текстура SSRTAmbientOcclusion создается с использованием такого POT HZB, и создается в half resolution. Там просто маппятся UV координаты.
Вот, например, как вычисляется один из параметров, который используется для маппинга:const FVector2D HZBUvFactor(float(InViewRect.Width()) / float(2 * HZBMipmap0Size.X), float(InViewRect.Height()) / float(2 * HZBMipmap0Size.Y));
Исходник тут: Engine\Source\Runtime\Renderer\Private\HZB.cpp
Согласен с тем что есть опенсорсные решения, вроде FidelityFX SPD. В статье я хотел разобрать именно реализацию HZB из Unreal Engine, а что использовать в итоге уже каждый решает сам. Кстати, даже у SPD есть нюансы с размерами текстур - при работе с нечетными текстурами он отбрасывает какие-то значения (как указано в разделе Limitations).
Только подход не универсальный, так как запись в UAV идет без компресии (кроме AMD), поэтому на встройках и мобилках такой код работает в разы медленее.
Получается для мобильного профиля в UE другой подход?
Для мобилок используется вариант генерации HZB через пиксельный шейдер. Там такой простенький код:
void HZBBuildPS(float4 SvPosition : SV_POSITION, out float4 OutColor : SV_Target0)
{
float2 BufferUV = SvPosition.xy * DispatchThreadIdToBufferUV.xy + DispatchThreadIdToBufferUV.zw;
float4 DeviceZ = Gather4(ParentTextureMip, ParentTextureMipSampler, BufferUV);
float FurthestDeviceZ = min(min(DeviceZ.x, DeviceZ.y), min(DeviceZ.z, DeviceZ.w));
OutColor = FurthestDeviceZ;
}Для всех остальных устройств можно выбирать какой вариант использовать: через compute шейдер или пиксельный шейдер. Например, через консоль:
r.HZB.BuildUseComputeКак в UE не знаю, но я тестировал разные варианты и на всех встройках быстрее через пиксельный и с sampler min фильтром вместо gather4. Округление до степени 2 тоже быстрее работает, хоть и меньше точности.

Как в Unreal Engine генерируется Hierarchical Z Buffer