Этот вопрос имеет 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-байтовых чанках;
не являются 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
регистр?В настоящее время я работаю над дальнейшим анализом проблемы, чтобы я мог дать более подробный и точный ответ. Я обновлю вопрос и ответ, чтобы отразить как можно большую точность, поскольку я узнаю больше деталей.
Точная проблема с вопросом выше (который был неизвестен на момент публикации), заключается в том, что 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);
Других решений пока нет …