From 9cba6bad6d6bc3c6915ecd25cd8f69475d40b274 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 May 2021 11:40:38 +0200 Subject: [PATCH] Add support for rendering into 3d textures on Vulkan #136 --- .../Graphics/Materials/MaterialShader.cpp | 5 ++ .../Graphics/Materials/MaterialShader.h | 2 + Source/Engine/Graphics/Shaders/GPUShader.cpp | 3 + .../Graphics/Shaders/GPUShaderProgram.h | 31 ++++++----- .../Vulkan/GPUContextVulkan.cpp | 28 ++++------ .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 49 +++++------------ .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 15 +++-- .../Vulkan/GPUTextureVulkan.cpp | 55 ++++++++++--------- .../GraphicsDevice/Vulkan/GPUTextureVulkan.h | 1 + Source/Engine/Renderer/RenderList.cpp | 2 +- 10 files changed, 89 insertions(+), 102 deletions(-) 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);