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) {