vulkan: garbage collect unused objects

This commit is contained in:
niki
2022-09-18 02:57:29 +02:00
parent cf099946a1
commit bf6915ae73
2 changed files with 109 additions and 35 deletions
+104 -34
View File
@@ -56,7 +56,9 @@ static const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
}; };
constexpr int MAX_FRAMES_IN_FLIGHT = 2; constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
constexpr uint32_t USAGES_POLL_INTERVAL = 5000;
const char *Graphics::getName() const const char *Graphics::getName() const
{ {
@@ -81,8 +83,6 @@ Graphics::Graphics()
volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr()); volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr());
vulkanApiVersion = volkGetInstanceVersion(); vulkanApiVersion = volkGetInstanceVersion();
enableValidationLayers = isDebugEnabled();
} }
Graphics::~Graphics() Graphics::~Graphics()
@@ -452,6 +452,7 @@ void Graphics::present(void *screenshotCallbackdata)
updatePendingReadbacks(); updatePendingReadbacks();
updateTemporaryResources(); updateTemporaryResources();
frameCounter++;
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
beginFrame(); beginFrame();
@@ -532,6 +533,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
Vulkan::resetShaderSwitches(); Vulkan::resetShaderSwitches();
frameCounter = 0;
currentFrame = 0; currentFrame = 0;
created = true; created = true;
drawCalls = 0; drawCalls = 0;
@@ -593,6 +595,10 @@ void Graphics::getAPIStats(int &shaderswitches) const
void Graphics::unSetMode() void Graphics::unSetMode()
{ {
renderPassUsages.clear();
framebufferUsages.clear();
pipelineUsages.clear();
created = false; created = false;
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
Volatile::unloadAll(); Volatile::unloadAll();
@@ -667,30 +673,39 @@ Graphics::RendererInfo Graphics::getRendererInfo() const
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
Graphics::RendererInfo info; Graphics::RendererInfo info;
if (isDebugEnabled())
{
std::stringstream ss;
ss << "Vulkan( ";
ss << renderPasses.size() << " ";
ss << framebuffers.size() << " ";
ss << graphicsPipelines.size() << " ";
if (optionalDeviceFeatures.extendedDynamicState)
ss << "eds ";
if (optionalDeviceFeatures.memoryRequirements2)
ss << "mr2 ";
if (optionalDeviceFeatures.dedicatedAllocation)
ss << "da ";
if (optionalDeviceFeatures.bufferDeviceAddress)
ss << "bda ";
if (optionalDeviceFeatures.memoryBudget)
ss << "mb ";
if (optionalDeviceFeatures.shaderFloatControls)
ss << "sfc ";
if (optionalDeviceFeatures.spirv14)
ss << "spv14 ";
ss << ")";
info.name = ss.str();
}
else
info.name = "Vulkan";
info.device = deviceProperties.deviceName; info.device = deviceProperties.deviceName;
info.vendor = Vulkan::getVendorName(deviceProperties.vendorID); info.vendor = Vulkan::getVendorName(deviceProperties.vendorID);
info.version = Vulkan::getVulkanApiVersion(deviceProperties.apiVersion); info.version = Vulkan::getVulkanApiVersion(deviceProperties.apiVersion);
std::stringstream ss;
ss << "Vulkan( ";
if (optionalDeviceFeatures.extendedDynamicState)
ss << "eds ";
if (optionalDeviceFeatures.memoryRequirements2)
ss << "mr2 ";
if (optionalDeviceFeatures.dedicatedAllocation)
ss << "da ";
if (optionalDeviceFeatures.bufferDeviceAddress)
ss << "bda ";
if (optionalDeviceFeatures.memoryBudget)
ss << "mb ";
if (optionalDeviceFeatures.shaderFloatControls)
ss << "sfc ";
if (optionalDeviceFeatures.spirv14)
ss << "spv14 ";
ss << ")";
info.name = ss.str();
return info; return info;
} }
@@ -1080,6 +1095,13 @@ void Graphics::beginFrame()
{ {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
if (frameCounter >= USAGES_POLL_INTERVAL)
{
vkDeviceWaitIdle(device);
cleanupUnusedObjects();
frameCounter = 0;
}
while (true) while (true)
{ {
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
@@ -1271,7 +1293,7 @@ static void checkOptionalInstanceExtensions(OptionalInstanceExtensions &ext)
void Graphics::createVulkanInstance() void Graphics::createVulkanInstance()
{ {
if (enableValidationLayers && !checkValidationSupport()) if (isDebugEnabled() && !checkValidationSupport())
throw love::Exception("validation layers requested, but not available"); throw love::Exception("validation layers requested, but not available");
VkApplicationInfo appInfo{}; VkApplicationInfo appInfo{};
@@ -1312,7 +1334,7 @@ void Graphics::createVulkanInstance()
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data(); createInfo.ppEnabledExtensionNames = extensions.data();
if (enableValidationLayers) if (isDebugEnabled())
{ {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data(); createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -1602,7 +1624,7 @@ void Graphics::createLogicalDevice()
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size()); createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size());
createInfo.ppEnabledExtensionNames = enabledExtensions.data(); createInfo.ppEnabledExtensionNames = enabledExtensions.data();
if (enableValidationLayers) if (isDebugEnabled())
{ {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data(); createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -1677,6 +1699,12 @@ void Graphics::initVMA()
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
allocatorCreateInfo.flags |= VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
if (optionalDeviceFeatures.dedicatedAllocation)
allocatorCreateInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
if (optionalDeviceFeatures.memoryBudget)
allocatorCreateInfo.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS) if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS)
throw love::Exception("failed to create vma allocator"); throw love::Exception("failed to create vma allocator");
} }
@@ -2029,15 +2057,20 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio
VkFramebuffer Graphics::getFramebuffer(FramebufferConfiguration &configuration) VkFramebuffer Graphics::getFramebuffer(FramebufferConfiguration &configuration)
{ {
VkFramebuffer framebuffer;
auto it = framebuffers.find(configuration); auto it = framebuffers.find(configuration);
if (it != framebuffers.end()) if (it != framebuffers.end())
return it->second; framebuffer = it->second;
else else
{ {
VkFramebuffer framebuffer = createFramebuffer(configuration); framebuffer = createFramebuffer(configuration);
framebuffers[configuration] = framebuffer; framebuffers[configuration] = framebuffer;
return framebuffer; }
}
framebufferUsages[framebuffer] = true;
return framebuffer;
} }
void Graphics::createDefaultShaders() void Graphics::createDefaultShaders()
@@ -2409,6 +2442,8 @@ void Graphics::startRenderPass()
} }
renderPassState.beginInfo.renderPass = renderPass; renderPassState.beginInfo.renderPass = renderPass;
renderPassUsages[renderPass] = true;
auto &framebufferConfiguration = renderPassState.framebufferConfiguration; auto &framebufferConfiguration = renderPassState.framebufferConfiguration;
framebufferConfiguration.staticData.renderPass = renderPass; framebufferConfiguration.staticData.renderPass = renderPass;
renderPassState.beginInfo.framebuffer = getFramebuffer(framebufferConfiguration); renderPassState.beginInfo.framebuffer = getFramebuffer(framebufferConfiguration);
@@ -2468,6 +2503,38 @@ VkSampler Graphics::createSampler(const SamplerState &samplerState)
return sampler; return sampler;
} }
template<typename Configuration, typename ObjectHandle, typename ConfigurationHasher, typename Deleter>
static void eraseUnusedObjects(
std::unordered_map<Configuration, ObjectHandle, ConfigurationHasher> &objects,
std::unordered_map<ObjectHandle, bool> &usages,
Deleter deleter,
VkDevice device)
{
std::vector<Configuration> deletionKeys;
for (const auto &[key, val] : objects)
{
if (!usages[val])
{
deletionKeys.push_back(key);
usages.erase(val);
deleter(device, val, nullptr);
}
else
usages[val] = false;
}
for (const auto &key : deletionKeys)
objects.erase(key);
}
void Graphics::cleanupUnusedObjects()
{
eraseUnusedObjects(renderPasses, renderPassUsages, vkDestroyRenderPass, device);
eraseUnusedObjects(framebuffers, framebufferUsages, vkDestroyFramebuffer, device);
eraseUnusedObjects(graphicsPipelines, pipelineUsages, vkDestroyPipeline, device);
}
void Graphics::setComputeShader(Shader *shader) void Graphics::setComputeShader(Shader *shader)
{ {
computeShader = shader; computeShader = shader;
@@ -2653,6 +2720,7 @@ void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration
{ {
vkCmdBindPipeline(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS, it->second); vkCmdBindPipeline(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS, it->second);
renderPassState.pipeline = it->second; renderPassState.pipeline = it->second;
pipelineUsages[it->second] = true;
} }
} }
else else
@@ -2661,6 +2729,7 @@ void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration
graphicsPipelines.insert({configuration, pipeline}); graphicsPipelines.insert({configuration, pipeline});
vkCmdBindPipeline(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindPipeline(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
renderPassState.pipeline = pipeline; renderPassState.pipeline = pipeline;
pipelineUsages[pipeline] = true;
} }
} }
@@ -2887,8 +2956,12 @@ void Graphics::cleanup()
for (const auto &[key, val] : renderPasses) for (const auto &[key, val] : renderPasses)
vkDestroyRenderPass(device, val, nullptr); vkDestroyRenderPass(device, val, nullptr);
renderPasses.clear();
// fixme: maybe we should clean up some pipelines if they haven't been used in a while. for (const auto &[key, val] : framebuffers)
vkDestroyFramebuffer(device, val, nullptr);
framebuffers.clear();
for (auto const &p : graphicsPipelines) for (auto const &p : graphicsPipelines)
vkDestroyPipeline(device, p.second, nullptr); vkDestroyPipeline(device, p.second, nullptr);
graphicsPipelines.clear(); graphicsPipelines.clear();
@@ -2913,9 +2986,6 @@ void Graphics::cleanupSwapChain()
vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation); vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation);
vkDestroyImageView(device, depthImageView, nullptr); vkDestroyImageView(device, depthImageView, nullptr);
vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation); vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation);
for (const auto &[key, val] : framebuffers)
vkDestroyFramebuffer(device, val, nullptr);
framebuffers.clear();
for (const auto &swapChainImageView : swapChainImageViews) for (const auto &swapChainImageView : swapChainImageViews)
vkDestroyImageView(device, swapChainImageView, nullptr); vkDestroyImageView(device, swapChainImageView, nullptr);
swapChainImageViews.clear(); swapChainImageViews.clear();
+5 -1
View File
@@ -389,9 +389,9 @@ private:
void startRenderPass(); void startRenderPass();
void endRenderPass(); void endRenderPass();
VkSampler createSampler(const SamplerState &samplerState); VkSampler createSampler(const SamplerState &samplerState);
void cleanupUnusedObjects();
uint32_t vulkanApiVersion = VK_VERSION_1_0; uint32_t vulkanApiVersion = VK_VERSION_1_0;
bool enableValidationLayers = false;
VkInstance instance = VK_NULL_HANDLE; VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
int requestedMsaa = 0; int requestedMsaa = 0;
@@ -420,6 +420,9 @@ private:
std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses; std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers; std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers;
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines; std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
std::unordered_map<VkRenderPass, bool> renderPassUsages;
std::unordered_map<VkFramebuffer, bool> framebufferUsages;
std::unordered_map<VkPipeline, bool> pipelineUsages;
std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers; std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers;
VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers; std::vector<VkCommandBuffer> commandBuffers;
@@ -430,6 +433,7 @@ private:
std::vector<VkFence> imagesInFlight; std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment = 0; VkDeviceSize minUniformBufferOffsetAlignment = 0;
bool imageRequested = false; bool imageRequested = false;
uint32_t frameCounter = 0;
size_t currentFrame = 0; size_t currentFrame = 0;
uint32_t imageIndex = 0; uint32_t imageIndex = 0;
bool framebufferResized = false; bool framebufferResized = false;