diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index e11012bc8f..b8e484321f 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -242,6 +242,7 @@ + @@ -313,6 +314,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index e5bc85c777..351cfd7608 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -67,6 +67,9 @@ Vulkan + + Vulkan + @@ -121,6 +124,9 @@ Vulkan + + Vulkan + diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index ac601ccd30..048f8f66ed 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -42,6 +42,8 @@ static const char *validationLayers[] = { */ }; +static VkBool32 CheckLayers(const std::vector &layer_props, const std::vector &layer_names); + VulkanContext::VulkanContext(const char *app_name, int app_ver, uint32_t flags) : device_(nullptr), gfx_queue_(VK_NULL_HANDLE), @@ -581,7 +583,7 @@ VkResult VulkanContext::InitDeviceLayerProperties() { * Return 1 (true) if all layer names specified in check_names * can be found in given layer properties. */ -VkBool32 CheckLayers(const std::vector &layer_props, const std::vector &layer_names) { +static VkBool32 CheckLayers(const std::vector &layer_props, const std::vector &layer_names) { uint32_t check_count = (uint32_t)layer_names.size(); uint32_t layer_count = (uint32_t)layer_props.size(); for (uint32_t i = 0; i < check_count; i++) { @@ -1188,361 +1190,6 @@ void VulkanContext::InitCommandPool() { assert(res == VK_SUCCESS); } -VkResult VulkanTexture::Create(int w, int h, VkFormat format) { - tex_width = w; - tex_height = h; - format_ = format; - - VkFormatProperties formatProps; - vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps); - - // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data. - // Linear tiling is usually only supported for 2D non-array textures. - // needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; - // Always stage. - needStaging = true; - - return VK_SUCCESS; -} - -void VulkanTexture::CreateMappableImage() { - // If we already have a mappableImage, forget it. - if (mappableImage) { - vulkan_->Delete().QueueDeleteImage(mappableImage); - mappableImage = VK_NULL_HANDLE; - } - if (mappableMemory) { - vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory); - mappableMemory = VK_NULL_HANDLE; - } - - bool U_ASSERT_ONLY pass; - - VkImageCreateInfo image_create_info = {}; - image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_create_info.pNext = NULL; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = format_; - image_create_info.extent.width = tex_width; - image_create_info.extent.height = tex_height; - image_create_info.extent.depth = 1; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = 1; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.tiling = VK_IMAGE_TILING_LINEAR; - image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; - image_create_info.queueFamilyIndexCount = 0; - image_create_info.pQueueFamilyIndices = NULL; - image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_create_info.flags = 0; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; - - VkMemoryAllocateInfo mem_alloc = {}; - mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_alloc.pNext = NULL; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; - - // Create a mappable image. It will be the texture if linear images are ok to be textures - // or it will be the staging image if they are not. - VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(vulkan_->GetDevice(), mappableImage, &mem_reqs); - assert(res == VK_SUCCESS); - - mem_alloc.allocationSize = mem_reqs.size; - - // Find the memory type that is host mappable. - pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mappableMemory); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0); - assert(res == VK_SUCCESS); -} - -uint8_t *VulkanTexture::Lock(int level, int *rowPitch) { - CreateMappableImage(); - - VkImageSubresource subres = {}; - subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subres.mipLevel = 0; - subres.arrayLayer = 0; - - VkSubresourceLayout layout; - void *data; - - // Get the subresource layout so we know what the row pitch is - vkGetImageSubresourceLayout(vulkan_->GetDevice(), mappableImage, &subres, &layout); - VkResult res = vkMapMemory(vulkan_->GetDevice(), mappableMemory, layout.offset, layout.size, 0, &data); - assert(res == VK_SUCCESS); - - *rowPitch = (int)layout.rowPitch; - return (uint8_t *)data; -} - -void VulkanTexture::Unlock() { - vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); - - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - - // if we already have an image, queue it for destruction and forget it. - Wipe(); - if (!needStaging) { - // If we can use the linear tiled image as a texture, just do it - image = mappableImage; - mem = mappableMemory; - TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - // Make sure we don't accidentally delete the main image. - mappableImage = VK_NULL_HANDLE; - mappableMemory = VK_NULL_HANDLE; - } else { - VkImageCreateInfo image_create_info = {}; - image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_create_info.pNext = NULL; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = format_; - image_create_info.extent.width = tex_width; - image_create_info.extent.height = tex_height; - image_create_info.extent.depth = 1; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = 1; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.queueFamilyIndexCount = 0; - image_create_info.pQueueFamilyIndices = NULL; - image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_create_info.flags = 0; - // The mappable image cannot be our texture, so create an optimally tiled image and blit to it - image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); - - VkMemoryAllocateInfo mem_alloc = {}; - mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_alloc.pNext = NULL; - mem_alloc.memoryTypeIndex = 0; - mem_alloc.allocationSize = mem_reqs.size; - - // Find memory type - don't specify any mapping requirements - bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0); - assert(res == VK_SUCCESS); - - // Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL - TransitionImageLayout(cmd, mappableImage, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - TransitionImageLayout(cmd, image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - VkImageCopy copy_region; - copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.srcSubresource.mipLevel = 0; - copy_region.srcSubresource.baseArrayLayer = 0; - copy_region.srcSubresource.layerCount = 1; - copy_region.srcOffset.x = 0; - copy_region.srcOffset.y = 0; - copy_region.srcOffset.z = 0; - copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.dstSubresource.mipLevel = 0; - copy_region.dstSubresource.baseArrayLayer = 0; - copy_region.dstSubresource.layerCount = 1; - copy_region.dstOffset.x = 0; - copy_region.dstOffset.y = 0; - copy_region.dstOffset.z = 0; - copy_region.extent.width = tex_width; - copy_region.extent.height = tex_height; - copy_region.extent.depth = 1; - - // Put the copy command into the command buffer - vkCmdCopyImage(cmd, - mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ©_region); - - assert(res == VK_SUCCESS); - - // Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY - TransitionImageLayout(cmd, image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - // Then drop the temporary mappable image - although should not be necessary... - vulkan_->Delete().QueueDeleteImage(mappableImage); - vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory); - - mappableImage = VK_NULL_HANDLE; - mappableMemory = VK_NULL_HANDLE; - } - - VkImageViewCreateInfo view_info = {}; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.pNext = NULL; - view_info.image = VK_NULL_HANDLE; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = format_; - view_info.components.r = VK_COMPONENT_SWIZZLE_R; - view_info.components.g = VK_COMPONENT_SWIZZLE_G; - view_info.components.b = VK_COMPONENT_SWIZZLE_B; - view_info.components.a = VK_COMPONENT_SWIZZLE_A; - view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = 1; - view_info.subresourceRange.baseArrayLayer = 0; - view_info.subresourceRange.layerCount = 1; - view_info.image = image; - VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view); - assert(res == VK_SUCCESS); -} - -void VulkanTexture::Wipe() { - if (image) { - vulkan_->Delete().QueueDeleteImage(image); - image = VK_NULL_HANDLE; - } - if (view) { - vulkan_->Delete().QueueDeleteImageView(view); - view = VK_NULL_HANDLE; - } - if (mem) { - vulkan_->Delete().QueueDeleteDeviceMemory(mem); - mem = VK_NULL_HANDLE; - } -} - -void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format) { - Wipe(); - - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - - tex_width = w; - tex_height = h; - numMips_ = numMips; - format_ = format; - - VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = format_; - image_create_info.extent.width = tex_width; - image_create_info.extent.height = tex_height; - image_create_info.extent.depth = 1; - image_create_info.mipLevels = numMips; - image_create_info.arrayLayers = 1; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.flags = 0; - image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); - VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - mem_alloc.memoryTypeIndex = 0; - mem_alloc.allocationSize = mem_reqs.size; - - // Find memory type - don't specify any mapping requirements - bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0); - assert(res == VK_SUCCESS); - - // Since we're going to blit to the target, set its layout to TRANSFER_DST - TransitionImageLayout(cmd, image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // Create the view while we're at it. - VkImageViewCreateInfo view_info = {}; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.pNext = NULL; - view_info.image = VK_NULL_HANDLE; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = format_; - view_info.components.r = VK_COMPONENT_SWIZZLE_R; - view_info.components.g = VK_COMPONENT_SWIZZLE_G; - view_info.components.b = VK_COMPONENT_SWIZZLE_B; - view_info.components.a = VK_COMPONENT_SWIZZLE_A; - view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = numMips; - view_info.subresourceRange.baseArrayLayer = 0; - view_info.subresourceRange.layerCount = 1; - view_info.image = image; - res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view); - assert(res == VK_SUCCESS); -} - -void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength) { - VkBufferImageCopy copy_region = {}; - copy_region.bufferOffset = offset; - copy_region.bufferRowLength = (uint32_t)rowLength; - copy_region.bufferImageHeight = 0; // 2D - copy_region.imageExtent.width = mipWidth; - copy_region.imageExtent.height = mipHeight; - copy_region.imageExtent.depth = 1; - copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.imageSubresource.mipLevel = mip; - copy_region.imageSubresource.baseArrayLayer = 0; - copy_region.imageSubresource.layerCount = 1; - - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); -} - -void VulkanTexture::EndCreate() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - TransitionImageLayout(cmd, image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void VulkanTexture::Destroy() { - if (view) { - vulkan_->Delete().QueueDeleteImageView(view); - } - if (image) { - vulkan_->Delete().QueueDeleteImage(image); - if (mappableImage == image) { - mappableImage = VK_NULL_HANDLE; - } - } - if (mem) { - vulkan_->Delete().QueueDeleteDeviceMemory(mem); - if (mappableMemory == mem) { - mappableMemory = VK_NULL_HANDLE; - } - } - - view = VK_NULL_HANDLE; - image = VK_NULL_HANDLE; - mem = VK_NULL_HANDLE; -} - VkFence VulkanContext::CreateFence(bool presignalled) { VkFence fence; VkFenceCreateInfo fenceInfo; @@ -1889,22 +1536,3 @@ const char *VulkanResultToString(VkResult res) { void VulkanAssertImpl(VkResult check, const char *function, const char *file, int line) { const char *error = "(none)"; } - -void VulkanFramebuffer::Create(VulkanContext *vulkan, int w, int h, VkFormat format) { - -} -// void TransitionToImage() - -void VulkanFramebuffer::BeginPass(VkCommandBuffer cmd) { - -} -void VulkanFramebuffer::EndPass(VkCommandBuffer cmd) { - -} -void VulkanFramebuffer::TransitionToTexture(VkCommandBuffer cmd) { - -} - -VkImageView VulkanFramebuffer::GetColorImageView() { - return VK_NULL_HANDLE; -} diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 3a4982175b..e6eda63762 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -340,71 +340,6 @@ private: std::vector cmdQueue_; }; -// Wrapper around what you need to use a texture. -// Not very optimal - if you have many small textures you should use other strategies. -// Only supports simple 2D textures for now. Mipmap support will be added later. -class VulkanTexture { -public: - VulkanTexture(VulkanContext *vulkan) - : vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE), - tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED), - mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), needStaging(false) { - memset(&mem_reqs, 0, sizeof(mem_reqs)); - } - ~VulkanTexture() { - Destroy(); - } - - // Simple usage - no cleverness, no mipmaps. - // Always call Create, Lock, Unlock. Unlock performs the upload if necessary. - // Can later Lock and Unlock again. This cannot change the format. Create cannot - // be called a second time without recreating the texture object until Destroy has - // been called. - VkResult Create(int w, int h, VkFormat format); - uint8_t *Lock(int level, int *rowPitch); - void Unlock(); - - // Fast uploads from buffer. Mipmaps supported. - void CreateDirect(int w, int h, int numMips, VkFormat format); - void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength); // rowLength is in pixels - void EndCreate(); - int GetNumMips() const { return numMips_; } - void Destroy(); - - VkImageView GetImageView() const { return view; } - -private: - void CreateMappableImage(); - void Wipe(); - VulkanContext *vulkan_; - VkImage image; - VkDeviceMemory mem; - VkImageView view; - int32_t tex_width, tex_height, numMips_; - VkFormat format_; - VkImage mappableImage; - VkDeviceMemory mappableMemory; - VkMemoryRequirements mem_reqs; - bool needStaging; -}; - -// Placeholder - -class VulkanFramebuffer { -public: - void Create(VulkanContext *vulkan, int w, int h, VkFormat format); - - void BeginPass(VkCommandBuffer cmd); - void EndPass(VkCommandBuffer cmd); - void TransitionToTexture(VkCommandBuffer cmd); - - VkImageView GetColorImageView(); - -private: - VkImage image_; - VkFramebuffer framebuffer_; -}; - // Use these to push vertex, index and uniform data. Generally you'll have two of these // and alternate on each frame. // TODO: Make it possible to suballocate pushbuffers from a large DeviceMemory block. @@ -505,16 +440,8 @@ private: uint8_t *writePtr_; }; - -VkBool32 CheckLayers(const std::vector &layer_props, const std::vector &layer_names); - +// Stand-alone utility functions void VulkanBeginCommandBuffer(VkCommandBuffer cmd); - -void init_glslang(); -void finalize_glslang(); - -bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector &spirv, std::string *errorMessage = nullptr); - void TransitionImageLayout( VkCommandBuffer cmd, VkImage image, @@ -522,5 +449,10 @@ void TransitionImageLayout( VkImageLayout old_image_layout, VkImageLayout new_image_layout); +// GLSL compiler +void init_glslang(); +void finalize_glslang(); +bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector &spirv, std::string *errorMessage = nullptr); + #endif // UTIL_INIT diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp new file mode 100644 index 0000000000..c2cd59e6c5 --- /dev/null +++ b/Common/Vulkan/VulkanImage.cpp @@ -0,0 +1,356 @@ +#include "Common/Vulkan/VulkanImage.h" + +VkResult VulkanTexture::Create(int w, int h, VkFormat format) { + tex_width = w; + tex_height = h; + format_ = format; + + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps); + + // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data. + // Linear tiling is usually only supported for 2D non-array textures. + // needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; + // Always stage. + needStaging = true; + + return VK_SUCCESS; +} + +void VulkanTexture::CreateMappableImage() { + // If we already have a mappableImage, forget it. + if (mappableImage) { + vulkan_->Delete().QueueDeleteImage(mappableImage); + mappableImage = VK_NULL_HANDLE; + } + if (mappableMemory) { + vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory); + mappableMemory = VK_NULL_HANDLE; + } + + bool U_ASSERT_ONLY pass; + + VkImageCreateInfo image_create_info = {}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = format_; + image_create_info.extent.width = tex_width; + image_create_info.extent.height = tex_height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_LINEAR; + image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = NULL; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.flags = 0; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + VkMemoryAllocateInfo mem_alloc = {}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + // Create a mappable image. It will be the texture if linear images are ok to be textures + // or it will be the staging image if they are not. + VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(vulkan_->GetDevice(), mappableImage, &mem_reqs); + assert(res == VK_SUCCESS); + + mem_alloc.allocationSize = mem_reqs.size; + + // Find the memory type that is host mappable. + pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mappableMemory); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0); + assert(res == VK_SUCCESS); +} + +uint8_t *VulkanTexture::Lock(int level, int *rowPitch) { + CreateMappableImage(); + + VkImageSubresource subres = {}; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.arrayLayer = 0; + + VkSubresourceLayout layout; + void *data; + + // Get the subresource layout so we know what the row pitch is + vkGetImageSubresourceLayout(vulkan_->GetDevice(), mappableImage, &subres, &layout); + VkResult res = vkMapMemory(vulkan_->GetDevice(), mappableMemory, layout.offset, layout.size, 0, &data); + assert(res == VK_SUCCESS); + + *rowPitch = (int)layout.rowPitch; + return (uint8_t *)data; +} + +void VulkanTexture::Unlock() { + vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); + + VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); + + // if we already have an image, queue it for destruction and forget it. + Wipe(); + if (!needStaging) { + // If we can use the linear tiled image as a texture, just do it + image = mappableImage; + mem = mappableMemory; + TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + // Make sure we don't accidentally delete the main image. + mappableImage = VK_NULL_HANDLE; + mappableMemory = VK_NULL_HANDLE; + } else { + VkImageCreateInfo image_create_info = {}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = format_; + image_create_info.extent.width = tex_width; + image_create_info.extent.height = tex_height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = NULL; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.flags = 0; + // The mappable image cannot be our texture, so create an optimally tiled image and blit to it + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); + + VkMemoryAllocateInfo mem_alloc = {}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.memoryTypeIndex = 0; + mem_alloc.allocationSize = mem_reqs.size; + + // Find memory type - don't specify any mapping requirements + bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0); + assert(res == VK_SUCCESS); + + // Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL + TransitionImageLayout(cmd, mappableImage, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PREINITIALIZED, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + TransitionImageLayout(cmd, image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkImageCopy copy_region; + copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.srcSubresource.mipLevel = 0; + copy_region.srcSubresource.baseArrayLayer = 0; + copy_region.srcSubresource.layerCount = 1; + copy_region.srcOffset.x = 0; + copy_region.srcOffset.y = 0; + copy_region.srcOffset.z = 0; + copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.dstSubresource.mipLevel = 0; + copy_region.dstSubresource.baseArrayLayer = 0; + copy_region.dstSubresource.layerCount = 1; + copy_region.dstOffset.x = 0; + copy_region.dstOffset.y = 0; + copy_region.dstOffset.z = 0; + copy_region.extent.width = tex_width; + copy_region.extent.height = tex_height; + copy_region.extent.depth = 1; + + // Put the copy command into the command buffer + vkCmdCopyImage(cmd, + mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©_region); + + assert(res == VK_SUCCESS); + + // Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY + TransitionImageLayout(cmd, image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Then drop the temporary mappable image - although should not be necessary... + vulkan_->Delete().QueueDeleteImage(mappableImage); + vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory); + + mappableImage = VK_NULL_HANDLE; + mappableMemory = VK_NULL_HANDLE; + } + + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = NULL; + view_info.image = VK_NULL_HANDLE; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = format_; + view_info.components.r = VK_COMPONENT_SWIZZLE_R; + view_info.components.g = VK_COMPONENT_SWIZZLE_G; + view_info.components.b = VK_COMPONENT_SWIZZLE_B; + view_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + view_info.image = image; + VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view); + assert(res == VK_SUCCESS); +} + +void VulkanTexture::Wipe() { + if (image) { + vulkan_->Delete().QueueDeleteImage(image); + image = VK_NULL_HANDLE; + } + if (view) { + vulkan_->Delete().QueueDeleteImageView(view); + view = VK_NULL_HANDLE; + } + if (mem) { + vulkan_->Delete().QueueDeleteDeviceMemory(mem); + mem = VK_NULL_HANDLE; + } +} + +void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage) { + Wipe(); + + VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); + + tex_width = w; + tex_height = h; + numMips_ = numMips; + format_ = format; + + VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = format_; + image_create_info.extent.width = tex_width; + image_create_info.extent.height = tex_height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = numMips; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.flags = 0; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); + VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + mem_alloc.memoryTypeIndex = 0; + mem_alloc.allocationSize = mem_reqs.size; + + // Find memory type - don't specify any mapping requirements + bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0); + assert(res == VK_SUCCESS); + + // Since we're going to blit to the target, set its layout to TRANSFER_DST + TransitionImageLayout(cmd, image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Create the view while we're at it. + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = NULL; + view_info.image = VK_NULL_HANDLE; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = format_; + view_info.components.r = VK_COMPONENT_SWIZZLE_R; + view_info.components.g = VK_COMPONENT_SWIZZLE_G; + view_info.components.b = VK_COMPONENT_SWIZZLE_B; + view_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = numMips; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + view_info.image = image; + res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view); + assert(res == VK_SUCCESS); +} + +void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength) { + VkBufferImageCopy copy_region = {}; + copy_region.bufferOffset = offset; + copy_region.bufferRowLength = (uint32_t)rowLength; + copy_region.bufferImageHeight = 0; // 2D + copy_region.imageExtent.width = mipWidth; + copy_region.imageExtent.height = mipHeight; + copy_region.imageExtent.depth = 1; + copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.imageSubresource.mipLevel = mip; + copy_region.imageSubresource.baseArrayLayer = 0; + copy_region.imageSubresource.layerCount = 1; + + VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); + vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); +} + +void VulkanTexture::EndCreate() { + VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); + TransitionImageLayout(cmd, image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +void VulkanTexture::Destroy() { + if (view) { + vulkan_->Delete().QueueDeleteImageView(view); + } + if (image) { + vulkan_->Delete().QueueDeleteImage(image); + if (mappableImage == image) { + mappableImage = VK_NULL_HANDLE; + } + } + if (mem) { + vulkan_->Delete().QueueDeleteDeviceMemory(mem); + if (mappableMemory == mem) { + mappableMemory = VK_NULL_HANDLE; + } + } + + view = VK_NULL_HANDLE; + image = VK_NULL_HANDLE; + mem = VK_NULL_HANDLE; +} diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h new file mode 100644 index 0000000000..482f52f98c --- /dev/null +++ b/Common/Vulkan/VulkanImage.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Common/Vulkan/VulkanContext.h" + +// Wrapper around what you need to use a texture. +// Not very optimal - if you have many small textures you should use other strategies. +class VulkanTexture { +public: + VulkanTexture(VulkanContext *vulkan) + : vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE), + tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED), + mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), needStaging(false) { + memset(&mem_reqs, 0, sizeof(mem_reqs)); + } + ~VulkanTexture() { + Destroy(); + } + + // Simple usage - no cleverness, no mipmaps. + // Always call Create, Lock, Unlock. Unlock performs the upload if necessary. + // Can later Lock and Unlock again. This cannot change the format. Create cannot + // be called a second time without recreating the texture object until Destroy has + // been called. + VkResult Create(int w, int h, VkFormat format); + uint8_t *Lock(int level, int *rowPitch); + void Unlock(); + + // Fast uploads from buffer. Mipmaps supported. Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. + void CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, size_t offset, size_t rowLength); // rowLength is in pixels + void EndCreate(); + int GetNumMips() const { return numMips_; } + void Destroy(); + + VkImageView GetImageView() const { return view; } + +private: + void CreateMappableImage(); + void Wipe(); + VulkanContext *vulkan_; + VkImage image; + VkDeviceMemory mem; + VkImageView view; + int32_t tex_width, tex_height, numMips_; + VkFormat format_; + VkImage mappableImage; + VkDeviceMemory mappableMemory; + VkMemoryRequirements mem_reqs; + bool needStaging; +}; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index f9ebd1e3ad..4456e3461c 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -51,7 +51,7 @@ namespace DX9 { struct FBO_DX9; } -class VulkanFramebuffer; +class VulkanFBO; struct VirtualFramebuffer { int last_frame_used; @@ -87,12 +87,13 @@ struct VirtualFramebuffer { int lastFrameNewSize; GEBufferFormat format; // virtual, right now they are all RGBA8888 + // TODO: Handle fbo and colorDepth better. u8 colorDepth; union { FBO *fbo; DX9::FBO_DX9 *fbo_dx9; - VulkanFramebuffer *fbo_vk; + VulkanFBO *fbo_vk; }; u16 drawnWidth; diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 69dbe4da53..14ed122bae 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -106,8 +106,7 @@ inline int RoundUp4(int x) { } // Reads decoded vertex formats in a convenient way. For software transform and debugging. -class VertexReader -{ +class VertexReader { public: VertexReader(u8 *base, const DecVtxFormat &decFmt, int vtype) : base_(base), data_(base), decFmt_(decFmt), vtype_(vtype) {} diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 1f21022378..cfb441cc35 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -345,6 +345,7 @@ + @@ -354,4 +355,4 @@ - + \ No newline at end of file diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 21183c6e72..7558d33b67 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -458,5 +458,6 @@ Vulkan + \ No newline at end of file diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 14f17ba6a8..74c1c94ec0 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -41,10 +41,12 @@ #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#include "Common/Vulkan/VulkanImage.h" #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" +#include "GPU/Vulkan/VulkanUtil.h" #include "UI/OnScreenDisplay.h" @@ -599,7 +601,7 @@ void FramebufferManagerVulkan::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, return; } - vfb->fbo_vk = new VulkanFramebuffer(); + vfb->fbo_vk = new VulkanFBO(); // bo_create(vfb->renderWidth, vfb->renderHeight, 1, true, (FBOColorDepth)vfb->colorDepth); if (old.fbo_vk) { INFO_LOG(SCEGE, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, w, h, vfb->format); @@ -777,7 +779,7 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir }*/ } -VulkanFramebuffer *FramebufferManagerVulkan::GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth) { +VulkanFBO *FramebufferManagerVulkan::GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth) { u64 key = ((u64)depth << 32) | ((u32)w << 16) | h; auto it = tempFBOs_.find(key); if (it != tempFBOs_.end()) { @@ -787,7 +789,7 @@ VulkanFramebuffer *FramebufferManagerVulkan::GetTempFBO(u16 w, u16 h, VulkanFBOC textureCache_->ForgetLastTexture(); // FBO *fbo_vk = fbo_create(w, h, 1, false, depth); - VulkanFramebuffer *fbo_vk = new VulkanFramebuffer(); + VulkanFBO *fbo_vk = new VulkanFBO(); if (!fbo_vk) return nullptr; // fbo_bind_as_render_target(fbo_vk); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 510714458e..0b93fabef0 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -36,13 +36,13 @@ class DrawEngineVulkan; class VulkanContext; class ShaderManagerVulkan; class VulkanTexture; -class VulkanFramebuffer; struct PostShaderUniforms { float texelDelta[2]; float pad[2]; float pixelDelta[2]; float pad0[2]; float time[4]; }; + // Simple struct for asynchronous PBO readbacks // TODO: Probably will need a complete redesign. struct AsyncPBOVulkan { @@ -122,7 +122,7 @@ public: virtual void RebindFramebuffer() override; - VulkanFramebuffer *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888); + VulkanFBO *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888); // Cardboard Settings Calculator struct CardboardSettings * GetCardboardSettings(struct CardboardSettings * cardboardSettings); @@ -182,7 +182,7 @@ private: bool resized_; struct TempFBO { - VulkanFramebuffer *fbo_vk; + VulkanFBO *fbo_vk; int last_frame_used; }; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index bcdc0eaa64..e6672b4fcc 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -1,5 +1,4 @@ -#pragma once -// Copyright (c) 2012- PPSSPP Project. +// Copyright (c) 2016- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 0cef7827de..655a12c05d 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -22,13 +22,16 @@ #include "i18n/i18n.h" #include "math/math_util.h" #include "profiler/profiler.h" -#include "Common/Vulkan/VulkanContext.h" #include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/Reporting.h" #include "Core/System.h" + +#include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanImage.h" + #include "GPU/ge_constants.h" #include "GPU/GPUState.h" #include "GPU/Vulkan/TextureCacheVulkan.h" @@ -81,6 +84,10 @@ SamplerCache::~SamplerCache() { } } +CachedTextureVulkan::~CachedTextureVulkan() { + delete texture_; +} + VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) { auto iter = cache_.find(key); if (iter != cache_.end()) { @@ -905,9 +912,9 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VkCommandBuffer cmd, TexCacheEn } if (depal) { // VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - VulkanFramebuffer *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888); + VulkanFBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888); - depalFBO->BeginPass(cmd); + //depalFBO->BeginPass(cmd); struct Pos { Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { @@ -1018,9 +1025,9 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VkCommandBuffer cmd, TexCacheEn glDisableVertexAttribArray(depal->a_position); glDisableVertexAttribArray(depal->a_texcoord0); */ - depalFBO->EndPass(cmd); - depalFBO->TransitionToTexture(cmd); - imageView = depalFBO->GetColorImageView(); + //depalFBO->EndPass(cmd); + //depalFBO->TransitionToTexture(cmd); + //imageView = depalFBO->GetColorImageView(); } /* diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 0d732f480d..0d256ba3a9 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -33,7 +33,7 @@ class ShaderManagerVulkan; class DrawEngineVulkan; class VulkanContext; -class VulkanImage; +class VulkanTexture; struct SamplerCacheKey { SamplerCacheKey() : fullKey(0) {} @@ -61,9 +61,8 @@ class CachedTextureVulkan { public: CachedTextureVulkan() : texture_(nullptr) { } - ~CachedTextureVulkan() { - delete texture_; - } + ~CachedTextureVulkan(); + // TODO: Switch away from VulkanImage to some kind of smart suballocating texture pool. VulkanTexture *texture_; }; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp new file mode 100644 index 0000000000..a8a39ccdd9 --- /dev/null +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2016- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "GPU/Vulkan/VulkanUtil.h" + +VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {} + +VulkanFBO::~VulkanFBO() { + delete color_; + delete depthStencil_; +} + +void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { + color_ = new VulkanTexture(vulkan); + VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + + VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; + + VkFramebufferCreateInfo fb = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + fb.pAttachments = views; + fb.attachmentCount = 2; + fb.flags = 0; + fb.renderPass = rp_compatible; + fb.width = width; + fb.height = height; + fb.layers = 1; + + vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_); +} diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index fe41a2b8e0..61a4160359 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -1,4 +1,48 @@ +// Copyright (c) 2016- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + #pragma once #include "Common/Vulkan/VulkanLoader.h" +#include "Common/Vulkan/VulkanImage.h" +// Vulkan doesn't really have the concept of an FBO that owns the images, +// but it does have the concept of a framebuffer as a set of attachments. +// VulkanFBO is an approximation of the FBO concept the other backends use +// to make things as similar as possible without being suboptimal. +// +class VulkanFBO { +public: + VulkanFBO(); + ~VulkanFBO(); + + // Depth-format is chosen automatically depending on hardware support. + // Color format will be 32-bit RGBA. + void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); + + VulkanTexture *GetColor() { return color_; } + VulkanTexture *GetDepthStencil() { return depthStencil_; } + + VkFramebuffer GetFramebuffer() { return framebuffer_; } + +private: + VulkanTexture *color_; + VulkanTexture *depthStencil_; + + // This point specifically to color and depth. + VkFramebuffer framebuffer_; +}; \ No newline at end of file diff --git a/android/jni/Android.mk b/android/jni/Android.mk index c752482e2d..45d2f9e0f7 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -127,6 +127,7 @@ EGL_FILES := \ VULKAN_FILES := \ $(SRC)/Common/Vulkan/VulkanLoader.cpp \ $(SRC)/Common/Vulkan/VulkanContext.cpp \ + $(SRC)/Common/Vulkan/VulkanImage.cpp \ $(SRC)/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp \ $(SRC)/GPU/Vulkan/DrawEngineVulkan.cpp \ $(SRC)/GPU/Vulkan/FramebufferVulkan.cpp \ @@ -137,7 +138,8 @@ VULKAN_FILES := \ $(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureScalerVulkan.cpp \ $(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \ - $(SRC)/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp + $(SRC)/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp \ + $(SRC)/GPU/Vulkan/VulkanUtil.cpp #endif EXEC_AND_LIB_FILES := \ diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index f547e58cb0..6a9585c538 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -30,7 +30,9 @@ #include "math/lin/matrix4x4.h" #include "math/dataconv.h" #include "thin3d/thin3d.h" + #include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanImage.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data