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";
}
const VkDevice Graphics::getDevice() const
VkDevice Graphics::getDevice() const
{
return device;
}
const VmaAllocator Graphics::getVmaAllocator() const
VmaAllocator Graphics::getVmaAllocator() const
{
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()
{
if (SDL_Vulkan_LoadLibrary(nullptr))
throw love::Exception("could not find vulkan");
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()
{
defaultConstantTexCoord.set(nullptr);
defaultConstantColor.set(nullptr);
defaultTexture.set(nullptr);
Volatile::unloadAll();
cleanup();
vkDestroyInstance(instance, nullptr);
SDL_Vulkan_UnloadLibrary();
}
@@ -302,14 +373,14 @@ void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
startRenderPass();
}
void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallbackData)
{
flushBatchedDraws();
if (renderPassState.active)
endRenderPass();
if (present)
if (submitMode == SUBMIT_PRESENT)
{
if (pendingScreenshotCallbacks.empty())
Vulkan::cmdTransitionImageLayout(
@@ -435,7 +506,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
VkFence fence = VK_NULL_HANDLE;
if (present)
if (submitMode == SUBMIT_PRESENT)
{
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
@@ -447,7 +518,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS)
throw love::Exception("failed to submit draw command buffer");
if (!present)
if (submitMode == SUBMIT_NOPRESENT || submitMode == SUBMIT_RESTART)
{
vkQueueWaitIdle(graphicsQueue);
@@ -458,7 +529,8 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
callbacks.clear();
}
startRecordingGraphicsCommands();
if (submitMode == SUBMIT_RESTART)
startRecordingGraphicsCommands();
}
}
@@ -475,7 +547,7 @@ void Graphics::present(void *screenshotCallbackdata)
deprecations.draw(this);
submitGpuCommands(true, screenshotCallbackdata);
submitGpuCommands(SUBMIT_PRESENT, screenshotCallbackdata);
VkPresentInfoKHR presentInfo{};
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.resize(MAX_FRAMES_IN_FLIGHT);
createVulkanInstance();
bool createBaseObjects = physicalDevice == VK_NULL_HANDLE;
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createPipelineCache();
initVMA();
initCapabilities();
if (createBaseObjects)
{
pickPhysicalDevice();
createLogicalDevice();
createPipelineCache();
initVMA();
initCapabilities();
}
msaaSamples = getMsaaCount(requestedMsaa);
createSwapChain();
createImageViews();
createScreenshotCallbackBuffers();
createSyncObjects();
createColorResources();
createDepthResources();
transitionColorDepthLayouts = true;
createCommandPool();
createCommandBuffers();
if (createBaseObjects)
{
createCommandPool();
createCommandBuffers();
createSyncObjects();
}
beginFrame();
if (batchedDrawState.vb[0] == nullptr)
if (createBaseObjects)
{
// Initial sizes that should be good enough for most cases. It will
// resize to fit if needed, later.
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);
}
if (batchedDrawState.vb[0] == nullptr)
{
// Initial sizes that should be good enough for most cases. It will
// resize to fit if needed, later.
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)
if (defaultConstantTexCoord == nullptr)
{
float zeroTexCoord[2] = { 0.0f, 0.0f };
Buffer::DataDeclaration format("ConstantTexCoord", DATAFORMAT_FLOAT_VEC2);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantTexCoord = newBuffer(settings, { format }, zeroTexCoord, sizeof(zeroTexCoord), 1);
}
// sometimes the VertexTexCoord is not set, so we manually adjust it to (0, 0)
if (defaultConstantTexCoord == nullptr)
{
float zeroTexCoord[2] = { 0.0f, 0.0f };
Buffer::DataDeclaration format("ConstantTexCoord", DATAFORMAT_FLOAT_VEC2);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantTexCoord = newBuffer(settings, { format }, zeroTexCoord, sizeof(zeroTexCoord), 1);
}
// sometimes the VertexColor is not set, so we manually adjust it to white color
if (defaultConstantColor == nullptr)
{
uint8 whiteColor[] = { 255, 255, 255, 255 };
Buffer::DataDeclaration format("ConstantColor", DATAFORMAT_UNORM8_VEC4);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
}
// sometimes the VertexColor is not set, so we manually adjust it to white color
if (defaultConstantColor == nullptr)
{
uint8 whiteColor[] = { 255, 255, 255, 255 };
Buffer::DataDeclaration format("ConstantColor", DATAFORMAT_UNORM8_VEC4);
Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
}
createDefaultTexture();
createDefaultShaders();
Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT];
createQuadIndexBuffer();
createFanIndexBuffer();
createDefaultTexture();
createDefaultShaders();
Shader::current = Shader::standardShaders[Shader::StandardShader::STANDARD_DEFAULT];
createQuadIndexBuffer();
createFanIndexBuffer();
frameCounter = 0;
currentFrame = 0;
}
restoreState(states.back());
Vulkan::resetShaderSwitches();
frameCounter = 0;
currentFrame = 0;
created = true;
drawCalls = 0;
drawCallsBatched = 0;
@@ -659,14 +747,12 @@ void Graphics::getAPIStats(int &shaderswitches) const
void Graphics::unSetMode()
{
renderPassUsages.clear();
framebufferUsages.clear();
pipelineUsages.clear();
submitGpuCommands(SUBMIT_NOPRESENT);
created = false;
vkDeviceWaitIdle(device);
Volatile::unloadAll();
cleanup();
cleanupSwapChain();
vkDestroySurfaceKHR(instance, surface, nullptr);
}
void Graphics::setActive(bool enable)
@@ -1332,75 +1418,6 @@ const OptionalDeviceExtensions &Graphics::getEnabledOptionalDeviceExtensions() c
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()
{
uint32_t layerCount;
@@ -1458,7 +1475,6 @@ void Graphics::pickPhysicalDevice()
minUniformBufferOffsetAlignment = properties.limits.minUniformBufferOffsetAlignment;
deviceApiVersion = properties.apiVersion;
msaaSamples = getMsaaCount(requestedMsaa);
depthStencilFormat = findDepthFormat();
}
@@ -1573,7 +1589,7 @@ static void findOptionalDeviceExtensions(VkPhysicalDevice physicalDevice, Option
optionalDeviceExtensions.memoryRequirements2 = true;
if (strcmp(extension.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) == 0)
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;
if (strcmp(extension.extensionName, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME) == 0)
optionalDeviceExtensions.shaderFloatControls = true;
@@ -3040,8 +3056,6 @@ void Graphics::createDefaultTexture()
void Graphics::cleanup()
{
cleanupSwapChain();
for (auto &cleanUpFns : cleanUpFunctions)
for (auto &cleanUpFn : cleanUpFns)
cleanUpFn();
@@ -3076,8 +3090,6 @@ void Graphics::cleanup()
vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroyPipelineCache(device, pipelineCache, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
}
void Graphics::cleanupSwapChain()
@@ -3087,8 +3099,11 @@ void Graphics::cleanupSwapChain()
vmaDestroyBuffer(vmaAllocator, readbackBuffer.buffer, readbackBuffer.allocation);
vmaDestroyImage(vmaAllocator, readbackBuffer.image, readbackBuffer.imageAllocation);
}
vkDestroyImageView(device, colorImageView, nullptr);
vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation);
if (colorImage)
{
vkDestroyImageView(device, colorImageView, nullptr);
vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation);
}
vkDestroyImageView(device, depthImageView, nullptr);
vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation);
for (const auto &swapChainImageView : swapChainImageViews)
+11 -4
View File
@@ -256,6 +256,14 @@ struct ScreenshotReadbackBuffer
VmaAllocation imageAllocation;
};
enum SubmitMode
{
SUBMIT_PRESENT,
SUBMIT_NOPRESENT,
SUBMIT_RESTART,
SUBMIT_MAXENUM,
};
class Graphics final : public love::graphics::Graphics
{
public:
@@ -300,12 +308,12 @@ public:
// internal functions.
const VkDevice getDevice() const;
const VmaAllocator getVmaAllocator() const;
VkDevice getDevice() const;
VmaAllocator getVmaAllocator() const;
VkCommandBuffer getCommandBufferForDataTransfer();
void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(std::function<void()> callback);
void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr);
void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
VkSampler getCachedSampler(const SamplerState &sampler);
void setComputeShader(Shader *computeShader);
@@ -326,7 +334,6 @@ protected:
void setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) override;
private:
void createVulkanInstance();
bool checkValidationSupport();
void pickPhysicalDevice();
int rateDeviceSuitability(VkPhysicalDevice device);
@@ -44,7 +44,7 @@ GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod
if (method == READBACK_IMMEDIATE)
{
vgfx->submitGpuCommands(false);
vgfx->submitGpuCommands(SUBMIT_RESTART);
if (stagingBuffer.get()) {
status = readbackBuffer(stagingBuffer, 0, size);
gfx->releaseTemporaryBuffer(stagingBuffer);
@@ -79,7 +79,7 @@ GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod
});
if (method == READBACK_IMMEDIATE)
vgfx->submitGpuCommands(false);
vgfx->submitGpuCommands(SUBMIT_RESTART);
}
GraphicsReadback::~GraphicsReadback()
@@ -89,7 +89,7 @@ GraphicsReadback::~GraphicsReadback()
void GraphicsReadback::wait()
{
if (status == STATUS_WAITING)
vgfx->submitGpuCommands(false);
vgfx->submitGpuCommands(SUBMIT_RESTART);
}
void GraphicsReadback::update()