Przeglądaj źródła

Vulkan texture creation

BearishSun 9 lat temu
rodzic
commit
cddbc55301

+ 30 - 3
Source/BansheeVulkanRenderAPI/Include/BsVulkanTexture.h

@@ -16,7 +16,8 @@ namespace BansheeEngine
 	class VulkanImage : public VulkanResource
 	{
 	public:
-		VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout);
+		VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout,
+					const TextureProperties& props);
 		~VulkanImage();
 
 		/** Returns the internal handle to the Vulkan object. */
@@ -28,10 +29,30 @@ namespace BansheeEngine
 		/** Notifies the resource that the current image layout has changed. */
 		void setLayout(VkImageLayout layout) { mLayout = layout; }
 
+		/** Returns an image view that covers all faces and mip maps of the texture. */
+		VkImageView getView() const { return mMainView; };
+
+		/** Returns an image view that covers the specified faces and mip maps of the texture. */
+		VkImageView getView(const TextureSurface& surface) const;
+
 	private:
+		/** Creates a new view of the provided part (or entirety) of surface. */
+		VkImageView createView(const TextureSurface& surface) const;
+
+		/** Contains information about view for a specific surface(s) of this image. */
+		struct ImageViewInfo
+		{
+			TextureSurface surface;
+			VkImageView view;
+		};
+
 		VkImage mImage;
 		VkDeviceMemory mMemory;
 		VkImageLayout mLayout;
+		VkImageView mMainView;
+
+		mutable VkImageViewCreateInfo mImageViewCI;
+		mutable Vector<ImageViewInfo> mImageInfos;
 	};
 
 	/**	Vulkan implementation of a texture. */
@@ -50,13 +71,13 @@ namespace BansheeEngine
 		 * Returns an image view that covers all faces and mip maps of the texture. Usable only on the specified device. 
 		 * If texture device mask doesn't include the provided device, null is returned. 
 		 */
-		VkImageView getView(UINT32 deviceIdx);
+		VkImageView getView(UINT32 deviceIdx) const;
 
 		/** 
 		 * Returns an image view that covers the specified faces and mip maps of the texture. Usable only on the specified 
 		 * device. If texture device mask doesn't include the provided device, null is returned. 
 		 */
-		VkImageView getView(UINT32 deviceIdx, const TextureSurface& surface);
+		VkImageView getView(UINT32 deviceIdx, const TextureSurface& surface) const;
 
 	protected:
 		friend class VulkanTextureCoreManager;
@@ -82,7 +103,13 @@ namespace BansheeEngine
 		void writeData(const PixelData& src, UINT32 mipLevel = 0, UINT32 face = 0, bool discardWholeBuffer = false) override;
 
 	private:
+		/** Creates a new image for the specified device, matching the current properties. */
+		VulkanImage* createImage(VulkanDevice& device, bool staging, bool readable);
+
 		VulkanImage* mImages[BS_MAX_DEVICES];
+		GpuDeviceFlags mDeviceMask;
+
+		VkImageCreateInfo mImageCI;
 	};
 
 	/** @} */

+ 214 - 23
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -1,16 +1,54 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsVulkanTexture.h"
+#include "BsVulkanRenderAPI.h"
 #include "BsVulkanDevice.h"
+#include "BsVulkanUtility.h"
 #include "BsCoreThread.h"
 #include "BsRenderStats.h"
 
 namespace BansheeEngine
 {
-	VulkanImage::VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout)
-		:VulkanResource(owner, false), mImage(image), mMemory(memory), mLayout(layout)
+	VulkanImage::VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout,
+							 const TextureProperties& props)
+		: VulkanResource(owner, false), mImage(image), mMemory(memory), mLayout(layout)
 	{
-		
+		mImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+		mImageViewCI.pNext = nullptr;
+		mImageViewCI.flags = 0;
+		mImageViewCI.image = image;
+		mImageViewCI.format = VulkanUtility::getPixelFormat(props.getFormat());
+		mImageViewCI.components = {
+			VK_COMPONENT_SWIZZLE_R,
+			VK_COMPONENT_SWIZZLE_G,
+			VK_COMPONENT_SWIZZLE_B,
+			VK_COMPONENT_SWIZZLE_A
+		};
+
+		switch (props.getTextureType())
+		{
+		case TEX_TYPE_1D:
+			mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D;
+			break;
+		default:
+		case TEX_TYPE_2D:
+			mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
+			break;
+		case TEX_TYPE_3D:
+			mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_3D;
+			break;
+		case TEX_TYPE_CUBE_MAP:
+			mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
+			break;
+		}
+
+		if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
+			mImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+		else
+			mImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+
+		TextureSurface completeSurface(0, props.getNumMipmaps() + 1, 0, props.getNumArraySlices());
+		mMainView = createView(completeSurface);
 	}
 
 	VulkanImage::~VulkanImage()
@@ -18,13 +56,75 @@ namespace BansheeEngine
 		VulkanDevice& device = mOwner->getDevice();
 		VkDevice vkDevice = device.getLogical();
 
+		vkDestroyImageView(vkDevice, mMainView, gVulkanAllocator);
+
+		for(auto& entry : mImageInfos)
+			vkDestroyImageView(vkDevice, entry.view, gVulkanAllocator);
+
 		vkDestroyImage(vkDevice, mImage, gVulkanAllocator);
 		device.freeMemory(mMemory);
 	}
 
+	VkImageView VulkanImage::getView(const TextureSurface& surface) const
+	{
+		for(auto& entry : mImageInfos)
+		{
+			if (surface.mipLevel == entry.surface.mipLevel &&
+				surface.numMipLevels == entry.surface.numMipLevels &&
+				surface.arraySlice == entry.surface.arraySlice &&
+				surface.numArraySlices == entry.surface.numArraySlices)
+			{
+				return entry.view;
+			}
+		}
+
+		ImageViewInfo info;
+		info.surface = surface;
+		info.view = createView(surface);
+
+		mImageInfos.push_back(info);
+
+		return info.view;
+	}
+
+	VkImageView VulkanImage::createView(const TextureSurface& surface) const
+	{
+		VkImageViewType oldViewType = mImageViewCI.viewType;
+
+		if(surface.numArraySlices > 1)
+		{
+			switch (oldViewType)
+			{
+			case VK_IMAGE_VIEW_TYPE_1D:
+				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
+				break;
+			case VK_IMAGE_VIEW_TYPE_2D:
+				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
+				break;
+			case VK_IMAGE_VIEW_TYPE_CUBE:
+				mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
+				break;
+			default:
+				break;
+			}
+		}
+
+		mImageViewCI.subresourceRange.baseMipLevel = surface.mipLevel;
+		mImageViewCI.subresourceRange.levelCount = surface.numMipLevels;
+		mImageViewCI.subresourceRange.baseArrayLayer = surface.arraySlice;
+		mImageViewCI.subresourceRange.layerCount = surface.numArraySlices;
+
+		VkImageView view;
+		VkResult result = vkCreateImageView(mOwner->getDevice().getLogical(), &mImageViewCI, gVulkanAllocator, &view);
+		assert(result == VK_SUCCESS);
+
+		mImageViewCI.viewType = oldViewType;
+		return view;
+	}
+
 	VulkanTextureCore::VulkanTextureCore(const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData,
 		GpuDeviceFlags deviceMask)
-		: TextureCore(desc, initialData, deviceMask), mImages()
+		: TextureCore(desc, initialData, deviceMask), mImages(), mDeviceMask(deviceMask)
 	{
 		
 	}
@@ -46,37 +146,128 @@ namespace BansheeEngine
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
+		const TextureProperties& props = mProperties;
+
+		mImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+		mImageCI.pNext = nullptr;
+		mImageCI.flags = 0;
+
+		TextureType texType = props.getTextureType();
+		switch(texType)
+		{
+		case TEX_TYPE_1D:
+			mImageCI.imageType = VK_IMAGE_TYPE_1D;
+			break;
+		case TEX_TYPE_2D:
+			mImageCI.imageType = VK_IMAGE_TYPE_2D;
+			break;
+		case TEX_TYPE_3D:
+			mImageCI.imageType = VK_IMAGE_TYPE_3D;
+			break;
+		case TEX_TYPE_CUBE_MAP:
+			mImageCI.imageType = VK_IMAGE_TYPE_2D;
+			mImageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+			break;
+		}
+
+		int usage = props.getUsage();
+		if((usage & TU_RENDERTARGET) != 0)
+			mImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+		else if((usage & TU_DEPTHSTENCIL) != 0)
+			mImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+		else if((usage & TU_LOADSTORE) != 0)
+			mImageCI.usage = VK_IMAGE_USAGE_STORAGE_BIT;
+		else
+			mImageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+
+		if ((usage & TU_CPUREADABLE) != 0)
+			mImageCI.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+
+		mImageCI.format = VulkanUtility::getPixelFormat(props.getFormat());
+		mImageCI.extent = { props.getWidth(), props.getHeight(), props.getDepth() };
+		mImageCI.mipLevels = props.getNumMipmaps() + 1;
+		mImageCI.arrayLayers = props.getNumFaces();
+		mImageCI.samples = VulkanUtility::getSampleFlags(props.getNumSamples());
+		mImageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
+		mImageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+		mImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+		mImageCI.queueFamilyIndexCount = 0;
+		mImageCI.pQueueFamilyIndices = nullptr;
+
+		bool isReadable = (usage & GBU_READABLE) != 0 || BS_EDITOR_BUILD;
+
+		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
+		VulkanDevice* devices[BS_MAX_DEVICES];
+		VulkanUtility::getDevices(rapi, mDeviceMask, devices);
+
+		// Allocate buffers per-device
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (devices[i] == nullptr)
+				continue;
+
+			mImages[i] = createImage(*devices[i], false, isReadable);
+		}
+
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Texture);
 		TextureCore::initialize();
 	}
 
-	VkImageView VulkanTextureCore::getView(UINT32 deviceIdx)
+	VulkanImage* VulkanTextureCore::createImage(VulkanDevice& device, bool staging, bool readable)
 	{
-		// TODO
-		// - If device idx doesn't match the mask, return VK_NULL_HANDLE
-		// - Otherwise return the default image view (created by default in initialize())
-		// - Free the view in destructor
+		VkBufferUsageFlags usage = mImageCI.usage;
+		if (staging)
+		{
+			mImageCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
 
-		return VK_NULL_HANDLE;
+			// Staging buffers are used as a destination for reads
+			if (readable)
+				mImageCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+		}
+		else if (readable) // Non-staging readable
+			mImageCI.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+		bool directlyMappable = (getProperties().getUsage() & TU_DYNAMIC) != 0;
+		VkMemoryPropertyFlags flags = (directlyMappable || staging) ?
+			(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) : // Note: Try using cached memory
+			VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+		VkDevice vkDevice = device.getLogical();
+
+		VkImage image;
+		VkResult result = vkCreateImage(vkDevice, &mImageCI, gVulkanAllocator, &image);
+		assert(result == VK_SUCCESS);
+
+		VkMemoryRequirements memReqs;
+		vkGetImageMemoryRequirements(vkDevice, image, &memReqs);
+
+		VkDeviceMemory memory = device.allocateMemory(memReqs, flags);
+		result = vkBindImageMemory(vkDevice, image, memory, 0);
+		assert(result == VK_SUCCESS);
+
+		mImageCI.usage = usage; // Restore original usage
+		return device.getResourceManager().create<VulkanImage>(image, memory, VK_IMAGE_LAYOUT_UNDEFINED, getProperties());
 	}
 
-	VkImageView VulkanTextureCore::getView(UINT32 deviceIdx, const TextureSurface& surface)
+	VkImageView VulkanTextureCore::getView(UINT32 deviceIdx) const
 	{
-		// TODO
-		// - If device idx doesn't match the mask, return VK_NULL_HANDLE
-		// - Otherwise
-		//  - Scan the mTextureViews array for an existing view matching the surface
-		//  - If view isn't found create a new one and add it to mTextureViews array
-		//   - Resize the mTextureViews array as needed
-		//   - By default mTextureViews is nullptr, so allocate it during first call
-		// - Free the views in destructor (if any were allocated)
-
-		return VK_NULL_HANDLE;
+		if (mImages[deviceIdx] == nullptr)
+			return VK_NULL_HANDLE;
+
+		return mImages[deviceIdx]->getView();
+	}
+
+	VkImageView VulkanTextureCore::getView(UINT32 deviceIdx, const TextureSurface& surface) const
+	{
+		if (mImages[deviceIdx] == nullptr)
+			return VK_NULL_HANDLE;
+
+		return mImages[deviceIdx]->getView(surface);
 	}
 
 	void VulkanTextureCore::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel, const SPtr<TextureCore>& target)
 	{
-		
+		// TODO - Handle resolve here as well
 	}
 
 	PixelData VulkanTextureCore::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face)
@@ -95,7 +286,7 @@ namespace BansheeEngine
 
 	void VulkanTextureCore::readData(PixelData& dest, UINT32 mipLevel, UINT32 face)
 	{
-		BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_GpuBuffer);
+		BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
 	}
 
 	void VulkanTextureCore::writeData(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardWholeBuffer)

+ 1 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

@@ -200,6 +200,7 @@ namespace BansheeEngine
 	{
 		switch(numSamples)
 		{
+		case 0:
 		case 1:
 			return VK_SAMPLE_COUNT_1_BIT;
 		case 2:
@@ -216,7 +217,6 @@ namespace BansheeEngine
 			return VK_SAMPLE_COUNT_64_BIT;
 		}
 
-		BS_EXCEPT(RenderingAPIException, "Unsupported sample count: " + toString(numSamples));
 		return VK_SAMPLE_COUNT_1_BIT;
 	}