Русский
Русский
English
Статистика
Реклама

Directx 12

DirectX raytracing всплываем

23.08.2020 08:23:39 | Автор: admin


В 2018 году компания Microsoft анонсировала raytracing API (DXR) как часть DirectX 12. Подход рейтрейсинга заставляет полностью переосмыслить способ ренедринга трехмерных сцен, смещающий классический подход растеризации на второй план. АПИ модернизируются, разрабатываются более производительные GPU, разработчики пакетов визуализации пробуют новые возможности. Однако даже на наиболее производительных видеокартах мощности хватает на генерирование всего нескольких лучей на пиксель для обеспечения стабильной частоты смены кадров. К тому же, производительность во многом зависит от сложности материалов и сцены. Но уже сегодня продвинутые алгоритмы шумоподавления и аккумуляции результата освещенности позволяют достичь высокой степени реализма. Все это мотивирует к экспериментам в данной области.


Возможность трассировки лучей на GPU стала возможна относительно недавно. В 2009 году вышел DirectX 11 с compute shaders это дало толчок в развитии вычислений не связанных с графикой. Однако конструирование ускоряющих структур ложилось полностью на плечи программиста, что замедляло разработку. Получили распространение специализированные библиотеки по пересечению, например, Radeon Rays от AMD. В DXR ускоряющие структуры представлены по принципу черного ящика и пересечение происходит с помощью специальных аппаратных блоков. Трассировка лучей была так же добавлена в Vulkan в качестве расширения VK_NV_ray_tracing для карт Nvidia. В марте 2020 с небольшими изменениями вышло расширения VK_KHR_ray_tracing, перестало быть vendor-specific, возможно его включат в спецификацию Vulkan 1.3. Планируется полноценная работа трассировки лучей и в AMD до конца 2020. Повсеместная поддержка повышает перспективность технологии.


Концептуально DXR предоставляет возможность пересечения лучей с предварительно загруженными геометрическими объектами. В местах пересечений возможно выполнение определенных пользователем программ шейдеров. Грубо говоря, объект это массив треугольников. Однако уточняющая форма, которая, например, говорит о прозрачности объекта может определяться и во время выполнения в отдельном шейдере. Такая возможность полезна, если степень прозрачности задается текстурой или в случае полностью процедурно сгенерированных объектов (облака, огонь). Любые события, которые возникают на пути луча (hit, miss, procedural hit, closest hit), подвержены программированию, например, в месте пересечений можно генерировать вторичные лучи, продолжающие движение. Такой pipeline похож на распространение света.


Окружение


Для запуска требуется DXR-совместимый GPU Nvidia RTX 2060 и выше. Windows SDK 19041 (для нашего примера подойдет и более раннее, но официальные Miscrosoft сэмплы ориентируются именно на эту версию), в качестве IDE используется Visual Studio 2019, язык C++.


Терминология


Как и в любой трассировке в DXR все начинается с генерирования необходимых лучей, за это отвечает отдельный raygen-шейдер. Глобально на стороне программы вызывается ID3D12GraphicsCommandList4::DispatchRays() c необходимым количеством лучей, а конкретные направления задает шейдер с помощью TraceRay(). Трассировка производится на определенной top level aceleartion structure. Дальнейшее выполнение может происходить по разным сценариям, зависящим от сцены. Например, могут обрабатываться всевозможные пересечения на пути следования луча, либо вызываться отдельные шейдеры для процедурной геометрии, либо обрабатываться только ближайшее пересечение (closest hit). Для простоты мы рассмотрим последний случай, общий иллюстрирует следующая схема.

В процессе выполнение может передаваться разным miss и hit-шейдерам. Инстансы шейдеров для одного луча могут обмениваться небольшой областью памятью называемой payload обычно это и есть результат вычислений. Ресурсы могут передаваться и быть видимым глобально для всех шейдеров с помощью стандартных средств DirectX, в этом случае создается global root signature. Либо локально per-shader ресурсы, в этом случае на каждый шейдер создается своя local root signature и передача параметров осуществляется с помощью Shader binding table. В такой ситуации надо следить, чтобы регистры глобальных ресурсов не пересекались с регистрами локальных.


Инициализация


На высоком уровне вся настройка делится на следующие этапы:


  • создание bottom-level, top-level acceleration structures
  • создание raytracing pipeline
  • создание shading binding table (SBT) ассоциирование шейдеров и per-shader ресурсов с геометрией

Разберем подробно эти этапы.


Bottom-level acceleration structure (BLAS)


Объект представляет собой список геометрических объектов, где каждый объект это массив треугольников. Один объект в BLAS для удобства в дальнейшем будем называть инстансом. Один BLAS может состоять из множества инстансов. Треугольники могут задаваться напрямую как тройки вершин либо индексироваться отдельным буфером. Генерирование BLAS происходит на GPU, и поэтому требует отдельного command list и небольших синхронизаций. Для генерирования требуется итоговый буфер, который и будет содержать BLAS а так же некоторое количество памяти для внутренних нужд (scratch-буфер). DirectX, как явное API, предоставляет возможность узнать необходимые размеры с помощью метода ID3D12Device5::GetRaytracingAccelerationStructurePrebuildInfo. В итоге создается буфер с флагом D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE содержит все необходимые данные для эффективного пересечения лучей с треугольниками.


Основная структура здесь D3D12_RAYTRACING_GEOMETRY_DESC описывает один инстанс в BLAS. Необходимо передать буфер вершин и их лэйаут (а так же index-buffer при наличии):


D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {};geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;geometryDesc.Triangles.IndexBuffer = 0;// GPU-address index-буфера (здесь не используем)geometryDesc.Triangles.IndexCount = 0;geometryDesc.Triangles.IndexFormat = 0;// например DXGI_FORMAT_R16_UINT;geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;geometryDesc.Triangles.VertexCount = <количество вершин>;geometryDesc.Triangles.VertexBuffer.StartAddress = <GPU-address вершинного буфера>;geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex);

Заполняем общую информацию о BLAS:


D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs;bottomLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;bottomLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;bottomLevelInputs.pGeometryDescs = &geometryDesc;bottomLevelInputs.NumDescs = 1;D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {};bottomLevelBuildDesc.Inputs = bottomLevelInputs;bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();bottomLevelBuildDesc.DestAccelerationStructureData = bottomLevelAccelerationStructure->GetGPUVirtualAddress(); // ID3D12Resource* bottomLevelAccelerationStructure - аллоцированный буфер необходимого размера

И генерируем BLAS:


dxrCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr);

Top-level acceleration structure (TLAS)


TLAS представляется матрицей трансформации с уже созданными BLAS и флагами специфичными для данного инстанса. Она может инстанциировать один и тот же BLAS много раз, используя разные матрицы для рендеринга одинаковых моделей в разных позициях. Создание TLAS во многом похож на BLAS здесь так же требуется дополнительный scratch-буфер. Более того, мы можем использовать один и тот же scratch-буфер, так как TLAS создается после BLAS. В нашем случае записываем в TLAS две одинаковые модели с разными матрицами трансформации:


    D3D12_RAYTRACING_INSTANCE_DESC instanceDesc[2] = {};    for (int i = 0; i < 2; ++i)    {        instanceDesc[i].Transform = YourEngineTransformToDXR(transforms[i]);        instanceDesc[i].InstanceMask = 1;        instanceDesc[i].InstanceID = 0; // не используем        instanceDesc[i].InstanceContributionToHitGroupIndex = i; // отвечает за смещение в hit SBT        instanceDesc[i].AccelerationStructure = bottomLevelAccelerationStructure->GetGPUVirtualAddress();    }

Заполнение общей информации о TLAS (D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS и D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC) производится аналогично BLAS.


Raytracing pipeline


Как и для graphic и compute нам требуется raytracing pipeline state object это текущая конфигурация трассировки. RT pipeline состоит из множества подобъектов и объектов-ассоциаторов, которые связывают уже созданные подобъекты. Для удобства будем использовать вспомогательный класс CD3DX12_STATE_OBJECT_DESC и метод CreateSubobject(<тип подобъекта>).


  1. Библиотеки шейдеров. Необходимо скомпилировать либо заранее либо программно и заполнить поля BytecodeLength и pShaderBytecode структуры D3D12_SHADER_BYTECODE, затем передать в подобъект эту структуру с помощью SetDXILLibrary() и определить имя функции на исполнение DefineExport(). Такой алгоритм проделываем для 3-х шейдеров: raygen, hit, miss.
  2. Hit group (CD3DX12_HIT_GROUP_SUBOBJECT). Необходимо обозначить hit group-ы, которые будут участвовать в трассировке. Придумываем имя группы и устанавливаем с помощью SetHitGroupExport, а так же тип в нашем случае SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES). Текущий подобъект лишь указывает на наличие hit-group, конкретный шейдер мы уже добавили на первом шаге.
  3. Конфигурация шейдеров (CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT). Размер payload данные, которые возвращает TraceRay после обработки пересечения, и барицентрические координаты в closest hit 2 float.
  4. Конфигурация пайплайна (CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT). Устанавливаем максимальную глубина трассировки 1.
  5. Локальная root signature (CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT). Описание аргументов, которые приходят в hit-шейдер. Конкретные аргументы передаются через SBT (об этом далее).
  6. Ассоциация local root signatore c hit group (CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT). Устанавливаем root signature SetSubobjectToAssociate() и имя группы для которой предназначаются параметры AddExport().
  7. Глобальная root signature (CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT). Описание аргументов, которые доступны во всех шейдерах. Аргументы устанавливаются непосредственно перед трассировкой с помощью стандартных методов SetComputeRootDescriptorTable, SetComputeRootShaderResourceView и SetComputeRoot32BitConstants.

Shader binding table (SBT)


SBT это соединение геометрических данных и шейдеров вместе. Фактически таблица хранит информацию о том, какие нужно выполнить шейдеры и с какими аргументами для инстансов с которыми произошло пересечение, поэтому это часть подверженная активному изменению с точки зрения программиста. Технически это не является таблицей, она представляется размеченной GPU-памятью, в которой записаны идентификаторы шейдеров и аргументы по определенным правилам. Но для удобства продолжим называть ее таблицей.


Одна ячейка в таблице называется shader record. В нашем случае при конструировании TLAS мы указали что нулевой инстанс будет смотреть на нулевую запись, первый на первую. Таким образом в рантайме придут разные константы. В общем случае формула расчета индекса в таблице может зависеть не только от номера геометрического инстанса в TLAS и BLAS, но и от типа лучей и передаваемых аргументов в TraceRay(), однако в данной статье мы рассматриваем самый простой случай.


Глобально все делится на 3 шейдера: ray generation, hit и miss, соответственно, можно сказать, что и таблиц буде тоже три. Мы можем разместить таблицы в разных буферах, либо в одном. В последнем случае нам нужно правильно рассчитать GPU-адрес на начало каждой из таблиц, которые принимает DispatchRays(). В таблице может быть любое количество shader record-ов.


Размер буфера который нам нужно аллоцировать для одной таблицы это размером одного shader record-a умноженный на их количество. Для raygen и miss у нас будет по одному шейдеру без каких либо локальных аргументов, поэтому размер shader record-a минимально возможный. Для глобальных аргументов, как мы уже знаем, которые видны всем шейдерам, мы создаем отдельную root signature, ее аргументы не записываются в SBT. Между shader record требуется выравнивание D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT (сейчас 32 байта).


Идентификатор шейдера представляет собой void*, который возвращает GetShaderIdentifier() из объекта ID3D12StateObjectProperties, последний можно получить из COM-интерфейса созданного ранее raytracing pipeline. Для raygen-шейдера (miss аналогично) это выглядит примерно так:


ComPtr<ID3D12StateObjectProperties> stateObjectProperties;dxrStateObject.As(&stateObjectProperties);void* rayGenShaderIdentifier = stateObjectProperties->GetShaderIdentifier(L"RayGen");

Указатель мы записываем в начало нашего буфера. SBT для raygen и miss шейдеров сформированы.


Для hit мы передаем в шейдер float4 и здесь более интересная ситуация. Запись аргументов в SBT очень похожа на установку в root signature: константы записываются напрямую либо передается virtual GPU-address/GPU-handle. В нашей ситуации два инстанса необходимо обработать одним шейдером но с разными constant buffer, поэтому в hit SBT будет два shader record-а. Запись состоит из 32-байтного идентификатора и 4x4-байт константы, всего 48, но из-за выравнивания получится 64 байта. Расположение записей иллюстрируются следующей картинкой:


Шейдеры


В отличие от классического классического подхода vertex-fragment shader в трассировке требуется минимум 3: raygen, hit, miss.


raygen:


#include "Common.hlsl"#include "Global.hlsl"struct CameraData{    float4 forward;    float4 right;    float4 up;    float4 origin;};ConstantBuffer<CameraData> gCamera : register(b0);[shader("raygeneration")] void RayGen() {    HitInfo payload;    payload.colorAndDistance = float4(0, 0, 0, 0);    uint2 launchIndex = DispatchRaysIndex().xy;    float2 dims = float2(DispatchRaysDimensions().xy);    float2 ndc = float2(        float(launchIndex.x + 0.5f) / dims.x * 2 - 1,        float(dims.y - launchIndex.y - 1 + 0.5) / dims.y * 2 - 1);    RayDesc ray;    ray.Origin = gCamera.origin.xyz;    ray.Direction = GetWorldRay(ndc, gCamera.forward.xyz, gCamera.right.xyz, gCamera.up.xyz);    ray.TMin = 0;    ray.TMax = 100000;    TraceRay(        SceneBVH,        RAY_FLAG_NONE,        0xFF,         0, // RayContributionToHitGroupIndex        0, // MultiplierForGeometryContributionToHitGroupIndex        0, // MissShaderIndex        ray,        payload);    gOutput[launchIndex] = float4(payload.colorAndDistance.rgb, 1.f);}

Задаем исходную точку и направление луча на основании позиции пикселя и системы координат камеры. Затем передаем TLAS, маски и набор параметров, отвечающих за расчет выполнения определенных шейдеров в SBT. TraceRay() возвращает payload после серии пересечений и результат записываем в выходой буфер. Идентификатор [shader("raygeneration")] говорит системе, что это именно шейдер, генерирующий лучи, а вот название функции "RayGen" может быть любое, его нужно будет экспортировать после компиляции.


closesthit


#include "Common.hlsl"#include "Global.hlsl"struct InstanceData{    float4 color;};ConstantBuffer<InstanceData> instanceData : register(b1);[shader("closesthit")] void ClosestHit(inout HitInfo payload, Attributes attrib){    float3 barycentrics = float3(1.f - attrib.bary.x - attrib.bary.y, attrib.bary.x, attrib.bary.y);    float3 vertexNormals[3] = {        Vertices[PrimitiveIndex() * 3].normal,        Vertices[PrimitiveIndex() * 3 + 1].normal,        Vertices[PrimitiveIndex() * 3 + 2].normal    };    float3 N = vertexNormals[0] * barycentrics.x +        vertexNormals[1] * barycentrics.y +        vertexNormals[2] * barycentrics.z;    const float3 L = normalize(float3(0, -0.4, 1));    payload.colorAndDistance = float4(instanceData.color.rgb * max(0, dot(N, L)), RayTCurrent());}

Выполняется только в случае ближайшего к камере пересечения. В результате мы должны записать что-нибудь в payload. В шейдер передается буфер вершин и нормалей и цвет текущей модели. Барицентрические координаты и номер треугольника передаются системой. Находим нормаль в точке пересечения с помощью барицентрических координат, и производим расчет освещения.


Наиболее просто выглядит miss.


#include "Common.hlsl"[shader("miss")] void Miss(inout HitInfo payload : SV_RayPayload){    payload.colorAndDistance = float4(0.0f, 0.2f, 0.7f, -1.0f);}

В этом случае у нас нет пересечения записываем цвет фона.


Main loop


Основная выдержка из main loop выгляди следующим образом:


//...// Устанавливаем глобальную root signaturedxrCommandList->SetComputeRootSignature(raytracingGlobalRootSignature.Get());// прикрепляем основной descriptor heapdxrCommandList->SetDescriptorHeaps(1, descriptorHeap.GetAddressOf());// устанавливаем глобальные ресурсыdxrCommandList->SetComputeRootDescriptorTable(0, raytracingOutputResourceUAVGpuDescriptor);dxrCommandList->SetComputeRootDescriptorTable(1, buffersGpuDescriptor);dxrCommandList->SetComputeRootShaderResourceView(2, topLevelAccelerationStructure->GetGPUVirtualAddress());// устанавливаем данные камеры//...D3D12_DISPATCH_RAYS_DESC dispatchDesc = {};dispatchDesc.HitGroupTable.StartAddress = hitGroupShaderTable->GetGPUVirtualAddress();dispatchDesc.HitGroupTable.SizeInBytes = hitGroupShaderTable->GetDesc().Width;dispatchDesc.HitGroupTable.StrideInBytes = 64; // размер hit shader record dispatchDesc.MissShaderTable.StartAddress = missShaderTable->GetGPUVirtualAddress();dispatchDesc.MissShaderTable.SizeInBytes = missShaderTable->GetDesc().Width;dispatchDesc.MissShaderTable.StrideInBytes = dispatchDesc.MissShaderTable.SizeInBytes;dispatchDesc.RayGenerationShaderRecord.StartAddress = rayGenShaderTable->GetGPUVirtualAddress();dispatchDesc.RayGenerationShaderRecord.SizeInBytes = rayGenShaderTable->GetDesc().Width;dispatchDesc.Width = width;dispatchDesc.Height = height;dispatchDesc.Depth = 1;dxrCommandList->SetPipelineState1(dxrStateObject.Get());dxrCommandList->DispatchRays(&dispatchDesc);//...


Заключение


В статье мы рассмотрели основные объекты необходимые для начала работы с DXR, а так же применили эти знания на практике. Намеренно были пропущены вопросы инициализации DirectX, загрузки моделей, компиляции шейдеров, и т.д., которые не относятся к теме. В следующих статьях планируется сместить акцент на, собственно, графику, нежели работу с API.


Сcылки:
https://github.com/k-payl/X12Lib/blob/master/src/examples/raytracing.cpp
https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html
https://developer.nvidia.com/rtx/raytracing/dxr/DX12-Raytracing-tutorial-Part-1

Подробнее..

Перевод Выжимаем из Gears Tactics максимальную производительность с минимальными артефактами при помощи VRS

10.07.2020 12:22:13 | Автор: admin
Gears Tactics динамичная пошаговая стратегия во вселенной одной из самых известных игровых франшиз Gears of War. Кроме того, это первая игра, поддерживающая одну из основных функций DirectX 12 Ultimate Variable Rate Shading (VRS).

VRS позволяет Gears Tactics добиться значительного прироста производительности до 18,9%! на широком спектре оборудования без заметной потери качества изображения.

Одна из основных целей при разработке Gears Tactics заключалась в том, чтобы охватить более широкую аудиторию ПК за счет снижения минимальных системных требований. Для этого было необходимо найти новые решения для повышения производительности, которые не требовали бы существенного снижения качества графики. Одним из таких решений стал VRS.

В этой статье мы подробно рассмотрим процесс настройки VRS для достижения оптимального баланса между качеством и производительностью.

image

Поскольку главной целью было включить в реализацию самый широкий спектр оборудования, речь пойдет только о первом уровне поддержки VRS (Tier 1), что позволяет устанавливать shading rate для каждого объекта. Для получения дополнительной информации о VRS можете почитать этот пост.

В случае Gears Tactics VRS не распространяется на уровень поддержки выше первого, поэтому нужно было определить, на каких проходах рендеринга можно его задействовать (оговоримся, что Gears Tactics создавалась на Unreal Engine 4), а также сформулировать критерии, когда следует уменьшить shading rate размер области пикселей, к которой применяется единовременный рендеринг. Чем он выше (самый высокий 11), тем выше точность прорисовки и, соответственно, выше нагрузка на GPU.

Как оказалось, выгоду из VRS можно извлечь на любом этапе рендеринга, включая некоторые полноэкранные отрисовки.


Оценка рендеринг-пайплайна


Поначалу казалось, что базовый проход рендеринга и проход по полупрозрачности (translucency) должны представлять собой наибольший потенциал для оптимизации по сравнению с остальными. Поэтому VRS включался для всех вызовов отрисовки в базовом проходе и в проходе по translucency, но на последнем возникали слишком серьезные артефакты.

Значит, следующим шагом следовало оценить, какие проходы наиболее перспективны для получения максимальной выгоды от VRS, а затем протестировать shading rate на этих проходах и исключить те, которые оставляли артефакты. Так, проходы, использующие точную информацию о пикселях, оказались одной из основных причин этих артефактов.

Чтобы выяснить, какие проходы покажут себя наиболее эффективными, понадобилось включить VRS на всех из них, а затем сузить в PIX их число до тех, которые показали себя лучше других.

Вот они:

  • Композиция после освещения (Composition after lighting): Освещение для подповерхностных рассеивающих материалов.
  • Фильтр полупрозрачных объемов (Filter Translucent Volumes): Сглаживает полупрозрачные сетки внутри объема, чтобы избежать проблем с наложением.
  • Затухание света (Light attenuation): Затухание это внешние границы при расчете спада заданного освещения. Этот проход повторяется для затененных источников света при учете рассчитанного спада с целью рендеринга теневых проекций.
  • Постановка световых композиций (Light composition tasks (PreLighting)): Отвечает за окклюзию окружающего света.
  • Блум световых лучей (Light Shaft Bloom): Эффект блума (свечения), возникающий при рендеринге световых лучей.
  • Отражения пространства экрана (Screen Space Reflections): Процесс повторного использования информации о пространстве экрана для создания отражений.
  • Временной анти-алиасинг отражений пространства экрана (SSR Temporal AA): Сглаживание результатов прохода по отражениям пространства экрана.
  • Прямое отложенное освещение (Direct Deferred Lighting): Отвечает за рендеринг любых прямых источников света в буфер цвета сцены.

Затем нужно было оценить серьезность артефактов, полученных в этих проходах из-за VRS. Процесс оказался довольно трудоемким, так как Gears Tactics имеет несколько биомов с различными типами погодных условий. Можно было получить хорошие результаты в городе днем, но вылезли бы артефакты в песчаной среде, или листве, или ночью под дождем.

Следующие проходы показали улучшение производительности с включенным VRS, но при этом имели слишком много артефактов для того, чтобы их стоило использовать:

  • Полупрозрачное освещение (Translucency);
  • Отложенное освещение (Deferred Lighting);
  • Композиция после освещения (Composition after lighting).

image
Пример, показывающий изображение до и после Composition after lighting с грубым размером пикселя 44

После исключения этих проходов началась работа над корректировкой shading rate на основе других факторов. В первую очередь были исключены динамические объекты и объекты с маской прозрачности.

Динамические объекты отбрасывают полностью динамическую тень. От этого с VRS они часто теряют важные детали и становятся более заметными по всей сцене, а также во время движения. Применение VRS к маске прозрачности тоже нередко приводило к большой потере детализации. И, поскольку объекты со смещением глубины пикселя, как оказалось, также отображались неправильно, они тоже были исключены.

image
VRS использовался на наборе маскированных объектов, что привело к недопустимой потере детализации

Динамические техники


Итак, мы составили хороший набор проходов рендеринга, к которым можно применить VRS. Однако повсеместное применение самого малого shading rate (44 или 22 в зависимости от аппаратной поддержки) на этих проходах приведет к существенной потере качества. Поэтому следующим шагом явилось исследование динамических методов, которые могли бы изменить shading rate в зависимости от конкретного прохода. В результате их сформировалось три. Каждый из них использовался в ключевых проходах непосредственно перед прорисовкой сетки.

Вот эти методы: определение размера объекта, маскировка глубины резкости и маскировка Туман войны.

Определение размера объекта


В этом методе VRS активно применяется на любой сетке, чрезвычайно малой в сравнении с игровым пространством. В основе его лежит быстрая проверка условия, ниже ли заданного порога значение размера отдельного полигона в сетке. Если это так, shading rate масштабируется пропорционально этому размеру.

Маскировка глубины резкости


Для большинства графических процессоров глубина резкости в Gears Tactics по умолчанию выключена, но включается во время кат-сцен. Этот метод определяет степень размытия сетки после применения глубины резкости в процессе постобработки. И, поскольку на этом этапе детали намеренно устраняются, на всех предыдущих ключевых проходах можно выставить небольшой shading rate.

image
Сетки, выделенные красным, из-за глубины резкости имеют самый грубый размер пикселя

Маскировка Туман войны


Gears Tactics использует туман войны для скрытия части поля битвы. Также с его помощью можно замаскировать и пониженный shading rate. Эта техника определяет, как далеко конкретная полигональная сетка скрыта туманом войны, и, отталкиваясь от этого, пропорционально снижает shading rate.

image
Интенсивность VRS растет пропорционально интенсивности тумана войны

Соотношение качества и производительности


Оказалось, при помощи совокупности описанных выше методов можно получить более 30% прироста производительности за счет значительного снижения shading rate. Правда, потеря качества при этом будет более заметной, чем хотелось бы. Поэтому следующим шагом явилась ручная настройка shading rate для каждого прохода по нескольким биомам и оценка прироста производительности и качества изображения после каждого изменения.

В результате VRS получил два уровня. Конфигурация On оказывает наименьшее влияние на качество изображения при условии, если отключить VRS для некоторых проходов рендеринга и ограничить снижение shading rate. В то же время настройка Performance приведет к небольшим компромиссам в качестве картинки в пользу увеличения производительности с использованием более низких shading rate.

Здесь прослеживается еще одно преимущество использования shading rate, для которых нужны дополнительные мощности. Размеры пикселей более 2 требуют дополнительной поддержки shading rate со стороны аппаратного обеспечения, поэтому любой проход, использующий shading rate размером 24, 42 или 44, ограничен уровнем Performance.

image

Результаты тестов


Тестирование проводилось на Intel Gen 11 и Intel Xe, а также на NVIDIA Turing. На всех поддерживаемых средствах наблюдается схожий прирост производительности.

Тестируемое оборудование


Операционная система: Windows 10 Pro 64-bit (10.0, Build 18362) (18362.19h1_release.190318-1202)
Процессор: Intel Core(TM) i9-9900X CPU @ 3.50GHz (20 CPUs), ~3.5GHz
Память: 98304 MB RAM
Видеокарта: NVIDIA GeForce RTX 2080 SUPER

Все тесты проводились при настройках игры Ultra с разрешением 4K









image
Все техники при настройках VRS On

image
Включение всех техник при VRS Performance




Расширение VRS


Можно получить дополнительные преимущества, расширив описанные здесь методы применения VRS.

Дальнейшие методы маскирования


Дополнительные методы маскирования могут повысить эффективность VRS или уменьшить количество артефактов. Эти методы могут варьироваться в зависимости от случаев, когда области затенены или размыты во время игры:

  • Размытие в движении (Motion Blur): Камеры, использующие быстро движущиеся сцены, могут легко использовать VRS в большинстве сеток с включенной функцией размытия в движении.
  • Частицы (Particles): Полигональные сетки, спрятанные за толстым слоем частиц, можно использовать для маскирования VRS высокой интенсивности.

Динамический VRS


Как и в случае с динамическим масштабированием разрешения, возможным улучшением здесь является масштабирование VRS в зависимости от частоты кадров с целью минимизации времени, необходимого для его использования. Это отдельная система слежения, которая может потребовать регулировки на разных частотах во избежание усиления артефактов.

Динамическое масштабирование разрешения (DRS)


Gears Tactics использует для оптимизации и VRS, и DRS, но эти функции могут иметь не очень хорошую совместимость. Это связано с тем, что, когда DRS меняет масштаб разрешения, меняются и артефакты VRS. Это приводит к тому, что игрок либо узнает и об артефактах VRS, и об изменении разрешения разом, либо и то, и другое остается незамеченным. Одним из способов одновременного использования обеих функций может стать мониторинг коэффициента изменения DRS и отключение использования VRS до тех пор, пока он не стабилизируется.

Вместо заключения


Умелое использование Tier 1 VRS позволяет добиться значительного прироста производительности на широком спектре оборудования с минимальным влиянием на качество изображения.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru