C # — Почему порядок имеет значение в шейдерах?

Быстрая заметка

Этот вопрос имеет C++ тег, потому что есть больше разработчиков, работающих с DirectX в C++ чем есть в C#, Я не верю, что этот вопрос напрямую связан с каким-либо языком, но вместо этого с используемыми типами (которые, как я понимаю, абсолютно одинаковы), или DirectX сам и как он компилирует шейдеры. Если кто-то работает в C++ знает лучший и более наглядный ответ, тогда я предпочел бы это своему собственному ответу. Я понимаю оба языка, но использую C# прежде всего.


обзор

В HLSL шейдер, при настройке моих постоянных буферов я столкнулся с довольно странной проблемой. Рассматриваемые исходные постоянные буферы были настроены следующим образом:

cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}

cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}

Если я поменяю b0 а также b1 регистры вокруг, рендеринг больше не работает (e1). Если я оставлю эти регистры в покое, и поменяю порядок между World а также WorldViewProjection опять рендеринг больше не работает (e2). Тем не менее, просто перемещая ViewBuffer выше ObjectBuffer в HLSL файл без внесения других изменений, он работает просто отлично.

Теперь я ожидаю, что размещение реестра довольно важно, и что первый регистр b0 требует три свойства, указанные в этом буфере, и я понимаю, что HLSL постоянные буферы должны быть в 16-байтовых блоках. Тем не менее, это оставляет меня с некоторыми вопросами.


Вопросы

Учитывая то что HLSL ожидает, что постоянные буферы будут в 16-байтовых чанках;

  • Почему заказ в e2 так важно?

не являются float4x4 типы так же, как Matrix типы, где это по сути массив массивов?

[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[    TOTAL   ] = 64 bytes

Так как float 4 байта само по себе, это будет означать float4 составляет 16 байтов, и, таким образом, float4x4 составляет 64 байта. Так почему заказ имеет значение, если размер остался прежним?

  • Почему ObjectBuffer должны быть назначены b0 в этом случае вместо любого другого b регистр?

0

Решение

Быстрая заметка

В настоящее время я работаю над дальнейшим анализом проблемы, чтобы я мог дать более подробный и точный ответ. Я обновлю вопрос и ответ, чтобы отразить как можно большую точность, поскольку я узнаю больше деталей.


Основной ответ

Точная проблема с вопросом выше (который был неизвестен на момент публикации), заключается в том, что HLSL буферы не соответствуют их C# представления; таким образом, переупорядочение переменных привело к сбою шейдера. Тем не менее, я до сих пор не уверен, почему, когда типы одинаковы. Я узнал о некоторых других вещах на своем пути для ответа и решил опубликовать их здесь.


Почему порядок имеет значение

После некоторых дальнейших исследований и испытаний я все еще не уверен на 100% в том, что стоит за этим, когда типы одинаковы. В целом, я считаю, что это может быть связано с ожидаемыми типами в cbuffer и порядок типов в struct, В этом случае, если ваш cbuffer ожидает bool сначала а потом floatЗатем перестановка вызывает проблемы.

cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}

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

1 Byte  : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal

Вы должны осознавать основные типы, используемые для создания более сложных типов. Скажем, например, у вас есть Vector2 с X собственность и Y имущество. Если они представлены float затем введите 8-байтовый отступ перед следующим свойством, если у вас нет других вещей, которые помогут достичь 16 байт. Однако, если они представлены double типы или decimal Типы, то размер отличается, и вы должны знать об этом.


Регистрация назначений

Я смог решить проблему с реестром; это также соответствует C# сторона, когда вы устанавливаете буферы. Когда вы устанавливаете буферы, вы назначаете индексы этим буферам и HLSL Ожидается, что будут использовать те же индексы.

// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);

// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);

Приведенный выше код будет работать как положено, поскольку буферы назначены на правильные регистры. Однако, если мы изменим буфер для камеры, например, на 8, то cbuffer должен быть назначен для регистрации b8 для того, чтобы работать правильно. Код ниже не работает именно по этой причине.

cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]