Refactor vertex shaders to use GPUShaderProgramVS::InputElement for input layout data

This commit is contained in:
Wojtek Figat
2023-10-26 14:36:02 +02:00
parent 186e13b5e8
commit 1d41aa01ce
5 changed files with 59 additions and 79 deletions

View File

@@ -122,6 +122,19 @@ public:
/// </summary> /// </summary>
class GPUShaderProgramVS : public GPUShaderProgram class GPUShaderProgramVS : public GPUShaderProgram
{ {
public:
// Input element run-time data (see VertexShaderMeta::InputElement for compile-time data)
PACK_STRUCT(struct InputElement
{
byte Type; // VertexShaderMeta::InputType
byte Index;
byte Format; // PixelFormat
byte InputSlot;
uint32 AlignedByteOffset; // Fixed value or INPUT_LAYOUT_ELEMENT_ALIGN if auto
byte InputSlotClass; // INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA
uint32 InstanceDataStepRate; // 0 if per-vertex
});
public: public:
/// <summary> /// <summary>
/// Gets input layout description handle (platform dependent). /// Gets input layout description handle (platform dependent).

View File

@@ -15,32 +15,21 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
{ {
case ShaderStage::Vertex: case ShaderStage::Vertex:
{ {
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; // Load Input Layout
// Temporary variables
byte Type, Format, Index, InputSlot, InputSlotClass;
uint32 AlignedByteOffset, InstanceDataStepRate;
// Load Input Layout (it may be empty)
byte inputLayoutSize; byte inputLayoutSize;
stream.ReadByte(&inputLayoutSize); stream.ReadByte(&inputLayoutSize);
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS); ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
for (int32 a = 0; a < inputLayoutSize; a++) for (int32 a = 0; a < inputLayoutSize; a++)
{ {
// Read description // Read description
// TODO: maybe use struct and load at once? GPUShaderProgramVS::InputElement inputElement;
stream.ReadByte(&Type); stream.Read(inputElement);
stream.ReadByte(&Index);
stream.ReadByte(&Format);
stream.ReadByte(&InputSlot);
stream.ReadUint32(&AlignedByteOffset);
stream.ReadByte(&InputSlotClass);
stream.ReadUint32(&InstanceDataStepRate);
// Get semantic name // Get semantic name
const char* semanticName = nullptr; const char* semanticName = nullptr;
// TODO: maybe use enum+mapping ? // TODO: maybe use enum+mapping ?
switch (Type) switch (inputElement.Type)
{ {
case 1: case 1:
semanticName = "POSITION"; semanticName = "POSITION";
@@ -70,7 +59,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
semanticName = "BLENDWEIGHT"; semanticName = "BLENDWEIGHT";
break; break;
default: default:
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type); LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
break; break;
} }
@@ -78,12 +67,12 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
inputLayoutDesc[a] = inputLayoutDesc[a] =
{ {
semanticName, semanticName,
static_cast<UINT>(Index), static_cast<UINT>(inputElement.Index),
static_cast<DXGI_FORMAT>(Format), static_cast<DXGI_FORMAT>(inputElement.Format),
static_cast<UINT>(InputSlot), static_cast<UINT>(inputElement.InputSlot),
static_cast<UINT>(AlignedByteOffset), static_cast<UINT>(inputElement.AlignedByteOffset),
static_cast<D3D11_INPUT_CLASSIFICATION>(InputSlotClass), static_cast<D3D11_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
static_cast<UINT>(InstanceDataStepRate) static_cast<UINT>(inputElement.InstanceDataStepRate)
}; };
} }

View File

@@ -20,32 +20,21 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
{ {
case ShaderStage::Vertex: case ShaderStage::Vertex:
{ {
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
// Temporary variables
byte Type, Format, Index, InputSlot, InputSlotClass;
uint32 AlignedByteOffset, InstanceDataStepRate;
// Load Input Layout (it may be empty) // Load Input Layout (it may be empty)
byte inputLayoutSize; byte inputLayoutSize;
stream.ReadByte(&inputLayoutSize); stream.ReadByte(&inputLayoutSize);
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS); ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
for (int32 a = 0; a < inputLayoutSize; a++) for (int32 a = 0; a < inputLayoutSize; a++)
{ {
// Read description // Read description
// TODO: maybe use struct and load at once? GPUShaderProgramVS::InputElement inputElement;
stream.ReadByte(&Type); stream.Read(inputElement);
stream.ReadByte(&Index);
stream.ReadByte(&Format);
stream.ReadByte(&InputSlot);
stream.ReadUint32(&AlignedByteOffset);
stream.ReadByte(&InputSlotClass);
stream.ReadUint32(&InstanceDataStepRate);
// Get semantic name // Get semantic name
const char* semanticName = nullptr; const char* semanticName = nullptr;
// TODO: maybe use enum+mapping ? // TODO: maybe use enum+mapping ?
switch (Type) switch (inputElement.Type)
{ {
case 1: case 1:
semanticName = "POSITION"; semanticName = "POSITION";
@@ -75,7 +64,7 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
semanticName = "BLENDWEIGHT"; semanticName = "BLENDWEIGHT";
break; break;
default: default:
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type); LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
break; break;
} }
@@ -83,12 +72,12 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
inputLayout[a] = inputLayout[a] =
{ {
semanticName, semanticName,
static_cast<UINT>(Index), static_cast<UINT>(inputElement.Index),
static_cast<DXGI_FORMAT>(Format), static_cast<DXGI_FORMAT>(inputElement.Format),
static_cast<UINT>(InputSlot), static_cast<UINT>(inputElement.InputSlot),
static_cast<UINT>(AlignedByteOffset), static_cast<UINT>(inputElement.AlignedByteOffset),
static_cast<D3D12_INPUT_CLASSIFICATION>(InputSlotClass), static_cast<D3D12_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
static_cast<UINT>(InstanceDataStepRate) static_cast<UINT>(inputElement.InstanceDataStepRate)
}; };
} }

View File

@@ -14,9 +14,9 @@
#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/PixelFormatExtensions.h"
#if PLATFORM_DESKTOP #if PLATFORM_DESKTOP
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 24 * 1024 * 1024 #define VULKAN_UNIFORM_RING_BUFFER_SIZE (24 * 1024 * 1024)
#else #else
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 8 * 1024 * 1024 #define VULKAN_UNIFORM_RING_BUFFER_SIZE (8 * 1024 * 1024)
#endif #endif
UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device) UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device)
@@ -153,10 +153,6 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
vertexBindingDescriptions[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; vertexBindingDescriptions[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
} }
// Temporary variables
byte Type, Format, Index, InputSlot, InputSlotClass;
uint32 AlignedByteOffset, InstanceDataStepRate;
// Load Input Layout (it may be empty) // Load Input Layout (it may be empty)
byte inputLayoutSize; byte inputLayoutSize;
stream.ReadByte(&inputLayoutSize); stream.ReadByte(&inputLayoutSize);
@@ -167,32 +163,26 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
for (int32 a = 0; a < inputLayoutSize; a++) for (int32 a = 0; a < inputLayoutSize; a++)
{ {
// Read description // Read description
// TODO: maybe use struct and load at once? GPUShaderProgramVS::InputElement inputElement;
stream.ReadByte(&Type); stream.Read(inputElement);
stream.ReadByte(&Index);
stream.ReadByte(&Format);
stream.ReadByte(&InputSlot);
stream.ReadUint32(&AlignedByteOffset);
stream.ReadByte(&InputSlotClass);
stream.ReadUint32(&InstanceDataStepRate);
const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)Format); const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)inputElement.Format);
if (AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN) if (inputElement.AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN)
offset = AlignedByteOffset; offset = inputElement.AlignedByteOffset;
auto& vertexBindingDescription = vertexBindingDescriptions[InputSlot]; auto& vertexBindingDescription = vertexBindingDescriptions[inputElement.InputSlot];
vertexBindingDescription.binding = InputSlot; vertexBindingDescription.binding = inputElement.InputSlot;
vertexBindingDescription.stride = Math::Max(vertexBindingDescription.stride, (uint32_t)(offset + size)); vertexBindingDescription.stride = Math::Max(vertexBindingDescription.stride, (uint32_t)(offset + size));
vertexBindingDescription.inputRate = InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE; vertexBindingDescription.inputRate = inputElement.InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
ASSERT(InstanceDataStepRate == 0 || InstanceDataStepRate == 1); ASSERT(inputElement.InstanceDataStepRate == 0 || inputElement.InstanceDataStepRate == 1);
auto& vertexAttributeDescription = vertexAttributeDescriptions[a]; auto& vertexAttributeDescription = vertexAttributeDescriptions[a];
vertexAttributeDescription.location = a; vertexAttributeDescription.location = a;
vertexAttributeDescription.binding = InputSlot; vertexAttributeDescription.binding = inputElement.InputSlot;
vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)Format); vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)inputElement.Format);
vertexAttributeDescription.offset = offset; vertexAttributeDescription.offset = offset;
bindingsCount = Math::Max(bindingsCount, (uint32)InputSlot + 1); bindingsCount = Math::Max(bindingsCount, (uint32)inputElement.InputSlot + 1);
offset += size; offset += size;
} }

View File

@@ -460,16 +460,15 @@ bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, Shader
auto& element = layout[a]; auto& element = layout[a];
if (!layoutVisible[a]) if (!layoutVisible[a])
continue; continue;
GPUShaderProgramVS::InputElement data;
// TODO: serialize whole struct? data.Type = static_cast<byte>(element.Type);
data.Index = element.Index;
output->WriteByte(static_cast<byte>(element.Type)); data.Format = static_cast<byte>(element.Format);
output->WriteByte(element.Index); data.InputSlot = element.InputSlot;
output->WriteByte(static_cast<byte>(element.Format)); data.AlignedByteOffset = element.AlignedByteOffset;
output->WriteByte(element.InputSlot); data.InputSlotClass = element.InputSlotClass;
output->WriteUint32(element.AlignedByteOffset); data.InstanceDataStepRate = element.InstanceDataStepRate;
output->WriteByte(element.InputSlotClass); output->Write(data);
output->WriteUint32(element.InstanceDataStepRate);
} }
return false; return false;