Merge pull request #1979 from nikeinikei/12.0-development

vk: fix crash caused by love.window.close()
This commit is contained in:
Sasha Szpakowski
2023-10-09 13:37:30 -03:00
committed by GitHub
3 changed files with 160 additions and 138 deletions
+146 -131
View File
@@ -65,29 +65,100 @@ const char *Graphics::getName() const
return "love.graphics.vulkan"; return "love.graphics.vulkan";
} }
const VkDevice Graphics::getDevice() const VkDevice Graphics::getDevice() const
{ {
return device; return device;
} }
const VmaAllocator Graphics::getVmaAllocator() const VmaAllocator Graphics::getVmaAllocator() const
{ {
return vmaAllocator; return vmaAllocator;
} }
static void checkOptionalInstanceExtensions(OptionalInstanceExtensions& ext)
{
uint32_t count;
vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
std::vector<VkExtensionProperties> extensions(count);
vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data());
for (const auto& extension : extensions)
{
if (strcmp(extension.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)
ext.physicalDeviceProperties2 = true;
}
}
Graphics::Graphics() Graphics::Graphics()
{ {
if (SDL_Vulkan_LoadLibrary(nullptr)) if (SDL_Vulkan_LoadLibrary(nullptr))
throw love::Exception("could not find vulkan"); throw love::Exception("could not find vulkan");
volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr()); volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr());
if (isDebugEnabled() && !checkValidationSupport())
throw love::Exception("validation layers requested, but not available");
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "LOVE";
appInfo.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); // get this version from somewhere else?
appInfo.pEngineName = "LOVE Game Framework";
appInfo.engineVersion = VK_MAKE_API_VERSION(0, VERSION_MAJOR, VERSION_MINOR, VERSION_REV);
appInfo.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.pNext = nullptr;
// GetInstanceExtensions works with a null window parameter as long as
// SDL_Vulkan_LoadLibrary has been called (which we do earlier).
unsigned int count;
if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr) != SDL_TRUE)
throw love::Exception("couldn't retrieve sdl vulkan extensions");
std::vector<const char*> extensions = {};
checkOptionalInstanceExtensions(optionalInstanceExtensions);
if (optionalInstanceExtensions.physicalDeviceProperties2)
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
size_t additional_extension_count = extensions.size();
extensions.resize(additional_extension_count + count);
if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, extensions.data() + additional_extension_count) != SDL_TRUE)
throw love::Exception("couldn't retrieve sdl vulkan extensions");
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
if (isDebugEnabled())
{
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS)
throw love::Exception("couldn't create vulkan instance");
volkLoadInstance(instance);
} }
Graphics::~Graphics() Graphics::~Graphics()
{ {
defaultConstantTexCoord.set(nullptr);
defaultConstantColor.set(nullptr); defaultConstantColor.set(nullptr);
defaultTexture.set(nullptr); defaultTexture.set(nullptr);
Volatile::unloadAll();
cleanup();
vkDestroyInstance(instance, nullptr);
SDL_Vulkan_UnloadLibrary(); SDL_Vulkan_UnloadLibrary();
} }
@@ -302,14 +373,14 @@ void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
startRenderPass(); startRenderPass();
} }
void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData) void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallbackData)
{ {
flushBatchedDraws(); flushBatchedDraws();
if (renderPassState.active) if (renderPassState.active)
endRenderPass(); endRenderPass();
if (present) if (submitMode == SUBMIT_PRESENT)
{ {
if (pendingScreenshotCallbacks.empty()) if (pendingScreenshotCallbacks.empty())
Vulkan::cmdTransitionImageLayout( Vulkan::cmdTransitionImageLayout(
@@ -435,7 +506,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
VkFence fence = VK_NULL_HANDLE; VkFence fence = VK_NULL_HANDLE;
if (present) if (submitMode == SUBMIT_PRESENT)
{ {
submitInfo.signalSemaphoreCount = 1; submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores; submitInfo.pSignalSemaphores = signalSemaphores;
@@ -447,7 +518,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS) if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS)
throw love::Exception("failed to submit draw command buffer"); throw love::Exception("failed to submit draw command buffer");
if (!present) if (submitMode == SUBMIT_NOPRESENT || submitMode == SUBMIT_RESTART)
{ {
vkQueueWaitIdle(graphicsQueue); vkQueueWaitIdle(graphicsQueue);
@@ -458,7 +529,8 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
callbacks.clear(); callbacks.clear();
} }
startRecordingGraphicsCommands(); if (submitMode == SUBMIT_RESTART)
startRecordingGraphicsCommands();
} }
} }
@@ -475,7 +547,7 @@ void Graphics::present(void *screenshotCallbackdata)
deprecations.draw(this); deprecations.draw(this);
submitGpuCommands(true, screenshotCallbackdata); submitGpuCommands(SUBMIT_PRESENT, screenshotCallbackdata);
VkPresentInfoKHR presentInfo{}; VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
@@ -540,64 +612,80 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
readbackCallbacks.clear(); readbackCallbacks.clear();
readbackCallbacks.resize(MAX_FRAMES_IN_FLIGHT); readbackCallbacks.resize(MAX_FRAMES_IN_FLIGHT);
createVulkanInstance(); bool createBaseObjects = physicalDevice == VK_NULL_HANDLE;
createSurface(); createSurface();
pickPhysicalDevice();
createLogicalDevice(); if (createBaseObjects)
createPipelineCache(); {
initVMA(); pickPhysicalDevice();
initCapabilities(); createLogicalDevice();
createPipelineCache();
initVMA();
initCapabilities();
}
msaaSamples = getMsaaCount(requestedMsaa);
createSwapChain(); createSwapChain();
createImageViews(); createImageViews();
createScreenshotCallbackBuffers(); createScreenshotCallbackBuffers();
createSyncObjects();
createColorResources(); createColorResources();
createDepthResources(); createDepthResources();
transitionColorDepthLayouts = true; transitionColorDepthLayouts = true;
createCommandPool();
createCommandBuffers(); if (createBaseObjects)
{
createCommandPool();
createCommandBuffers();
createSyncObjects();
}
beginFrame(); beginFrame();
if (batchedDrawState.vb[0] == nullptr) if (createBaseObjects)
{ {
// Initial sizes that should be good enough for most cases. It will if (batchedDrawState.vb[0] == nullptr)
// resize to fit if needed, later. {
batchedDrawState.vb[0] = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); // Initial sizes that should be good enough for most cases. It will
batchedDrawState.vb[1] = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); // resize to fit if needed, later.
batchedDrawState.indexBuffer = new StreamBuffer(this, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); batchedDrawState.vb[0] = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1);
} batchedDrawState.vb[1] = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 256 * 1024 * 1);
batchedDrawState.indexBuffer = new StreamBuffer(this, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
}
// sometimes the VertexTexCoord is not set, so we manually adjust it to (0, 0) // sometimes the VertexTexCoord is not set, so we manually adjust it to (0, 0)
if (defaultConstantTexCoord == nullptr) if (defaultConstantTexCoord == nullptr)
{ {
float zeroTexCoord[2] = { 0.0f, 0.0f }; float zeroTexCoord[2] = { 0.0f, 0.0f };
Buffer::DataDeclaration format("ConstantTexCoord", DATAFORMAT_FLOAT_VEC2); Buffer::DataDeclaration format("ConstantTexCoord", DATAFORMAT_FLOAT_VEC2);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC); Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantTexCoord = newBuffer(settings, { format }, zeroTexCoord, sizeof(zeroTexCoord), 1); defaultConstantTexCoord = newBuffer(settings, { format }, zeroTexCoord, sizeof(zeroTexCoord), 1);
} }
// sometimes the VertexColor is not set, so we manually adjust it to white color // sometimes the VertexColor is not set, so we manually adjust it to white color
if (defaultConstantColor == nullptr) if (defaultConstantColor == nullptr)
{ {
uint8 whiteColor[] = { 255, 255, 255, 255 }; uint8 whiteColor[] = { 255, 255, 255, 255 };
Buffer::DataDeclaration format("ConstantColor", DATAFORMAT_UNORM8_VEC4); Buffer::DataDeclaration format("ConstantColor", DATAFORMAT_UNORM8_VEC4);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC); Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1); defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
} }
createDefaultTexture(); createDefaultTexture();
createDefaultShaders(); createDefaultShaders();
Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT]; Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT];
createQuadIndexBuffer(); createQuadIndexBuffer();
createFanIndexBuffer(); createFanIndexBuffer();
frameCounter = 0;
currentFrame = 0;
}
restoreState(states.back()); restoreState(states.back());
Vulkan::resetShaderSwitches(); Vulkan::resetShaderSwitches();
frameCounter = 0;
currentFrame = 0;
created = true; created = true;
drawCalls = 0; drawCalls = 0;
drawCallsBatched = 0; drawCallsBatched = 0;
@@ -659,14 +747,12 @@ void Graphics::getAPIStats(int &shaderswitches) const
void Graphics::unSetMode() void Graphics::unSetMode()
{ {
renderPassUsages.clear(); submitGpuCommands(SUBMIT_NOPRESENT);
framebufferUsages.clear();
pipelineUsages.clear();
created = false; created = false;
vkDeviceWaitIdle(device);
Volatile::unloadAll(); cleanupSwapChain();
cleanup(); vkDestroySurfaceKHR(instance, surface, nullptr);
} }
void Graphics::setActive(bool enable) void Graphics::setActive(bool enable)
@@ -1332,75 +1418,6 @@ const OptionalDeviceExtensions &Graphics::getEnabledOptionalDeviceExtensions() c
return optionalDeviceExtensions; return optionalDeviceExtensions;
} }
static void checkOptionalInstanceExtensions(OptionalInstanceExtensions &ext)
{
uint32_t count;
vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
std::vector<VkExtensionProperties> extensions(count);
vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data());
for (const auto &extension : extensions)
{
if (strcmp(extension.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0)
ext.physicalDeviceProperties2 = true;
}
}
void Graphics::createVulkanInstance()
{
if (isDebugEnabled() && !checkValidationSupport())
throw love::Exception("validation layers requested, but not available");
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "LOVE";
appInfo.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); // get this version from somewhere else?
appInfo.pEngineName = "LOVE Game Framework";
appInfo.engineVersion = VK_MAKE_API_VERSION(0, VERSION_MAJOR, VERSION_MINOR, VERSION_REV);
appInfo.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.pNext = nullptr;
// GetInstanceExtensions works with a null window parameter as long as
// SDL_Vulkan_LoadLibrary has been called (which we do earlier).
unsigned int count;
if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr) != SDL_TRUE)
throw love::Exception("couldn't retrieve sdl vulkan extensions");
std::vector<const char*> extensions = {};
checkOptionalInstanceExtensions(optionalInstanceExtensions);
if (optionalInstanceExtensions.physicalDeviceProperties2)
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
size_t additional_extension_count = extensions.size();
extensions.resize(additional_extension_count + count);
if (SDL_Vulkan_GetInstanceExtensions(nullptr, &count, extensions.data() + additional_extension_count) != SDL_TRUE)
throw love::Exception("couldn't retrieve sdl vulkan extensions");
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
if (isDebugEnabled())
{
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS)
throw love::Exception("couldn't create vulkan instance");
volkLoadInstance(instance);
}
bool Graphics::checkValidationSupport() bool Graphics::checkValidationSupport()
{ {
uint32_t layerCount; uint32_t layerCount;
@@ -1458,7 +1475,6 @@ void Graphics::pickPhysicalDevice()
minUniformBufferOffsetAlignment = properties.limits.minUniformBufferOffsetAlignment; minUniformBufferOffsetAlignment = properties.limits.minUniformBufferOffsetAlignment;
deviceApiVersion = properties.apiVersion; deviceApiVersion = properties.apiVersion;
msaaSamples = getMsaaCount(requestedMsaa);
depthStencilFormat = findDepthFormat(); depthStencilFormat = findDepthFormat();
} }
@@ -1573,7 +1589,7 @@ static void findOptionalDeviceExtensions(VkPhysicalDevice physicalDevice, Option
optionalDeviceExtensions.memoryRequirements2 = true; optionalDeviceExtensions.memoryRequirements2 = true;
if (strcmp(extension.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) == 0) if (strcmp(extension.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) == 0)
optionalDeviceExtensions.dedicatedAllocation = true; optionalDeviceExtensions.dedicatedAllocation = true;
if (strcmp(extension.extensionName, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) == 0) if (strcmp(extension.extensionName, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0)
optionalDeviceExtensions.memoryBudget = true; optionalDeviceExtensions.memoryBudget = true;
if (strcmp(extension.extensionName, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) == 0) if (strcmp(extension.extensionName, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) == 0)
optionalDeviceExtensions.shaderFloatControls = true; optionalDeviceExtensions.shaderFloatControls = true;
@@ -3040,8 +3056,6 @@ void Graphics::createDefaultTexture()
void Graphics::cleanup() void Graphics::cleanup()
{ {
cleanupSwapChain();
for (auto &cleanUpFns : cleanUpFunctions) for (auto &cleanUpFns : cleanUpFunctions)
for (auto &cleanUpFn : cleanUpFns) for (auto &cleanUpFn : cleanUpFns)
cleanUpFn(); cleanUpFn();
@@ -3076,8 +3090,6 @@ void Graphics::cleanup()
vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroyPipelineCache(device, pipelineCache, nullptr); vkDestroyPipelineCache(device, pipelineCache, nullptr);
vkDestroyDevice(device, nullptr); vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
} }
void Graphics::cleanupSwapChain() void Graphics::cleanupSwapChain()
@@ -3087,8 +3099,11 @@ void Graphics::cleanupSwapChain()
vmaDestroyBuffer(vmaAllocator, readbackBuffer.buffer, readbackBuffer.allocation); vmaDestroyBuffer(vmaAllocator, readbackBuffer.buffer, readbackBuffer.allocation);
vmaDestroyImage(vmaAllocator, readbackBuffer.image, readbackBuffer.imageAllocation); vmaDestroyImage(vmaAllocator, readbackBuffer.image, readbackBuffer.imageAllocation);
} }
vkDestroyImageView(device, colorImageView, nullptr); if (colorImage)
vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation); {
vkDestroyImageView(device, colorImageView, nullptr);
vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation);
}
vkDestroyImageView(device, depthImageView, nullptr); vkDestroyImageView(device, depthImageView, nullptr);
vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation); vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation);
for (const auto &swapChainImageView : swapChainImageViews) for (const auto &swapChainImageView : swapChainImageViews)
+11 -4
View File
@@ -256,6 +256,14 @@ struct ScreenshotReadbackBuffer
VmaAllocation imageAllocation; VmaAllocation imageAllocation;
}; };
enum SubmitMode
{
SUBMIT_PRESENT,
SUBMIT_NOPRESENT,
SUBMIT_RESTART,
SUBMIT_MAXENUM,
};
class Graphics final : public love::graphics::Graphics class Graphics final : public love::graphics::Graphics
{ {
public: public:
@@ -300,12 +308,12 @@ public:
// internal functions. // internal functions.
const VkDevice getDevice() const; VkDevice getDevice() const;
const VmaAllocator getVmaAllocator() const; VmaAllocator getVmaAllocator() const;
VkCommandBuffer getCommandBufferForDataTransfer(); VkCommandBuffer getCommandBufferForDataTransfer();
void queueCleanUp(std::function<void()> cleanUp); void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(std::function<void()> callback); void addReadbackCallback(std::function<void()> callback);
void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr); void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
const VkDeviceSize getMinUniformBufferOffsetAlignment() const; const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
VkSampler getCachedSampler(const SamplerState &sampler); VkSampler getCachedSampler(const SamplerState &sampler);
void setComputeShader(Shader *computeShader); void setComputeShader(Shader *computeShader);
@@ -326,7 +334,6 @@ protected:
void setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) override; void setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
private: private:
void createVulkanInstance();
bool checkValidationSupport(); bool checkValidationSupport();
void pickPhysicalDevice(); void pickPhysicalDevice();
int rateDeviceSuitability(VkPhysicalDevice device); int rateDeviceSuitability(VkPhysicalDevice device);
@@ -44,7 +44,7 @@ GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod
if (method == READBACK_IMMEDIATE) if (method == READBACK_IMMEDIATE)
{ {
vgfx->submitGpuCommands(false); vgfx->submitGpuCommands(SUBMIT_RESTART);
if (stagingBuffer.get()) { if (stagingBuffer.get()) {
status = readbackBuffer(stagingBuffer, 0, size); status = readbackBuffer(stagingBuffer, 0, size);
gfx->releaseTemporaryBuffer(stagingBuffer); gfx->releaseTemporaryBuffer(stagingBuffer);
@@ -79,7 +79,7 @@ GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod
}); });
if (method == READBACK_IMMEDIATE) if (method == READBACK_IMMEDIATE)
vgfx->submitGpuCommands(false); vgfx->submitGpuCommands(SUBMIT_RESTART);
} }
GraphicsReadback::~GraphicsReadback() GraphicsReadback::~GraphicsReadback()
@@ -89,7 +89,7 @@ GraphicsReadback::~GraphicsReadback()
void GraphicsReadback::wait() void GraphicsReadback::wait()
{ {
if (status == STATUS_WAITING) if (status == STATUS_WAITING)
vgfx->submitGpuCommands(false); vgfx->submitGpuCommands(SUBMIT_RESTART);
} }
void GraphicsReadback::update() void GraphicsReadback::update()