Просмотр исходного кода

Vulkan: Work on the texture (WIP)

Panagiotis Christopoulos Charitos 9 лет назад
Родитель
Сommit
51edefba1f

+ 17 - 4
include/anki/gr/Texture.h

@@ -17,15 +17,24 @@ namespace anki
 class SamplerInitInfo
 {
 public:
+	F32 m_minLod = -1000.0;
+	F32 m_maxLod = 1000.0;
 	SamplingFilter m_minMagFilter = SamplingFilter::NEAREST;
 	SamplingFilter m_mipmapFilter = SamplingFilter::BASE;
 	CompareOperation m_compareOperation = CompareOperation::ALWAYS;
-	F32 m_minLod = -1000.0;
-	F32 m_maxLod = 1000.0;
 	I8 m_anisotropyLevel = 0;
 	Bool8 m_repeat = true; ///< Repeat or clamp.
+
+	U64 computeHash() const
+	{
+		return anki::computeHash(
+			this, offsetof(SamplerInitInfo, m_repeat) + sizeof(m_repeat));
+	}
 };
 
+static_assert(offsetof(SamplerInitInfo, m_repeat) == 12,
+	"Class needs to be tightly packed since we hash it");
+
 /// Texture initializer.
 class TextureInitInfo
 {
@@ -33,12 +42,16 @@ public:
 	TextureType m_type = TextureType::_2D;
 	U32 m_width = 0;
 	U32 m_height = 0;
-	U32 m_depth =
-		0; ///< Relevant only for 3D, 2DynamicArray and CubeArray textures
+
+	/// Relevant only for 3D, 2DynamicArray and CubeArray textures.
+	U32 m_depth = 0;
+
 	PixelFormat m_format;
 	U8 m_mipmapsCount = 0;
 	U8 m_samples = 1;
 
+	Bool8 m_framebufferAttachment = false;
+
 	SamplerInitInfo m_sampling;
 };
 

+ 18 - 0
include/anki/gr/vulkan/Common.h

@@ -51,6 +51,20 @@ class GrManagerImpl;
 #define ANKI_VK_MEMSET_DBG(struct_) ((void)0)
 #endif
 
+ANKI_USE_RESULT inline Bool formatIsDepthStencil(PixelFormat fmt)
+{
+	if(fmt.m_components == ComponentFormat::D16
+		|| fmt.m_components == ComponentFormat::D24
+		|| fmt.m_components == ComponentFormat::D32)
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
 /// Convert compare op.
 ANKI_USE_RESULT VkCompareOp convertCompareOp(CompareOperation ak);
 
@@ -87,6 +101,10 @@ ANKI_USE_RESULT VkAttachmentStoreOp convertStoreOp(AttachmentStoreOperation ak);
 /// Convert buffer usage bitmask.
 ANKI_USE_RESULT VkBufferUsageFlags convertBufferUsageBit(
 	BufferUsageBit usageMask);
+
+ANKI_USE_RESULT VkImageType convertTextureType(TextureType ak);
+
+ANKI_USE_RESULT VkImageViewType convertTextureViewType(TextureType ak);
 /// @}
 
 } // end namespace anki

+ 5 - 0
include/anki/gr/vulkan/GpuMemoryAllocator.h

@@ -32,6 +32,11 @@ public:
 		return m_memory == VK_NULL_HANDLE;
 	}
 
+	operator bool() const
+	{
+		return m_memory != VK_NULL_HANDLE;
+	}
+
 private:
 	GpuMemoryAllocatorChunk* m_chunk = nullptr;
 };

+ 6 - 0
include/anki/gr/vulkan/GrManagerImpl.h

@@ -55,6 +55,12 @@ public:
 		return m_device;
 	}
 
+	VkPhysicalDevice getPhysicalDevice() const
+	{
+		ANKI_ASSERT(m_physicalDevice);
+		return m_physicalDevice;
+	}
+
 	VkPipelineLayout getGlobalPipelineLayout() const
 	{
 		ANKI_ASSERT(m_globalPipelineLayout);

+ 39 - 3
include/anki/gr/vulkan/TextureImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/VulkanObject.h>
+#include <anki/gr/vulkan/GpuMemoryAllocator.h>
 
 namespace anki
 {
@@ -17,14 +18,49 @@ namespace anki
 class TextureImpl : public VulkanObject
 {
 public:
-	TextureImpl(GrManager* manager)
-		: VulkanObject(manager)
+	TextureImpl(GrManager* manager);
+
+	~TextureImpl();
+
+	ANKI_USE_RESULT Error init(const TextureInitInfo& init);
+
+	const SamplerPtr& getSampler() const
 	{
+		return m_sampler;
 	}
 
-	~TextureImpl()
+	VkImage getImageHandle() const
 	{
+		ANKI_ASSERT(m_imageHandle);
+		return m_imageHandle;
 	}
+
+private:
+	class CreateContext;
+
+	VkImage m_imageHandle = VK_NULL_HANDLE;
+	VkImageView m_viewHandle = VK_NULL_HANDLE;
+	SamplerPtr m_sampler;
+	U32 m_memIdx = MAX_U32;
+	GpuMemoryAllocationHandle m_memHandle;
+
+	U8 m_mipCount = 0;
+	TextureType m_type = TextureType::CUBE;
+	VkImageAspectFlags m_aspect = 0;
+
+	ANKI_USE_RESULT static VkFormatFeatureFlags calcFeatures(
+		const TextureInitInfo& init);
+
+	ANKI_USE_RESULT static VkImageUsageFlags calcUsage(
+		const TextureInitInfo& init);
+
+	ANKI_USE_RESULT static VkImageCreateFlags calcCreateFlags(
+		const TextureInitInfo& init);
+
+	ANKI_USE_RESULT Bool imageSupported(const TextureInitInfo& init);
+
+	ANKI_USE_RESULT Error initImage(CreateContext& ctx);
+	ANKI_USE_RESULT Error initView(CreateContext& ctx);
 };
 /// @}
 

+ 6 - 0
include/anki/gr/vulkan/VulkanObject.h

@@ -33,6 +33,12 @@ public:
 
 	GrManagerImpl& getGrManagerImpl();
 
+	GrManager& getGrManager()
+	{
+		ANKI_ASSERT(m_manager);
+		return *m_manager;
+	}
+
 protected:
 	GrManager* m_manager = nullptr;
 };

+ 56 - 0
src/gr/vulkan/Common.cpp

@@ -521,4 +521,60 @@ VkBufferUsageFlags convertBufferUsageBit(BufferUsageBit usageMask)
 	return out;
 }
 
+//==============================================================================
+VkImageType convertTextureType(TextureType ak)
+{
+	VkImageType out = VK_IMAGE_TYPE_MAX_ENUM;
+	switch(ak)
+	{
+	case TextureType::CUBE:
+	case TextureType::CUBE_ARRAY:
+	case TextureType::_2D:
+	case TextureType::_2D_ARRAY:
+		out = VK_IMAGE_TYPE_2D;
+		break;
+	case TextureType::_3D:
+		out = VK_IMAGE_TYPE_3D;
+		break;
+	case TextureType::_1D:
+		out = VK_IMAGE_TYPE_1D;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return out;
+}
+
+//==============================================================================
+VkImageViewType convertTextureViewType(TextureType ak)
+{
+	VkImageViewType out = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
+	switch(ak)
+	{
+	case TextureType::_1D:
+		out = VK_IMAGE_VIEW_TYPE_1D;
+		break;
+	case TextureType::_2D:
+		out = VK_IMAGE_VIEW_TYPE_2D;
+		break;
+	case TextureType::_2D_ARRAY:
+		out = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
+		break;
+	case TextureType::_3D:
+		out = VK_IMAGE_VIEW_TYPE_3D;
+		break;
+	case TextureType::CUBE:
+		out = VK_IMAGE_VIEW_TYPE_CUBE;
+		break;
+	case TextureType::CUBE_ARRAY:
+		out = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return out;
+}
+
 } // end namespace anki

+ 305 - 0
src/gr/vulkan/TextureImpl.cpp

@@ -4,3 +4,308 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/gr/vulkan/TextureImpl.h>
+#include <anki/gr/Sampler.h>
+#include <anki/gr/GrManager.h>
+#include <anki/gr/vulkan/GrManagerImpl.h>
+
+namespace anki
+{
+
+//==============================================================================
+// TextureImpl::CreateContext                                                  =
+//==============================================================================
+
+class TextureImpl::CreateContext
+{
+public:
+	TextureInitInfo m_init;
+	VkImageCreateInfo m_imgCi = {};
+};
+
+//==============================================================================
+// TextureImpl                                                                 =
+//==============================================================================
+
+//==============================================================================
+TextureImpl::TextureImpl(GrManager* manager)
+	: VulkanObject(manager)
+{
+}
+
+//==============================================================================
+TextureImpl::~TextureImpl()
+{
+	if(m_imageHandle)
+	{
+		vkDestroyImage(getDevice(), m_imageHandle, nullptr);
+	}
+
+	if(m_memHandle)
+	{
+		getGrManagerImpl().freeMemory(m_memIdx, m_memHandle);
+	}
+}
+
+//==============================================================================
+VkFormatFeatureFlags TextureImpl::calcFeatures(const TextureInitInfo& init)
+{
+	VkFormatFeatureFlags flags = 0;
+
+	if(init.m_mipmapsCount > 1)
+	{
+		// May be used for mip gen.
+		flags |=
+			VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT;
+	}
+
+	if(init.m_framebufferAttachment)
+	{
+		if(formatIsDepthStencil(init.m_format))
+		{
+			flags |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
+		}
+		else
+		{
+			flags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
+				| VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
+		}
+	}
+
+	// This is quite common
+	flags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
+
+	return flags;
+}
+
+//==============================================================================
+VkImageUsageFlags TextureImpl::calcUsage(const TextureInitInfo& init)
+{
+	VkImageUsageFlags usage = 0;
+
+	if(init.m_framebufferAttachment)
+	{
+		if(formatIsDepthStencil(init.m_format))
+		{
+			usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+		}
+		else
+		{
+			usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+		}
+	}
+	else
+	{
+		usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+	}
+
+	usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
+
+	return usage;
+}
+
+//==============================================================================
+VkImageCreateFlags TextureImpl::calcCreateFlags(const TextureInitInfo& init)
+{
+	VkImageCreateFlags flags = 0;
+	if(init.m_type == TextureType::CUBE
+		|| init.m_type == TextureType::CUBE_ARRAY)
+	{
+		flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+	}
+
+	return flags;
+}
+
+//==============================================================================
+Bool TextureImpl::imageSupported(const TextureInitInfo& init)
+{
+	VkImageFormatProperties props = {};
+
+	VkResult res = vkGetPhysicalDeviceImageFormatProperties(
+		getGrManagerImpl().getPhysicalDevice(),
+		convertFormat(init.m_format),
+		convertTextureType(init.m_type),
+		VK_IMAGE_TILING_OPTIMAL,
+		calcUsage(init),
+		calcCreateFlags(init),
+		&props);
+
+	if(res == VK_ERROR_FORMAT_NOT_SUPPORTED)
+	{
+		return false;
+	}
+	else
+	{
+		ANKI_ASSERT(res == VK_SUCCESS);
+		return true;
+	}
+}
+
+//==============================================================================
+Error TextureImpl::init(const TextureInitInfo& init)
+{
+	m_sampler = getGrManager().newInstanceCached<Sampler>(init.m_sampling);
+
+	CreateContext ctx;
+	ctx.m_init = init;
+	ANKI_CHECK(initImage(ctx));
+	ANKI_CHECK(initView(ctx));
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error TextureImpl::initImage(CreateContext& ctx)
+{
+	TextureInitInfo& init = ctx.m_init;
+
+	// Check if format is supported
+	Bool supported;
+	while(!(supported = imageSupported(init)))
+	{
+		// Try to find a fallback
+		if(init.m_format.m_components == ComponentFormat::R8G8B8)
+		{
+			init.m_format.m_components = ComponentFormat::R8G8B8A8;
+		}
+		else
+		{
+			break;
+		}
+	}
+
+	if(!supported)
+	{
+		ANKI_LOGE("Unsupported texture format: %u %u",
+			U(init.m_format.m_components),
+			U(init.m_format.m_transform));
+	}
+
+	// Contunue with the creation
+	m_type = init.m_type;
+
+	VkImageCreateFlags flags = 0;
+	if(init.m_type == TextureType::CUBE
+		|| init.m_type == TextureType::CUBE_ARRAY)
+	{
+		flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+	}
+
+	VkImageCreateInfo& ci = ctx.m_imgCi;
+	ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+	ci.flags = flags;
+	ci.imageType = convertTextureType(init.m_type);
+	ci.format = convertFormat(init.m_format);
+	ci.extent.width = init.m_width;
+	ci.extent.height = init.m_height;
+
+	switch(init.m_type)
+	{
+	case TextureType::_1D:
+	case TextureType::_2D:
+		ci.extent.depth = 1;
+		ci.arrayLayers = 1;
+		ANKI_ASSERT(init.m_depth == 1);
+		break;
+	case TextureType::_2D_ARRAY:
+		ci.extent.depth = 1;
+		ci.arrayLayers = init.m_depth;
+		break;
+	case TextureType::CUBE:
+		ci.extent.depth = 1;
+		ci.arrayLayers = 6;
+		ANKI_ASSERT(init.m_depth == 1);
+		break;
+	case TextureType::CUBE_ARRAY:
+		ci.extent.depth = 1;
+		ci.arrayLayers = 6 * init.m_depth;
+		break;
+	case TextureType::_3D:
+		ci.extent.depth = init.m_depth;
+		ci.arrayLayers = 1;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	ci.mipLevels = init.m_mipmapsCount;
+	ANKI_ASSERT(ci.mipLevels > 0);
+	m_mipCount = init.m_mipmapsCount;
+
+	ci.samples = VK_SAMPLE_COUNT_1_BIT;
+	ci.tiling = VK_IMAGE_TILING_OPTIMAL;
+	ci.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
+	ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+	ci.queueFamilyIndexCount = 1;
+	U32 queueIdx = getGrManagerImpl().getGraphicsQueueFamily();
+	ci.pQueueFamilyIndices = &queueIdx;
+	ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+	ANKI_VK_CHECK(vkCreateImage(getDevice(), &ci, nullptr, &m_imageHandle));
+
+	// Allocate memory
+	//
+	VkMemoryRequirements req = {};
+	vkGetImageMemoryRequirements(getDevice(), m_imageHandle, &req);
+
+	m_memIdx = getGrManagerImpl().findMemoryType(req.memoryTypeBits,
+		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+		VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+
+	// Fallback
+	if(m_memIdx == MAX_U32)
+	{
+		m_memIdx = getGrManagerImpl().findMemoryType(
+			req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
+	}
+
+	ANKI_ASSERT(m_memIdx != MAX_U32);
+
+	// Allocate
+	getGrManagerImpl().allocateMemory(
+		m_memIdx, req.size, req.alignment, m_memHandle);
+
+	// Bind mem to image
+	ANKI_VK_CHECK(vkBindImageMemory(getDevice(),
+		m_imageHandle,
+		m_memHandle.m_memory,
+		m_memHandle.m_offset));
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error TextureImpl::initView(CreateContext& ctx)
+{
+	const TextureInitInfo& init = ctx.m_init;
+
+	if(formatIsDepthStencil(init.m_format))
+	{
+		m_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
+	}
+	else
+	{
+		m_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
+	}
+
+	VkImageViewCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+	ci.image = m_imageHandle;
+	ci.viewType = convertTextureViewType(init.m_type);
+	ci.format = ctx.m_imgCi.format;
+	ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+	ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+	ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+	ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+	ci.subresourceRange.aspectMask = m_aspect;
+	ci.subresourceRange.baseArrayLayer = 0;
+	ci.subresourceRange.baseMipLevel = 0;
+	ci.subresourceRange.layerCount = ctx.m_imgCi.arrayLayers;
+	ci.subresourceRange.levelCount = ctx.m_imgCi.mipLevels;
+
+	ANKI_VK_CHECK(vkCreateImageView(getDevice(), &ci, nullptr, &m_viewHandle));
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki