diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp
index 91c4fe8b8..e897c6258 100644
--- a/Source/Engine/Graphics/Materials/MaterialShader.cpp
+++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp
@@ -145,6 +145,11 @@ MaterialShader* MaterialShader::CreateDummy(MemoryReadStream& shaderCacheStream,
return material;
}
+GPUShader* MaterialShader::GetShader() const
+{
+ return _shader;
+}
+
const MaterialInfo& MaterialShader::GetInfo() const
{
return _info;
diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h
index af0045f04..9879c87ec 100644
--- a/Source/Engine/Graphics/Materials/MaterialShader.h
+++ b/Source/Engine/Graphics/Materials/MaterialShader.h
@@ -100,6 +100,8 @@ public:
/// The created and loaded material or null if failed.
static MaterialShader* CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info);
+ GPUShader* GetShader() const;
+
///
/// Clears the loaded data.
///
diff --git a/Source/Engine/Graphics/Shaders/GPUShader.cpp b/Source/Engine/Graphics/Shaders/GPUShader.cpp
index c3095da4e..d14a204a5 100644
--- a/Source/Engine/Graphics/Shaders/GPUShader.cpp
+++ b/Source/Engine/Graphics/Shaders/GPUShader.cpp
@@ -82,6 +82,9 @@ bool GPUShader::Create(MemoryReadStream& stream)
int32 shadersCount;
stream.ReadInt32(&shadersCount);
GPUShaderProgramInitializer initializer;
+#if !BUILD_RELEASE
+ initializer.Owner = this;
+#endif
for (int32 i = 0; i < shadersCount; i++)
{
const ShaderStage type = static_cast(stream.ReadByte());
diff --git a/Source/Engine/Graphics/Shaders/GPUShaderProgram.h b/Source/Engine/Graphics/Shaders/GPUShaderProgram.h
index c6a065a62..9e62d2495 100644
--- a/Source/Engine/Graphics/Shaders/GPUShaderProgram.h
+++ b/Source/Engine/Graphics/Shaders/GPUShaderProgram.h
@@ -6,6 +6,8 @@
#include "Engine/Core/Types/String.h"
#include "Config.h"
+class GPUShader;
+
///
/// The shader program metadata container. Contains description about resources used by the shader.
///
@@ -37,6 +39,9 @@ struct GPUShaderProgramInitializer
StringAnsi Name;
ShaderBindings Bindings;
ShaderFlags Flags;
+#if !BUILD_RELEASE
+ GPUShader* Owner;
+#endif
};
///
@@ -49,12 +54,18 @@ protected:
StringAnsi _name;
ShaderBindings _bindings;
ShaderFlags _flags;
+#if !BUILD_RELEASE
+ GPUShader* _owner;
+#endif
void Init(const GPUShaderProgramInitializer& initializer)
{
_name = initializer.Name;
_bindings = initializer.Bindings;
_flags = initializer.Flags;
+#if !BUILD_RELEASE
+ _owner = initializer.Owner;
+#endif
}
public:
@@ -69,9 +80,8 @@ public:
public:
///
- /// Gets name of the shader program
+ /// Gets name of the shader program.
///
- /// Name
FORCE_INLINE const StringAnsi& GetName() const
{
return _name;
@@ -80,7 +90,6 @@ public:
///
/// Gets the shader resource bindings.
///
- /// The bindings.
FORCE_INLINE const ShaderBindings& GetBindings() const
{
return _bindings;
@@ -89,7 +98,6 @@ public:
///
/// Gets the shader flags.
///
- /// The flags.
FORCE_INLINE ShaderFlags GetFlags() const
{
return _flags;
@@ -98,21 +106,18 @@ public:
public:
///
- /// Gets shader program stage type
+ /// Gets shader program stage type.
///
- /// Shader Stage type
virtual ShaderStage GetStage() const = 0;
///
- /// Gets buffer handle (platform dependent)
+ /// Gets buffer handle (platform dependent).
///
- /// Handle
virtual void* GetBufferHandle() const = 0;
///
- /// Gets buffer size (in bytes)
+ /// Gets buffer size (in bytes).
///
- /// Size of the buffer in bytes
virtual uint32 GetBufferSize() const = 0;
};
@@ -124,15 +129,13 @@ class GPUShaderProgramVS : public GPUShaderProgram
public:
///
- /// Gets input layout description handle (platform dependent)
+ /// Gets input layout description handle (platform dependent).
///
- /// Input layout
virtual void* GetInputLayout() const = 0;
///
- /// Gets input layout description size (in bytes)
+ /// Gets input layout description size (in bytes).
///
- /// Input layout description size in bytes
virtual byte GetInputLayoutSize() const = 0;
public:
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
index 13cb2333f..5a4a1c77a 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp
@@ -346,7 +346,6 @@ void GPUContextVulkan::BeginRenderPass()
{
layout.RTVsFormats[i] = handle->GetFormat();
framebufferKey.Attachments[i] = handle->GetFramebufferView();
-
AddImageBarrier(handle, handle->LayoutRTV);
}
else
@@ -355,36 +354,31 @@ void GPUContextVulkan::BeginRenderPass()
framebufferKey.Attachments[i] = VK_NULL_HANDLE;
}
}
+ GPUTextureViewVulkan* handle;
if (_rtDepth)
{
- auto handle = _rtDepth;
- layout.MSAA = handle->GetMSAA();
- layout.Extent = handle->Extent;
+ handle = _rtDepth;
layout.ReadDepth = true; // TODO: use proper depthStencilAccess flags
layout.WriteDepth = handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // TODO: do it in a proper way
framebufferKey.AttachmentCount++;
framebufferKey.Attachments[_rtCount] = handle->GetFramebufferView();
-
AddImageBarrier(handle, handle->LayoutRTV);
}
- else if (_rtHandles[0])
- {
- layout.MSAA = _rtHandles[0]->GetMSAA();
- layout.Extent = _rtHandles[0]->Extent;
- layout.ReadDepth = false;
- layout.WriteDepth = false;
- }
else
{
- // No depth or render target binded?
- CRASH;
+ handle = _rtHandles[0];
+ layout.ReadDepth = false;
+ layout.WriteDepth = false;
}
+ layout.MSAA = handle->GetMSAA();
+ layout.Extent.width = handle->Extent.width;
+ layout.Extent.height = handle->Extent.height;
+ layout.Layers = handle->Layers;
// Get or create objects
auto renderPass = _device->GetOrCreateRenderPass(layout);
framebufferKey.RenderPass = renderPass;
- uint32 layers = 1; // TODO: support rendering to many layers (eg. texture array)
- auto framebuffer = _device->GetOrCreateFramebuffer(framebufferKey, layout.Extent, layers);
+ auto framebuffer = _device->GetOrCreateFramebuffer(framebufferKey, layout.Extent, layout.Layers);
_renderPass = renderPass;
FlushBarriers();
@@ -519,7 +513,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
}
default:
// Unknown or invalid descriptor type
- CRASH;
+ CRASH;
break;
}
}
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
index d4624c017..5cae6a7ff 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
@@ -61,7 +61,6 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
{
msgPrefix = TEXT("ERROR");
-
if (!StringUtils::Compare(layerPrefix, "SC"))
{
if (msgCode == 3)
@@ -79,7 +78,6 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{
msgPrefix = TEXT("WARN");
-
if (!StringUtils::Compare(layerPrefix, "SC"))
{
if (msgCode == 2)
@@ -92,7 +90,6 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
{
msgPrefix = TEXT("PERF");
-
if (!StringUtils::Compare(layerPrefix, "SC"))
{
if (msgCode == 2)
@@ -118,14 +115,8 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
{
msgPrefix = TEXT("DEBUG");
}
- else
- {
- CRASH;
- }
- // Send info
LOG(Info, "[Vulkan] {0}:{1}:{2} {3}", msgPrefix, String(layerPrefix), msgCode, String(msg));
-
return VK_FALSE;
}
@@ -147,6 +138,9 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugUtilsCallback(VkDebugUtilsMessageSever
case 5: // SPIR-V module not valid: MemoryBarrier: Vulkan specification requires Memory Semantics to have one of the following bits set: Acquire, Release, AcquireRelease or SequentiallyConsistent
case -1666394502: // After query pool creation, each query must be reset before it is used. Queries must also be reset between uses.
case 602160055: // Attachment 4 not written by fragment shader; undefined values will be written to attachment. TODO: investigate it for PS_GBuffer shader from Deferred material with USE_LIGHTMAP=1
+ case 7060244: // Image Operand Offset can only be used with OpImage*Gather operations
+ case -1539028524: // SortedIndices is null so Vulkan backend sets it to default R32_SFLOAT format which is not good for UINT format of the buffer
+ case -1810835948: // SortedIndices is null so Vulkan backend sets it to default R32_SFLOAT format which is not good for UINT format of the buffer
return VK_FALSE;
}
break;
@@ -214,15 +208,10 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugUtilsCallback(VkDebugUtilsMessageSever
type = TEXT("Perf");
}
- // Send info
if (callbackData->pMessageIdName)
- {
LOG(Info, "[Vulkan] {0} {1}:{2}({3}) {4}", type, severity, callbackData->messageIdNumber, String(callbackData->pMessageIdName), String(callbackData->pMessage));
- }
else
- {
LOG(Info, "[Vulkan] {0} {1}:{2} {3}", type, severity, callbackData->messageIdNumber, String(callbackData->pMessage));
- }
return VK_FALSE;
}
@@ -440,10 +429,11 @@ uint32 GetHash(const RenderTargetLayoutVulkan& key)
CombineHash(hash, (uint32)key.WriteDepth);
CombineHash(hash, (uint32)key.DepthFormat * 93473262);
CombineHash(hash, key.RTsCount * 136);
+ CombineHash(hash, key.Extent.width);
+ CombineHash(hash, key.Extent.height);
+ CombineHash(hash, key.Layers);
for (int32 i = 0; i < ARRAY_COUNT(key.RTVsFormats); i++)
- {
CombineHash(hash, (uint32)key.RTVsFormats[i]);
- }
return hash;
}
@@ -452,13 +442,11 @@ uint32 GetHash(const FramebufferVulkan::Key& key)
uint32 hash = (int32)(intptr)key.RenderPass;
CombineHash(hash, (uint32)key.AttachmentCount * 136);
for (int32 i = 0; i < ARRAY_COUNT(key.Attachments); i++)
- {
CombineHash(hash, (uint32)(intptr)key.Attachments[i]);
- }
return hash;
}
-FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent3D& extent, uint32 layers)
+FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent2D& extent, uint32 layers)
: _device(device)
, _handle(VK_NULL_HANDLE)
, Extent(extent)
@@ -1271,33 +1259,23 @@ GPUDeviceVulkan::~GPUDeviceVulkan()
RenderPassVulkan* GPUDeviceVulkan::GetOrCreateRenderPass(RenderTargetLayoutVulkan& layout)
{
- // Try reuse cached version
RenderPassVulkan* renderPass;
if (_renderPasses.TryGet(layout, renderPass))
- {
return renderPass;
- }
PROFILE_CPU_NAMED("Create Render Pass");
-
- // Create object and cache it
renderPass = New(this, layout);
_renderPasses.Add(layout, renderPass);
return renderPass;
}
-FramebufferVulkan* GPUDeviceVulkan::GetOrCreateFramebuffer(FramebufferVulkan::Key& key, VkExtent3D& extent, uint32 layers)
+FramebufferVulkan* GPUDeviceVulkan::GetOrCreateFramebuffer(FramebufferVulkan::Key& key, VkExtent2D& extent, uint32 layers)
{
- // Try reuse cached version
FramebufferVulkan* framebuffer;
if (_framebuffers.TryGet(key, framebuffer))
- {
return framebuffer;
- }
PROFILE_CPU_NAMED("Create Framebuffer");
-
- // Create object and cache it
framebuffer = New(this, key, extent, layers);
_framebuffers.Add(key, framebuffer);
return framebuffer;
@@ -1305,16 +1283,11 @@ FramebufferVulkan* GPUDeviceVulkan::GetOrCreateFramebuffer(FramebufferVulkan::Ke
PipelineLayoutVulkan* GPUDeviceVulkan::GetOrCreateLayout(DescriptorSetLayoutInfoVulkan& key)
{
- // Try reuse cached version
PipelineLayoutVulkan* layout;
if (_layouts.TryGet(key, layout))
- {
return layout;
- }
PROFILE_CPU_NAMED("Create Pipeline Layout");
-
- // Create object and cache it
layout = New(this, key);
_layouts.Add(key, layout);
return layout;
@@ -1698,7 +1671,11 @@ bool GPUDeviceVulkan::Init()
auto& limits = Limits;
limits.HasCompute = GetShaderProfile() == ShaderProfile::Vulkan_SM5 && PhysicalDeviceLimits.maxComputeWorkGroupCount[0] >= GPU_MAX_CS_DISPATCH_THREAD_GROUPS && PhysicalDeviceLimits.maxComputeWorkGroupCount[1] >= GPU_MAX_CS_DISPATCH_THREAD_GROUPS;
limits.HasTessellation = !!PhysicalDeviceFeatures.tessellationShader && PhysicalDeviceLimits.maxBoundDescriptorSets > (uint32_t)DescriptorSet::Domain;
- limits.HasGeometryShaders = false; // TODO: add geometry shaders support for Vulkan
+#if PLATFORM_ANDROID
+ limits.HasGeometryShaders = false; // Don't even try GS on mobile
+#else
+ limits.HasGeometryShaders = !!PhysicalDeviceFeatures.geometryShader;
+#endif
limits.HasInstancing = true;
limits.HasVolumeTextureRendering = true;
limits.HasDrawIndirect = PhysicalDeviceLimits.maxDrawIndirectCount >= 1;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
index f7da7d30a..1868999b7 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h
@@ -237,13 +237,14 @@ public:
bool WriteDepth;
PixelFormat DepthFormat;
PixelFormat RTVsFormats[GPU_MAX_RT_BINDED];
- VkExtent3D Extent;
+ VkExtent2D Extent;
+ uint32 Layers;
public:
bool operator==(const RenderTargetLayoutVulkan& other) const
{
- return Platform::MemoryCompare((void*)this, &other, sizeof(RenderTargetLayoutVulkan)) == 0;
+ return Platform::MemoryCompare(this, &other, sizeof(RenderTargetLayoutVulkan)) == 0;
}
};
@@ -263,7 +264,7 @@ public:
bool operator==(const Key& other) const
{
- return Platform::MemoryCompare((void*)this, &other, sizeof(Key)) == 0;
+ return Platform::MemoryCompare(this, &other, sizeof(Key)) == 0;
}
};
@@ -274,13 +275,13 @@ private:
public:
- FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent3D& extent, uint32 layers);
+ FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent2D& extent, uint32 layers);
~FramebufferVulkan();
public:
VkImageView Attachments[GPU_MAX_RT_BINDED + 1];
- VkExtent3D Extent;
+ VkExtent2D Extent;
uint32 Layers;
public:
@@ -498,8 +499,6 @@ private:
public:
- // Create new graphics device (returns Vulkan if failed)
- // @returns Created device or Vulkan
static GPUDevice* Create();
///
@@ -685,7 +684,7 @@ public:
}
RenderPassVulkan* GetOrCreateRenderPass(RenderTargetLayoutVulkan& layout);
- FramebufferVulkan* GetOrCreateFramebuffer(FramebufferVulkan::Key& key, VkExtent3D& extent, uint32 layers);
+ FramebufferVulkan* GetOrCreateFramebuffer(FramebufferVulkan::Key& key, VkExtent2D& extent, uint32 layers);
PipelineLayoutVulkan* GetOrCreateLayout(DescriptorSetLayoutInfoVulkan& key);
void OnImageViewDestroy(VkImageView imageView);
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
index 272443b1e..28451bd66 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
@@ -21,6 +21,7 @@ void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* ow
Extent.width = Math::Max(1, extent.width >> firstMipIndex);
Extent.height = Math::Max(1, extent.height >> firstMipIndex);
Extent.depth = Math::Max(1, extent.depth >> firstMipIndex);
+ Layers = arraySize;
RenderToolsVulkan::ZeroStruct(Info, VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO);
Info.image = image;
@@ -74,16 +75,30 @@ VkImageView GPUTextureViewVulkan::GetFramebufferView()
if (ViewFramebuffer)
return ViewFramebuffer;
- if (Info.subresourceRange.levelCount == 1)
- return View;
-
- // Special case:
- // Render Target Handle can be created for full texture including its mip maps but framebuffer image view can use only a single surface
- // Use an additional view for that case with modified level count to 1.
-
- VkImageViewCreateInfo createInfo = Info;
- createInfo.subresourceRange.levelCount = 1;
- VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewFramebuffer));
+ if (Info.viewType == VK_IMAGE_VIEW_TYPE_3D)
+ {
+ // Special case:
+ // Render Target Handle to a 3D Volume texture.
+ // Use it as Texture2D Array with layers.
+ VkImageViewCreateInfo createInfo = Info;
+ createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
+ createInfo.subresourceRange.layerCount = Extent.depth;
+ Layers = Extent.depth;
+ VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewFramebuffer));
+ }
+ else if (Info.subresourceRange.levelCount != 1)
+ {
+ // Special case:
+ // Render Target Handle can be created for full texture including its mip maps but framebuffer image view can use only a single surface
+ // Use an additional view for that case with modified level count to 1.
+ VkImageViewCreateInfo createInfo = Info;
+ createInfo.subresourceRange.levelCount = 1;
+ VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewFramebuffer));
+ }
+ else
+ {
+ ViewFramebuffer = View;
+ }
return ViewFramebuffer;
}
@@ -92,15 +107,15 @@ void GPUTextureViewVulkan::Release()
{
if (View != VK_NULL_HANDLE)
{
- Device->OnImageViewDestroy(View);
- Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View);
-
- if (ViewFramebuffer != VK_NULL_HANDLE)
+ if (ViewFramebuffer != View && ViewFramebuffer != VK_NULL_HANDLE)
{
Device->OnImageViewDestroy(ViewFramebuffer);
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewFramebuffer);
}
+ Device->OnImageViewDestroy(View);
+ Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View);
+
View = VK_NULL_HANDLE;
ViewFramebuffer = VK_NULL_HANDLE;
@@ -236,32 +251,20 @@ bool GPUTextureVulkan::OnInit()
imageInfo.extent.depth = Depth();
imageInfo.flags = IsCubeMap() ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
if (IsSRGB())
- {
imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- }
#if VK_KHR_maintenance1
if (_device->OptionalDeviceExtensions.HasKHRMaintenance1 && imageInfo.imageType == VK_IMAGE_TYPE_3D)
- {
imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR;
- }
#endif
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (useSRV)
- {
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
- }
if (useDSV)
- {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
- }
if (useRTV)
- {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
- }
if (useUAV)
- {
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
- }
imageInfo.tiling = optimalTiling ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR;
imageInfo.samples = (VkSampleCountFlagBits)MultiSampleLevel();
// TODO: set initialLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL for IsRegularTexture() ???
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
index aa6546362..78583ea47 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
@@ -51,6 +51,7 @@ public:
VkImageView View = VK_NULL_HANDLE;
VkImageView ViewFramebuffer = VK_NULL_HANDLE;
VkExtent3D Extent;
+ uint32 Layers;
VkImageViewCreateInfo Info;
int32 SubresourceIndex;
VkImageLayout LayoutRTV;
diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp
index 8f9be5e74..ee66fc7b2 100644
--- a/Source/Engine/Renderer/RenderList.cpp
+++ b/Source/Engine/Renderer/RenderList.cpp
@@ -695,7 +695,7 @@ DRAW:
if (drawCall.InstanceCount == 0)
{
// No support for batching indirect draw calls
- ASSERT(batch.BatchSize == 1);
+ ASSERT_LOW_LAYER(batch.BatchSize == 1);
context->BindVB(ToSpan(vb, vbCount), vbOffsets);
context->DrawIndexedInstancedIndirect(drawCall.Draw.IndirectArgsBuffer, drawCall.Draw.IndirectArgsOffset);