Plug the texture memory leak

This commit is contained in:
Henrik Rydgard 2016-01-10 11:41:46 +01:00
parent 29341e53a5
commit 3bf88d7475
7 changed files with 88 additions and 63 deletions

View file

@ -104,7 +104,7 @@ struct UB_VS_FS_Base {
};
static const char *ub_baseStr =
R"( mat4 proj_mtx;
R"( mat4 proj_mtx;
mat4 view_mtx;
mat4 world_mtx;
mat4 tex_mtx;
@ -139,15 +139,15 @@ struct UB_VS_Lights {
};
static const char *ub_vs_lightsStr =
R"(vec4 globalAmbient;
R"( vec4 globalAmbient;
vec3 matdiffuse;
vec4 matspecular;
vec3 matemissive;
vec3 pos[4];
vec3 dir[4];
vec3 att[4];
vec4 matspecular;
vec3 matemissive;
vec3 pos[4];
vec3 dir[4];
vec3 att[4];
float angle[4];
float spotCoef[4];
float spotCoef[4];
vec3 ambient[4];
vec3 diffuse[4];
vec3 specular[4];
@ -158,7 +158,7 @@ struct UB_VS_Bones {
};
static const char *ub_vs_bonesStr =
R"(mat4 m[8];
R"( mat4 m[8];
)";
class VulkanContext;

View file

@ -202,6 +202,7 @@ void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerV
// Set cull
bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
key.cullMode = wantCull ? (gstate.getCullMode() ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT) : VK_CULL_MODE_NONE;
key.cullMode = VK_CULL_MODE_NONE;
// Depth Test
if (gstate.isDepthTestEnabled()) {
@ -213,6 +214,8 @@ void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerV
}
} else {
key.depthTestEnable = false;
key.depthWriteEnable = false;
key.depthCompareOp = VK_COMPARE_OP_ALWAYS;
}
// PSP color/alpha mask is per bit but we can only support per byte.

View file

@ -1446,9 +1446,10 @@ void TextureCacheVulkan::SetTexture() {
}
} else {
entry->vkTex = new CachedTextureVulkan();
entry->vkTex->texture_ = new VulkanTexture();
entry->vkTex->texture_ = new VulkanTexture(vulkan_);
VulkanTexture *image = entry->vkTex->texture_;
image->Create(vulkan_, w, h, dstFmt);
VkResult res = image->Create(w, h, dstFmt);
assert(res == VK_SUCCESS);
}
lastBoundTexture = entry->vkTex;
@ -1786,11 +1787,11 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, int level, bool
// Upload the texture data. TODO: Decode directly into this buffer.
int rowPitch;
uint8_t *writePtr = entry.vkTex->texture_->Lock(vulkan_, level, &rowPitch);
uint8_t *writePtr = entry.vkTex->texture_->Lock(level, &rowPitch);
for (int y = 0; y < h; y++) {
memcpy(writePtr + rowPitch * y, (const uint8_t *)pixelData + decPitch * y, rowBytes);
}
entry.vkTex->texture_->Unlock(vulkan_);
entry.vkTex->texture_->Unlock();
/*
if (!lowMemoryMode_) {

View file

@ -138,11 +138,11 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer) {
// are present and what parts aren't, but we will not be ultra detailed about it.
WRITE(p, "\n");
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s\n} base;\n", ub_baseStr);
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr);
if (enableLighting || doShadeMapping)
WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s\n} light;\n", ub_vs_lightsStr);
WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr);
if (enableBones)
WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s\n} bone;\n", ub_vs_bonesStr);
WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr);
const char *shading = doFlatShading ? "flat " : "";

View file

@ -1131,13 +1131,13 @@ void VulkanContext::InitCommandPool() {
assert(res == VK_SUCCESS);
}
VkResult VulkanTexture::Create(VulkanContext *vulkan, int w, int h, VkFormat format) {
VkResult VulkanTexture::Create(int w, int h, VkFormat format) {
tex_width = w;
tex_height = h;
format_ = format;
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(vulkan->GetPhysicalDevice(), format, &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.
@ -1148,14 +1148,14 @@ VkResult VulkanTexture::Create(VulkanContext *vulkan, int w, int h, VkFormat for
return VK_SUCCESS;
}
void VulkanTexture::CreateMappableImage(VulkanContext *vulkan) {
void VulkanTexture::CreateMappableImage() {
// If we already have a mappableImage, forget it.
if (mappableImage) {
vulkan->QueueDelete(mappableImage);
vulkan_->QueueDelete(mappableImage);
mappableImage = nullptr;
}
if (mappableMemory) {
vulkan->QueueDelete(mappableMemory);
vulkan_->QueueDelete(mappableMemory);
mappableMemory = nullptr;
}
@ -1187,27 +1187,27 @@ void VulkanTexture::CreateMappableImage(VulkanContext *vulkan) {
// 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);
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage);
assert(res == VK_SUCCESS);
vkGetImageMemoryRequirements(vulkan->GetDevice(), mappableImage, &mem_reqs);
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);
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));
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &(mappableMemory));
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan->GetDevice(), mappableImage, mappableMemory, 0);
res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0);
assert(res == VK_SUCCESS);
}
uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int level, int *rowPitch) {
CreateMappableImage(vulkan);
uint8_t *VulkanTexture::Lock(int level, int *rowPitch) {
CreateMappableImage();
VkImageSubresource subres = {};
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@ -1218,28 +1218,32 @@ uint8_t *VulkanTexture::Lock(VulkanContext *vulkan, int level, int *rowPitch) {
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);
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(VulkanContext *vulkan) {
vkUnmapMemory(vulkan->GetDevice(), mappableMemory);
void VulkanTexture::Unlock() {
vkUnmapMemory(vulkan_->GetDevice(), mappableMemory);
VkCommandBuffer cmd = vulkan->GetInitCommandBuffer();
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
// if we already have an image, queue it for destruction and forget it.
if (image) {
vulkan->QueueDelete(image);
vulkan_->QueueDelete(image);
image = nullptr;
}
if (view) {
vulkan->QueueDelete(view);
vulkan_->QueueDelete(view);
view = nullptr;
}
if (mem) {
vulkan_->QueueDelete(mem);
mem = nullptr;
}
if (!needStaging) {
/* If we can use the linear tiled image as a texture, just do it */
image = mappableImage;
@ -1267,10 +1271,10 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
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);
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
assert(res == VK_SUCCESS);
vkGetImageMemoryRequirements(vulkan->GetDevice(), image, &mem_reqs);
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
VkMemoryAllocateInfo mem_alloc = {};
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
@ -1279,13 +1283,13 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
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);
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);
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan->GetDevice(), image, mem, 0);
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
@ -1334,8 +1338,8 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
imageLayout);
vulkan->QueueDelete(mappableMemory);
vulkan->QueueDelete(mappableImage);
vulkan_->QueueDelete(mappableMemory);
vulkan_->QueueDelete(mappableImage);
mappableImage = nullptr;
mappableMemory = nullptr;
}
@ -1358,18 +1362,30 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
/* create image view */
view_info.image = image;
VkResult res = vkCreateImageView(vulkan->GetDevice(), &view_info, NULL, &view);
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
assert(res == VK_SUCCESS);
}
void VulkanTexture::Destroy(VulkanContext *vulkan) {
vulkan->QueueDelete(view);
vulkan->QueueDelete(image);
vulkan->QueueDelete(mem);
void VulkanTexture::Destroy() {
if (view) {
vulkan_->QueueDelete(view);
}
if (image) {
vulkan_->QueueDelete(image);
if (mappableImage == image) {
mappableImage = nullptr;
}
}
if (mem) {
vulkan_->QueueDelete(mem);
if (mappableMemory == mem) {
mappableMemory = nullptr;
}
}
view = NULL;
image = NULL;
mem = NULL;
view = nullptr;
image = nullptr;
mem = nullptr;
}
VkFence VulkanContext::CreateFence(bool presignalled) {

View file

@ -342,25 +342,30 @@ private:
// Only supports simple 2D textures for now. Mipmap support will be added later.
class VulkanTexture {
public:
VulkanTexture()
: image(nullptr), imageLayout(VK_IMAGE_LAYOUT_UNDEFINED), mem(nullptr), view(nullptr), tex_width(0), tex_height(0), format_(VK_FORMAT_UNDEFINED),
VulkanTexture(VulkanContext *vulkan)
: vulkan_(vulkan), image(nullptr), imageLayout(VK_IMAGE_LAYOUT_UNDEFINED), mem(nullptr), view(nullptr), tex_width(0), tex_height(0), format_(VK_FORMAT_UNDEFINED),
mappableImage(nullptr), mappableMemory(nullptr), needStaging(false) {
}
~VulkanTexture() {
Destroy();
}
// 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.
// be called a second time without recreating the texture object until Destroy has
// been called.
VkResult Create(VulkanContext *vulkan, int w, int h, VkFormat format);
uint8_t *Lock(VulkanContext *vulkan, int level, int *rowPitch);
void Unlock(VulkanContext *vulkan);
VkResult Create(int w, int h, VkFormat format);
uint8_t *Lock(int level, int *rowPitch);
void Unlock();
void Destroy(VulkanContext *vulkan);
void Destroy();
VkImageView GetImageView() const { return view; }
private:
void CreateMappableImage(VulkanContext *vulkan);
void CreateMappableImage();
VulkanContext *vulkan_;
VkImage image;
VkImageLayout imageLayout;
VkDeviceMemory mem;

View file

@ -548,7 +548,7 @@ public:
width_ = width;
height_ = height;
depth_ = depth;
vkTex_ = new VulkanTexture();
vkTex_ = new VulkanTexture(vulkan_);
// We don't actually do anything here.
return true;
}
@ -563,7 +563,7 @@ private:
void Destroy() {
ILOG("texture destroyed: %p", this);
if (vkTex_) {
vkTex_->Destroy(vulkan_);
vkTex_->Destroy();
delete vkTex_;
}
}
@ -955,13 +955,13 @@ void Thin3DVKTexture::SetImageData(int x, int y, int z, int width, int height, i
int bpp;
VkFormat vulkanFormat = FormatToVulkan(format_, &bpp);
int bytesPerPixel = bpp / 8;
vkTex_->Create(vulkan_, width, height, vulkanFormat);
vkTex_->Create(width, height, vulkanFormat);
int rowPitch;
uint8_t *dstData = vkTex_->Lock(vulkan_, 0, &rowPitch);
uint8_t *dstData = vkTex_->Lock(0, &rowPitch);
for (int y = 0; y < height; y++) {
memcpy(dstData + rowPitch * y, data + stride * y, width * bytesPerPixel);
}
vkTex_->Unlock(vulkan_);
vkTex_->Unlock();
}
void Thin3DVKTexture::Finalize(int zim_flags) {