diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h
index 23636bfdf..9fdba7885 100644
--- a/Source/Engine/Graphics/GPUDevice.h
+++ b/Source/Engine/Graphics/GPUDevice.h
@@ -401,7 +401,7 @@ public:
/// Creates the vertex buffer layout.
///
/// The vertex buffer layout.
- API_FUNCTION() virtual GPUVertexLayout* CreateVertexLayout(const Array>& elements) = 0;
+ API_FUNCTION() virtual GPUVertexLayout* CreateVertexLayout(const Array>& elements, bool explicitOffsets = false) = 0;
typedef Array> VertexElements;
///
diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp
index 659bcb344..91f19c701 100644
--- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp
+++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp
@@ -45,6 +45,26 @@ namespace
CriticalSection CacheLocker;
Dictionary LayoutCache;
Dictionary VertexBufferCache;
+
+ GPUVertexLayout* AddCache(const VertexBufferLayouts& key, int32 count)
+ {
+ GPUVertexLayout::Elements elements;
+ bool anyValid = false;
+ for (int32 slot = 0; slot < count; slot++)
+ {
+ if (key.Layouts[slot])
+ {
+ anyValid = true;
+ int32 start = elements.Count();
+ elements.Add(key.Layouts[slot]->GetElements());
+ for (int32 j = start; j < elements.Count(); j++)
+ elements.Get()[j].Slot = (byte)slot;
+ }
+ }
+ GPUVertexLayout* result = anyValid ? GPUVertexLayout::Get(elements) : nullptr;
+ VertexBufferCache.Add(key, result);
+ return result;
+ }
}
String VertexElement::ToString() const
@@ -76,22 +96,27 @@ GPUVertexLayout::GPUVertexLayout()
{
}
-void GPUVertexLayout::SetElements(const Elements& elements, uint32 offsets[GPU_MAX_VS_ELEMENTS])
+void GPUVertexLayout::SetElements(const Elements& elements, bool explicitOffsets)
{
+ uint32 offsets[GPU_MAX_VB_BINDED] = {};
_elements = elements;
- uint32 strides[GPU_MAX_VB_BINDED] = {};
- for (int32 i = 0; i < elements.Count(); i++)
+ for (int32 i = 0; i < _elements.Count(); i++)
{
- const VertexElement& e = elements[i];
+ VertexElement& e = _elements[i];
ASSERT(e.Slot < GPU_MAX_VB_BINDED);
- strides[e.Slot] = Math::Max(strides[e.Slot], offsets[e.Slot]);
+ uint32& offset = offsets[e.Slot];
+ if (e.Offset != 0 || explicitOffsets)
+ offset = e.Offset;
+ else
+ e.Offset = (byte)offset;
+ offset += PixelFormatExtensions::SizeInBytes(e.Format);
}
_stride = 0;
- for (uint32 stride : strides)
- _stride += stride;
+ for (uint32 offset : offsets)
+ _stride += offset;
}
-GPUVertexLayout* GPUVertexLayout::Get(const Elements& elements)
+GPUVertexLayout* GPUVertexLayout::Get(const Elements& elements, bool explicitOffsets)
{
// Hash input layout
uint32 hash = 0;
@@ -105,7 +130,7 @@ GPUVertexLayout* GPUVertexLayout::Get(const Elements& elements)
GPUVertexLayout* result;
if (!LayoutCache.TryGet(hash, result))
{
- result = GPUDevice::Instance->CreateVertexLayout(elements);
+ result = GPUDevice::Instance->CreateVertexLayout(elements, explicitOffsets);
if (!result)
{
#if GPU_ENABLE_ASSERTION_LOW_LAYERS
@@ -118,16 +143,6 @@ GPUVertexLayout* GPUVertexLayout::Get(const Elements& elements)
}
LayoutCache.Add(hash, result);
}
-#if GPU_ENABLE_ASSERTION_LOW_LAYERS
- else if (result->GetElements() != elements)
- {
- for (auto& e : result->GetElements())
- LOG(Error, " (a) {}", e.ToString());
- for (auto& e : elements)
- LOG(Error, " (b) {}", e.ToString());
- LOG(Fatal, "Vertex layout cache collision for hash {}", hash);
- }
-#endif
CacheLocker.Unlock();
return result;
@@ -141,38 +156,94 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span& vertexBuffers)
return vertexBuffers.Get()[0] ? vertexBuffers.Get()[0]->GetVertexLayout() : nullptr;
// Build hash key for set of buffers (in case there is layout sharing by different sets of buffers)
- VertexBufferLayouts layouts;
- for (int32 i = 0; i < vertexBuffers.Length(); i++)
- layouts.Layouts[i] = vertexBuffers.Get()[i] ? vertexBuffers.Get()[i]->GetVertexLayout() : nullptr;
+ VertexBufferLayouts key;
+ for (int32 i = 0; i < vertexBuffers.Length() && i < GPU_MAX_VB_BINDED; i++)
+ key.Layouts[i] = vertexBuffers.Get()[i] ? vertexBuffers.Get()[i]->GetVertexLayout() : nullptr;
for (int32 i = vertexBuffers.Length(); i < GPU_MAX_VB_BINDED; i++)
- layouts.Layouts[i] = nullptr;
+ key.Layouts[i] = nullptr;
// Lookup existing cache
CacheLocker.Lock();
GPUVertexLayout* result;
- if (!VertexBufferCache.TryGet(layouts, result))
- {
- Elements elements;
- bool anyValid = false;
- for (int32 slot = 0; slot < vertexBuffers.Length(); slot++)
- {
- if (layouts.Layouts[slot])
- {
- anyValid = true;
- int32 start = elements.Count();
- elements.Add(layouts.Layouts[slot]->GetElements());
- for (int32 j = start; j < elements.Count(); j++)
- elements.Get()[j].Slot = (byte)slot;
- }
- }
- result = anyValid ? Get(elements) : nullptr;
- VertexBufferCache.Add(layouts, result);
- }
+ if (!VertexBufferCache.TryGet(key, result))
+ result = AddCache(key, vertexBuffers.Length());
CacheLocker.Unlock();
return result;
}
+GPUVertexLayout* GPUVertexLayout::Get(const Span& layouts)
+{
+ if (layouts.Length() == 0)
+ return nullptr;
+ if (layouts.Length() == 1)
+ return layouts.Get()[0] ? layouts.Get()[0] : nullptr;
+
+ // Build hash key for set of buffers (in case there is layout sharing by different sets of buffers)
+ VertexBufferLayouts key;
+ for (int32 i = 0; i < layouts.Length() && i < GPU_MAX_VB_BINDED; i++)
+ key.Layouts[i] = layouts.Get()[i];
+ for (int32 i = layouts.Length(); i < GPU_MAX_VB_BINDED; i++)
+ key.Layouts[i] = nullptr;
+
+ // Lookup existing cache
+ CacheLocker.Lock();
+ GPUVertexLayout* result;
+ if (!VertexBufferCache.TryGet(key, result))
+ result = AddCache(key, layouts.Length());
+ CacheLocker.Unlock();
+
+ return result;
+}
+
+GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, const GPUVertexLayout* reference)
+{
+ if (!reference || !base || base == reference)
+ return base;
+ GPUVertexLayout* result = base;
+ if (base && reference)
+ {
+ bool anyMissing = false;
+ const Elements& baseElements = base->GetElements();
+ Elements newElements = baseElements;
+ for (const VertexElement& e : reference->GetElements())
+ {
+ bool missing = true;
+ for (const VertexElement& ee : baseElements)
+ {
+ if (ee.Type == e.Type)
+ {
+ missing = false;
+ break;
+ }
+ }
+ if (missing)
+ {
+ // Insert any missing elements
+ VertexElement ne = { e.Type, e.Slot, 0, e.PerInstance, e.Format };
+ if (e.Type == VertexElement::Types::TexCoord1 || e.Type == VertexElement::Types::TexCoord2 || e.Type == VertexElement::Types::TexCoord3)
+ {
+ // Alias missing texcoords with existing texcoords
+ for (const VertexElement& ee : newElements)
+ {
+ if (ee.Type == VertexElement::Types::TexCoord0)
+ {
+ ne = ee;
+ ne.Type = e.Type;
+ break;
+ }
+ }
+ }
+ newElements.Add(ne);
+ anyMissing = true;
+ }
+ }
+ if (anyMissing)
+ result = Get(newElements, true);
+ }
+ return result;
+}
+
void ClearVertexLayoutCache()
{
for (const auto& e : LayoutCache)
diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h
index e234b32b1..56cf413bc 100644
--- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h
+++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h
@@ -23,7 +23,7 @@ private:
protected:
GPUVertexLayout();
- void SetElements(const Elements& elements, uint32 offsets[GPU_MAX_VS_ELEMENTS]);
+ void SetElements(const Elements& elements, bool explicitOffsets);
public:
///
@@ -46,8 +46,9 @@ public:
/// Gets the vertex layout for a given list of elements. Uses internal cache to skip creating layout if it's already exists for a given list.
///
/// The list of elements for the layout.
+ /// If set to true, input elements offsets will be used without automatic calculations (offsets with value 0).
/// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.
- API_FUNCTION() static GPUVertexLayout* Get(const Array>& elements);
+ API_FUNCTION() static GPUVertexLayout* Get(const Array>& elements, bool explicitOffsets = false);
///
/// Gets the vertex layout for a given list of vertex buffers (sequence of binding slots based on layouts set on those buffers). Uses internal cache to skip creating layout if it's already exists for a given list.
@@ -56,6 +57,21 @@ public:
/// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.
API_FUNCTION() static GPUVertexLayout* Get(const Span& vertexBuffers);
+ ///
+ /// Merges list of layouts in a single one. Uses internal cache to skip creating layout if it's already exists for a given list.
+ ///
+ /// The list of layouts to merge.
+ /// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.
+ API_FUNCTION() static GPUVertexLayout* Get(const Span& layouts);
+
+ ///
+ /// Merges reference vertex elements into the given set of elements to ensure the reference list is satisfied (vertex shader input requirement). Returns base layout if it's valid.
+ ///
+ /// The list of vertex buffers for the layout.
+ /// The list of reference inputs.
+ /// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.
+ static GPUVertexLayout* Merge(GPUVertexLayout* base, const GPUVertexLayout* reference);
+
public:
// [GPUResource]
GPUResourceType GetResourceType() const override
diff --git a/Source/Engine/Graphics/Shaders/VertexElement.h b/Source/Engine/Graphics/Shaders/VertexElement.h
index f15bb86c7..4bc628fcc 100644
--- a/Source/Engine/Graphics/Shaders/VertexElement.h
+++ b/Source/Engine/Graphics/Shaders/VertexElement.h
@@ -66,7 +66,7 @@ PACK_BEGIN() struct FLAXENGINE_API VertexElement
API_FIELD() Types Type;
// Index of the input vertex buffer slot (as provided in GPUContext::BindVB).
API_FIELD() byte Slot;
- // Byte offset of this element relative to the start of a vertex buffer. Use value 0 to use auto-calculated offset based on previous elements in the layout (or for the first one).
+ // Byte offset of this element relative to the start of a vertex buffer. Use value 0 to use auto-calculated offset based on previous elements in the layout (except when explicitOffsets is false).
API_FIELD() byte Offset;
// Flag used to mark data using hardware-instancing (element will be repeated for every instance). Empty to step data per-vertex when reading input buffer stream (rather than per-instance step).
API_FIELD() byte PerInstance;
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
index 35656606a..ac11989d5 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp
@@ -148,27 +148,22 @@ static bool TryCreateDevice(IDXGIAdapter* adapter, D3D_FEATURE_LEVEL maxFeatureL
return false;
}
-GPUVertexLayoutDX11::GPUVertexLayoutDX11(GPUDeviceDX11* device, const Elements& elements)
+GPUVertexLayoutDX11::GPUVertexLayoutDX11(GPUDeviceDX11* device, const Elements& elements, bool explicitOffsets)
: GPUResourceBase(device, StringView::Empty)
, InputElementsCount(elements.Count())
{
- uint32 offsets[GPU_MAX_VB_BINDED] = {};
+ SetElements(elements, explicitOffsets);
for (int32 i = 0; i < elements.Count(); i++)
{
- const VertexElement& src = elements.Get()[i];
+ const VertexElement& src = GetElements().Get()[i];
D3D11_INPUT_ELEMENT_DESC& dst = InputElements[i];
- uint32& offset = offsets[src.Slot];
- if (src.Offset != 0)
- offset = src.Offset;
dst.SemanticName = RenderToolsDX::GetVertexInputSemantic(src.Type, dst.SemanticIndex);
dst.Format = RenderToolsDX::ToDxgiFormat(src.Format);
dst.InputSlot = src.Slot;
- dst.AlignedByteOffset = offset;
+ dst.AlignedByteOffset = src.Offset;
dst.InputSlotClass = src.PerInstance ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
dst.InstanceDataStepRate = src.PerInstance ? 1 : 0;
- offset += PixelFormatExtensions::SizeInBytes(src.Format);
}
- SetElements(elements, offsets);
}
GPUDevice* GPUDeviceDX11::Create()
@@ -832,9 +827,9 @@ GPUSampler* GPUDeviceDX11::CreateSampler()
return New(this);
}
-GPUVertexLayout* GPUDeviceDX11::CreateVertexLayout(const VertexElements& elements)
+GPUVertexLayout* GPUDeviceDX11::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets)
{
- return New(this, elements);
+ return New(this, elements, explicitOffsets);
}
GPUSwapChain* GPUDeviceDX11::CreateSwapChain(Window* window)
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
index 079d38967..066f02863 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.h
@@ -128,7 +128,7 @@ public:
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
GPUSampler* CreateSampler() override;
- GPUVertexLayout* CreateVertexLayout(const VertexElements& elements) override;
+ GPUVertexLayout* CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) override;
GPUSwapChain* CreateSwapChain(Window* window) override;
GPUConstantBuffer* CreateConstantBuffer(uint32 size, const StringView& name) override;
};
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp
index faf50a927..7dcfb0d31 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUShaderDX11.cpp
@@ -11,19 +11,23 @@
GPUShaderProgramVSDX11::~GPUShaderProgramVSDX11()
{
for (const auto& e : _cache)
- e.Value->Release();
+ {
+ if (e.Value)
+ e.Value->Release();
+ }
}
ID3D11InputLayout* GPUShaderProgramVSDX11::GetInputLayout(GPUVertexLayoutDX11* vertexLayout)
{
- if (!vertexLayout)
- vertexLayout = (GPUVertexLayoutDX11*)Layout;
ID3D11InputLayout* inputLayout = nullptr;
if (!_cache.TryGet(vertexLayout, inputLayout))
{
+ if (!vertexLayout)
+ vertexLayout = (GPUVertexLayoutDX11*)Layout;
if (vertexLayout && vertexLayout->InputElementsCount)
{
- VALIDATE_DIRECTX_CALL(vertexLayout->GetDevice()->GetDevice()->CreateInputLayout(vertexLayout->InputElements, vertexLayout->InputElementsCount, Bytecode.Get(), Bytecode.Length(), &inputLayout));
+ auto actualLayout = (GPUVertexLayoutDX11*)GPUVertexLayout::Merge(vertexLayout, Layout);
+ LOG_DIRECTX_RESULT(vertexLayout->GetDevice()->GetDevice()->CreateInputLayout(actualLayout->InputElements, actualLayout->InputElementsCount, Bytecode.Get(), Bytecode.Length(), &inputLayout));
}
_cache.Add(vertexLayout, inputLayout);
}
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUVertexLayoutDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUVertexLayoutDX11.h
index 119ee2964..d5ff07a0c 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUVertexLayoutDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUVertexLayoutDX11.h
@@ -13,7 +13,7 @@
class GPUVertexLayoutDX11 : public GPUResourceBase
{
public:
- GPUVertexLayoutDX11(GPUDeviceDX11* device, const Elements& elements);
+ GPUVertexLayoutDX11(GPUDeviceDX11* device, const Elements& elements, bool explicitOffsets);
uint32 InputElementsCount;
D3D11_INPUT_ELEMENT_DESC InputElements[GPU_MAX_VS_ELEMENTS];
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
index ab4a0ab81..65e3f6d83 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp
@@ -36,27 +36,22 @@ static bool CheckDX12Support(IDXGIAdapter* adapter)
return false;
}
-GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements)
+GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets)
: GPUResourceDX12(device, StringView::Empty)
, InputElementsCount(elements.Count())
{
- uint32 offsets[GPU_MAX_VB_BINDED] = {};
+ SetElements(elements, explicitOffsets);
for (int32 i = 0; i < elements.Count(); i++)
{
- const VertexElement& src = elements.Get()[i];
+ const VertexElement& src = GetElements().Get()[i];
D3D12_INPUT_ELEMENT_DESC& dst = InputElements[i];
- uint32& offset = offsets[src.Slot];
- if (src.Offset != 0)
- offset = src.Offset;
dst.SemanticName = RenderToolsDX::GetVertexInputSemantic(src.Type, dst.SemanticIndex);
dst.Format = RenderToolsDX::ToDxgiFormat(src.Format);
dst.InputSlot = src.Slot;
- dst.AlignedByteOffset = offset;
+ dst.AlignedByteOffset = src.Offset;
dst.InputSlotClass = src.PerInstance ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
dst.InstanceDataStepRate = src.PerInstance ? 1 : 0;
- offset += PixelFormatExtensions::SizeInBytes(src.Format);
}
- SetElements(elements, offsets);
}
GPUDevice* GPUDeviceDX12::Create()
@@ -868,9 +863,9 @@ GPUSampler* GPUDeviceDX12::CreateSampler()
return New(this);
}
-GPUVertexLayout* GPUDeviceDX12::CreateVertexLayout(const VertexElements& elements)
+GPUVertexLayout* GPUDeviceDX12::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets)
{
- return New(this, elements);
+ return New(this, elements, explicitOffsets);
}
GPUSwapChain* GPUDeviceDX12::CreateSwapChain(Window* window)
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
index 82f59a8ee..81c42070f 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h
@@ -196,7 +196,7 @@ public:
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
GPUSampler* CreateSampler() override;
- GPUVertexLayout* CreateVertexLayout(const VertexElements& elements) override;
+ GPUVertexLayout* CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) override;
GPUSwapChain* CreateSwapChain(Window* window) override;
GPUConstantBuffer* CreateConstantBuffer(uint32 size, const StringView& name) override;
};
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUVertexLayoutDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUVertexLayoutDX12.h
index 225c3367c..79b4aa9de 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUVertexLayoutDX12.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUVertexLayoutDX12.h
@@ -13,7 +13,7 @@
class GPUVertexLayoutDX12 : public GPUResourceDX12
{
public:
- GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements);
+ GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets);
uint32 InputElementsCount;
D3D12_INPUT_ELEMENT_DESC InputElements[GPU_MAX_VS_ELEMENTS];
diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
index d1fe23dd8..f7fdd1eba 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
+++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp
@@ -173,7 +173,7 @@ GPUSampler* GPUDeviceNull::CreateSampler()
return New();
}
-GPUVertexLayout* GPUDeviceNull::CreateVertexLayout(const VertexElements& elements)
+GPUVertexLayout* GPUDeviceNull::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets)
{
return New(elements);
}
diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
index 80de34db3..b9d53d2f4 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
+++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.h
@@ -47,7 +47,7 @@ public:
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
GPUSampler* CreateSampler() override;
- GPUVertexLayout* CreateVertexLayout(const VertexElements& elements) override;
+ GPUVertexLayout* CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) override;
GPUSwapChain* CreateSwapChain(Window* window) override;
GPUConstantBuffer* CreateConstantBuffer(uint32 size, const StringView& name) override;
};
diff --git a/Source/Engine/GraphicsDevice/Null/GPUVertexLayoutNull.h b/Source/Engine/GraphicsDevice/Null/GPUVertexLayoutNull.h
index f1a5f0abd..a483a881f 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUVertexLayoutNull.h
+++ b/Source/Engine/GraphicsDevice/Null/GPUVertexLayoutNull.h
@@ -15,7 +15,7 @@ public:
GPUVertexLayoutNull(const Elements& elements)
: GPUVertexLayout()
{
- SetElements(elements, {});
+ SetElements(elements, false);
}
};
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
index e5f7f2bcc..21e653a36 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
@@ -449,10 +449,10 @@ uint32 GetHash(const FramebufferVulkan::Key& key)
return hash;
}
-GPUVertexLayoutVulkan::GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elements& elements)
+GPUVertexLayoutVulkan::GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elements& elements, bool explicitOffsets)
: GPUResourceVulkan(device, StringView::Empty)
{
- uint32 offsets[GPU_MAX_VB_BINDED] = {};
+ SetElements(elements, explicitOffsets);
for (int32 i = 0; i < GPU_MAX_VB_BINDED; i++)
{
VkVertexInputBindingDescription& binding = Bindings[i];
@@ -463,28 +463,23 @@ GPUVertexLayoutVulkan::GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elem
uint32 bindingsCount = 0;
for (int32 i = 0; i < elements.Count(); i++)
{
- const VertexElement& src = elements.Get()[i];
- uint32& offset = offsets[src.Slot];
- if (src.Offset != 0)
- offset = src.Offset;
+ const VertexElement& src = GetElements().Get()[i];
const int32 size = PixelFormatExtensions::SizeInBytes(src.Format);
ASSERT_LOW_LAYER(src.Slot < GPU_MAX_VB_BINDED);
VkVertexInputBindingDescription& binding = Bindings[src.Slot];
binding.binding = src.Slot;
- binding.stride = Math::Max(binding.stride, (uint32_t)(offset + size));
+ binding.stride = Math::Max(binding.stride, (uint32_t)(src.Offset + size));
binding.inputRate = src.PerInstance ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription& attribute = Attributes[i];
attribute.location = i;
attribute.binding = src.Slot;
attribute.format = RenderToolsVulkan::ToVulkanFormat(src.Format);
- attribute.offset = offset;
+ attribute.offset = src.Offset;
bindingsCount = Math::Max(bindingsCount, (uint32)src.Slot + 1);
- offset += size;
}
- SetElements(elements, offsets);
RenderToolsVulkan::ZeroStruct(CreateInfo, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO);
CreateInfo.vertexBindingDescriptionCount = bindingsCount;
@@ -2129,9 +2124,9 @@ GPUSampler* GPUDeviceVulkan::CreateSampler()
return New(this);
}
-GPUVertexLayout* GPUDeviceVulkan::CreateVertexLayout(const VertexElements& elements)
+GPUVertexLayout* GPUDeviceVulkan::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets)
{
- return New(this, elements);
+ return New(this, elements, explicitOffsets);
}
GPUSwapChain* GPUDeviceVulkan::CreateSwapChain(Window* window)
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
index 8b64eeb9c..10a6c1b77 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
@@ -610,7 +610,7 @@ public:
GPUTimerQuery* CreateTimerQuery() override;
GPUBuffer* CreateBuffer(const StringView& name) override;
GPUSampler* CreateSampler() override;
- GPUVertexLayout* CreateVertexLayout(const VertexElements& elements) override;
+ GPUVertexLayout* CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) override;
GPUSwapChain* CreateSwapChain(Window* window) override;
GPUConstantBuffer* CreateConstantBuffer(uint32 size, const StringView& name) override;
};
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h
index b2d0e4b75..67a05824f 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h
@@ -13,7 +13,7 @@
class GPUVertexLayoutVulkan : public GPUResourceVulkan
{
public:
- GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elements& elements);
+ GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elements& elements, bool explicitOffsets);
VkPipelineVertexInputStateCreateInfo CreateInfo;
VkVertexInputBindingDescription Bindings[GPU_MAX_VB_BINDED];