diff --git a/src/modules/graphics/vulkan/Buffer.h b/src/modules/graphics/vulkan/Buffer.h index 1c4fe2cae..7e6d6b8b5 100644 --- a/src/modules/graphics/vulkan/Buffer.h +++ b/src/modules/graphics/vulkan/Buffer.h @@ -42,7 +42,7 @@ class Buffer final , public Volatile { public: - Buffer(love::graphics::Graphics *gfx, const Settings& settings, const std::vector &format, const void *data, size_t size, size_t arraylength); + Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength); virtual ~Buffer(); virtual bool loadVolatile() override; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 8c8e39ee0..8da5a92a3 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -81,8 +81,6 @@ Graphics::Graphics() throw love::Exception("could not find vulkan"); volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr()); - - instanceVersion = volkGetInstanceVersion(); } Graphics::~Graphics() @@ -360,7 +358,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData) } } - endRecordingGraphicsCommands(present); + endRecordingGraphicsCommands(); if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); @@ -412,7 +410,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData) callbacks.clear(); } - startRecordingGraphicsCommands(false); + startRecordingGraphicsCommands(); } } @@ -438,9 +436,9 @@ void Graphics::present(void *screenshotCallbackdata) VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || swapChainRecreationRequested) { - framebufferResized = false; + swapChainRecreationRequested = false; recreateSwapChain(); } else if (result != VK_SUCCESS) @@ -465,6 +463,9 @@ void Graphics::present(void *screenshotCallbackdata) void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + if (swapChain != VK_NULL_HANDLE && (pixelWidth != this->pixelWidth || pixelHeight != this->pixelHeight || width != this->width || height != this->height)) + requestSwapchainRecreation(); + this->width = width; this->height = height; this->pixelWidth = pixelwidth; @@ -601,7 +602,7 @@ void Graphics::unSetMode() renderPassUsages.clear(); framebufferUsages.clear(); pipelineUsages.clear(); - + created = false; vkDeviceWaitIdle(device); Volatile::unloadAll(); @@ -675,34 +676,7 @@ Graphics::RendererInfo Graphics::getRendererInfo() const 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.name = "Vulkan"; info.device = deviceProperties.deviceName; info.vendor = Vulkan::getVendorName(deviceProperties.vendorID); info.version = Vulkan::getVulkanApiVersion(deviceProperties.apiVersion); @@ -832,11 +806,7 @@ void Graphics::setScissor(const Rect &rect) { flushBatchedDraws(); - VkRect2D scissor = computeScissor(rect, - static_cast(swapChainExtent.width), - static_cast(swapChainExtent.height), - getCurrentDPIScale(), - preTransform); + VkRect2D scissor = computeScissor(rect, static_cast(swapChainExtent.width), static_cast(swapChainExtent.height), getCurrentDPIScale(), preTransform); vkCmdSetScissor(commandBuffers.at(currentFrame), 0, 1, &scissor); states.back().scissor = true; @@ -1133,7 +1103,7 @@ void Graphics::beginFrame() cleanUpFn(); cleanUpFunctions.at(currentFrame).clear(); - startRecordingGraphicsCommands(true); + startRecordingGraphicsCommands(); Vulkan::cmdTransitionImageLayout( commandBuffers.at(currentFrame), @@ -1164,7 +1134,7 @@ void Graphics::beginFrame() usedShadersInFrame.clear(); } -void Graphics::startRecordingGraphicsCommands(bool newFrame) +void Graphics::startRecordingGraphicsCommands() { VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -1179,7 +1149,7 @@ void Graphics::startRecordingGraphicsCommands(bool newFrame) setDefaultRenderPass(); } -void Graphics::endRecordingGraphicsCommands(bool present) { +void Graphics::endRecordingGraphicsCommands() { if (renderPassState.active) endRenderPass(); @@ -1187,16 +1157,6 @@ void Graphics::endRecordingGraphicsCommands(bool present) { throw love::Exception("failed to record command buffer"); } -uint32_t Graphics::getNumImagesInFlight() const -{ - return MAX_FRAMES_IN_FLIGHT; -} - -uint32_t Graphics::getFrameIndex() const -{ - return static_cast(currentFrame); -} - const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const { return minUniformBufferOffsetAlignment; @@ -1297,7 +1257,7 @@ void Graphics::createVulkanInstance() 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 = instanceVersion; + appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; @@ -1351,7 +1311,7 @@ bool Graphics::checkValidationSupport() { bool layerFound = false; - for (const auto& layerProperties : availableLayers) + for (const auto &layerProperties : availableLayers) { if (strcmp(layerName, layerProperties.layerName) == 0) { @@ -1546,8 +1506,8 @@ void Graphics::createLogicalDevice() // sanity check for dependencies. - if (optionalDeviceFeatures.extendedDynamicState && !optionalInstanceExtensions.physicalDeviceProperties2) - optionalDeviceFeatures.extendedDynamicState = false; + if (optionalDeviceFeatures.extendedDynamicState && !optionalInstanceExtensions.physicalDeviceProperties2) + optionalDeviceFeatures.extendedDynamicState = false; if (optionalDeviceFeatures.dedicatedAllocation && !optionalDeviceFeatures.memoryRequirements2) optionalDeviceFeatures.dedicatedAllocation = false; if (optionalDeviceFeatures.bufferDeviceAddress && !optionalInstanceExtensions.physicalDeviceProperties2) @@ -1606,7 +1566,7 @@ void Graphics::createLogicalDevice() if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) throw love::Exception("failed to create logical device"); - volkLoadDevice(device); + volkLoadDevice(device); vkGetDeviceQueue(device, indices.graphicsFamily.value, 0, &graphicsQueue); vkGetDeviceQueue(device, indices.presentFamily.value, 0, &presentQueue); @@ -1705,15 +1665,15 @@ void Graphics::createSwapChain() VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); - if ((swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) || - (swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) + if ((swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) || + (swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) { - uint32_t width, height; - width = extent.width; - height = extent.height; - extent.width = height; - extent.height = width; - } + uint32_t width, height; + width = extent.width; + height = extent.height; + extent.width = height; + extent.height = width; + } auto currentTransform = swapChainSupport.capabilities.currentTransform; constexpr float PI = 3.14159265358979323846f; @@ -1789,7 +1749,7 @@ VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector &availablePresentModes) { - int vsync = Vulkan::getVsync(); - const auto begin = availablePresentModes.begin(); const auto end = availablePresentModes.end(); @@ -1871,12 +1829,12 @@ VkCompositeAlphaFlagBitsKHR Graphics::chooseCompositeAlpha(const VkSurfaceCapabi return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) return VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; - else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) - return VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; - else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) - return VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; - else - throw love::Exception("failed to find composite alpha"); + else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + return VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + return VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + else + throw love::Exception("failed to find composite alpha"); } void Graphics::createImageViews() @@ -2002,7 +1960,7 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio { std::vector attachments; - for (const auto& colorView : configuration.colorViews) + for (const auto &colorView : configuration.colorViews) attachments.push_back(colorView); if (configuration.staticData.depthView) @@ -2062,8 +2020,8 @@ void Graphics::createDefaultShaders() VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) { - VkSubpassDescription subPass{}; - subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + VkSubpassDescription subPass{}; + subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; std::vector attachments; std::vector colorAttachmentRefs; @@ -2091,8 +2049,8 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) attachments.push_back(colorDescription); } - subPass.colorAttachmentCount = static_cast(colorAttachmentRefs.size()); - subPass.pColorAttachments = colorAttachmentRefs.data(); + subPass.colorAttachmentCount = static_cast(colorAttachmentRefs.size()); + subPass.pColorAttachments = colorAttachmentRefs.data(); VkAttachmentReference depthStencilAttachmentRef{}; if (configuration.staticData.depthAttachment.format != VK_FORMAT_UNDEFINED) @@ -2156,20 +2114,20 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) std::array dependencies = { dependency, readbackDependency }; - VkRenderPassCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - createInfo.attachmentCount = static_cast(attachments.size()); - createInfo.pAttachments = attachments.data(); - createInfo.subpassCount = 1; - createInfo.pSubpasses = &subPass; + VkRenderPassCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + createInfo.attachmentCount = static_cast(attachments.size()); + createInfo.pAttachments = attachments.data(); + createInfo.subpassCount = 1; + createInfo.pSubpasses = &subPass; createInfo.dependencyCount = static_cast(dependencies.size()); createInfo.pDependencies = dependencies.data(); - VkRenderPass renderPass; - if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) - throw love::Exception("failed to create render pass"); + VkRenderPass renderPass; + if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) + throw love::Exception("failed to create render pass"); - return renderPass; + return renderPass; } bool Graphics::usesConstantVertexColor(const VertexAttributes &vertexAttributes) @@ -2261,7 +2219,7 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi GraphicsPipelineConfiguration configuration{}; - configuration.renderPass = renderPassState.beginInfo.renderPass; + configuration.renderPass = renderPassState.beginInfo.renderPass; configuration.vertexAttributes = attributes; configuration.shader = (Shader*)Shader::current; configuration.wireFrame = states.back().wireframe; @@ -2295,7 +2253,7 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi } } - if (usesConstantVertexColor(attributes)) + if (!usesConstantVertexColor(attributes)) { bufferVector.push_back((VkBuffer)defaultConstantColor->getHandle()); offsets.push_back((VkDeviceSize)0); @@ -2328,35 +2286,15 @@ void Graphics::setDefaultRenderPass() renderPassState.msaa = msaaSamples; renderPassState.numColorAttachments = 1; renderPassState.transitionImages.clear(); - - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = renderPassState.width; - viewport.height = renderPassState.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport); } void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) { - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(pixelw); - viewport.height = static_cast(pixelh); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport); - auto currentCommandBuffer = commandBuffers.at(currentFrame); // fixme: hasSRGBtexture RenderPassConfiguration renderPassConfiguration{}; - for (const auto& color : rts.colors) + for (const auto &color : rts.colors) renderPassConfiguration.colorAttachments.push_back({ Vulkan::getTextureFormat(color.texture->getPixelFormat(), isPixelFormatSRGB(color.texture->getPixelFormat())).internalFormat, false, @@ -2406,6 +2344,16 @@ void Graphics::startRenderPass() { renderPassState.active = true; + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = renderPassState.width; + viewport.height = renderPassState.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport); + if (renderPassState.useConfigurations) { auto &renderPassConfiguration = renderPassState.renderPassConfiguration; @@ -2518,6 +2466,14 @@ void Graphics::cleanupUnusedObjects() eraseUnusedObjects(graphicsPipelines, pipelineUsages, vkDestroyPipeline, device); } +void Graphics::requestSwapchainRecreation() +{ + if (swapChain != VK_NULL_HANDLE) + { + swapChainRecreationRequested = true; + } +} + void Graphics::setComputeShader(Shader *shader) { computeShader = shader; @@ -2688,7 +2644,7 @@ VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration &confi pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - pipelineInfo.renderPass = configuration.renderPass; + pipelineInfo.renderPass = configuration.renderPass; VkPipeline graphicsPipeline; if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) @@ -2740,6 +2696,26 @@ VkSampleCountFlagBits Graphics::getMsaaCount(int requestedMsaa) const return VK_SAMPLE_COUNT_1_BIT; } +void Graphics::setVsync(int vsync) +{ + if (vsync != this->vsync) + { + this->vsync = vsync; + + // With the extension VK_EXT_swapchain_maintenance1 a swapchain recreation might not be needed + // https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_EXT_swapchain_maintenance1.adoc + // However, there are not any drivers that support it, yet. + // Reevaluate again in the future. + + requestSwapchainRecreation(); + } +} + +int Graphics::getVsync() const +{ + return vsync; +} + void Graphics::createColorResources() { if (msaaSamples & VK_SAMPLE_COUNT_1_BIT) @@ -2770,7 +2746,8 @@ void Graphics::createColorResources() allocationInfo.usage = VMA_MEMORY_USAGE_AUTO; allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; - vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr); + if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr)) + throw love::Exception("failed to create color image"); VkImageViewCreateInfo imageViewInfo{}; imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -2787,7 +2764,8 @@ void Graphics::createColorResources() imageViewInfo.subresourceRange.baseArrayLayer = 0; imageViewInfo.subresourceRange.layerCount = 1; - vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView); + if (vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView) != VK_SUCCESS) + throw love::Exception("failed to create color image view"); } } @@ -2838,7 +2816,8 @@ void Graphics::createDepthResources() allocationInfo.usage = VMA_MEMORY_USAGE_AUTO; allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; - vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr); + if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr) != VK_SUCCESS) + throw love::Exception("failed to create depth image"); VkImageViewCreateInfo imageViewInfo{}; imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -2857,7 +2836,8 @@ void Graphics::createDepthResources() imageViewInfo.subresourceRange.baseArrayLayer = 0; imageViewInfo.subresourceRange.layerCount = 1; - vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView); + if (vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView) != VK_SUCCESS) + throw love::Exception("failed to create depth image view"); } void Graphics::createCommandPool() @@ -2972,10 +2952,12 @@ void Graphics::cleanupSwapChain() vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation); vkDestroyImageView(device, depthImageView, nullptr); vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation); - for (const auto &swapChainImageView : swapChainImageViews) - vkDestroyImageView(device, swapChainImageView, nullptr); - swapChainImageViews.clear(); + for (const auto &swapChainImageView : swapChainImageViews) + vkDestroyImageView(device, swapChainImageView, nullptr); + swapChainImageViews.clear(); vkDestroySwapchainKHR(device, swapChain, nullptr); + + swapChain = VK_NULL_HANDLE; } void Graphics::recreateSwapChain() diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 34f276fd6..e7c43ebc5 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -70,23 +70,23 @@ struct RenderPassConfiguration bool resolve = false; } staticData; - bool operator==(const RenderPassConfiguration &conf) const + bool operator==(const RenderPassConfiguration &conf) const { return colorAttachments == conf.colorAttachments && (memcmp(&staticData, &conf.staticData, sizeof(StaticRenderPassConfiguration)) == 0); - } + } }; struct RenderPassConfigurationHasher { - size_t operator()(const RenderPassConfiguration &configuration) const + size_t operator()(const RenderPassConfiguration &configuration) const { size_t hashes[] = { XXH32(configuration.colorAttachments.data(), configuration.colorAttachments.size() * sizeof(VkFormat), 0), XXH32(&configuration.staticData, sizeof(configuration.staticData), 0), }; return XXH32(hashes, sizeof(hashes), 0); - } + } }; struct FramebufferConfiguration @@ -126,7 +126,8 @@ struct FramebufferConfigurationHasher struct OptionalInstanceExtensions { - bool physicalDeviceProperties2 = false; + // VK_KHR_get_physical_device_properties2 + bool physicalDeviceProperties2 = false; }; struct OptionalDeviceFeatures @@ -155,7 +156,7 @@ struct OptionalDeviceFeatures struct GraphicsPipelineConfiguration { - VkRenderPass renderPass; + VkRenderPass renderPass; VertexAttributes vertexAttributes; Shader *shader = nullptr; bool wireFrame; @@ -288,8 +289,6 @@ public: void queueCleanUp(std::function cleanUp); void addReadbackCallback(std::function callback); void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr); - uint32_t getNumImagesInFlight() const; - uint32_t getFrameIndex() const; const VkDeviceSize getMinUniformBufferOffsetAlignment() const; graphics::Texture *getDefaultTexture() const; VkSampler getCachedSampler(const SamplerState &sampler); @@ -298,6 +297,8 @@ public: graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData(); const OptionalDeviceFeatures &getEnabledOptionalDeviceExtensions() const; VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const; + void setVsync(int vsync); + int getVsync() const; protected: graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override; @@ -328,10 +329,10 @@ private: void createScreenshotCallbackBuffers(); void createDefaultRenderPass(); void createDefaultFramebuffers(); - VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration); - VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration); + VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration); + VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration); void createDefaultShaders(); - VkRenderPass createRenderPass(RenderPassConfiguration &configuration); + VkRenderPass createRenderPass(RenderPassConfiguration &configuration); VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration &configuration); void createColorResources(); VkFormat findSupportedFormat(const std::vector &candidates, VkImageTiling tiling, VkFormatFeatureFlags features); @@ -346,8 +347,8 @@ private: void recreateSwapChain(); void initDynamicState(); void beginFrame(); - void startRecordingGraphicsCommands(bool newFrame); - void endRecordingGraphicsCommands(bool present); + void startRecordingGraphicsCommands(); + void endRecordingGraphicsCommands(); void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration &configuration); bool usesConstantVertexColor(const VertexAttributes &attribs); void createVulkanVertexFormat( @@ -364,8 +365,8 @@ private: void endRenderPass(); VkSampler createSampler(const SamplerState &sampler); void cleanupUnusedObjects(); + void requestSwapchainRecreation(); - uint32_t instanceVersion = VK_API_VERSION_1_0; VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; uint32_t deviceApiVersion = VK_API_VERSION_1_0; @@ -377,7 +378,7 @@ private: VkQueue graphicsQueue = VK_NULL_HANDLE; VkQueue presentQueue = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE; - VkSwapchainKHR swapChain = VK_NULL_HANDLE; + VkSwapchainKHR swapChain = VK_NULL_HANDLE; VkSurfaceTransformFlagBitsKHR preTransform = {}; Matrix4 displayRotation; std::vector swapChainImages; @@ -393,7 +394,7 @@ private: VmaAllocation depthImageAllocation = VK_NULL_HANDLE; VkRenderPass defaultRenderPass = VK_NULL_HANDLE; std::vector defaultFramebuffers; - std::unordered_map renderPasses; + std::unordered_map renderPasses; std::unordered_map framebuffers; std::unordered_map graphicsPipelines; std::unordered_map renderPassUsages; @@ -402,17 +403,18 @@ private: std::unordered_map samplers; VkCommandPool commandPool = VK_NULL_HANDLE; std::vector commandBuffers; - Shader* computeShader = nullptr; + Shader *computeShader = nullptr; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; std::vector imagesInFlight; + int vsync = 1; VkDeviceSize minUniformBufferOffsetAlignment = 0; bool imageRequested = false; uint32_t frameCounter = 0; size_t currentFrame = 0; uint32_t imageIndex = 0; - bool framebufferResized = false; + bool swapChainRecreationRequested = false; bool transitionColorDepthLayouts = false; VmaAllocator vmaAllocator = VK_NULL_HANDLE; StrongRef defaultTexture; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 893a5abd6..628b5815a 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -196,7 +196,7 @@ bool Shader::loadVolatile() createPipelineLayout(); createDescriptorPoolSizes(); createStreamBuffers(); - descriptorSetsVector.resize(vgfx->getNumImagesInFlight()); + descriptorSetsVector.resize(MAX_FRAMES_IN_FLIGHT); currentFrame = 0; currentUsedUniformStreamBuffersCount = 0; currentUsedDescriptorSetsCount = 0; @@ -234,8 +234,7 @@ void Shader::unloadVolatile() } } - auto gfx = Module::getInstance(Module::M_GRAPHICS); - gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){ + vgfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){ for (const auto pool : descriptorPools) vkDestroyDescriptorPool(device, pool, nullptr); for (const auto shaderModule : shaderModules) @@ -274,10 +273,11 @@ VkPipeline Shader::getComputePipeline() const return computePipeline; } -void Shader::newFrame(uint32_t frameIndex) +void Shader::newFrame() { - currentFrame = frameIndex; + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + updatedUniforms.clear(); currentUsedUniformStreamBuffersCount = 0; currentUsedDescriptorSetsCount = 0; @@ -295,12 +295,10 @@ void Shader::newFrame(uint32_t frameIndex) else streamBuffers.at(0)->nextFrame(); - if (currentUsedDescriptorSetsCount >= static_cast(descriptorSetsVector.at(currentFrame).size())) + if (descriptorSetsVector.at(currentFrame).size() == 0) descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet()); - currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount); - - initDescriptorSet(); + currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(0); } void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint) @@ -336,15 +334,17 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind VkWriteDescriptorSet uniformWrite{}; uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; uniformWrite.dstSet = currentDescriptorSet; - uniformWrite.dstBinding = uniformLocation; + uniformWrite.dstBinding = localUniformLocation; uniformWrite.dstArrayElement = 0; uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uniformWrite.descriptorCount = 1; - uniformWrite.pBufferInfo = &bufferInfo; + uniformWrite.pBufferInfo = &bufferInfo; vkUpdateDescriptorSets(device, 1, &uniformWrite, 0, nullptr); currentUsedUniformStreamBuffersCount++; + + updatedUniforms.insert(localUniformLocation); } static const std::vector builtinUniformTextures = { @@ -365,18 +365,28 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind imageInfo.imageView = (VkImageView)texture->getRenderTargetHandle(); imageInfo.sampler = (VkSampler)texture->getSamplerHandle(); + auto location = builtinUniformInfo[builtin]->location; + VkWriteDescriptorSet textureWrite{}; textureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; textureWrite.dstSet = currentDescriptorSet; - textureWrite.dstBinding = builtinUniformInfo[builtin]->location; + textureWrite.dstBinding = location; textureWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; textureWrite.descriptorCount = 1; textureWrite.pImageInfo = &imageInfo; vkUpdateDescriptorSets(device, 1, &textureWrite, 0, nullptr); + + updatedUniforms.insert(location); } } + for (const auto &u : uniformInfos) + { + if (updatedUniforms.find(u.second.location) == updatedUniforms.end()) + updateUniform(&u.second, u.second.count); + } + vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, ¤tDescriptorSet, 0, nullptr); currentUsedDescriptorSetsCount++; @@ -386,7 +396,7 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount); - initDescriptorSet(); + updatedUniforms.clear(); } Shader::~Shader() @@ -399,7 +409,7 @@ void Shader::attach() auto &usedShadersInFrame = vgfx->getUsedShadersInFrame(); if (usedShadersInFrame.find(this) == usedShadersInFrame.end()) { - newFrame(vgfx->getFrameIndex()); + newFrame(); usedShadersInFrame.insert(this); } @@ -463,6 +473,9 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal) { auto vkTexture = dynamic_cast(info->textures[i]); + if (vkTexture == nullptr) + throw love::Exception("uniform variable %s is not set.", info->name.c_str()); + VkDescriptorImageInfo imageInfo{}; imageInfo.imageLayout = vkTexture->getImageLayout(); @@ -501,6 +514,9 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal) for (int i = 0; i < info->count; i++) { + if (info->buffers[i] == nullptr) + throw love::Exception("uniform variable %s is not set.", info->name.c_str()); + VkDescriptorBufferInfo bufferInfo{}; bufferInfo.buffer = (VkBuffer)info->buffers[i]->getHandle();; bufferInfo.offset = 0; @@ -526,12 +542,19 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal) std::vector bufferViews; for (int i = 0; i < info->count; i++) + { + if (info->buffers[i] == nullptr) + throw love::Exception("uniform variable %s is not set.", info->name.c_str()); + bufferViews.push_back((VkBufferView)info->buffers[i]->getTexelBufferHandle()); + } write.pTexelBufferView = bufferViews.data(); vkUpdateDescriptorSets(device, 1, &write, 0, nullptr); } + + updatedUniforms.insert(info->location); } void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) @@ -544,6 +567,8 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, if (oldTexture) oldTexture->release(); } + + updateUniform(info, count); } void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) @@ -556,6 +581,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe if (oldBuffer) oldBuffer->release(); } + + updateUniform(info, count); } void Shader::calculateUniformBufferSizeAligned() @@ -568,13 +595,6 @@ void Shader::calculateUniformBufferSizeAligned() uniformBufferSizeAligned = factor * minAlignment; } -void Shader::initDescriptorSet() -{ - for (const auto &entry : uniformInfos) - if (Vulkan::getDescriptorType(entry.second.baseType) != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) - updateUniform(&entry.second, entry.second.count, true); -} - void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename) { using namespace spirv_cross; @@ -776,7 +796,7 @@ void Shader::compileShaders() auto defaultUniformBlockSize = comp.get_declared_struct_size(type); localUniformStagingData.resize(defaultUniformBlockSize); localUniformData.resize(defaultUniformBlockSize); - uniformLocation = comp.get_decoration(resource.id, spv::DecorationBinding); + localUniformLocation = comp.get_decoration(resource.id, spv::DecorationBinding); memset(localUniformStagingData.data(), 0, defaultUniformBlockSize); memset(localUniformData.data(), 0, defaultUniformBlockSize); @@ -956,7 +976,7 @@ void Shader::createDescriptorSetLayout() if (!localUniformStagingData.empty()) { VkDescriptorSetLayoutBinding uniformBinding{}; - uniformBinding.binding = uniformLocation; + uniformBinding.binding = localUniformLocation; uniformBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uniformBinding.descriptorCount = 1; uniformBinding.stageFlags = stageFlags; @@ -1023,7 +1043,9 @@ void Shader::createDescriptorPoolSizes() void Shader::createStreamBuffers() { - streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned)); + size_t size = STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned; + if (size > 0) + streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, size)); } void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 66abcd9ab..e10e09d95 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace love @@ -63,7 +64,7 @@ public: const VkPipelineLayout getGraphicsPipelineLayout() const; - void newFrame(uint32_t frameIndex); + void newFrame(); void cmdPushDescriptorSets(VkCommandBuffer, VkPipelineBindPoint); @@ -101,8 +102,7 @@ private: const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename); - void initDescriptorSet(); - void updateUniform(const UniformInfo* info, int count, bool internal); + void updateUniform(const UniformInfo *info, int count, bool internal); VkDescriptorSet allocateDescriptorSet(); @@ -121,6 +121,8 @@ private: std::queue freeDescriptorSets; std::vector> descriptorSetsVector; + std::set updatedUniforms; + std::vector shaderStages; std::vector shaderModules; @@ -135,7 +137,7 @@ private: std::unique_ptr uniformBufferObjectBuffer; std::vector localUniformData; std::vector localUniformStagingData; - uint32_t uniformLocation; + uint32_t localUniformLocation; OptionalInt builtinUniformDataOffset; std::unordered_map attributes; diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 742f0cf93..2e344e433 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -31,7 +31,6 @@ namespace vulkan { static uint32_t numShaderSwitches; -static int vsync = 1; void Vulkan::shaderSwitch() { @@ -48,16 +47,6 @@ void Vulkan::resetShaderSwitches() numShaderSwitches = 0; } -void Vulkan::setVsync(int value) -{ - vsync = value; -} - -int Vulkan::getVsync() -{ - return vsync; -} - VkFormat Vulkan::getVulkanVertexFormat(DataFormat format) { switch (format) diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index 580f06cab..9db8ffa90 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/src/modules/graphics/vulkan/Vulkan.h @@ -58,9 +58,6 @@ public: static uint32_t getNumShaderSwitches(); static void resetShaderSwitches(); - static void setVsync(int vsync); - static int getVsync(); - static VkFormat getVulkanVertexFormat(DataFormat format); static TextureFormat getTextureFormat(PixelFormat, bool sRGB); static std::string getVendorName(uint32_t vendorId); diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 01f44dd76..e405a73c8 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -1100,8 +1100,11 @@ void Window::setVSync(int vsync) } #ifdef LOVE_GRAPHICS_VULKAN - // TODO: this doesn't update the swap-chain, but it should. - love::graphics::vulkan::Vulkan::setVsync(vsync); + if (windowRenderer == love::graphics::RENDERER_VULKAN) + { + auto vgfx = dynamic_cast(graphics.get()); + vgfx->setVsync(vsync); + } #endif #if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS) @@ -1132,7 +1135,10 @@ int Window::getVSync() const #ifdef LOVE_GRAPHICS_VULKAN if (windowRenderer == love::graphics::RENDERER_VULKAN) - return love::graphics::vulkan::Vulkan::getVsync(); + { + auto vgfx = dynamic_cast(graphics.get()); + return vgfx->getVsync(); + } #endif return 0;