Use a VulkanDeviceAllocator for thin3d textures. Many devices have a hard limit on the total number of allocs and it's unnecessary to have the UI put pressure on that.

This commit is contained in:
Henrik Rydgård 2017-12-03 10:29:41 +01:00
parent 97bdc72d90
commit d0c248368d
9 changed files with 36 additions and 12 deletions

View File

@ -1,5 +1,6 @@
#include "Common/Vulkan/VulkanImage.h"
#include "Common/Vulkan/VulkanMemory.h"
#include "Common/Log.h"
VkResult VulkanTexture::Create(int w, int h, VkFormat format) {
tex_width = w;
@ -294,6 +295,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips,
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
if (res != VK_SUCCESS) {
_assert_msg_(G3D, 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;
}

View File

@ -8,7 +8,7 @@ class VulkanDeviceAllocator;
// Not very optimal - if you have many small textures you should use other strategies.
class VulkanTexture {
public:
VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator = nullptr)
VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator)
: 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),

View File

@ -146,6 +146,10 @@ public:
static const size_t ALLOCATE_FAILED = -1;
int GetBlockCount() const { return (int)slabs_.size(); }
int GetMinSlabSize() const { return (int)minSlabSize_; }
int GetMaxSlabSize() const { return (int)maxSlabSize_; }
private:
static const size_t SLAB_GRAIN_SIZE = 1024;
static const uint8_t SLAB_GRAIN_SHIFT = 10;

View File

@ -296,7 +296,7 @@ void DrawEngineVulkan::BeginFrame() {
if (!nullTexture_) {
ILOG("INIT : Creating null texture");
VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
nullTexture_ = new VulkanTexture(vulkan_);
nullTexture_ = new VulkanTexture(vulkan_, textureCache_->GetAllocator());
int w = 8;
int h = 8;
nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,

View File

@ -205,7 +205,8 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
drawPixelsTex_ = new VulkanTexture(vulkan_);
// There's only ever a few of these alive, don't need to stress the allocator with these big ones.
drawPixelsTex_ = new VulkanTexture(vulkan_, nullptr);
if (!drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) {
// out of memory?
delete drawPixelsTex_;

View File

@ -638,6 +638,8 @@ void GPU_Vulkan::DeviceRestore() {
void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) {
const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats();
char texStats[256];
textureCacheVulkan_->GetStats(texStats, sizeof(texStats));
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
snprintf(buffer, bufsize - 1,
"DL processing time: %0.2f ms\n"
@ -652,7 +654,8 @@ void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) {
"Textures active: %i, decoded: %i invalidated: %i\n"
"Readbacks: %d\n"
"Vertex, Fragment, Pipelines loaded: %i, %i, %i\n"
"Pushbuffer space used: UBO %d, Vtx %d, Idx %d\n",
"Pushbuffer space used: UBO %d, Vtx %d, Idx %d\n"
"%s\n",
gpuStats.msProcessingDisplayLists * 1000.0f,
gpuStats.numDrawCalls,
gpuStats.numFlushes,
@ -674,7 +677,8 @@ void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) {
pipelineManager_->GetNumPipelines(),
drawStats.pushUBOSpaceUsed,
drawStats.pushVertexSpaceUsed,
drawStats.pushIndexSpaceUsed
drawStats.pushIndexSpaceUsed,
texStats
);
}

View File

@ -816,6 +816,10 @@ bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int leve
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
framebufferManager_->RebindFramebuffer();
return true;
}
void TextureCacheVulkan::GetStats(char *ptr, size_t size) {
snprintf(ptr, size, "Alloc: %d blocks\nSlab min/max: %d/%d\n",
allocator_->GetBlockCount(), allocator_->GetMinSlabSize(), allocator_->GetMaxSlabSize());
}

View File

@ -107,6 +107,10 @@ public:
bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override;
void GetStats(char *ptr, size_t size);
VulkanDeviceAllocator *GetAllocator() { return allocator_; }
protected:
void BindTexture(TexCacheEntry *entry) override;
void Unbind() override;

View File

@ -312,9 +312,9 @@ struct DescriptorSetKey {
class VKTexture : public Texture {
public:
VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc)
VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc)
: vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) {
bool result = Create(cmd, desc);
bool result = Create(cmd, desc, alloc);
assert(result);
}
@ -327,7 +327,7 @@ public:
private:
void SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data);
bool Create(VkCommandBuffer cmd, const TextureDesc &desc);
bool Create(VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc);
void Destroy() {
if (vkTex_) {
@ -497,6 +497,8 @@ private:
VulkanRenderManager renderManager_;
VulkanDeviceAllocator *allocator_ = nullptr;
VKPipeline *curPipeline_ = nullptr;
VKBuffer *curVBuffers_[4]{};
int curVBufferOffsets_[4]{};
@ -657,7 +659,7 @@ enum class TextureState {
PENDING_DESTRUCTION,
};
bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) {
bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc) {
// Zero-sized textures not allowed.
if (desc.width * desc.height * desc.depth == 0)
return false;
@ -666,7 +668,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) {
width_ = desc.width;
height_ = desc.height;
depth_ = desc.depth;
vkTex_ = new VulkanTexture(vulkan_);
vkTex_ = new VulkanTexture(vulkan_, alloc);
if (desc.initData.size()) {
for (int i = 0; i < (int)desc.initData.size(); i++) {
this->SetImageData(cmd, 0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]);
@ -758,9 +760,12 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
pipelineCache_ = vulkan_->CreatePipelineCache();
renderManager_.SetSplitSubmit(splitSubmit);
allocator_ = new VulkanDeviceAllocator(vulkan_, 256 * 1024, 2048 * 1024);
}
VKContext::~VKContext() {
delete allocator_;
// This also destroys all descriptor sets.
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frame_[i].descSets_.clear();
@ -996,7 +1001,7 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
}
Texture *VKContext::CreateTexture(const TextureDesc &desc) {
return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc);
return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc, allocator_);
}
void VKTexture::SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {