summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-07-07 22:40:03 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-07-07 22:40:03 +0000
commit6896a3c7bae82319682f3bb0af80c20a0b907a1d (patch)
treebf7e822be87f5a9dcfc9692d7aea3b622a205c77 /Graphics/GraphicsEngineVulkan
parentFixed Vulkan swapchain present error when window is minimized (diff)
downloadDiligentCore-6896a3c7bae82319682f3bb0af80c20a0b907a1d.tar.gz
DiligentCore-6896a3c7bae82319682f3bb0af80c20a0b907a1d.zip
Implemented mipmap generation in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/CMakeLists.txt76
-rw-r--r--Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h14
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h29
-rw-r--r--Graphics/GraphicsEngineVulkan/include/GenerateMipsVkHelper.h (renamed from Graphics/GraphicsEngineVulkan/include/GenerateMips.h)54
-rw-r--r--Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h24
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanDebug.h6
-rw-r--r--Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS.csh211
-rw-r--r--Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS_inc.h211
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp43
-rw-r--r--Graphics/GraphicsEngineVulkan/src/GenerateMips.cpp196
-rw-r--r--Graphics/GraphicsEngineVulkan/src/GenerateMipsVkHelper.cpp310
-rw-r--r--Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp8
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceFactoryVk.cpp33
-rw-r--r--Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp21
-rw-r--r--Graphics/GraphicsEngineVulkan/src/TextureViewVkImpl.cpp4
-rw-r--r--Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp34
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp65
19 files changed, 1000 insertions, 343 deletions
diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt
index d93101be..0c675546 100644
--- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt
+++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt
@@ -13,7 +13,7 @@ set(INCLUDE
include/DeviceContextVkImpl.h
include/VulkanDynamicHeap.h
include/FramebufferCache.h
- include/GenerateMips.h
+ include/GenerateMipsVkHelper.h
include/pch.h
include/PipelineLayout.h
include/PipelineStateVkImpl.h
@@ -74,7 +74,7 @@ set(SRC
src/DeviceContextVkImpl.cpp
src/VulkanDynamicHeap.cpp
src/FramebufferCache.cpp
- src/GenerateMips.cpp
+ src/GenerateMipsVkHelper.cpp
src/PipelineLayout.cpp
src/PipelineStateVkImpl.cpp
src/RenderDeviceVkImpl.cpp
@@ -105,25 +105,34 @@ set(VULKAN_UTILS_SRC
src/VulkanUtilities/VulkanUploadHeap.cpp
)
-#set(SHADERS
-# shaders/GenerateMips/GenerateMipsGammaCS.hlsl
-# shaders/GenerateMips/GenerateMipsGammaOddCS.hlsl
-# shaders/GenerateMips/GenerateMipsGammaOddXCS.hlsl
-# shaders/GenerateMips/GenerateMipsGammaOddYCS.hlsl
-# shaders/GenerateMips/GenerateMipsLinearCS.hlsl
-# shaders/GenerateMips/GenerateMipsLinearOddCS.hlsl
-# shaders/GenerateMips/GenerateMipsLinearOddXCS.hlsl
-# shaders/GenerateMips/GenerateMipsLinearOddYCS.hlsl
-#)
-#
-#set_source_files_properties(${SHADERS} PROPERTIES
-# VS_SHADER_TYPE Compute
-# VS_SHADER_ENTRYPOINT main
-# VS_SHADER_MODEL 5.0
-# VS_SHADER_VARIABLE_NAME "g_p%(Filename)"
-# VS_SHADER_OUTPUT_HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/CompiledShaders/GenerateMips/%(Filename).h"
-# #VS_SHADER_FLAGS "/O3"
-#)
+set(GENERATE_MIPS_SHADER
+ shaders/GenerateMipsCS.csh
+)
+set(GENERATE_MIPS_SHADER_INC
+ shaders/GenerateMipsCS_inc.h
+)
+
+set_source_files_properties(
+ ${CMAKE_CURRENT_SOURCE_DIR}/${GENERATE_MIPS_SHADER_INC}
+ PROPERTIES GENERATED TRUE
+)
+
+# Create custom target to convert GenerateMipsCS.csh to GenerateMipsCS_inc.h
+add_custom_target(ProcessGenerateMipsVkShader
+SOURCES
+ ${GENERATE_MIPS_SHADER}
+)
+
+add_custom_command(TARGET ProcessGenerateMipsVkShader
+ # Unfortunately it is not possible to set TARGET directly to GraphicsEngineVk-*
+ # because PRE_BUILD is only supported on Visual Studio 8 or later. For all other generators
+ # PRE_BUILD is treated as PRE_LINK.
+ COMMAND ${FILE2STRING_PATH} ${GENERATE_MIPS_SHADER} ${GENERATE_MIPS_SHADER_INC}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Processing GenerateMipsCS.csh"
+ VERBATIM
+)
+
add_library(GraphicsEngineVkInterface INTERFACE)
target_include_directories(GraphicsEngineVkInterface
@@ -136,29 +145,28 @@ INTERFACE
)
add_library(GraphicsEngineVk-static STATIC
- ${SRC} ${VULKAN_UTILS_SRC} ${INTERFACE} ${INCLUDE} ${VULKAN_UTILS_INCLUDE} ${SHADERS}
+ ${SRC} ${VULKAN_UTILS_SRC} ${INTERFACE} ${INCLUDE} ${VULKAN_UTILS_INCLUDE} ${GENERATE_MIPS_SHADER} ${GENERATE_MIPS_SHADER_INC}
readme.md
- #shaders/GenerateMips/GenerateMipsCS.hlsli
)
add_library(GraphicsEngineVk-shared SHARED
- ${SRC} ${VULKAN_UTILS_SRC} ${INTERFACE} ${INCLUDE} ${VULKAN_UTILS_INCLUDE} ${SHADERS}
+ ${SRC} ${VULKAN_UTILS_SRC} ${INTERFACE} ${INCLUDE} ${VULKAN_UTILS_INCLUDE} ${GENERATE_MIPS_SHADER} ${GENERATE_MIPS_SHADER_INC}
src/DLLMain.cpp
src/GraphicsEngineVk.def
readme.md
- #shaders/GenerateMips/GenerateMipsCS.hlsli
)
+add_dependencies(GraphicsEngineVk-static ProcessGenerateMipsVkShader)
+add_dependencies(GraphicsEngineVk-shared ProcessGenerateMipsVkShader)
+
target_include_directories(GraphicsEngineVk-static
PRIVATE
- #${CMAKE_CURRENT_BINARY_DIR}/CompiledShaders
include
../../External/vulkan
)
target_include_directories(GraphicsEngineVk-shared
PRIVATE
- #${CMAKE_CURRENT_BINARY_DIR}/CompiledShaders
include
../../External/vulkan
)
@@ -209,10 +217,12 @@ source_group("dll" FILES
source_group("include" FILES ${INCLUDE})
source_group("interface" FILES ${INTERFACE})
source_group("include\\Vulkan Utilities" FILES ${VULKAN_UTILS_INCLUDE})
-#source_group("shaders" FILES
-# ${SHADERS}
-# shaders/GenerateMips/GenerateMipsCS.hlsli
-#)
+source_group("shaders" FILES
+ ${GENERATE_MIPS_SHADER}
+)
+source_group("shaders\\generated" FILES
+ ${GENERATE_MIPS_SHADER_INC}
+)
set_target_properties(GraphicsEngineVk-static PROPERTIES
FOLDER Core/Graphics
@@ -224,3 +234,7 @@ set_target_properties(GraphicsEngineVk-shared PROPERTIES
set_source_files_properties(
readme.md PROPERTIES HEADER_FILE_ONLY TRUE
)
+
+set_target_properties(ProcessGenerateMipsVkShader PROPERTIES
+ FOLDER Core/Graphics/Helper
+) \ No newline at end of file
diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
index 407eb4d7..3d4f11a0 100644
--- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
@@ -95,8 +95,18 @@ public:
return VkBuffer;
}
- virtual void SetAccessFlags(VkAccessFlags AccessFlags)override final{ m_AccessFlags = AccessFlags; }
- bool CheckAccessFlags(VkAccessFlags Flags)const{return (m_AccessFlags & Flags) == Flags;}
+ virtual void SetAccessFlags(VkAccessFlags AccessFlags)override final
+ {
+ m_AccessFlags = AccessFlags;
+ }
+ bool CheckAccessFlags(VkAccessFlags Flags)const
+ {
+ return (m_AccessFlags & Flags) == Flags;
+ }
+ VkAccessFlags GetAccessFlags()const
+ {
+ return m_AccessFlags;
+ }
private:
friend class DeviceContextVkImpl;
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
index 5242cce8..79cbc04a 100644
--- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
@@ -29,7 +29,6 @@
#include "DeviceContextVk.h"
#include "DeviceContextBase.h"
-#include "GenerateMips.h"
#include "VulkanUtilities/VulkanCommandBufferPool.h"
#include "VulkanUtilities/VulkanCommandBuffer.h"
#include "VulkanUtilities/VulkanUploadHeap.h"
@@ -37,6 +36,7 @@
#include "ResourceReleaseQueue.h"
#include "DescriptorPoolManager.h"
#include "PipelineLayout.h"
+#include "GenerateMipsVkHelper.h"
#ifdef _DEBUG
# define VERIFY_CONTEXT_BINDINGS
@@ -51,11 +51,12 @@ class DeviceContextVkImpl : public DeviceContextBase<IDeviceContextVk>
public:
typedef DeviceContextBase<IDeviceContextVk> TDeviceContextBase;
- DeviceContextVkImpl(IReferenceCounters* pRefCounters,
- class RenderDeviceVkImpl* pDevice,
- bool bIsDeferred,
- const EngineVkAttribs& Attribs,
- Uint32 ContextId);
+ DeviceContextVkImpl(IReferenceCounters* pRefCounters,
+ class RenderDeviceVkImpl* pDevice,
+ bool bIsDeferred,
+ const EngineVkAttribs& Attribs,
+ Uint32 ContextId,
+ std::shared_ptr<GenerateMipsVkHelper> GenerateMipsHelper);
~DeviceContextVkImpl();
virtual void QueryInterface( const Diligent::INTERFACE_ID& IID, IObject** ppInterface )override final;
@@ -97,6 +98,7 @@ public:
virtual void ExecuteCommandList(class ICommandList* pCommandList)override final;
void TransitionImageLayout(class TextureVkImpl &TextureVk, VkImageLayout NewLayout);
+ void TransitionImageLayout(class TextureVkImpl &TextureVk, VkImageLayout OldLayout, VkImageLayout NewLayout, const VkImageSubresourceRange& SubresRange);
virtual void TransitionImageLayout(ITexture* pTexture, VkImageLayout NewLayout)override final;
void BufferMemoryBarrier(class BufferVkImpl &BufferVk, VkAccessFlags NewAccessFlags);
@@ -117,9 +119,6 @@ public:
///// that are only kept alive by references in the cache
//void ClearShaderStateCache();
- ///// Number of different shader types (Vertex, Pixel, Geometry, Domain, Hull, Compute)
- //static constexpr int NumShaderTypes = 6;
-
void UpdateBufferRegion(class BufferVkImpl* pBuffVk, Uint64 DstOffset, Uint64 NumBytes, VkBuffer vkSrcBuffer, Uint64 SrcOffset);
void UpdateBufferRegion(class BufferVkImpl* pBuffVk, const void* pData, Uint64 DstOffset, Uint64 NumBytes);
@@ -128,7 +127,11 @@ public:
#if 0
void CopyTextureRegion(IBuffer* pSrcBuffer, Uint32 SrcStride, Uint32 SrcDepthStride, class TextureVkImpl* pTextureVk, Uint32 DstSubResIndex, const Box &DstBox);
#endif
- void GenerateMips(class TextureViewVkImpl* pTexView);
+ void GenerateMips(class TextureViewVkImpl& TexView)
+ {
+ m_GenerateMipsHelper->GenerateMips(TexView, *this, *m_GenerateMipsSRB);
+ }
+
void* AllocateUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes);
void CopyAndFreeDynamicUploadData(BufferVkImpl* pBuffer);
@@ -193,10 +196,6 @@ private:
/// This framebuffer may or may not be currently set in the command buffer
VkFramebuffer m_Framebuffer = VK_NULL_HANDLE;
-#if 0
- GenerateMipsHelper m_MipsGenerator;
-#endif
-
FixedBlockMemoryAllocator m_CmdListAllocator;
const Uint32 m_ContextId;
@@ -220,6 +219,8 @@ private:
PipelineLayout::DescriptorSetBindInfo m_DesrSetBindInfo;
VulkanDynamicHeap m_DynamicHeap;
+ std::shared_ptr<GenerateMipsVkHelper> m_GenerateMipsHelper;
+ RefCntAutoPtr<IShaderResourceBinding> m_GenerateMipsSRB;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/GenerateMips.h b/Graphics/GraphicsEngineVulkan/include/GenerateMipsVkHelper.h
index 5f10e5d1..bb7f7c6d 100644
--- a/Graphics/GraphicsEngineVulkan/include/GenerateMips.h
+++ b/Graphics/GraphicsEngineVulkan/include/GenerateMipsVkHelper.h
@@ -21,44 +21,44 @@
* of the possibility of such damages.
*/
-
-// The source code in this file is derived from ColorBuffer.h and GraphicsCore.h developed by Minigraph
-// Original source files header:
-
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-// Developed by Minigraph
-//
-// Author: James Stanard
-//
-
-
#pragma once
-/// \file
+ /// \file
/// Implementation of mipmap generation routines
+#include <array>
+#include <unordered_map>
+#include "VulkanUtilities/VulkanLogicalDevice.h"
+#include "VulkanUtilities/VulkanCommandBuffer.h"
namespace Diligent
{
- class GenerateMipsHelper
+ class RenderDeviceVkImpl;
+ class TextureViewVkImpl;
+ class DeviceContextVkImpl;
+
+ class GenerateMipsVkHelper
{
-#if 0
public:
- GenerateMipsHelper(ID3D12Device *pd3d12Device);
+ GenerateMipsVkHelper(RenderDeviceVkImpl& DeviceVkImpl);
+
+ GenerateMipsVkHelper (const GenerateMipsVkHelper&) = delete;
+ GenerateMipsVkHelper ( GenerateMipsVkHelper&&) = delete;
+ GenerateMipsVkHelper& operator = (const GenerateMipsVkHelper&) = delete;
+ GenerateMipsVkHelper& operator = ( GenerateMipsVkHelper&&) = delete;
- void GenerateMips(class RenderDeviceD3D12Impl *pRenderDeviceD3D12, class TextureViewD3D12Impl *pTexView, class CommandContext &Ctx);
+ void GenerateMips(TextureViewVkImpl& TexView, DeviceContextVkImpl& Ctx, IShaderResourceBinding& SRB);
+ void CreateSRB(IShaderResourceBinding** ppSRB);
private:
- CComPtr<ID3D12RootSignature> m_pGenerateMipsRS;
- CComPtr<ID3D12PipelineState> m_pGenerateMipsLinearPSO[4];
- CComPtr<ID3D12PipelineState> m_pGenerateMipsGammaPSO[4];
-#endif
+ std::array<RefCntAutoPtr<IPipelineState>, 4> CreatePSOs(TEXTURE_FORMAT Fmt);
+ std::array<RefCntAutoPtr<IPipelineState>, 4>& FindPSOs (TEXTURE_FORMAT Fmt);
+
+ RenderDeviceVkImpl& m_DeviceVkImpl;
+
+ std::mutex m_PSOMutex;
+ std::unordered_map< TEXTURE_FORMAT, std::array<RefCntAutoPtr<IPipelineState>, 4> > m_PSOHash;
+ static void GetGlImageFormat(const TextureFormatAttribs& FmtAttribs, std::array<char, 16>& GlFmt);
+ RefCntAutoPtr<IBuffer> m_ConstantsCB;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h
index ab78374b..fa50a025 100644
--- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h
@@ -66,7 +66,7 @@ public:
void CommitAndTransitionShaderResources(IShaderResourceBinding* pShaderResourceBinding,
DeviceContextVkImpl* pCtxVkImpl,
bool CommitResources,
- bool TransitionResources,
+ Uint32 Flags,
PipelineLayout::DescriptorSetBindInfo* pDescrSetBindInfo)const;
void BindDescriptorSetsWithDynamicOffsets(DeviceContextVkImpl* pCtxVkImpl,
diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.h b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.h
index 3daa9d20..6764f301 100644
--- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.h
@@ -33,7 +33,7 @@
#include "SPIRVShaderResources.h"
#include "ShaderVariableVk.h"
-#ifdef _DEBUG
+#ifdef DEVELOPMENT
# define VERIFY_SHADER_BINDINGS
#endif
diff --git a/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h b/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
index f2f186c6..20223b3e 100644
--- a/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
@@ -83,17 +83,16 @@ public:
Uint32 DstX,
Uint32 DstY,
Uint32 DstZ);
-/*
- Vk_CPU_DESCRIPTOR_HANDLE GetMipLevelUAV(Uint32 Mip)
+
+ ITextureView* GetMipLevelSRV(Uint32 MipLevel)
{
- return m_MipUAVs.GetCpuHandle(Mip);
+ return m_MipLevelSRV[MipLevel].get();
}
- Vk_CPU_DESCRIPTOR_HANDLE GetTexArraySRV()
+ ITextureView* GetMipLevelUAV(Uint32 MipLevel)
{
- return m_TexArraySRV.GetCpuHandle();
+ return m_MipLevelUAV[MipLevel].get();
}
-*/
void SetLayout(VkImageLayout NewLayout){ m_CurrentLayout = NewLayout;}
VkImageLayout GetLayout()const{return m_CurrentLayout;}
@@ -105,18 +104,13 @@ protected:
VulkanUtilities::ImageViewWrapper CreateImageView(TextureViewDesc &ViewDesc);
-/*
- // UAVs for every mip level to facilitate mipmap generation
- DescriptorHeapAllocation m_MipUAVs;
- // SRV as texture array (even for a non-array texture) required for mipmap generation
- DescriptorHeapAllocation m_TexArraySRV;
-
- friend class RenderDeviceVkImpl;
-*/
-
VulkanUtilities::ImageWrapper m_VulkanImage;
VulkanUtilities::VulkanMemoryAllocation m_MemoryAllocation;
VkImageLayout m_CurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ // Texture views needed for mipmap generation
+ std::vector<std::unique_ptr<TextureViewVkImpl, STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator> > > m_MipLevelSRV;
+ std::vector<std::unique_ptr<TextureViewVkImpl, STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator> > > m_MipLevelUAV;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanDebug.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanDebug.h
index 50b1a0a8..f5f2bc78 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanDebug.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanDebug.h
@@ -1,5 +1,6 @@
#pragma once
+#include <string>
#include "vulkan.h"
namespace VulkanUtilities
@@ -103,5 +104,8 @@ namespace VulkanUtilities
void SetVulkanObjectName(VkDevice device, VkFence fence, const char * name);
void SetVulkanObjectName(VkDevice device, VkEvent _event, const char * name);
- const char* VkResultToString(VkResult errorCode);
+ const char* VkResultToString (VkResult errorCode);
+ const char* VkAccessFlagBitToString(VkAccessFlagBits Bit);
+ const char* VkImageLayoutToString (VkImageLayout Layout);
+ std::string VkAccessFlagsToString (VkAccessFlags Flags);
}
diff --git a/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS.csh b/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS.csh
new file mode 100644
index 00000000..b85e0bf2
--- /dev/null
+++ b/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS.csh
@@ -0,0 +1,211 @@
+#ifndef NON_POWER_OF_TWO
+#define NON_POWER_OF_TWO 0
+#endif
+
+#ifndef CONVERT_TO_SRGB
+#define CONVERT_TO_SRGB 0
+#endif
+
+#ifndef IMG_FORMAT
+#define IMG_FORMAT rgba8
+#endif
+
+layout(IMG_FORMAT) uniform writeonly image2DArray OutMip[4];
+
+uniform sampler2DArray SrcMip;
+
+uniform CB
+{
+ int SrcMipLevel; // Texture level of source mip
+ int NumMipLevels; // Number of OutMips to write: [1, 4]
+ int FirstArraySlice;
+ int Dummy;
+ vec2 TexelSize; // 1.0 / OutMip1.Dimensions
+};
+
+//
+// The reason for separating channels is to reduce bank conflicts in the
+// local data memory controller. A large stride will cause more threads
+// to collide on the same memory bank.
+shared float gs_R[64];
+shared float gs_G[64];
+shared float gs_B[64];
+shared float gs_A[64];
+
+void StoreColor( uint Index, vec4 Color )
+{
+ gs_R[Index] = Color.r;
+ gs_G[Index] = Color.g;
+ gs_B[Index] = Color.b;
+ gs_A[Index] = Color.a;
+}
+
+vec4 LoadColor( uint Index )
+{
+ return vec4( gs_R[Index], gs_G[Index], gs_B[Index], gs_A[Index]);
+}
+
+float LinearToSRGB(float x)
+{
+ // This is exactly the sRGB curve
+ //return x < 0.0031308 ? 12.92 * x : 1.055 * pow(abs(x), 1.0 / 2.4) - 0.055;
+
+ // This is cheaper but nearly equivalent
+ return x < 0.0031308 ? 12.92 * x : 1.13005 * sqrt(abs(x - 0.00228)) - 0.13448 * x + 0.005719;
+}
+
+vec4 PackColor(vec4 Linear)
+{
+#if CONVERT_TO_SRGB
+ return vec4(LinearToSRGB(Linear.r), LinearToSRGB(Linear.g), LinearToSRGB(Linear.b), Linear.a);
+#else
+ return Linear;
+#endif
+}
+
+void GroupMemoryBarrierWithGroupSync()
+{
+ // OpenGL.org: groupMemoryBarrier() waits on the completion of all memory accesses
+ // performed by an invocation of a compute shader relative to the same access performed
+ // by other invocations in the same work group and then returns with no other effect.
+
+ // groupMemoryBarrier() acts like memoryBarrier(), ordering memory writes for all kinds
+ // of variables, but it only orders read/writes for the current work group.
+ groupMemoryBarrier();
+
+ // OpenGL.org: memoryBarrierShared() waits on the completion of
+ // all memory accesses resulting from the use of SHARED variables
+ // and then returns with no other effect.
+ memoryBarrierShared();
+
+ // Thread execution barrier
+ barrier();
+}
+
+layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+void main()
+{
+ uint LocalInd = gl_LocalInvocationIndex;
+ uvec3 GlobalInd = gl_GlobalInvocationID;
+
+ ivec3 SrcMipSize = textureSize(SrcMip, 0); // SrcMip is the view of the source mip level
+ bool IsValidThread = GlobalInd.x < SrcMipSize.x && GlobalInd.y < SrcMipSize.y;
+ int ArraySlice = FirstArraySlice + int(GlobalInd.z);
+
+ vec4 Src1 = vec4(0.0, 0.0, 0.0, 0.0);
+ float fSrcMipLevel = 0.0; // SrcMip is the view of the source mip level
+ if( IsValidThread )
+ {
+ // One bilinear sample is insufficient when scaling down by more than 2x.
+ // You will slightly undersample in the case where the source dimension
+ // is odd. This is why it's a really good idea to only generate mips on
+ // power-of-two sized textures. Trying to handle the undersampling case
+ // will force this shader to be slower and more complicated as it will
+ // have to take more source texture samples.
+#if NON_POWER_OF_TWO == 0
+ vec2 UV = TexelSize * (vec2(GlobalInd.xy) + 0.5);
+ Src1 = textureLod(SrcMip, vec3(UV, ArraySlice), fSrcMipLevel);
+#elif NON_POWER_OF_TWO == 1
+ // > 2:1 in X dimension
+ // Use 2 bilinear samples to guarantee we don't undersample when downsizing by more than 2x
+ // horizontally.
+ vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.25, 0.5));
+ vec2 Off = TexelSize * vec2(0.5, 0.0);
+ Src1 = 0.5 * (textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel) +
+ textureLod(SrcMip, vec3(UV1 + Off, ArraySlice), fSrcMipLevel));
+#elif NON_POWER_OF_TWO == 2
+ // > 2:1 in Y dimension
+ // Use 2 bilinear samples to guarantee we don't undersample when downsizing by more than 2x
+ // vertically.
+ vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.5, 0.25));
+ vec2 Off = TexelSize * vec2(0.0, 0.5);
+ Src1 = 0.5 * (textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel) +
+ textureLod(SrcMip, vec3(UV1 + Off, ArraySlice), fSrcMipLevel));
+#elif NON_POWER_OF_TWO == 3
+ // > 2:1 in in both dimensions
+ // Use 4 bilinear samples to guarantee we don't undersample when downsizing by more than 2x
+ // in both directions.
+ vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.25, 0.25));
+ vec2 Off = TexelSize * 0.5;
+ Src1 += textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel);
+ Src1 += textureLod(SrcMip, vec3(UV1 + vec2(Off.x, 0.0), ArraySlice), fSrcMipLevel);
+ Src1 += textureLod(SrcMip, vec3(UV1 + vec2(0.0, Off.y), ArraySlice), fSrcMipLevel);
+ Src1 += textureLod(SrcMip, vec3(UV1 + vec2(Off.x, Off.y), ArraySlice), fSrcMipLevel);
+ Src1 *= 0.25;
+#endif
+
+ imageStore(OutMip[0], ivec3(GlobalInd.xy, ArraySlice), PackColor(Src1));
+ }
+
+ // A scalar (constant) branch can exit all threads coherently.
+ if (NumMipLevels == 1)
+ return;
+
+ if( IsValidThread )
+ {
+ // Without lane swizzle operations, the only way to share data with other
+ // threads is through LDS.
+ StoreColor(LocalInd, Src1);
+ }
+
+ // This guarantees all LDS writes are complete and that all threads have
+ // executed all instructions so far (and therefore have issued their LDS
+ // write instructions.)
+ GroupMemoryBarrierWithGroupSync();
+
+ if( IsValidThread )
+ {
+ // With low three bits for X and high three bits for Y, this bit mask
+ // (binary: 001001) checks that X and Y are even.
+ if ((LocalInd & 0x9) == 0)
+ {
+ vec4 Src2 = LoadColor(LocalInd + 0x01);
+ vec4 Src3 = LoadColor(LocalInd + 0x08);
+ vec4 Src4 = LoadColor(LocalInd + 0x09);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ imageStore(OutMip[1], ivec3(GlobalInd.xy / 2, ArraySlice), PackColor(Src1));
+ StoreColor(LocalInd, Src1);
+ }
+ }
+
+ if (NumMipLevels == 2)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ if( IsValidThread )
+ {
+ // This bit mask (binary: 011011) checks that X and Y are multiples of four.
+ if ((LocalInd & 0x1B) == 0)
+ {
+ vec4 Src2 = LoadColor(LocalInd + 0x02);
+ vec4 Src3 = LoadColor(LocalInd + 0x10);
+ vec4 Src4 = LoadColor(LocalInd + 0x12);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ imageStore(OutMip[2], ivec3(GlobalInd.xy / 4, ArraySlice), PackColor(Src1));
+ StoreColor(LocalInd, Src1);
+ }
+ }
+
+ if (NumMipLevels == 3)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ if( IsValidThread )
+ {
+ // This bit mask would be 111111 (X & Y multiples of 8), but only one
+ // thread fits that criteria.
+ if (LocalInd == 0)
+ {
+ vec4 Src2 = LoadColor(LocalInd + 0x04);
+ vec4 Src3 = LoadColor(LocalInd + 0x20);
+ vec4 Src4 = LoadColor(LocalInd + 0x24);
+ Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);
+
+ imageStore(OutMip[3], ivec3(GlobalInd.xy / 8, ArraySlice), PackColor(Src1));
+ }
+ }
+}
diff --git a/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS_inc.h b/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS_inc.h
new file mode 100644
index 00000000..8d02d782
--- /dev/null
+++ b/Graphics/GraphicsEngineVulkan/shaders/GenerateMipsCS_inc.h
@@ -0,0 +1,211 @@
+"#ifndef NON_POWER_OF_TWO\n"
+"#define NON_POWER_OF_TWO 0\n"
+"#endif\n"
+"\n"
+"#ifndef CONVERT_TO_SRGB\n"
+"#define CONVERT_TO_SRGB 0\n"
+"#endif\n"
+"\n"
+"#ifndef IMG_FORMAT\n"
+"#define IMG_FORMAT rgba8\n"
+"#endif\n"
+"\n"
+"layout(IMG_FORMAT) uniform writeonly image2DArray OutMip[4];\n"
+"\n"
+"uniform sampler2DArray SrcMip;\n"
+"\n"
+"uniform CB\n"
+"{\n"
+" int SrcMipLevel; // Texture level of source mip\n"
+" int NumMipLevels; // Number of OutMips to write: [1, 4]\n"
+" int FirstArraySlice;\n"
+" int Dummy;\n"
+" vec2 TexelSize; // 1.0 / OutMip1.Dimensions\n"
+"};\n"
+"\n"
+"//\n"
+"// The reason for separating channels is to reduce bank conflicts in the\n"
+"// local data memory controller. A large stride will cause more threads\n"
+"// to collide on the same memory bank.\n"
+"shared float gs_R[64];\n"
+"shared float gs_G[64];\n"
+"shared float gs_B[64];\n"
+"shared float gs_A[64];\n"
+"\n"
+"void StoreColor( uint Index, vec4 Color )\n"
+"{\n"
+" gs_R[Index] = Color.r;\n"
+" gs_G[Index] = Color.g;\n"
+" gs_B[Index] = Color.b;\n"
+" gs_A[Index] = Color.a;\n"
+"}\n"
+"\n"
+"vec4 LoadColor( uint Index )\n"
+"{\n"
+" return vec4( gs_R[Index], gs_G[Index], gs_B[Index], gs_A[Index]);\n"
+"}\n"
+"\n"
+"float LinearToSRGB(float x)\n"
+"{\n"
+" // This is exactly the sRGB curve\n"
+" //return x < 0.0031308 ? 12.92 * x : 1.055 * pow(abs(x), 1.0 / 2.4) - 0.055;\n"
+" \n"
+" // This is cheaper but nearly equivalent\n"
+" return x < 0.0031308 ? 12.92 * x : 1.13005 * sqrt(abs(x - 0.00228)) - 0.13448 * x + 0.005719;\n"
+"}\n"
+"\n"
+"vec4 PackColor(vec4 Linear)\n"
+"{\n"
+"#if CONVERT_TO_SRGB\n"
+" return vec4(LinearToSRGB(Linear.r), LinearToSRGB(Linear.g), LinearToSRGB(Linear.b), Linear.a);\n"
+"#else\n"
+" return Linear;\n"
+"#endif\n"
+"}\n"
+"\n"
+"void GroupMemoryBarrierWithGroupSync()\n"
+"{\n"
+" // OpenGL.org: groupMemoryBarrier() waits on the completion of all memory accesses \n"
+" // performed by an invocation of a compute shader relative to the same access performed \n"
+" // by other invocations in the same work group and then returns with no other effect.\n"
+"\n"
+" // groupMemoryBarrier() acts like memoryBarrier(), ordering memory writes for all kinds \n"
+" // of variables, but it only orders read/writes for the current work group.\n"
+" groupMemoryBarrier();\n"
+"\n"
+" // OpenGL.org: memoryBarrierShared() waits on the completion of \n"
+" // all memory accesses resulting from the use of SHARED variables\n"
+" // and then returns with no other effect. \n"
+" memoryBarrierShared();\n"
+"\n"
+" // Thread execution barrier\n"
+" barrier();\n"
+"}\n"
+"\n"
+"layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\n"
+"void main()\n"
+"{\n"
+" uint LocalInd = gl_LocalInvocationIndex;\n"
+" uvec3 GlobalInd = gl_GlobalInvocationID;\n"
+" \n"
+" ivec3 SrcMipSize = textureSize(SrcMip, 0); // SrcMip is the view of the source mip level\n"
+" bool IsValidThread = GlobalInd.x < SrcMipSize.x && GlobalInd.y < SrcMipSize.y;\n"
+" int ArraySlice = FirstArraySlice + int(GlobalInd.z);\n"
+"\n"
+" vec4 Src1 = vec4(0.0, 0.0, 0.0, 0.0);\n"
+" float fSrcMipLevel = 0.0; // SrcMip is the view of the source mip level\n"
+" if( IsValidThread )\n"
+" {\n"
+" // One bilinear sample is insufficient when scaling down by more than 2x.\n"
+" // You will slightly undersample in the case where the source dimension\n"
+" // is odd. This is why it\'s a really good idea to only generate mips on\n"
+" // power-of-two sized textures. Trying to handle the undersampling case\n"
+" // will force this shader to be slower and more complicated as it will\n"
+" // have to take more source texture samples.\n"
+"#if NON_POWER_OF_TWO == 0\n"
+" vec2 UV = TexelSize * (vec2(GlobalInd.xy) + 0.5);\n"
+" Src1 = textureLod(SrcMip, vec3(UV, ArraySlice), fSrcMipLevel);\n"
+"#elif NON_POWER_OF_TWO == 1\n"
+" // > 2:1 in X dimension\n"
+" // Use 2 bilinear samples to guarantee we don\'t undersample when downsizing by more than 2x\n"
+" // horizontally.\n"
+" vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.25, 0.5));\n"
+" vec2 Off = TexelSize * vec2(0.5, 0.0);\n"
+" Src1 = 0.5 * (textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel) +\n"
+" textureLod(SrcMip, vec3(UV1 + Off, ArraySlice), fSrcMipLevel));\n"
+"#elif NON_POWER_OF_TWO == 2\n"
+" // > 2:1 in Y dimension\n"
+" // Use 2 bilinear samples to guarantee we don\'t undersample when downsizing by more than 2x\n"
+" // vertically.\n"
+" vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.5, 0.25));\n"
+" vec2 Off = TexelSize * vec2(0.0, 0.5);\n"
+" Src1 = 0.5 * (textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel) +\n"
+" textureLod(SrcMip, vec3(UV1 + Off, ArraySlice), fSrcMipLevel));\n"
+"#elif NON_POWER_OF_TWO == 3\n"
+" // > 2:1 in in both dimensions\n"
+" // Use 4 bilinear samples to guarantee we don\'t undersample when downsizing by more than 2x\n"
+" // in both directions.\n"
+" vec2 UV1 = TexelSize * (vec2(GlobalInd.xy) + vec2(0.25, 0.25));\n"
+" vec2 Off = TexelSize * 0.5;\n"
+" Src1 += textureLod(SrcMip, vec3(UV1, ArraySlice), fSrcMipLevel);\n"
+" Src1 += textureLod(SrcMip, vec3(UV1 + vec2(Off.x, 0.0), ArraySlice), fSrcMipLevel);\n"
+" Src1 += textureLod(SrcMip, vec3(UV1 + vec2(0.0, Off.y), ArraySlice), fSrcMipLevel);\n"
+" Src1 += textureLod(SrcMip, vec3(UV1 + vec2(Off.x, Off.y), ArraySlice), fSrcMipLevel);\n"
+" Src1 *= 0.25;\n"
+"#endif\n"
+"\n"
+" imageStore(OutMip[0], ivec3(GlobalInd.xy, ArraySlice), PackColor(Src1));\n"
+" }\n"
+"\n"
+" // A scalar (constant) branch can exit all threads coherently.\n"
+" if (NumMipLevels == 1)\n"
+" return;\n"
+"\n"
+" if( IsValidThread )\n"
+" {\n"
+" // Without lane swizzle operations, the only way to share data with other\n"
+" // threads is through LDS.\n"
+" StoreColor(LocalInd, Src1);\n"
+" }\n"
+"\n"
+" // This guarantees all LDS writes are complete and that all threads have\n"
+" // executed all instructions so far (and therefore have issued their LDS\n"
+" // write instructions.)\n"
+" GroupMemoryBarrierWithGroupSync();\n"
+"\n"
+" if( IsValidThread )\n"
+" {\n"
+" // With low three bits for X and high three bits for Y, this bit mask\n"
+" // (binary: 001001) checks that X and Y are even.\n"
+" if ((LocalInd & 0x9) == 0)\n"
+" {\n"
+" vec4 Src2 = LoadColor(LocalInd + 0x01);\n"
+" vec4 Src3 = LoadColor(LocalInd + 0x08);\n"
+" vec4 Src4 = LoadColor(LocalInd + 0x09);\n"
+" Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);\n"
+"\n"
+" imageStore(OutMip[1], ivec3(GlobalInd.xy / 2, ArraySlice), PackColor(Src1));\n"
+" StoreColor(LocalInd, Src1);\n"
+" }\n"
+" }\n"
+"\n"
+" if (NumMipLevels == 2)\n"
+" return;\n"
+"\n"
+" GroupMemoryBarrierWithGroupSync();\n"
+"\n"
+" if( IsValidThread )\n"
+" {\n"
+" // This bit mask (binary: 011011) checks that X and Y are multiples of four.\n"
+" if ((LocalInd & 0x1B) == 0)\n"
+" {\n"
+" vec4 Src2 = LoadColor(LocalInd + 0x02);\n"
+" vec4 Src3 = LoadColor(LocalInd + 0x10);\n"
+" vec4 Src4 = LoadColor(LocalInd + 0x12);\n"
+" Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);\n"
+"\n"
+" imageStore(OutMip[2], ivec3(GlobalInd.xy / 4, ArraySlice), PackColor(Src1));\n"
+" StoreColor(LocalInd, Src1);\n"
+" }\n"
+" }\n"
+"\n"
+" if (NumMipLevels == 3)\n"
+" return;\n"
+"\n"
+" GroupMemoryBarrierWithGroupSync();\n"
+"\n"
+" if( IsValidThread )\n"
+" {\n"
+" // This bit mask would be 111111 (X & Y multiples of 8), but only one\n"
+" // thread fits that criteria.\n"
+" if (LocalInd == 0)\n"
+" {\n"
+" vec4 Src2 = LoadColor(LocalInd + 0x04);\n"
+" vec4 Src3 = LoadColor(LocalInd + 0x20);\n"
+" vec4 Src4 = LoadColor(LocalInd + 0x24);\n"
+" Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4);\n"
+"\n"
+" imageStore(OutMip[3], ivec3(GlobalInd.xy / 8, ArraySlice), PackColor(Src1));\n"
+" }\n"
+" }\n"
+"}\n"
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index a538ab1b..fda3cf55 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -59,14 +59,14 @@ namespace Diligent
return "Dynamic heap of immediate context";
}
- DeviceContextVkImpl::DeviceContextVkImpl( IReferenceCounters* pRefCounters,
- RenderDeviceVkImpl* pDeviceVkImpl,
- bool bIsDeferred,
- const EngineVkAttribs& Attribs,
- Uint32 ContextId) :
+ DeviceContextVkImpl::DeviceContextVkImpl( IReferenceCounters* pRefCounters,
+ RenderDeviceVkImpl* pDeviceVkImpl,
+ bool bIsDeferred,
+ const EngineVkAttribs& Attribs,
+ Uint32 ContextId,
+ std::shared_ptr<GenerateMipsVkHelper> GenerateMipsHelper) :
TDeviceContextBase{pRefCounters, pDeviceVkImpl, bIsDeferred},
m_NumCommandsToFlush{bIsDeferred ? std::numeric_limits<decltype(m_NumCommandsToFlush)>::max() : Attribs.NumCommandsToFlushCmdBuffer},
- /*m_MipsGenerator(pDeviceVkImpl->GetVkDevice()),*/
m_CmdListAllocator{ GetRawAllocator(), sizeof(CommandListVkImpl), 64 },
m_ContextId{ContextId},
// Command pools for deferred contexts must be thread safe because finished command buffers are executed and released from another thread
@@ -113,8 +113,10 @@ namespace Diligent
pDeviceVkImpl->GetDynamicHeapRingBuffer(),
GetDynamicHeapName(bIsDeferred, ContextId),
bIsDeferred ? Attribs.DeferredCtxDynamicHeapPageSize : Attribs.ImmediateCtxDynamicHeapPageSize
- }
+ },
+ m_GenerateMipsHelper(std::move(GenerateMipsHelper))
{
+ m_GenerateMipsHelper->CreateSRB(&m_GenerateMipsSRB);
}
DeviceContextVkImpl::~DeviceContextVkImpl()
@@ -244,7 +246,7 @@ namespace Diligent
VERIFY_EXPR(pPipelineState != nullptr);
auto *pPipelineStateVk = ValidatedCast<PipelineStateVkImpl>(pPipelineState);
- pPipelineStateVk->CommitAndTransitionShaderResources(pShaderResourceBinding, this, false, true, nullptr);
+ pPipelineStateVk->CommitAndTransitionShaderResources(pShaderResourceBinding, this, false, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES, nullptr);
}
void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding *pShaderResourceBinding, Uint32 Flags)
@@ -253,7 +255,7 @@ namespace Diligent
return;
auto *pPipelineStateVk = m_pPipelineState.RawPtr<PipelineStateVkImpl>();
- pPipelineStateVk->CommitAndTransitionShaderResources(pShaderResourceBinding, this, true, (Flags & COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES) != 0, &m_DesrSetBindInfo);
+ pPipelineStateVk->CommitAndTransitionShaderResources(pShaderResourceBinding, this, true, Flags, &m_DesrSetBindInfo);
}
void DeviceContextVkImpl::SetStencilRef(Uint32 StencilRef)
@@ -1150,16 +1152,6 @@ namespace Diligent
}
#endif
- void DeviceContextVkImpl::GenerateMips(TextureViewVkImpl *pTexView)
- {
- UNSUPPORTED("Not yet implemented");
-#if 0
- auto *pCtx = RequestCmdContext();
- m_MipsGenerator.GenerateMips(m_pDevice.RawPtr<RenderDeviceVkImpl>(), pTexView, *pCtx);
- ++m_State.NumCommands;
-#endif
- }
-
void DeviceContextVkImpl::FinishCommandList(class ICommandList **ppCommandList)
{
if (m_CommandBuffer.GetState().RenderPass != VK_NULL_HANDLE)
@@ -1255,15 +1247,14 @@ namespace Diligent
}
}
- void DeviceContextVkImpl::TransitionImageLayout(TextureVkImpl &TextureVk, VkImageLayout NewLayout)
+ void DeviceContextVkImpl::TransitionImageLayout(TextureVkImpl& TextureVk, VkImageLayout NewLayout)
{
VERIFY(TextureVk.GetLayout() != NewLayout, "The texture is already transitioned to correct layout");
EnsureVkCmdBuffer();
auto vkImg = TextureVk.GetVkImage();
const auto& TexDesc = TextureVk.GetDesc();
- auto Fmt = TexDesc.Format;
- const auto& FmtAttribs = GetTextureFormatAttribs(Fmt);
+ const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
VkImageSubresourceRange SubresRange;
if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
SubresRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
@@ -1284,6 +1275,14 @@ namespace Diligent
TextureVk.SetLayout(NewLayout);
}
+ void DeviceContextVkImpl::TransitionImageLayout(TextureVkImpl &TextureVk, VkImageLayout OldLayout, VkImageLayout NewLayout, const VkImageSubresourceRange& SubresRange)
+ {
+ VERIFY(TextureVk.GetLayout() != NewLayout, "The texture is already transitioned to correct layout");
+ EnsureVkCmdBuffer();
+ auto vkImg = TextureVk.GetVkImage();
+ m_CommandBuffer.TransitionImageLayout(vkImg, OldLayout, NewLayout, SubresRange);
+ }
+
void DeviceContextVkImpl::BufferMemoryBarrier(IBuffer *pBuffer, VkAccessFlags NewAccessFlags)
{
VERIFY_EXPR(pBuffer != nullptr);
diff --git a/Graphics/GraphicsEngineVulkan/src/GenerateMips.cpp b/Graphics/GraphicsEngineVulkan/src/GenerateMips.cpp
deleted file mode 100644
index fc9facf1..00000000
--- a/Graphics/GraphicsEngineVulkan/src/GenerateMips.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/* Copyright 2015-2018 Egor Yusov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
- *
- * In no event and under no legal theory, whether in tort (including negligence),
- * contract, or otherwise, unless required by applicable law (such as deliberate
- * and grossly negligent acts) or agreed to in writing, shall any Contributor be
- * liable for any damages, including any direct, indirect, special, incidental,
- * or consequential damages of any character arising as a result of this License or
- * out of the use or inability to use the software (including but not limited to damages
- * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
- * all other commercial damages or losses), even if such Contributor has been advised
- * of the possibility of such damages.
- */
-
-// The source code in this file is derived from ColorBuffer.cpp and GraphicsCore.cpp developed by Minigraph
-// Original source files header:
-
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-// Developed by Minigraph
-//
-// Author: James Stanard
-//
-
-
-#include "pch.h"
-
-/*
-#include "RenderDeviceVkImpl.h"
-#include "GenerateMips.h"
-#include "CommandContext.h"
-#include "TextureViewVkImpl.h"
-#include "TextureVkImpl.h"
-
-#include "GenerateMips/GenerateMipsLinearCS.h"
-#include "GenerateMips/GenerateMipsLinearOddCS.h"
-#include "GenerateMips/GenerateMipsLinearOddXCS.h"
-#include "GenerateMips/GenerateMipsLinearOddYCS.h"
-#include "GenerateMips/GenerateMipsGammaCS.h"
-#include "GenerateMips/GenerateMipsGammaOddCS.h"
-#include "GenerateMips/GenerateMipsGammaOddXCS.h"
-#include "GenerateMips/GenerateMipsGammaOddYCS.h"
-
-namespace Diligent
-{
- GenerateMipsHelper::GenerateMipsHelper(IVkDevice *pVkDevice)
- {
- CD3DX12_ROOT_PARAMETER Params[3];
- Params[0].InitAsConstants(6, 0);
- CD3DX12_DESCRIPTOR_RANGE SRVRange(Vk_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
- Params[1].InitAsDescriptorTable(1, &SRVRange);
- CD3DX12_DESCRIPTOR_RANGE UAVRange(Vk_DESCRIPTOR_RANGE_TYPE_UAV, 4, 0);
- Params[2].InitAsDescriptorTable(1, &UAVRange);
- CD3DX12_STATIC_SAMPLER_DESC SamplerLinearClampDesc(
- 0, Vk_FILTER_MIN_MAG_MIP_LINEAR, Vk_TEXTURE_ADDRESS_MODE_CLAMP, Vk_TEXTURE_ADDRESS_MODE_CLAMP, Vk_TEXTURE_ADDRESS_MODE_CLAMP);
- CD3DX12_ROOT_SIGNATURE_DESC RootSigDesc;
- RootSigDesc.NumParameters = _countof(Params);
- RootSigDesc.pParameters = Params;
- RootSigDesc.NumStaticSamplers = 1;
- RootSigDesc.pStaticSamplers = &SamplerLinearClampDesc;
- RootSigDesc.Flags = Vk_ROOT_SIGNATURE_FLAG_NONE;
-
- CComPtr<ID3DBlob> signature;
- CComPtr<ID3DBlob> error;
- HRESULT hr = VkSerializeRootSignature(&RootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
- hr = pVkDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), __uuidof(m_pGenerateMipsRS), reinterpret_cast<void**>( static_cast<IVkRootSignature**>(&m_pGenerateMipsRS)));
- CHECK_D3D_RESULT_THROW(hr, "Failed to create root signature for mipmap generation")
-
- Vk_COMPUTE_PIPELINE_STATE_DESC PSODesc = {};
- PSODesc.pRootSignature = m_pGenerateMipsRS;
- PSODesc.NodeMask = 0;
- PSODesc.Flags = Vk_PIPELINE_STATE_FLAG_NONE;
-
-#define CreatePSO(PSO, ShaderByteCode) \
- PSODesc.CS.pShaderBytecode = ShaderByteCode;\
- PSODesc.CS.BytecodeLength = sizeof(ShaderByteCode);\
- hr = pVkDevice->CreateComputePipelineState(&PSODesc, __uuidof(PSO), reinterpret_cast<void**>( static_cast<IVkPipelineState**>(&PSO))); \
- CHECK_D3D_RESULT_THROW(hr, "Failed to create Pipeline state for mipmap generation") \
- PSO->SetName(L"Generate mips PSO");
-
- CreatePSO(m_pGenerateMipsLinearPSO[0], g_pGenerateMipsLinearCS);
- CreatePSO(m_pGenerateMipsLinearPSO[1], g_pGenerateMipsLinearOddXCS);
- CreatePSO(m_pGenerateMipsLinearPSO[2], g_pGenerateMipsLinearOddYCS);
- CreatePSO(m_pGenerateMipsLinearPSO[3], g_pGenerateMipsLinearOddCS);
- CreatePSO(m_pGenerateMipsGammaPSO[0], g_pGenerateMipsGammaCS);
- CreatePSO(m_pGenerateMipsGammaPSO[1], g_pGenerateMipsGammaOddXCS);
- CreatePSO(m_pGenerateMipsGammaPSO[2], g_pGenerateMipsGammaOddYCS);
- CreatePSO(m_pGenerateMipsGammaPSO[3], g_pGenerateMipsGammaOddCS);
- }
-
- void GenerateMipsHelper::GenerateMips(RenderDeviceVkImpl *pRenderDeviceVk, TextureViewVkImpl *pTexView, CommandContext& Ctx)
- {
- auto &ComputeCtx = Ctx.AsComputeContext();
- ComputeCtx.SetRootSignature(m_pGenerateMipsRS);
- auto *pTexture = pTexView->GetTexture();
- auto *pTexVk = ValidatedCast<TextureVkImpl>( pTexture );
- auto &TexDesc = pTexture->GetDesc();
- auto SRVDescriptorHandle = pTexVk->GetTexArraySRV();
-
- Ctx.TransitionResource(pTexVk, Vk_RESOURCE_STATE_UNORDERED_ACCESS);
- auto *pVkDevice = pRenderDeviceVk->GetVkDevice();
-
- const auto &ViewDesc = pTexView->GetDesc();
- for (Uint32 ArrSlice = ViewDesc.FirstArraySlice; ArrSlice < ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices; ++ArrSlice)
- {
- for (uint32_t TopMip = 0; TopMip < TexDesc.MipLevels - 1; )
- {
- uint32_t SrcWidth = TexDesc.Width >> TopMip;
- uint32_t SrcHeight = TexDesc.Height >> TopMip;
- uint32_t DstWidth = SrcWidth >> 1;
- uint32_t DstHeight = SrcHeight >> 1;
-
- // Determine if the first downsample is more than 2:1. This happens whenever
- // the source width or height is odd.
- uint32_t NonPowerOfTwo = (SrcWidth & 1) | (SrcHeight & 1) << 1;
- if (TexDesc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB)
- ComputeCtx.SetPipelineState(m_pGenerateMipsGammaPSO[NonPowerOfTwo]);
- else
- ComputeCtx.SetPipelineState(m_pGenerateMipsLinearPSO[NonPowerOfTwo]);
-
- // We can downsample up to four times, but if the ratio between levels is not
- // exactly 2:1, we have to shift our blend weights, which gets complicated or
- // expensive. Maybe we can update the code later to compute sample weights for
- // each successive downsample. We use _BitScanForward to count number of zeros
- // in the low bits. Zeros indicate we can divide by two without truncating.
- uint32_t AdditionalMips;
- _BitScanForward((unsigned long*)&AdditionalMips, DstWidth | DstHeight);
- uint32_t NumMips = 1 + (AdditionalMips > 3 ? 3 : AdditionalMips);
- if (TopMip + NumMips > TexDesc.MipLevels - 1)
- NumMips = TexDesc.MipLevels - 1 - TopMip;
-
- // These are clamped to 1 after computing additional mips because clamped
- // dimensions should not limit us from downsampling multiple times. (E.g.
- // 16x1 -> 8x1 -> 4x1 -> 2x1 -> 1x1.)
- if (DstWidth == 0)
- DstWidth = 1;
- if (DstHeight == 0)
- DstHeight = 1;
-
- Vk_DESCRIPTOR_HEAP_TYPE HeapType = Vk_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
- auto DescriptorAlloc = Ctx.AllocateDynamicGPUVisibleDescriptor(HeapType, 5);
- CommandContext::ShaderDescriptorHeaps Heaps(DescriptorAlloc.GetDescriptorHeap());
- ComputeCtx.SetDescriptorHeaps(Heaps);
- Ctx.GetCommandList()->SetComputeRootDescriptorTable(1, DescriptorAlloc.GetGpuHandle(0));
- Ctx.GetCommandList()->SetComputeRootDescriptorTable(2, DescriptorAlloc.GetGpuHandle(1));
- struct RootCBData
- {
- Uint32 SrcMipLevel; // Texture level of source mip
- Uint32 NumMipLevels; // Number of OutMips to write: [1, 4]
- Uint32 ArraySlice;
- Uint32 Dummy;
- float TexelSize[2]; // 1.0 / OutMip1.Dimensions
- }CBData = { TopMip, NumMips, ArrSlice, 0, 1.0f / static_cast<float>(DstWidth), 1.0f / static_cast<float>(DstHeight) };
- Ctx.GetCommandList()->SetComputeRoot32BitConstants(0, 6, &CBData, 0);
-
- // TODO: Shouldn't we transition top mip to shader resource state?
- Vk_CPU_DESCRIPTOR_HANDLE DstDescriptorRange = DescriptorAlloc.GetCpuHandle();
- const Uint32 MaxMipsHandledByCS = 4; // Max number of mip levels processed by one CS shader invocation
- UINT DstRangeSize = 1 + MaxMipsHandledByCS;
- Vk_CPU_DESCRIPTOR_HANDLE SrcDescriptorRanges[5] = {};
- SrcDescriptorRanges[0] = SRVDescriptorHandle;
- UINT SrcRangeSizes[5] = { 1,1,1,1,1 };
- // On Resource Binding Tier 2 hardware, all descriptor tables of type CBV and UAV declared in the set
- // Root Signature must be populated and initialized, even if the shaders do not need the descriptor.
- // So we must populate all 4 slots even though we may actually process less than 4 mip levels
- // Copy top mip level UAV descriptor handle to all unused slots
- for (Uint32 u = 0; u < MaxMipsHandledByCS; ++u)
- SrcDescriptorRanges[1 + u] = pTexVk->GetMipLevelUAV(std::min(TopMip + u + 1, TexDesc.MipLevels - 1));
-
- pVkDevice->CopyDescriptors(1, &DstDescriptorRange, &DstRangeSize, 1 + MaxMipsHandledByCS, SrcDescriptorRanges, SrcRangeSizes, Vk_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
-
- ComputeCtx.Dispatch((DstWidth + 7) / 8, (DstHeight + 7) / 8);
-
- Ctx.InsertUAVBarrier(*pTexVk, *pTexVk);
-
- TopMip += NumMips;
- }
- }
- }
-}
-*/ \ No newline at end of file
diff --git a/Graphics/GraphicsEngineVulkan/src/GenerateMipsVkHelper.cpp b/Graphics/GraphicsEngineVulkan/src/GenerateMipsVkHelper.cpp
new file mode 100644
index 00000000..8427ea78
--- /dev/null
+++ b/Graphics/GraphicsEngineVulkan/src/GenerateMipsVkHelper.cpp
@@ -0,0 +1,310 @@
+/* Copyright 2015-2018 Egor Yusov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#include "pch.h"
+#include <sstream>
+#include "GenerateMipsVkHelper.h"
+#include "RenderDeviceVkImpl.h"
+#include "DeviceContextVkImpl.h"
+#include "TextureViewVkImpl.h"
+#include "TextureVkImpl.h"
+#include "MapHelper.h"
+#include "../../GraphicsTools/include/ShaderMacroHelper.h"
+#include "../../GraphicsTools/include/CommonlyUsedStates.h"
+
+
+static const char* g_GenerateMipsCSSource =
+{
+ #include "../shaders/GenerateMipsCS_inc.h"
+};
+
+namespace Diligent
+{
+ void GenerateMipsVkHelper::GetGlImageFormat(const TextureFormatAttribs& FmtAttribs, std::array<char, 16>& GlFmt)
+ {
+ size_t pos = 0;
+ GlFmt[pos++] = 'r';
+ if(FmtAttribs.NumComponents >= 2)
+ GlFmt[pos++] = 'g';
+ if(FmtAttribs.NumComponents >= 3)
+ GlFmt[pos++] = 'b';
+ if(FmtAttribs.NumComponents >= 4)
+ GlFmt[pos++] = 'a';
+ VERIFY_EXPR(FmtAttribs.NumComponents <= 4);
+ auto ComponentSize = FmtAttribs.ComponentSize * 8;
+ int pow10 = 1;
+ while(ComponentSize / (10*pow10) != 0)
+ pow10 *= 10;
+ VERIFY_EXPR(ComponentSize !=0);
+ while(ComponentSize != 0)
+ {
+ char digit = static_cast<char>(ComponentSize/pow10);
+ GlFmt[pos++] = '0' + digit;
+ ComponentSize -= digit * pow10;
+ pow10 /= 10;
+ }
+
+ switch(FmtAttribs.ComponentType)
+ {
+ case COMPONENT_TYPE_FLOAT:
+ GlFmt[pos++] = 'f';
+ break;
+
+ case COMPONENT_TYPE_UNORM:
+ case COMPONENT_TYPE_UNORM_SRGB:
+ // No suffix
+ break;
+
+ case COMPONENT_TYPE_SNORM:
+ GlFmt[pos++] = '_';
+ GlFmt[pos++] = 's';
+ GlFmt[pos++] = 'n';
+ GlFmt[pos++] = 'o';
+ GlFmt[pos++] = 'r';
+ GlFmt[pos++] = 'm';
+ break;
+
+ case COMPONENT_TYPE_SINT:
+ GlFmt[pos++] = 'i';
+ break;
+
+ case COMPONENT_TYPE_UINT:
+ GlFmt[pos++] = 'u';
+ GlFmt[pos++] = 'i';
+ break;
+
+ default:
+ UNSUPPORTED("Unsupported component type");
+ }
+
+ GlFmt[pos] = 0;
+ }
+
+ std::array<RefCntAutoPtr<IPipelineState>, 4> GenerateMipsVkHelper::CreatePSOs(TEXTURE_FORMAT Fmt)
+ {
+ ShaderCreationAttribs CSCreateAttribs;
+ std::array<RefCntAutoPtr<IPipelineState>, 4> PSOs;
+
+ CSCreateAttribs.Source = g_GenerateMipsCSSource;
+ CSCreateAttribs.EntryPoint = "main";
+ CSCreateAttribs.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL;
+ CSCreateAttribs.Desc.ShaderType = SHADER_TYPE_COMPUTE;
+ CSCreateAttribs.Desc.DefaultVariableType = SHADER_VARIABLE_TYPE_DYNAMIC;
+
+ ShaderVariableDesc VarDesc("CB", SHADER_VARIABLE_TYPE_STATIC);
+ CSCreateAttribs.Desc.VariableDesc = &VarDesc;
+ CSCreateAttribs.Desc.NumVariables = 1;
+
+ const StaticSamplerDesc StaticSampler("SrcMip", Sam_LinearClamp);
+ CSCreateAttribs.Desc.StaticSamplers = &StaticSampler;
+ CSCreateAttribs.Desc.NumStaticSamplers = 1;
+
+ const auto& FmtAttribs = GetTextureFormatAttribs(Fmt);
+ bool IsGamma = FmtAttribs.ComponentType == COMPONENT_TYPE_UNORM_SRGB;
+ std::array<char, 16> GlFmt;
+ GetGlImageFormat(FmtAttribs, GlFmt);
+
+ for(Uint32 NonPowOfTwo=0; NonPowOfTwo < 4; ++NonPowOfTwo)
+ {
+ ShaderMacroHelper Macros;
+ Macros.AddShaderMacro("NON_POWER_OF_TWO", NonPowOfTwo);
+ Macros.AddShaderMacro("CONVERT_TO_SRGB", IsGamma);
+ Macros.AddShaderMacro("IMG_FORMAT", GlFmt.data());
+
+ Macros.Finalize();
+ CSCreateAttribs.Macros = Macros;
+
+ std::stringstream name_ss;
+ name_ss << "Generate mips " << GlFmt.data();
+ switch(NonPowOfTwo)
+ {
+ case 0: name_ss << " even"; break;
+ case 1: name_ss << " odd X"; break;
+ case 2: name_ss << " odd Y"; break;
+ case 3: name_ss << " odd XY"; break;
+ default: UNEXPECTED("Unexpected value");
+ }
+ auto name = name_ss.str();
+ CSCreateAttribs.Desc.Name = name.c_str();
+ RefCntAutoPtr<IShader> pCS;
+
+ m_DeviceVkImpl.CreateShader(CSCreateAttribs, &pCS);
+ PipelineStateDesc PSODesc;
+ PSODesc.IsComputePipeline = true;
+ PSODesc.Name = name.c_str();
+ PSODesc.ComputePipeline.pCS = pCS;
+ pCS->GetShaderVariable("CB")->Set(m_ConstantsCB);
+ m_DeviceVkImpl.CreatePipelineState(PSODesc, &PSOs[NonPowOfTwo]);
+ }
+
+ return PSOs;
+ }
+
+ GenerateMipsVkHelper::GenerateMipsVkHelper(RenderDeviceVkImpl& DeviceVkImpl) :
+ m_DeviceVkImpl(DeviceVkImpl)
+ {
+ BufferDesc ConstantsCBDesc;
+ ConstantsCBDesc.Name = "Constants CB buffer";
+ ConstantsCBDesc.BindFlags = BIND_UNIFORM_BUFFER;
+ ConstantsCBDesc.Usage = USAGE_DYNAMIC;
+ ConstantsCBDesc.CPUAccessFlags = CPU_ACCESS_WRITE;
+ ConstantsCBDesc.uiSizeInBytes = 32;
+ DeviceVkImpl.CreateBuffer(ConstantsCBDesc, BufferData(), &m_ConstantsCB);
+
+ FindPSOs(TEX_FORMAT_RGBA8_UNORM);
+ FindPSOs(TEX_FORMAT_BGRA8_UNORM);
+ FindPSOs(TEX_FORMAT_RGBA8_UNORM_SRGB);
+ FindPSOs(TEX_FORMAT_BGRA8_UNORM_SRGB);
+ }
+
+ void GenerateMipsVkHelper::CreateSRB(IShaderResourceBinding** ppSRB)
+ {
+ // All PSOs are compatible
+ auto& PSO = FindPSOs(TEX_FORMAT_RGBA8_UNORM);
+ PSO[0]->CreateShaderResourceBinding(ppSRB);
+ }
+
+ std::array<RefCntAutoPtr<IPipelineState>, 4>& GenerateMipsVkHelper::FindPSOs(TEXTURE_FORMAT Fmt)
+ {
+ std::lock_guard<std::mutex> Lock(m_PSOMutex);
+ auto it = m_PSOHash.find(Fmt);
+ if(it == m_PSOHash.end())
+ it = m_PSOHash.emplace(Fmt, CreatePSOs(Fmt)).first;
+ return it->second;
+ }
+
+ void GenerateMipsVkHelper::GenerateMips(TextureViewVkImpl& TexView, DeviceContextVkImpl& Ctx, IShaderResourceBinding& SRB)
+ {
+ auto* pTexVk = TexView.GetTexture<TextureVkImpl>();
+ const auto& TexDesc = pTexVk->GetDesc();
+ const auto& ViewDesc = TexView.GetDesc();
+ auto* pSrcMipVar = SRB.GetVariable(SHADER_TYPE_COMPUTE, "SrcMip");
+ auto* pOutMipVar = SRB.GetVariable(SHADER_TYPE_COMPUTE, "OutMip");
+
+ auto& PSOs = FindPSOs(ViewDesc.Format);
+
+ const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
+ VkImageSubresourceRange SubresRange;
+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
+ SubresRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
+ {
+ // If image has a depth / stencil format with both depth and stencil components, then the
+ // aspectMask member of subresourceRange must include both VK_IMAGE_ASPECT_DEPTH_BIT and
+ // VK_IMAGE_ASPECT_STENCIL_BIT (6.7.3)
+ SubresRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ }
+ else
+ SubresRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ SubresRange.baseArrayLayer = 0;
+ SubresRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+
+ auto CurrLayout = pTexVk->GetLayout();
+
+ // Transition the lowest mip level to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ SubresRange.baseMipLevel = 0;
+ SubresRange.levelCount = 1;
+ if (CurrLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ Ctx.TransitionImageLayout(*pTexVk, CurrLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, SubresRange);
+
+ for (uint32_t TopMip = 0; TopMip < TexDesc.MipLevels - 1; )
+ {
+ // In Vulkan all subresources of a view must be transitioned to the same layout, so
+ // we can't bind the entire texture and have to bind single mip level at a time
+ pSrcMipVar->Set(pTexVk->GetMipLevelSRV(TopMip));
+
+ uint32_t SrcWidth = std::max(TexDesc.Width >> TopMip, 1u);
+ uint32_t SrcHeight = std::max(TexDesc.Height >> TopMip, 1u);
+ uint32_t DstWidth = std::max(SrcWidth >> 1, 1u);
+ uint32_t DstHeight = std::max(SrcHeight >> 1, 1u);
+
+ // Determine if the first downsample is more than 2:1. This happens whenever
+ // the source width or height is odd.
+ uint32_t NonPowerOfTwo = (SrcWidth & 1) | (SrcHeight & 1) << 1;
+ Ctx.SetPipelineState(PSOs[NonPowerOfTwo]);
+
+ // We can downsample up to four times, but if the ratio between levels is not
+ // exactly 2:1, we have to shift our blend weights, which gets complicated or
+ // expensive. Maybe we can update the code later to compute sample weights for
+ // each successive downsample. We use _BitScanForward to count number of zeros
+ // in the low bits. Zeros indicate we can divide by two without truncating.
+ uint32_t AdditionalMips;
+ _BitScanForward((unsigned long*)&AdditionalMips, DstWidth | DstHeight);
+ uint32_t NumMips = 1 + (AdditionalMips > 3 ? 3 : AdditionalMips);
+ if (TopMip + NumMips > TexDesc.MipLevels - 1)
+ NumMips = TexDesc.MipLevels - 1 - TopMip;
+
+ // These are clamped to 1 after computing additional mips because clamped
+ // dimensions should not limit us from downsampling multiple times. (E.g.
+ // 16x1 -> 8x1 -> 4x1 -> 2x1 -> 1x1.)
+ if (DstWidth == 0)
+ DstWidth = 1;
+ if (DstHeight == 0)
+ DstHeight = 1;
+
+ {
+ struct CBData
+ {
+ Int32 SrcMipLevel; // Texture level of source mip
+ Int32 NumMipLevels; // Number of OutMips to write: [1, 4]
+ Int32 ArraySlice;
+ Int32 Dummy;
+ float TexelSize[2]; // 1.0 / OutMip1.Dimensions
+ };
+ MapHelper<CBData> MappedData(&Ctx, m_ConstantsCB, MAP_WRITE, MAP_FLAG_DISCARD);
+
+ *MappedData =
+ {
+ static_cast<Int32>(TopMip),
+ static_cast<Int32>(NumMips),
+ static_cast<Int32>(ViewDesc.FirstArraySlice),
+ 0,
+ 1.0f / static_cast<float>(DstWidth),
+ 1.0f / static_cast<float>(DstHeight)
+ };
+ }
+
+ constexpr const Uint32 MaxMipsHandledByCS = 4; // Max number of mip levels processed by one CS shader invocation
+ std::array<IDeviceObject*, MaxMipsHandledByCS> MipLevelUAVs;
+ for (Uint32 u = 0; u < MaxMipsHandledByCS; ++u)
+ MipLevelUAVs[u] = pTexVk->GetMipLevelUAV(std::min(TopMip + u + 1, TexDesc.MipLevels - 1));
+ pOutMipVar->SetArray(MipLevelUAVs.data(), 0, MaxMipsHandledByCS);
+
+ SubresRange.baseMipLevel = TopMip + 1;
+ SubresRange.levelCount = std::min(4u, TexDesc.MipLevels - (TopMip + 1));
+ if (CurrLayout != VK_IMAGE_LAYOUT_GENERAL)
+ Ctx.TransitionImageLayout(*pTexVk, CurrLayout, VK_IMAGE_LAYOUT_GENERAL, SubresRange);
+
+ Ctx.CommitShaderResources(&SRB, 0);
+ DispatchComputeAttribs DispatchAttrs((DstWidth + 7) / 8, (DstHeight + 7) / 8, ViewDesc.NumArraySlices);
+ Ctx.DispatchCompute(DispatchAttrs);
+
+ Ctx.TransitionImageLayout(*pTexVk, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, SubresRange);
+
+ TopMip += NumMips;
+ }
+
+ // All mip levels are now in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL state
+ pTexVk->SetLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ }
+}
diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp
index ed3d334e..a202c59c 100644
--- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp
@@ -491,7 +491,7 @@ bool PipelineStateVkImpl::IsCompatibleWith(const IPipelineState *pPSO)const
void PipelineStateVkImpl::CommitAndTransitionShaderResources(IShaderResourceBinding* pShaderResourceBinding,
DeviceContextVkImpl* pCtxVkImpl,
bool CommitResources,
- bool TransitionResources,
+ Uint32 Flags,
PipelineLayout::DescriptorSetBindInfo* pDescrSetBindInfo)const
{
if (!m_HasStaticResources && !m_HasNonStaticResources)
@@ -546,9 +546,9 @@ void PipelineStateVkImpl::CommitAndTransitionShaderResources(IShaderResourceBind
if (CommitResources)
{
- if (TransitionResources)
+ if (Flags & COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES)
ResourceCache.TransitionResources<false>(pCtxVkImpl);
- else
+ else if (Flags & COMMIT_SHADER_RESOURCES_FLAG_VERIFY_STATES)
{
#ifdef VERIFY_SHADER_BINDINGS
ResourceCache.TransitionResources<true>(pCtxVkImpl);
@@ -578,7 +578,7 @@ void PipelineStateVkImpl::CommitAndTransitionShaderResources(IShaderResourceBind
}
else
{
- VERIFY(TransitionResources, "Resources should be transitioned or committed or both");
+ VERIFY( (Flags & COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES) != 0 , "Resources should be transitioned or committed or both");
ResourceCache.TransitionResources<false>(pCtxVkImpl);
}
}
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceFactoryVk.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceFactoryVk.cpp
index b02c4d23..4508a9ce 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceFactoryVk.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceFactoryVk.cpp
@@ -150,19 +150,20 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk( const EngineVkAttribs& Crea
DeviceCreateInfo.queueCreateInfoCount = 1;
DeviceCreateInfo.pQueueCreateInfos = &QueueInfo;
VkPhysicalDeviceFeatures DeviceFeatures = {};
- DeviceFeatures.depthBiasClamp = CreationAttribs.EnabledFeatures.depthBiasClamp ? VK_TRUE : VK_FALSE;
- DeviceFeatures.fillModeNonSolid = CreationAttribs.EnabledFeatures.fillModeNonSolid ? VK_TRUE : VK_FALSE;
- DeviceFeatures.depthClamp = CreationAttribs.EnabledFeatures.depthClamp ? VK_TRUE : VK_FALSE;
- DeviceFeatures.independentBlend = CreationAttribs.EnabledFeatures.independentBlend ? VK_TRUE : VK_FALSE;
- DeviceFeatures.samplerAnisotropy = CreationAttribs.EnabledFeatures.samplerAnisotropy ? VK_TRUE : VK_FALSE;
- DeviceFeatures.geometryShader = CreationAttribs.EnabledFeatures.geometryShader ? VK_TRUE : VK_FALSE;
- DeviceFeatures.tessellationShader = CreationAttribs.EnabledFeatures.tessellationShader ? VK_TRUE : VK_FALSE;
- DeviceFeatures.dualSrcBlend = CreationAttribs.EnabledFeatures.dualSrcBlend ? VK_TRUE : VK_FALSE;
- DeviceFeatures.multiViewport = CreationAttribs.EnabledFeatures.multiViewport ? VK_TRUE : VK_FALSE;
- DeviceFeatures.imageCubeArray = CreationAttribs.EnabledFeatures.imageCubeArray ? VK_TRUE : VK_FALSE;
- DeviceFeatures.textureCompressionBC = CreationAttribs.EnabledFeatures.textureCompressionBC ? VK_TRUE : VK_FALSE;
- DeviceFeatures.vertexPipelineStoresAndAtomics = CreationAttribs.EnabledFeatures.vertexPipelineStoresAndAtomics ? VK_TRUE : VK_FALSE;
- DeviceFeatures.fragmentStoresAndAtomics = CreationAttribs.EnabledFeatures.fragmentStoresAndAtomics ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.depthBiasClamp = CreationAttribs.EnabledFeatures.depthBiasClamp ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.fillModeNonSolid = CreationAttribs.EnabledFeatures.fillModeNonSolid ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.depthClamp = CreationAttribs.EnabledFeatures.depthClamp ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.independentBlend = CreationAttribs.EnabledFeatures.independentBlend ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.samplerAnisotropy = CreationAttribs.EnabledFeatures.samplerAnisotropy ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.geometryShader = CreationAttribs.EnabledFeatures.geometryShader ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.tessellationShader = CreationAttribs.EnabledFeatures.tessellationShader ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.dualSrcBlend = CreationAttribs.EnabledFeatures.dualSrcBlend ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.multiViewport = CreationAttribs.EnabledFeatures.multiViewport ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.imageCubeArray = CreationAttribs.EnabledFeatures.imageCubeArray ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.textureCompressionBC = CreationAttribs.EnabledFeatures.textureCompressionBC ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.vertexPipelineStoresAndAtomics = CreationAttribs.EnabledFeatures.vertexPipelineStoresAndAtomics ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.fragmentStoresAndAtomics = CreationAttribs.EnabledFeatures.fragmentStoresAndAtomics ? VK_TRUE : VK_FALSE;
+ DeviceFeatures.shaderStorageImageExtendedFormats = CreationAttribs.EnabledFeatures.shaderStorageImageExtendedFormats ? VK_TRUE : VK_FALSE;
DeviceCreateInfo.pEnabledFeatures = &DeviceFeatures; // NULL or a pointer to a VkPhysicalDeviceFeatures structure that contains
// boolean indicators of all the features to be enabled.
@@ -240,7 +241,9 @@ void EngineFactoryVkImpl::AttachToVulkanDevice(std::shared_ptr<VulkanUtilities::
RenderDeviceVkImpl *pRenderDeviceVk( NEW_RC_OBJ(RawMemAllocator, "RenderDeviceVkImpl instance", RenderDeviceVkImpl)(RawMemAllocator, EngineAttribs, pCommandQueue, Instance, std::move(PhysicalDevice), LogicalDevice, NumDeferredContexts ) );
pRenderDeviceVk->QueryInterface(IID_RenderDevice, reinterpret_cast<IObject**>(ppDevice) );
- RefCntAutoPtr<DeviceContextVkImpl> pImmediateCtxVk( NEW_RC_OBJ(RawMemAllocator, "DeviceContextVkImpl instance", DeviceContextVkImpl)(pRenderDeviceVk, false, EngineAttribs, 0) );
+ std::shared_ptr<GenerateMipsVkHelper> GenerateMipsHelper(new GenerateMipsVkHelper(*pRenderDeviceVk));
+
+ RefCntAutoPtr<DeviceContextVkImpl> pImmediateCtxVk( NEW_RC_OBJ(RawMemAllocator, "DeviceContextVkImpl instance", DeviceContextVkImpl)(pRenderDeviceVk, false, EngineAttribs, 0, GenerateMipsHelper) );
// We must call AddRef() (implicitly through QueryInterface()) because pRenderDeviceVk will
// keep a weak reference to the context
pImmediateCtxVk->QueryInterface(IID_DeviceContext, reinterpret_cast<IObject**>(ppContexts) );
@@ -248,7 +251,7 @@ void EngineFactoryVkImpl::AttachToVulkanDevice(std::shared_ptr<VulkanUtilities::
for (Uint32 DeferredCtx = 0; DeferredCtx < NumDeferredContexts; ++DeferredCtx)
{
- RefCntAutoPtr<DeviceContextVkImpl> pDeferredCtxVk( NEW_RC_OBJ(RawMemAllocator, "DeviceContextVkImpl instance", DeviceContextVkImpl)(pRenderDeviceVk, true, EngineAttribs, 1+DeferredCtx) );
+ RefCntAutoPtr<DeviceContextVkImpl> pDeferredCtxVk( NEW_RC_OBJ(RawMemAllocator, "DeviceContextVkImpl instance", DeviceContextVkImpl)(pRenderDeviceVk, true, EngineAttribs, 1+DeferredCtx, GenerateMipsHelper) );
// We must call AddRef() (implicitly through QueryInterface()) because pRenderDeviceVk will
// keep a weak reference to the context
pDeferredCtxVk->QueryInterface(IID_DeviceContext, reinterpret_cast<IObject**>(ppContexts + 1 + DeferredCtx) );
diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
index ec05b846..e1091a88 100644
--- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
@@ -114,7 +114,12 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl *pCtxVkImpl)
if(!pBufferVk->CheckAccessFlags(RequiredAccessFlags))
{
if(VerifyOnly)
- LOG_ERROR_MESSAGE("Buffer \"", pBufferVk->GetDesc().Name, "\" is not in correct state. Did you forget to call TransitionShaderResources() or specify COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag in a call to CommitShaderResources()?");
+ {
+ LOG_ERROR_MESSAGE("State of buffer \"", pBufferVk->GetDesc().Name, "\" is incorrect. Required access flags: ",
+ VulkanUtilities::VkAccessFlagsToString(RequiredAccessFlags), ". Actual access flags: ",
+ VulkanUtilities::VkAccessFlagsToString(pBufferVk->GetAccessFlags()),
+ ". Call TransitionShaderResources() or provide COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag to CommitShaderResources()");
+ }
else
pCtxVkImpl->BufferMemoryBarrier(*pBufferVk, RequiredAccessFlags);
}
@@ -134,7 +139,12 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl *pCtxVkImpl)
if (!pBufferVk->CheckAccessFlags(RequiredAccessFlags))
{
if (VerifyOnly)
- LOG_ERROR_MESSAGE("Buffer \"", pBufferVk->GetDesc().Name, "\" is not in correct state. Did you forget to call TransitionShaderResources() or specify COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag in a call to CommitShaderResources()?");
+ {
+ LOG_ERROR_MESSAGE("State of buffer \"", pBufferVk->GetDesc().Name, "\" is incorrect. Required access flags: ",
+ VulkanUtilities::VkAccessFlagsToString(RequiredAccessFlags), ". Actual access flags: ",
+ VulkanUtilities::VkAccessFlagsToString(pBufferVk->GetAccessFlags()),
+ ". Call TransitionShaderResources() or provide COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag to CommitShaderResources()");
+ }
else
pCtxVkImpl->BufferMemoryBarrier(*pBufferVk, RequiredAccessFlags);
}
@@ -172,7 +182,12 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl *pCtxVkImpl)
if(pTextureVk->GetLayout() != RequiredLayout)
{
if (VerifyOnly)
- LOG_ERROR_MESSAGE("Texture \"", pTextureVk->GetDesc().Name, "\" is not in correct state. Did you forget to call TransitionShaderResources() or specify COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag in a call to CommitShaderResources()?");
+ {
+ LOG_ERROR_MESSAGE("State of texture \"", pTextureVk->GetDesc().Name, "\" is incorrect. Required layout: ",
+ VulkanUtilities::VkImageLayoutToString(RequiredLayout), ". Actual layout: ",
+ VulkanUtilities::VkImageLayoutToString(pTextureVk->GetLayout()),
+ ". Call TransitionShaderResources() or specify COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag in a call to CommitShaderResources()");
+ }
else
pCtxVkImpl->TransitionImageLayout(*pTextureVk, RequiredLayout);
}
diff --git a/Graphics/GraphicsEngineVulkan/src/TextureViewVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureViewVkImpl.cpp
index f798e3b2..a1263f98 100644
--- a/Graphics/GraphicsEngineVulkan/src/TextureViewVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/TextureViewVkImpl.cpp
@@ -58,8 +58,8 @@ void TextureViewVkImpl::GenerateMips( IDeviceContext *pContext )
LOG_ERROR("GenerateMips() is allowed for shader resource views only, ", GetTexViewTypeLiteralName(m_Desc.ViewType), " is not allowed.");
return;
}
- auto *pDeviceCtxVk = ValidatedCast<DeviceContextVkImpl>( pContext );
- pDeviceCtxVk->GenerateMips(this);
+ auto* pDeviceCtxVk = ValidatedCast<DeviceContextVkImpl>( pContext );
+ pDeviceCtxVk->GenerateMips(*this);
}
}
diff --git a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
index 16fb400d..eb396192 100644
--- a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
@@ -361,7 +361,7 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters* pRefCounters,
pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(vkCmdBuff, std::move(CmdPool));
}
-#if 0
+
if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS)
{
if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY)
@@ -369,10 +369,15 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters* pRefCounters,
LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays");
}
- m_MipUAVs = pRenderDeviceVk->AllocateDescriptor(Vk_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_Desc.MipLevels);
+ m_MipLevelUAV.reserve(m_Desc.MipLevels);
for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
{
+ // Create mip level UAV
TextureViewDesc UAVDesc;
+ std::stringstream name_ss;
+ name_ss << "Mip " << MipLevel << " UAV for texture '" << m_Desc.Name << "'";
+ auto name = name_ss.str();
+ UAVDesc.Name = name.c_str();
// Always create texture array UAV
UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS;
@@ -381,23 +386,34 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters* pRefCounters,
UAVDesc.MostDetailedMip = MipLevel;
if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB)
UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM;
- CreateUAV( UAVDesc, m_MipUAVs.GetCpuHandle(MipLevel) );
+ ITextureView* pMipUAV = nullptr;
+ CreateViewInternal( UAVDesc, &pMipUAV, true );
+ m_MipLevelUAV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipUAV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
}
+ VERIFY_EXPR(m_MipLevelUAV.size() == m_Desc.MipLevels);
+ m_MipLevelSRV.reserve(m_Desc.MipLevels);
+ for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
{
- m_TexArraySRV = pRenderDeviceVk->AllocateDescriptor(Vk_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
+ // Create mip level SRV
TextureViewDesc TexArraySRVDesc;
- // Create texture array SRV
+ std::stringstream name_ss;
+ name_ss << "Mip " << MipLevel << " SRV for texture '" << m_Desc.Name << "'";
+ auto name = name_ss.str();
+ TexArraySRVDesc.Name = name.c_str();
+ // Alaways create texture array view
TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE;
TexArraySRVDesc.FirstArraySlice = 0;
TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize;
- TexArraySRVDesc.MostDetailedMip = 0;
- TexArraySRVDesc.NumMipLevels = m_Desc.MipLevels;
- CreateSRV( TexArraySRVDesc, m_TexArraySRV.GetCpuHandle() );
+ TexArraySRVDesc.MostDetailedMip = MipLevel;
+ TexArraySRVDesc.NumMipLevels = 1;
+ ITextureView* pMipLevelSRV = nullptr;
+ CreateViewInternal( TexArraySRVDesc, &pMipLevelSRV, true );
+ m_MipLevelSRV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipLevelSRV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
}
+ VERIFY_EXPR(m_MipLevelSRV.size() == m_Desc.MipLevels);
}
-#endif
}
TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters,
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp
index 0aa12c89..f971bcab 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp
@@ -429,4 +429,69 @@ namespace VulkanUtilities
return "UNKNOWN_ERROR";
}
}
+
+ const char* VkAccessFlagBitToString(VkAccessFlagBits Bit)
+ {
+ VERIFY(Bit != 0 && (Bit & (Bit-1)) == 0, "Single bit is expected");
+ switch(Bit)
+ {
+#define ACCESS_FLAG_BIT_TO_STRING(ACCESS_FLAG_BIT)case ACCESS_FLAG_BIT: return #ACCESS_FLAG_BIT;
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_INDIRECT_COMMAND_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_INDEX_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_UNIFORM_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_INPUT_ATTACHMENT_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_SHADER_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_SHADER_WRITE_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_TRANSFER_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_TRANSFER_WRITE_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_HOST_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_HOST_WRITE_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_MEMORY_READ_BIT)
+ ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_MEMORY_WRITE_BIT)
+#undef ACCESS_FLAG_BIT_TO_STRING
+ default: UNEXPECTED("Unexpected bit"); return "";
+ }
+ }
+
+ const char* VkImageLayoutToString(VkImageLayout Layout)
+ {
+ switch(Layout)
+ {
+#define IMAGE_LAYOUT_TO_STRING(LAYOUT)case LAYOUT: return #LAYOUT;
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_UNDEFINED)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_GENERAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_PREINITIALIZED)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
+ IMAGE_LAYOUT_TO_STRING(VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR)
+#undef IMAGE_LAYOUT_TO_STRING
+ default: UNEXPECTED("Unknown layout"); return "";
+ }
+ }
+
+ std::string VkAccessFlagsToString(VkAccessFlags Flags)
+ {
+ std::string FlagsString;
+ while(Flags != 0)
+ {
+ auto Bit = Flags & ~(Flags - 1);
+ if (!FlagsString.empty())
+ FlagsString += ", ";
+ FlagsString += VkAccessFlagBitToString( static_cast<VkAccessFlagBits>(Bit) );
+ Flags = Flags & (Flags - 1);
+ }
+ return std::move(FlagsString);
+ }
}