mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-01 09:21:34 +00:00
Vulkan: Handle texture allocation failure.
Users hit out of memory even using desktop GL devices, and it will definitely be possible on mobile and desktop Vulkan.
This commit is contained in:
parent
27a5697a96
commit
16570f10bd
@ -206,10 +206,8 @@ void VulkanTexture::Unlock() {
|
||||
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;
|
||||
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
view_info.image = image;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = format_;
|
||||
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||
@ -221,7 +219,6 @@ void VulkanTexture::Unlock() {
|
||||
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);
|
||||
}
|
||||
@ -244,7 +241,7 @@ void VulkanTexture::Wipe() {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
|
||||
bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
|
||||
Wipe();
|
||||
|
||||
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
|
||||
@ -268,12 +265,18 @@ void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
|
||||
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);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
|
||||
|
||||
if (allocator_) {
|
||||
offset_ = allocator_->Allocate(mem_reqs, &mem);
|
||||
if (offset_ == VulkanDeviceAllocator::ALLOCATE_FAILED) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||
mem_alloc.memoryTypeIndex = 0;
|
||||
@ -284,13 +287,19 @@ void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
|
||||
assert(pass);
|
||||
|
||||
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
|
||||
assert(res == VK_SUCCESS);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
offset_ = 0;
|
||||
}
|
||||
|
||||
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, offset_);
|
||||
assert(res == VK_SUCCESS);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since we're going to blit to the target, set its layout to TRANSFER_DST
|
||||
TransitionImageLayout(cmd, image,
|
||||
@ -299,10 +308,8 @@ void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
|
||||
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;
|
||||
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||
view_info.image = image;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = format_;
|
||||
if (mapping) {
|
||||
@ -318,9 +325,12 @@ void VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI
|
||||
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);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
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, const VkComponentMapping *mapping = nullptr);
|
||||
bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
|
||||
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
|
||||
void EndCreate();
|
||||
int GetNumMips() const { return numMips_; }
|
||||
|
@ -1320,11 +1320,8 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) {
|
||||
|
||||
VkFormat actualFmt = scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt;
|
||||
|
||||
if (replaceImages) {
|
||||
if (!entry->vkTex) {
|
||||
Crash();
|
||||
}
|
||||
} else {
|
||||
if (!replaceImages || !entry->vkTex) {
|
||||
delete entry->vkTex;
|
||||
entry->vkTex = new CachedTextureVulkan();
|
||||
entry->vkTex->texture_ = new VulkanTexture(vulkan_, allocator_);
|
||||
VulkanTexture *image = entry->vkTex->texture_;
|
||||
@ -1348,22 +1345,49 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) {
|
||||
break;
|
||||
}
|
||||
|
||||
image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
bool allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
if (!allocSuccess && !lowMemoryMode_) {
|
||||
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
|
||||
lowMemoryMode_ = true;
|
||||
decimationCounter_ = 0;
|
||||
Decimate();
|
||||
// TODO: We should stall the GPU here and wipe things out of memory.
|
||||
// As is, it will almost definitely fail the second time, but next frame it may recover.
|
||||
|
||||
I18NCategory *err = GetI18NCategory("Error");
|
||||
if (scaleFactor > 1) {
|
||||
osm.Show(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
|
||||
} else {
|
||||
osm.Show(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
|
||||
}
|
||||
|
||||
scaleFactor = 1;
|
||||
actualFmt = dstFmt;
|
||||
|
||||
allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
}
|
||||
|
||||
if (!allocSuccess) {
|
||||
delete entry->vkTex;
|
||||
entry->vkTex = nullptr;
|
||||
}
|
||||
}
|
||||
lastBoundTexture = entry->vkTex;
|
||||
|
||||
// Upload the texture data.
|
||||
for (int i = 0; i <= maxLevel; i++) {
|
||||
int mipWidth = gstate.getTextureWidth(i) * scaleFactor;
|
||||
int mipHeight = gstate.getTextureHeight(i) * scaleFactor;
|
||||
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2;
|
||||
int stride = (mipWidth * bpp + 15) & ~15;
|
||||
int size = stride * mipHeight;
|
||||
uint32_t bufferOffset;
|
||||
VkBuffer texBuf;
|
||||
void *data = uploadBuffer->Push(size, &bufferOffset, &texBuf);
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, i, replaceImages, scaleFactor, dstFmt);
|
||||
entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
if (entry->vkTex) {
|
||||
// Upload the texture data.
|
||||
for (int i = 0; i <= maxLevel; i++) {
|
||||
int mipWidth = gstate.getTextureWidth(i) * scaleFactor;
|
||||
int mipHeight = gstate.getTextureHeight(i) * scaleFactor;
|
||||
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2;
|
||||
int stride = (mipWidth * bpp + 15) & ~15;
|
||||
int size = stride * mipHeight;
|
||||
uint32_t bufferOffset;
|
||||
VkBuffer texBuf;
|
||||
void *data = uploadBuffer->Push(size, &bufferOffset, &texBuf);
|
||||
LoadTextureLevel(*entry, (uint8_t *)data, stride, i, replaceImages, scaleFactor, dstFmt);
|
||||
entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
}
|
||||
}
|
||||
|
||||
gstate_c.textureFullAlpha = entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL;
|
||||
@ -1658,29 +1682,4 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt
|
||||
memcpy(writePtr + rowPitch * y, (const uint8_t *)pixelData + decPitch * y, rowBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (!lowMemoryMode_) {
|
||||
GLenum err = glGetError();
|
||||
if (err == GL_OUT_OF_MEMORY) {
|
||||
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
|
||||
lowMemoryMode_ = true;
|
||||
decimationCounter_ = 0;
|
||||
Decimate();
|
||||
// TODO: We need to stall the GPU here and wipe things out of memory.
|
||||
|
||||
// Try again, now that we've cleared out textures in lowMemoryMode_.
|
||||
glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData);
|
||||
|
||||
I18NCategory *err = GetI18NCategory("Error");
|
||||
if (scaleFactor > 1) {
|
||||
osm.Show(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
|
||||
} else {
|
||||
osm.Show(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
|
||||
}
|
||||
} else if (err != GL_NO_ERROR) {
|
||||
// We checked the err anyway, might as well log if there is one.
|
||||
WARN_LOG(G3D, "Got an error in texture upload: %08x", err);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user