diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 4e988922c5..2bdf069582 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -437,6 +437,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 4cee6365b7..32c392cf6a 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -409,6 +409,9 @@ ext\vma + + GPU\Vulkan + diff --git a/Common/GPU/Vulkan/VulkanAlloc.h b/Common/GPU/Vulkan/VulkanAlloc.h new file mode 100644 index 0000000000..dd81c07df8 --- /dev/null +++ b/Common/GPU/Vulkan/VulkanAlloc.h @@ -0,0 +1,17 @@ +#pragma once + +#ifdef USE_CRT_DBG +#undef new +#endif + +#include "Common/GPU/Vulkan/VulkanLoader.h" + +using namespace PPSSPP_VK; + +#undef VK_NO_PROTOTYPES +#include "ext/vma/vk_mem_alloc.h" +#define VK_NO_PROTOTYPES + +#ifdef USE_CRT_DBG +#define new DBG_NEW +#endif diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index d416bb371f..3621322cea 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -283,7 +283,7 @@ void VulkanContext::DestroyInstance() { void VulkanContext::BeginFrame() { FrameData *frame = &frame_[curFrame_]; // Process pending deletes. - frame->deleteList.PerformDeletes(device_); + frame->deleteList.PerformDeletes(device_, allocator_); } void VulkanContext::EndFrame() { @@ -665,9 +665,7 @@ VkResult VulkanContext::CreateDevice() { allocatorInfo.physicalDevice = physical_devices_[physical_device_]; allocatorInfo.device = device_; allocatorInfo.instance = instance_; - - VmaAllocator allocator; - vmaCreateAllocator(&allocatorInfo, &allocator); + vmaCreateAllocator(&allocatorInfo, &allocator_); return res; } @@ -1099,9 +1097,9 @@ VkFence VulkanContext::CreateFence(bool presignalled) { void VulkanContext::PerformPendingDeletes() { for (int i = 0; i < ARRAY_SIZE(frame_); i++) { - frame_[i].deleteList.PerformDeletes(device_); + frame_[i].deleteList.PerformDeletes(device_, allocator_); } - Delete().PerformDeletes(device_); + Delete().PerformDeletes(device_, allocator_); } void VulkanContext::DestroyDevice() { @@ -1343,7 +1341,7 @@ void VulkanDeleteList::Take(VulkanDeleteList &del) { del.callbacks_.clear(); } -void VulkanDeleteList::PerformDeletes(VkDevice device) { +void VulkanDeleteList::PerformDeletes(VkDevice device, VmaAllocator allocator) { for (auto &callback : callbacks_) { callback.func(callback.userdata); } @@ -1372,6 +1370,10 @@ void VulkanDeleteList::PerformDeletes(VkDevice device) { vkDestroyImage(device, image, nullptr); } images_.clear(); + for (auto &imageWithAlloc : imagesWithAllocs_) { + vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc); + } + imagesWithAllocs_.clear(); for (auto &imageView : imageViews_) { vkDestroyImageView(device, imageView, nullptr); } diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index e0b2c7ee91..232161ba3b 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -8,6 +8,7 @@ #include "Common/Log.h" #include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/GPU/Vulkan/VulkanDebug.h" +#include "Common/GPU/Vulkan/VulkanAlloc.h" enum { VULKAN_FLAG_VALIDATE = 1, @@ -27,6 +28,7 @@ enum { }; VK_DEFINE_HANDLE(VmaAllocator); +VK_DEFINE_HANDLE(VmaAllocation); std::string VulkanVendorString(uint32_t vendorId); @@ -62,6 +64,11 @@ struct VulkanPhysicalDeviceInfo { // This is a bit repetitive... class VulkanDeleteList { + struct ImageWithAlloc { + VkImage image; + VmaAllocation alloc; + }; + struct Callback { explicit Callback(void(*f)(void *userdata), void *u) : func(f), userdata(u) { @@ -79,6 +86,7 @@ public: void QueueDeleteBuffer(VkBuffer &buffer) { _dbg_assert_(buffer != VK_NULL_HANDLE); buffers_.push_back(buffer); buffer = VK_NULL_HANDLE; } void QueueDeleteBufferView(VkBufferView &bufferView) { _dbg_assert_(bufferView != VK_NULL_HANDLE); bufferViews_.push_back(bufferView); bufferView = VK_NULL_HANDLE; } void QueueDeleteImage(VkImage &image) { _dbg_assert_(image != VK_NULL_HANDLE); images_.push_back(image); image = VK_NULL_HANDLE; } + void QueueDeleteImageAllocation(VkImage &image, VmaAllocation &alloc) { _dbg_assert_(image != VK_NULL_HANDLE && alloc != VK_NULL_HANDLE); imagesWithAllocs_.push_back(ImageWithAlloc{ image, alloc }); image = VK_NULL_HANDLE; alloc = VK_NULL_HANDLE; } void QueueDeleteImageView(VkImageView &imageView) { _dbg_assert_(imageView != VK_NULL_HANDLE); imageViews_.push_back(imageView); imageView = VK_NULL_HANDLE; } void QueueDeleteDeviceMemory(VkDeviceMemory &deviceMemory) { _dbg_assert_(deviceMemory != VK_NULL_HANDLE); deviceMemory_.push_back(deviceMemory); deviceMemory = VK_NULL_HANDLE; } void QueueDeleteSampler(VkSampler &sampler) { _dbg_assert_(sampler != VK_NULL_HANDLE); samplers_.push_back(sampler); sampler = VK_NULL_HANDLE; } @@ -91,7 +99,7 @@ public: void QueueCallback(void(*func)(void *userdata), void *userdata) { callbacks_.push_back(Callback(func, userdata)); } void Take(VulkanDeleteList &del); - void PerformDeletes(VkDevice device); + void PerformDeletes(VkDevice device, VmaAllocator allocator); private: std::vector cmdPools_; @@ -100,6 +108,7 @@ private: std::vector buffers_; std::vector bufferViews_; std::vector images_; + std::vector imagesWithAllocs_; std::vector imageViews_; std::vector deviceMemory_; std::vector samplers_; @@ -279,6 +288,10 @@ public: void GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation); + VmaAllocator Allocator() const { + return allocator_; + } + private: bool ChooseQueue(); diff --git a/Common/GPU/Vulkan/VulkanImage.cpp b/Common/GPU/Vulkan/VulkanImage.cpp index e9ddee5f3b..3a6cd2ce88 100644 --- a/Common/GPU/Vulkan/VulkanImage.cpp +++ b/Common/GPU/Vulkan/VulkanImage.cpp @@ -1,24 +1,19 @@ #include #include "Common/Log.h" - +#include "Common/GPU/Vulkan/VulkanAlloc.h" #include "Common/GPU/Vulkan/VulkanImage.h" #include "Common/GPU/Vulkan/VulkanMemory.h" using namespace PPSSPP_VK; void VulkanTexture::Wipe() { - if (image_) { - vulkan_->Delete().QueueDeleteImage(image_); - } - if (view_) { + if (view_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteImageView(view_); } - if (mem_ && !allocator_) { - vulkan_->Delete().QueueDeleteDeviceMemory(mem_); - } else if (mem_) { - allocator_->Free(mem_, offset_); - mem_ = VK_NULL_HANDLE; + if (image_ != VK_NULL_HANDLE) { + _dbg_assert_(allocation_ != VK_NULL_HANDLE); + vulkan_->Delete().QueueDeleteImageAllocation(image_, allocation_); } } @@ -35,7 +30,7 @@ static bool IsDepthStencilFormat(VkFormat format) { } } -bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *allocator, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { +bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { if (w == 0 || h == 0 || numMips == 0) { ERROR_LOG(G3D, "Can't create a zero-size VulkanTexture"); return false; @@ -73,63 +68,13 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *all if (vulkan_->GetFlags() & VULKAN_FLAG_VALIDATE) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } - - VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image_); - if (res != VK_SUCCESS) { - _assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS); - ERROR_LOG(G3D, "vkCreateImage failed: %s", VulkanResultToString(res)); - return false; - } + VmaAllocationCreateInfo allocCreateInfo{}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + VmaAllocationInfo allocInfo{}; + VkResult res = vmaCreateImage(vulkan_->Allocator(), &image_create_info, &allocCreateInfo, &image_, &allocation_, &allocInfo); // Apply the tag - vulkan_ ->SetDebugName(image_, VK_OBJECT_TYPE_IMAGE, tag_.c_str()); - - VkMemoryRequirements mem_reqs{}; - bool dedicatedAllocation = false; - vulkan_->GetImageMemoryRequirements(image_, &mem_reqs, &dedicatedAllocation); - - if (allocator && !dedicatedAllocation) { - allocator_ = allocator; - // ok to use the tag like this, because the lifetime of the VulkanImage exceeds that of the allocation. - offset_ = allocator_->Allocate(mem_reqs, &mem_, Tag().c_str()); - if (offset_ == VulkanDeviceAllocator::ALLOCATE_FAILED) { - ERROR_LOG(G3D, "Image memory allocation failed (mem_reqs.size=%d, typebits=%08x", (int)mem_reqs.size, (int)mem_reqs.memoryTypeBits); - // Destructor will take care of the image. - return false; - } - } else { - VkMemoryAllocateInfo mem_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - mem_alloc.memoryTypeIndex = 0; - mem_alloc.allocationSize = mem_reqs.size; - - VkMemoryDedicatedAllocateInfoKHR dedicatedAllocateInfo{VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR}; - if (dedicatedAllocation) { - dedicatedAllocateInfo.image = image_; - mem_alloc.pNext = &dedicatedAllocateInfo; - } - - // 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_); - if (res != VK_SUCCESS) { - ERROR_LOG(G3D, "vkAllocateMemory failed: %s", VulkanResultToString(res)); - _assert_msg_(res != VK_ERROR_TOO_MANY_OBJECTS, "Too many Vulkan memory objects!"); - _assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS); - return false; - } - - offset_ = 0; - } - - res = vkBindImageMemory(vulkan_->GetDevice(), image_, mem_, offset_); - if (res != VK_SUCCESS) { - ERROR_LOG(G3D, "vkBindImageMemory failed: %s", VulkanResultToString(res)); - // This leaks the image and memory. Should not really happen though... - _assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS); - return false; - } + vulkan_->SetDebugName(image_, VK_OBJECT_TYPE_IMAGE, tag_.c_str()); // Write a command to transition the image to the requested layout, if it's not already that layout. if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) { @@ -268,12 +213,6 @@ void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelin prevStage == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ? VK_ACCESS_SHADER_WRITE_BIT : VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } -void VulkanTexture::Touch() { - if (allocator_ && mem_ != VK_NULL_HANDLE) { - allocator_->Touch(mem_, offset_); - } -} - VkImageView VulkanTexture::CreateViewForMip(int mip) { VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; view_info.image = image_; @@ -299,15 +238,7 @@ void VulkanTexture::Destroy() { vulkan_->Delete().QueueDeleteImageView(view_); } if (image_ != VK_NULL_HANDLE) { - vulkan_->Delete().QueueDeleteImage(image_); - } - if (mem_ != VK_NULL_HANDLE) { - if (allocator_) { - allocator_->Free(mem_, offset_); - mem_ = VK_NULL_HANDLE; - allocator_ = nullptr; - } else { - vulkan_->Delete().QueueDeleteDeviceMemory(mem_); - } + _dbg_assert_(allocation_ != VK_NULL_HANDLE); + vulkan_->Delete().QueueDeleteImageAllocation(image_, allocation_); } } diff --git a/Common/GPU/Vulkan/VulkanImage.h b/Common/GPU/Vulkan/VulkanImage.h index 6d45c7ac63..356a517494 100644 --- a/Common/GPU/Vulkan/VulkanImage.h +++ b/Common/GPU/Vulkan/VulkanImage.h @@ -4,6 +4,8 @@ class VulkanDeviceAllocator; +VK_DEFINE_HANDLE(VmaAllocation); + // Wrapper around what you need to use a texture. // ALWAYS use an allocator when calling CreateDirect. class VulkanTexture { @@ -18,7 +20,7 @@ public: // Fast uploads from buffer. Mipmaps supported. // Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. // When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. - bool CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *allocator, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); + bool CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); void ClearMip(VkCommandBuffer cmd, int mip, uint32_t value); void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels @@ -37,7 +39,7 @@ public: const std::string &Tag() const { return tag_; } - void Touch(); + void Touch() {} // Used in image copies, etc. VkImage GetImage() const { return image_; } @@ -56,12 +58,12 @@ private: VulkanContext *vulkan_; VkImage image_ = VK_NULL_HANDLE; VkImageView view_ = VK_NULL_HANDLE; - VkDeviceMemory mem_ = VK_NULL_HANDLE; + VmaAllocation allocation_; + int32_t width_ = 0; int32_t height_ = 0; int32_t numMips_ = 1; VkFormat format_ = VK_FORMAT_UNDEFINED; - VulkanDeviceAllocator *allocator_ = nullptr; // If set, memory is from this allocator. size_t offset_ = 0; std::string tag_; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 45f1eef7a0..9bc29bd3bb 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -319,7 +319,7 @@ class VKTexture : public Texture { public: VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, VulkanPushBuffer *pushBuffer, const TextureDesc &desc) : vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) {} - bool Create(VkCommandBuffer cmd, VulkanPushBuffer *pushBuffer, const TextureDesc &desc, VulkanDeviceAllocator *alloc); + bool Create(VkCommandBuffer cmd, VulkanPushBuffer *pushBuffer, const TextureDesc &desc); ~VKTexture() { Destroy(); @@ -510,8 +510,6 @@ private: VulkanRenderManager renderManager_; - VulkanDeviceAllocator *allocator_ = nullptr; - VulkanTexture *nullTexture_ = nullptr; AutoRef curPipeline_; @@ -640,7 +638,7 @@ VulkanTexture *VKContext::GetNullTexture() { nullTexture_->SetTag("Null"); int w = 8; int h = 8; - nullTexture_->CreateDirect(cmdInit, allocator_, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); uint32_t bindOffset; VkBuffer bindBuf; @@ -707,7 +705,7 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const TextureDesc &desc, VulkanDeviceAllocator *alloc) { +bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const TextureDesc &desc) { // Zero-sized textures not allowed. _assert_(desc.width * desc.height * desc.depth > 0); // remember to set depth to 1! if (desc.width * desc.height * desc.depth <= 0) { @@ -733,7 +731,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur usageBits |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } - if (!vkTex_->CreateDirect(cmd, alloc, width_, height_, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits)) { + if (!vkTex_->CreateDirect(cmd, width_, height_, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits)) { ERROR_LOG(G3D, "Failed to create VulkanTexture: %dx%dx%d fmt %d, %d levels", width_, height_, depth_, (int)vulkanFormat, mipLevels_); return false; } @@ -878,19 +876,10 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) _assert_(VK_SUCCESS == res); renderManager_.SetSplitSubmit(splitSubmit); - - allocator_ = new VulkanDeviceAllocator(vulkan_, 256 * 1024, 2048 * 1024); } VKContext::~VKContext() { delete nullTexture_; - allocator_->Destroy(); - // We have to delete on queue, so this can free its queued deletions. - vulkan_->Delete().QueueCallback([](void *ptr) { - auto allocator = static_cast(ptr); - delete allocator; - }, allocator_); - allocator_ = nullptr; // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); @@ -912,7 +901,6 @@ void VKContext::BeginFrame() { // OK, we now know that nothing is reading from this frame's data pushbuffer, push_->Reset(); push_->Begin(vulkan_); - allocator_->Begin(); frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); @@ -922,7 +910,6 @@ void VKContext::BeginFrame() { void VKContext::EndFrame() { // Stop collecting data in the frame's data pushbuffer. push_->End(); - allocator_->End(); renderManager_.Finish(); @@ -1227,7 +1214,7 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) { return nullptr; } VKTexture *tex = new VKTexture(vulkan_, initCmd, push_, desc); - if (tex->Create(initCmd, push_, desc, allocator_)) { + if (tex->Create(initCmd, push_, desc)) { return tex; } else { ERROR_LOG(G3D, "Failed to create texture"); diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 7707853bb9..a72c2af038 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -168,7 +168,7 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat VulkanTexture *vktex = new VulkanTexture(vulkan); vktex->SetTag("DepalClut"); VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - if (!vktex->CreateDirect(cmd, alloc_, texturePixels, 1, 1, destFormat, + if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &componentMapping)) { ERROR_LOG(G3D, "Failed to create texture for CLUT"); return nullptr; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index c3396e3982..d534d12fa3 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -755,7 +755,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); - bool allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, imageLayout, usage, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, imageLayout, usage, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -774,7 +774,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) {