diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index bb7a0b350..01f7e1e4d 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -48,7 +48,16 @@ namespace love { return "love.graphics.vulkan"; } - Graphics::Graphics() { + const VkDevice Graphics::getDevice() const { + return device; + } + + const VkPhysicalDevice Graphics::getPhysicalDevice() const { + return physicalDevice; + } + + const VmaAllocator Graphics::getVmaAllocator() const { + return vmaAllocator; } Graphics::~Graphics() { @@ -294,6 +303,19 @@ namespace love { created = false; cleanup(); } + + Graphics::RendererInfo Graphics::getRendererInfo() const { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + + Graphics::RendererInfo info; + info.device = deviceProperties.deviceName; + info.vendor = Vulkan::getVendorName(deviceProperties.vendorID); + info.version = Vulkan::getVulkanApiVersion(deviceProperties.apiVersion); + info.name = "Vulkan"; + + return info; + } void Graphics::draw(const DrawIndexedCommand& cmd) { std::cout << "drawIndexed "; @@ -356,12 +378,8 @@ namespace love { bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { std::cout << "isPixelFormatSupported "; - switch (format) { - case PIXELFORMAT_LA8_UNORM: - return false; - default: - return true; - } + + return true; } void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) { @@ -431,6 +449,10 @@ namespace love { // END IMPLEMENTATION OVERRIDDEN FUNCTIONS + void Graphics::setTexture(graphics::Texture* texture) { + currentTexture = texture; + } + void Graphics::updatedBatchedDrawBuffers() { batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1; batchedDrawState.vb[0]->nextFrame(); @@ -456,7 +478,7 @@ namespace love { graphics::StreamBuffer* Graphics::getUniformBuffer() { auto data = getCurrentBuiltinUniformData(); - for (auto it : uniformBufferMap) { + for (auto &it : uniformBufferMap) { if (it.first == data) { return it.second; } @@ -556,7 +578,7 @@ namespace love { appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); //todo, get this version from somewhere else? appInfo.pEngineName = "LOVE Engine"; appInfo.engineVersion = VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_REV); - appInfo.apiVersion = VK_API_VERSION_1_0; + appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; @@ -682,6 +704,12 @@ namespace love { if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { score += 1000; } + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { + score += 100; + } + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU) { + score += 10; + } // definitely needed @@ -710,7 +738,7 @@ namespace love { return score; } - Graphics::QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; @@ -761,16 +789,12 @@ namespace love { VkPhysicalDeviceFeatures deviceFeatures{}; deviceFeatures.samplerAnisotropy = VK_TRUE; - VkPhysicalDeviceFeatures2 deviceFeatures2{}; - deviceFeatures2.features.robustBufferAccess = VK_TRUE; - deviceFeatures2.pNext = nullptr; - VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.pNext = &deviceFeatures2; + createInfo.pNext = nullptr; createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); @@ -804,7 +828,9 @@ namespace love { allocatorCreateInfo.instance = instance; allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; - vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator); + if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS) { + throw love::Exception("failed to create vma allocator"); + } } void Graphics::createSurface() { @@ -815,7 +841,7 @@ namespace love { } } - Graphics::SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { SwapChainSupportDetails details; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); @@ -1416,7 +1442,7 @@ namespace love { quadIndexBuffer->unmap(size); } - bool operator==(const Graphics::GraphicsPipelineConfiguration& first, const Graphics::GraphicsPipelineConfiguration& other) { + bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) { if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) { return false; } diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index af6590b98..269a39b0b 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -19,25 +19,61 @@ namespace love { namespace graphics { namespace vulkan { + struct GraphicsPipelineConfiguration { + std::vector vertexInputBindingDescriptions; + std::vector vertexInputAttributeDescriptions; + + friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other); + }; + + struct DecriptorSetConfiguration { + graphics::Texture* texture; + graphics::StreamBuffer* buffer; + + bool operator==(const DecriptorSetConfiguration& conf2) { + return this->texture == conf2.texture && this->buffer == conf2.buffer; + } + }; + + struct BatchedDrawBuffers { + StreamBuffer* vertexBuffer1; + StreamBuffer* vertexBuffer2; + StreamBuffer* indexBuffer; + StreamBuffer* constantColorBuffer; + + ~BatchedDrawBuffers() { + delete vertexBuffer1; + delete vertexBuffer2; + delete indexBuffer; + delete constantColorBuffer; + } + }; + + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; + + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + class Graphics final : public love::graphics::Graphics { public: - Graphics(); + Graphics() = default; virtual ~Graphics(); const char* getName() const override; - - const VkDevice getDevice() const { - return device; - } - - const VkPhysicalDevice getPhysicalDevice() const { - return physicalDevice; - } - - const VmaAllocator getVmaAllocator() const { - return vmaAllocator; - } + const VkDevice getDevice() const; + const VkPhysicalDevice getPhysicalDevice() const; + const VmaAllocator getVmaAllocator() const; // implementation for virtual functions love::graphics::Texture* newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data = nullptr) override { @@ -69,7 +105,7 @@ namespace love { bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override; Renderer getRenderer() const override { std::cout << "getRenderer "; return RENDERER_VULKAN; } bool usesGLSLES() const override { std::cout << "usesGLSES "; return false; } - RendererInfo getRendererInfo() const override { std::cout << "getRendererInfo "; return {}; } + RendererInfo getRendererInfo() const override; void draw(const DrawCommand& cmd) override { std::cout << "draw "; } void draw(const DrawIndexedCommand& cmd) override; void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override; @@ -80,22 +116,6 @@ namespace love { VkCommandBuffer beginSingleTimeCommands(); void endSingleTimeCommands(VkCommandBuffer); - struct GraphicsPipelineConfiguration { - std::vector vertexInputBindingDescriptions; - std::vector vertexInputAttributeDescriptions; - - friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other); - }; - - struct DecriptorSetConfiguration { - graphics::Texture* texture; - graphics::StreamBuffer* buffer; - - bool operator==(const DecriptorSetConfiguration& conf2) { - return this->texture == conf2.texture && this->buffer == conf2.buffer; - } - }; - void setShader(Shader*); protected: @@ -114,58 +134,6 @@ namespace love { void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override { std::cout << "setRenderTargetsInternal "; } private: - graphics::Texture* currentTexture; - - void setTexture(graphics::Texture* texture) { - currentTexture = texture; - } - - VkDescriptorSet* getDescriptorSet(int currentFrame); - graphics::StreamBuffer* getUniformBuffer(); - - std::vector> uniformBufferMap; - std::vector>> descriptorSetsMap; - - struct BatchedDrawBuffers { - StreamBuffer* vertexBuffer1; - StreamBuffer* vertexBuffer2; - StreamBuffer* indexBuffer; - StreamBuffer* constantColorBuffer; - - ~BatchedDrawBuffers() { - delete vertexBuffer1; - delete vertexBuffer2; - delete indexBuffer; - delete constantColorBuffer; - } - }; - - // we need an array of draw buffers, since the frames are being rendered asynchronously - // and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered. - std::vector batchedDrawBuffers; - void updatedBatchedDrawBuffers(); - - void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration); - - StreamBuffer* quadIndexBuffer = nullptr; - - // vulkan specific member functions and variables - - struct QueueFamilyIndices { - std::optional graphicsFamily; - std::optional presentFamily; - - bool isComplete() { - return graphicsFamily.has_value() && presentFamily.has_value(); - } - }; - - struct SwapChainSupportDetails { - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; - }; - void createVulkanInstance(); bool checkValidationSupport(); void pickPhysicalDevice(); @@ -201,37 +169,49 @@ namespace love { void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration); graphics::StreamBuffer* createUniformBufferFromData(graphics::Shader::BuiltinUniformData); graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData(); + void setTexture(graphics::Texture* texture); + void updatedBatchedDrawBuffers(); + VkDescriptorSet* getDescriptorSet(int currentFrame); + graphics::StreamBuffer* getUniformBuffer(); + void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration); - VkInstance instance; + VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VkDevice device; - VkQueue graphicsQueue; - VkQueue presentQueue; - VkSurfaceKHR surface; - VkSwapchainKHR swapChain; + VkDevice device = VK_NULL_HANDLE; + VkQueue graphicsQueue = VK_NULL_HANDLE; + VkQueue presentQueue = VK_NULL_HANDLE; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkSwapchainKHR swapChain = VK_NULL_HANDLE; std::vector swapChainImages; - VkFormat swapChainImageFormat; - VkExtent2D swapChainExtent; + VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED; + VkExtent2D swapChainExtent = VkExtent2D(); std::vector swapChainImageViews; - VkDescriptorSetLayout descriptorSetLayout; - VkPipelineLayout pipelineLayout; - VkRenderPass renderPass; + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + VkRenderPass renderPass = VK_NULL_HANDLE; VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE; std::vector> graphicsPipelines; // FIXME improve performance by using a hash map std::vector swapChainFramBuffers; - VkCommandPool commandPool; + VkCommandPool commandPool = VK_NULL_HANDLE; std::vector commandBuffers; VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; - VkDescriptorPool descriptorPool; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; std::vector imagesInFlight; size_t currentFrame = 0; - uint32_t imageIndex; + uint32_t imageIndex = 0; bool framebufferResized = false; - VmaAllocator vmaAllocator; - graphics::Texture* standardTexture; + VmaAllocator vmaAllocator = VK_NULL_HANDLE; + graphics::Texture* standardTexture = nullptr; + // we need an array of draw buffers, since the frames are being rendered asynchronously + // and we can't (or shouldn't) update the contents of the buffers while they're still in flight / being rendered. + std::vector batchedDrawBuffers; + StreamBuffer* quadIndexBuffer = nullptr; + graphics::Texture* currentTexture = nullptr; + std::vector> uniformBufferMap; + std::vector>> descriptorSetsMap; }; } } diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 4f559f4af..9c4b2c1f4 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -1,5 +1,8 @@ #include "Vulkan.h" +#include + + namespace love { namespace graphics { namespace vulkan { @@ -257,6 +260,32 @@ namespace love { return textureFormat; } + + // values taken from https://pcisig.com/membership/member-companies + // as specified at https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html + std::string Vulkan::getVendorName(uint32_t vendorId) { + switch (vendorId) { + case 4318: + return "Nvidia"; + case 8086: + return "Intel"; + case 4130: + return "Advanced Micro Devices"; + default: + return "unknown"; + } + } + + std::string Vulkan::getVulkanApiVersion(uint32_t version) { + std::stringstream ss; + + ss << VK_API_VERSION_VARIANT(version) + << "." << VK_API_VERSION_MAJOR(version) + << "." << VK_API_VERSION_MINOR(version) + << "." << VK_API_VERSION_PATCH(version); + + return ss.str(); + } } } } diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index 714a296b3..ea0d9fed3 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/src/modules/graphics/vulkan/Vulkan.h @@ -20,6 +20,8 @@ namespace love { public: static VkFormat getVulkanVertexFormat(DataFormat format); static TextureFormat getTextureFormat(PixelFormat); + static std::string getVendorName(uint32_t vendorId); + static std::string getVulkanApiVersion(uint32_t apiVersion); }; } }