From 7bbc042fce9558c258201a73b2ec4356fce9ee40 Mon Sep 17 00:00:00 2001 From: niki Date: Sat, 30 Jul 2022 01:16:56 +0200 Subject: [PATCH] vulkan: remove redundant tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this makes reading the source files easier and is more in line with the rest of the löve codebase --- src/modules/graphics/vulkan/Buffer.cpp | 169 +- src/modules/graphics/vulkan/Buffer.h | 67 +- src/modules/graphics/vulkan/Graphics.cpp | 3188 +++++++++--------- src/modules/graphics/vulkan/Graphics.h | 400 +-- src/modules/graphics/vulkan/Shader.cpp | 956 +++--- src/modules/graphics/vulkan/Shader.h | 124 +- src/modules/graphics/vulkan/ShaderStage.cpp | 16 +- src/modules/graphics/vulkan/ShaderStage.h | 20 +- src/modules/graphics/vulkan/StreamBuffer.cpp | 140 +- src/modules/graphics/vulkan/StreamBuffer.h | 54 +- src/modules/graphics/vulkan/Texture.cpp | 516 +-- src/modules/graphics/vulkan/Texture.h | 72 +- src/modules/graphics/vulkan/Vulkan.cpp | 1060 +++--- src/modules/graphics/vulkan/Vulkan.h | 74 +- 14 files changed, 3431 insertions(+), 3425 deletions(-) diff --git a/src/modules/graphics/vulkan/Buffer.cpp b/src/modules/graphics/vulkan/Buffer.cpp index 9eef9c713..cda9bb3ba 100644 --- a/src/modules/graphics/vulkan/Buffer.cpp +++ b/src/modules/graphics/vulkan/Buffer.cpp @@ -2,91 +2,92 @@ #include "Graphics.h" namespace love { - namespace graphics { - namespace vulkan { - static VkBufferUsageFlags getUsageBit(BufferUsage mode) { - switch (mode) { - case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - default: - throw love::Exception("unsupported BufferUsage mode"); - } - } +namespace graphics { +namespace vulkan { - static VkBufferUsageFlags getVulkanUsageFlags(BufferUsageFlags flags) { - VkBufferUsageFlags vkFlags = 0; - for (int i = 0; i < BUFFERUSAGE_MAX_ENUM; i++) { - BufferUsageFlags flag = static_cast(1u << i); - if (flags & flag) { - vkFlags |= getUsageBit((BufferUsage)i); - } - } - return vkFlags; - } +static VkBufferUsageFlags getUsageBit(BufferUsage mode) { + switch (mode) { + case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + default: + throw love::Exception("unsupported BufferUsage mode"); + } +} - Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) - : love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), gfx(gfx) { - loadVolatile(); - } - - bool Buffer::loadVolatile() { - Graphics* vgfx = (Graphics*)gfx; - allocator = vgfx->getVmaAllocator(); - - VkBufferCreateInfo bufferInfo{}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = getSize(); - bufferInfo.usage = getVulkanUsageFlags(usageFlags); - - VmaAllocationCreateInfo allocCreateInfo = {}; - allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; - allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; - - vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo); - - return true; - } - - void Buffer::unloadVolatile() { - if (buffer == VK_NULL_HANDLE) - return; - - Graphics* vgfx = (Graphics*)gfx; - auto device = vgfx->getDevice(); - - vgfx->queueCleanUp( - [device=device, allocator=allocator, buffer=buffer, allocation=allocation](){ - vkDeviceWaitIdle(device); - vmaDestroyBuffer(allocator, buffer, allocation); - }); - - buffer = VK_NULL_HANDLE; - } - - Buffer::~Buffer() { - unloadVolatile(); - } - - void* Buffer::map(MapType map, size_t offset, size_t size) { - char* data = (char*)allocInfo.pMappedData; - return (void*) (data + offset); - } - - bool Buffer::fill(size_t offset, size_t size, const void *data) { - void* dst = (void*)((char*)allocInfo.pMappedData + offset); - memcpy(dst, data, size); - return true; - } - - void Buffer::unmap(size_t usedoffset, size_t usedsize) { - (void)usedoffset; - (void)usedsize; - } - - void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) { - throw love::Exception("not implemented yet"); - } +static VkBufferUsageFlags getVulkanUsageFlags(BufferUsageFlags flags) { + VkBufferUsageFlags vkFlags = 0; + for (int i = 0; i < BUFFERUSAGE_MAX_ENUM; i++) { + BufferUsageFlags flag = static_cast(1u << i); + if (flags & flag) { + vkFlags |= getUsageBit((BufferUsage)i); } } -} \ No newline at end of file + return vkFlags; +} + +Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) + : love::graphics::Buffer(gfx, settings, format, size, arraylength), usageFlags(settings.usageFlags), gfx(gfx) { + loadVolatile(); +} + +bool Buffer::loadVolatile() { + Graphics* vgfx = (Graphics*)gfx; + allocator = vgfx->getVmaAllocator(); + + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = getVulkanUsageFlags(usageFlags); + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + + vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo); + + return true; +} + +void Buffer::unloadVolatile() { + if (buffer == VK_NULL_HANDLE) + return; + + Graphics* vgfx = (Graphics*)gfx; + auto device = vgfx->getDevice(); + + vgfx->queueCleanUp( + [device=device, allocator=allocator, buffer=buffer, allocation=allocation](){ + vkDeviceWaitIdle(device); + vmaDestroyBuffer(allocator, buffer, allocation); + }); + + buffer = VK_NULL_HANDLE; +} + +Buffer::~Buffer() { + unloadVolatile(); +} + +void* Buffer::map(MapType map, size_t offset, size_t size) { + char* data = (char*)allocInfo.pMappedData; + return (void*) (data + offset); +} + +bool Buffer::fill(size_t offset, size_t size, const void *data) { + void* dst = (void*)((char*)allocInfo.pMappedData + offset); + memcpy(dst, data, size); + return true; +} + +void Buffer::unmap(size_t usedoffset, size_t usedsize) { + (void)usedoffset; + (void)usedsize; +} + +void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) { + throw love::Exception("not implemented yet"); +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/Buffer.h b/src/modules/graphics/vulkan/Buffer.h index 20985dc00..9dca79de0 100644 --- a/src/modules/graphics/vulkan/Buffer.h +++ b/src/modules/graphics/vulkan/Buffer.h @@ -1,3 +1,6 @@ +#ifndef LOVE_GRAPHICS_VULKAN_BUFFER_H +#define LOVE_GRAPHICS_VULKAN_BUFFER_H + #include "graphics/Buffer.h" #include #include "vk_mem_alloc.h" @@ -5,37 +8,39 @@ namespace love { - namespace graphics { - namespace vulkan { - class Buffer : public love::graphics::Buffer, public Volatile { - public: - Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength); - virtual ~Buffer(); +namespace graphics { +namespace vulkan { +class Buffer : public love::graphics::Buffer, public Volatile { +public: + 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; - virtual void unloadVolatile() override; + virtual bool loadVolatile() override; + virtual void unloadVolatile() override; - void* map(MapType map, size_t offset, size_t size) override; - void unmap(size_t usedoffset, size_t usedsize) override; - bool fill(size_t offset, size_t size, const void* data) override; - void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override; - ptrdiff_t getHandle() const override { - return (ptrdiff_t) buffer; // todo ? - } - ptrdiff_t getTexelBufferHandle() const override { - throw love::Exception("unimplemented Buffer::getTexelBufferHandle"); - return (ptrdiff_t) nullptr; // todo ? - } - - private: - // todo use a staging buffer for improved performance - VkBuffer buffer = VK_NULL_HANDLE; - love::graphics::Graphics* gfx; - VmaAllocator allocator; - VmaAllocation allocation; - VmaAllocationInfo allocInfo; - BufferUsageFlags usageFlags; - }; - } + void* map(MapType map, size_t offset, size_t size) override; + void unmap(size_t usedoffset, size_t usedsize) override; + bool fill(size_t offset, size_t size, const void* data) override; + void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override; + ptrdiff_t getHandle() const override { + return (ptrdiff_t) buffer; // todo ? } -} + ptrdiff_t getTexelBufferHandle() const override { + throw love::Exception("unimplemented Buffer::getTexelBufferHandle"); + return (ptrdiff_t) nullptr; // todo ? + } + +private: + // todo use a staging buffer for improved performance + VkBuffer buffer = VK_NULL_HANDLE; + love::graphics::Graphics* gfx; + VmaAllocator allocator; + VmaAllocation allocation; + VmaAllocationInfo allocInfo; + BufferUsageFlags usageFlags; +}; +} // vulkan +} // graphics +} // love + +#endif diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index fa279338f..1dc3acd9d 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -19,1609 +19,1609 @@ namespace love { - namespace graphics { - namespace vulkan { - static VkIndexType getVulkanIndexBufferType(IndexDataType type) { - switch (type) { - case INDEX_UINT16: return VK_INDEX_TYPE_UINT16; - case INDEX_UINT32: return VK_INDEX_TYPE_UINT32; - default: - throw love::Exception("unknown Index Data type"); - } - } +namespace graphics { +namespace vulkan { +static VkIndexType getVulkanIndexBufferType(IndexDataType type) { + switch (type) { + case INDEX_UINT16: return VK_INDEX_TYPE_UINT16; + case INDEX_UINT32: return VK_INDEX_TYPE_UINT32; + default: + throw love::Exception("unknown Index Data type"); + } +} - const std::vector validationLayers = { - "VK_LAYER_KHRONOS_validation" - }; +const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" +}; - const std::vector deviceExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, +const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_push_descriptor.html - VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME - }; + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_push_descriptor.html + VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME +}; #ifdef NDEBUG - constexpr bool enableValidationLayers = false; +constexpr bool enableValidationLayers = false; #else - constexpr bool enableValidationLayers = true; +constexpr bool enableValidationLayers = true; #endif - constexpr int MAX_FRAMES_IN_FLIGHT = 2; +constexpr int MAX_FRAMES_IN_FLIGHT = 2; + +constexpr uint32_t vulkanApiVersion = VK_API_VERSION_1_3; + +const char* Graphics::getName() const { + return "love.graphics.vulkan"; +} + +const VkDevice Graphics::getDevice() const { + return device; +} + +const VkPhysicalDevice Graphics::getPhysicalDevice() const { + return physicalDevice; +} + +const VmaAllocator Graphics::getVmaAllocator() const { + return vmaAllocator; +} + +Graphics::~Graphics() { + // We already cleaned those up by clearing out batchedDrawBuffers. + // We set them to nullptr here so the base class doesn't crash + // when it tries to free this. + batchedDrawState.vb[0] = nullptr; + batchedDrawState.vb[1] = nullptr; + batchedDrawState.indexBuffer = nullptr; +} + +// START OVERRIDEN FUNCTIONS + +love::graphics::Texture* Graphics::newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data) { + return new Texture(this, settings, data); +} - constexpr uint32_t vulkanApiVersion = VK_API_VERSION_1_3; - - const char* Graphics::getName() const { - return "love.graphics.vulkan"; - } - - const VkDevice Graphics::getDevice() const { - return device; - } - - const VkPhysicalDevice Graphics::getPhysicalDevice() const { - return physicalDevice; - } - - const VmaAllocator Graphics::getVmaAllocator() const { - return vmaAllocator; - } - - Graphics::~Graphics() { - // We already cleaned those up by clearing out batchedDrawBuffers. - // We set them to nullptr here so the base class doesn't crash - // when it tries to free this. - batchedDrawState.vb[0] = nullptr; - batchedDrawState.vb[1] = nullptr; - batchedDrawState.indexBuffer = nullptr; - } - - // START OVERRIDEN FUNCTIONS - - love::graphics::Texture* Graphics::newTexture(const love::graphics::Texture::Settings& settings, const love::graphics::Texture::Slices* data) { - return new Texture(this, settings, data); - } - - love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { - return new Buffer(this, settings, format, data, size, arraylength); - } - - // FIXME: clear stencil and depth missing. - void Graphics::clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) { - VkClearAttachment attachment{}; - - if (color.hasValue) { - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.clearValue.color.float32[0] = static_cast(color.value.r); - attachment.clearValue.color.float32[1] = static_cast(color.value.g); - attachment.clearValue.color.float32[2] = static_cast(color.value.b); - attachment.clearValue.color.float32[3] = static_cast(color.value.a); - } - - VkClearRect rect{}; - rect.layerCount = 1; - rect.rect.extent.width = static_cast(currentViewportWidth); - rect.rect.extent.height = static_cast(currentViewportHeight); - - vkCmdClearAttachments(commandBuffers[imageIndex], 1, &attachment, 1, &rect); - } - - void Graphics::clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) { - std::vector attachments; - for (const auto& color : colors) { - VkClearAttachment attachment{}; - if (color.hasValue) { - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.clearValue.color.float32[0] = static_cast(color.value.r); - attachment.clearValue.color.float32[1] = static_cast(color.value.g); - attachment.clearValue.color.float32[2] = static_cast(color.value.b); - attachment.clearValue.color.float32[3] = static_cast(color.value.a); - } - attachments.push_back(attachment); - } - - VkClearRect rect{}; - rect.layerCount = 1; - rect.rect.extent.width = static_cast(currentViewportWidth); - rect.rect.extent.height = static_cast(currentViewportHeight); - - vkCmdClearAttachments(commandBuffers[imageIndex], static_cast(attachments.size()), attachments.data(), 1, &rect); - } - - void Graphics::present(void* screenshotCallbackdata) { - if (!isActive()) { - return; - } - - flushBatchedDraws(); - - endRecordingGraphicsCommands(); - - if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { - vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); - } - imagesInFlight[imageIndex] = inFlightFences[currentFrame]; - - // all data transfers should happen before any draw calls. - std::vector submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(imageIndex) }; - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - - submitInfo.commandBufferCount = static_cast(submitCommandbuffers.size()); - submitInfo.pCommandBuffers = submitCommandbuffers.data(); - - VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) }; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - vkResetFences(device, 1, &inFlightFences[currentFrame]); - - if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) { - throw love::Exception("failed to submit draw command buffer"); - } - - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - - VkSwapchainKHR swapChains[] = { swapChain }; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - - presentInfo.pImageIndices = &imageIndex; - - VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } - else if (result != VK_SUCCESS) { - throw love::Exception("failed to present swap chain image"); - } - - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - - updatedBatchedDrawBuffers(); - startRecordingGraphicsCommands(); - } - - void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { - this->width = width; - this->height = height; - this->pixelWidth = pixelwidth; - this->pixelHeight = pixelheight; - - resetProjection(); - } - - bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) { - cleanUpFunctions.clear(); - cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT); - - createVulkanInstance(); - createSurface(); - pickPhysicalDevice(); - createLogicalDevice(); - initVMA(); - initCapabilities(); - createSwapChain(); - createImageViews(); - createCommandPool(); - createCommandBuffers(); - createDefaultTexture(); - createDefaultShaders(); - createQuadIndexBuffer(); - createSyncObjects(); - startRecordingGraphicsCommands(); - currentFrame = 0; - - created = true; - - float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - - batchedDrawBuffers.clear(); - batchedDrawBuffers.reserve(MAX_FRAMES_IN_FLIGHT); - for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - batchedDrawBuffers.emplace_back(); - // Initial sizes that should be good enough for most cases. It will - // resize to fit if needed, later. - batchedDrawBuffers[i].vertexBuffer1 = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); - batchedDrawBuffers[i].vertexBuffer2 = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); - batchedDrawBuffers[i].indexBuffer = new StreamBuffer(this, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); - - // sometimes the VertexColor is not set, so we manually adjust it to white color - batchedDrawBuffers[i].constantColorBuffer = new StreamBuffer(this, BUFFERUSAGE_VERTEX, sizeof(whiteColor)); - auto mapInfo = batchedDrawBuffers[i].constantColorBuffer->map(sizeof(whiteColor)); - memcpy(mapInfo.data, whiteColor, sizeof(whiteColor)); - batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor)); - batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor)); - } - - updatedBatchedDrawBuffers(); - - Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT]; - currentPolygonMode = VK_POLYGON_MODE_FILL; - restoreState(states.back()); - - setViewportSize(width, height, pixelwidth, pixelheight); - renderTargetTexture = nullptr; - currentViewportWidth = 0.0f; - currentViewportHeight = 0.0f; - - Vulkan::resetShaderSwitches(); - - return true; - } - - void Graphics::initCapabilities() { - // todo - capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false; - capabilities.features[FEATURE_CLAMP_ZERO] = false; - capabilities.features[FEATURE_CLAMP_ONE] = false; - capabilities.features[FEATURE_BLEND_MINMAX] = false; - capabilities.features[FEATURE_LIGHTEN] = false; - capabilities.features[FEATURE_FULL_NPOT] = false; - capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = true; - capabilities.features[FEATURE_SHADER_DERIVATIVES] = false; - capabilities.features[FEATURE_GLSL3] = true; - capabilities.features[FEATURE_GLSL4] = true; - capabilities.features[FEATURE_INSTANCING] = false; - capabilities.features[FEATURE_TEXEL_BUFFER] = false; - capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true; - capabilities.features[FEATURE_COPY_BUFFER] = false; - capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = false; - capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = false; - capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false; - static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); - - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - - capabilities.limits[LIMIT_POINT_SIZE] = properties.limits.pointSizeRange[1]; - capabilities.limits[LIMIT_TEXTURE_SIZE] = properties.limits.maxImageDimension2D; - capabilities.limits[LIMIT_TEXTURE_LAYERS] = properties.limits.maxImageArrayLayers; - capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = properties.limits.maxImageDimension3D; - capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = properties.limits.maxImageDimensionCube; - capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = properties.limits.maxTexelBufferElements; // ? - capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = properties.limits.maxStorageBufferRange; // ? - capabilities.limits[LIMIT_THREADGROUPS_X] = 0; // todo - capabilities.limits[LIMIT_THREADGROUPS_Y] = 0; // todo - capabilities.limits[LIMIT_THREADGROUPS_Z] = 0; // todo - capabilities.limits[LIMIT_RENDER_TARGETS] = 1; // todo - capabilities.limits[LIMIT_TEXTURE_MSAA] = 1; // todo - capabilities.limits[LIMIT_ANISOTROPY] = 1.0f; // todo - static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!"); - - capabilities.textureTypes[TEXTURE_2D] = true; - capabilities.textureTypes[TEXTURE_VOLUME] = false; - capabilities.textureTypes[TEXTURE_2D_ARRAY] = false; - capabilities.textureTypes[TEXTURE_CUBE] = false; - } - - void Graphics::getAPIStats(int& shaderswitches) const { - shaderswitches = Vulkan::getNumShaderSwitches(); - } - - void Graphics::unSetMode() { - created = false; - vkDeviceWaitIdle(device); - Volatile::unloadAll(); - cleanup(); - } - - void Graphics::setActive(bool enable) { - flushBatchedDraws(); - active = enable; - } - - void Graphics::setFrontFaceWinding(Winding winding) { - const auto& currentState = states.back(); - - if (currentState.winding == winding) { - return; - } - - flushBatchedDraws(); - - states.back().winding = winding; - } - - void Graphics::setColorMask(ColorChannelMask mask) { - flushBatchedDraws(); - - states.back().colorMask = mask; - } - - void Graphics::setBlendState(const BlendState& blend) { - flushBatchedDraws(); - - states.back().blend = blend; - } - - void Graphics::setPointSize(float size) { - if (size != states.back().pointSize) - flushBatchedDraws(); - - states.back().pointSize = size; - } - - bool Graphics::usesGLSLES() const { - return false; - } - - 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 DrawCommand& cmd) { - prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode); - - vkCmdDraw(commandBuffers.at(imageIndex), static_cast(cmd.vertexCount), static_cast(cmd.instanceCount), static_cast(cmd.vertexStart), 0); - } - - void Graphics::draw(const DrawIndexedCommand& cmd) { - prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode); - - vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), static_cast(cmd.indexBufferOffset), getVulkanIndexBufferType(cmd.indexType)); - vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(cmd.indexCount), static_cast(cmd.instanceCount), 0, 0, 0); - } - - void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) { - const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; - const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; - - prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK); - - vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16)); - - int baseVertex = start * 4; - - for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) { - int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); - - vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(quadcount * 6), 1, 0, baseVertex, 0); - baseVertex += quadcount * 4; - } - } - - void Graphics::setColor(Colorf c) { - c.r = std::min(std::max(c.r, 0.0f), 1.0f); - c.g = std::min(std::max(c.g, 0.0f), 1.0f); - c.b = std::min(std::max(c.b, 0.0f), 1.0f); - c.a = std::min(std::max(c.a, 0.0f), 1.0f); - - states.back().color = c; - } - - void Graphics::setScissor(const Rect& rect) { - flushBatchedDraws(); - - states.back().scissor = true; - states.back().scissorRect = rect; - } - - void Graphics::setScissor() { - flushBatchedDraws(); - - states.back().scissor = false; - } - - void Graphics::setWireframe(bool enable) { - flushBatchedDraws(); - - if (enable) { - currentPolygonMode = VK_POLYGON_MODE_LINE; - } - else { - currentPolygonMode = VK_POLYGON_MODE_FILL; - } - - states.back().wireframe = enable; - } - - PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const { - switch (format) { - case PIXELFORMAT_NORMAL: - if (isGammaCorrect()) { - return PIXELFORMAT_RGBA8_UNORM_sRGB; - } - else { - return PIXELFORMAT_RGBA8_UNORM; - } - case PIXELFORMAT_HDR: - return PIXELFORMAT_RGBA16_FLOAT; - default: - return format; - } - } - - bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { - return true; - } - - Renderer Graphics::getRenderer() const { - return RENDERER_VULKAN; - } - - graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { - return new StreamBuffer(this, type, size); - } - - Matrix4 Graphics::computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const { - uint32 flags = DEVICE_PROJECTION_DEFAULT; - return calculateDeviceProjection(projection, flags); - } - - void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) { - endRenderPass(); - - if (rts.colors.size() == 0) { - startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height); - } else { - auto& firstRenderTarget = rts.getFirstTarget(); - startRenderPass(static_cast(firstRenderTarget.texture), pixelw, pixelh); - } - } - - // END IMPLEMENTATION OVERRIDDEN FUNCTIONS - - void Graphics::startRecordingGraphicsCommands() { - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); - - while (true) { - VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(); - continue; - } - else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - throw love::Exception("failed to acquire swap chain image"); - } - - break; - } - - for (auto cleanUpFn : cleanUpFunctions.at(currentFrame)) { - cleanUpFn(); - } - cleanUpFunctions.at(currentFrame).clear(); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) { - throw love::Exception("failed to begin recording command buffer"); - } - if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) { - throw love::Exception("failed to begin recording data transfer command buffer"); - } - - Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height); - - Vulkan::resetShaderSwitches(); - } - - void Graphics::endRecordingGraphicsCommands() { - endRenderPass(); - - Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) { - throw love::Exception("failed to record command buffer"); - } - if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) { - throw love::Exception("failed to record data transfer command buffer"); - } - } - - void Graphics::updatedBatchedDrawBuffers() { - batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1; - batchedDrawState.vb[0]->nextFrame(); - batchedDrawState.vb[1] = batchedDrawBuffers[currentFrame].vertexBuffer2; - batchedDrawState.vb[1]->nextFrame(); - batchedDrawState.indexBuffer = batchedDrawBuffers[currentFrame].indexBuffer; - batchedDrawState.indexBuffer->nextFrame(); - } - - uint32_t Graphics::getNumImagesInFlight() const { - return MAX_FRAMES_IN_FLIGHT; - } - - const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const { - return minUniformBufferOffsetAlignment; - } - - graphics::Texture* Graphics::getDefaultTexture() const { - return dynamic_cast(standardTexture.get()); - } - - const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const { - return vkCmdPushDescriptorSet; - } - - void Graphics::queueDatatransfer(std::function command, std::function cleanUp) { - command(dataTransferCommandBuffers.at(currentFrame)); - cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); - } - - void Graphics::queueCleanUp(std::function cleanUp) { - cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); - } - - VkCommandBuffer Graphics::beginSingleTimeCommands() { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = commandPool; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - return commandBuffer; - } - - void Graphics::endSingleTimeCommands(VkCommandBuffer commandBuffer) { - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(graphicsQueue); - vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); - } - - graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData() { - love::graphics::Shader::BuiltinUniformData data; - - data.transformMatrix = getTransform(); - data.projectionMatrix = getDeviceProjection(); - - // The normal matrix is the transpose of the inverse of the rotation portion - // (top-left 3x3) of the transform matrix. - { - Matrix3 normalmatrix = Matrix3(data.transformMatrix).transposedInverse(); - const float* e = normalmatrix.getElements(); - for (int i = 0; i < 3; i++) - { - data.normalMatrix[i].x = e[i * 3 + 0]; - data.normalMatrix[i].y = e[i * 3 + 1]; - data.normalMatrix[i].z = e[i * 3 + 2]; - data.normalMatrix[i].w = 0.0f; - } - } - - // Store DPI scale in an unused component of another vector. - data.normalMatrix[0].w = (float)getCurrentDPIScale(); - - // Same with point size. - data.normalMatrix[1].w = getPointSize(); - - data.screenSizeParams.x = static_cast(swapChainExtent.width); - data.screenSizeParams.y = static_cast(swapChainExtent.height); - - data.screenSizeParams.z = 1.0f; - data.screenSizeParams.w = 0.0f; - - data.constantColor = getColor(); - gammaCorrectColor(data.constantColor); - - return data; - } - - void Graphics::createVulkanInstance() { - if (enableValidationLayers && !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_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 = vulkanApiVersion; - - VkInstanceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - createInfo.pNext = nullptr; - - auto window = Module::getInstance(M_WINDOW); - const void* handle = window->getHandle(); - - unsigned int count; - if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, nullptr) != SDL_TRUE) { - throw love::Exception("couldn't retrieve sdl vulkan extensions"); - } - - std::vector extensions = {}; // can add more here - size_t addition_extension_count = extensions.size(); - extensions.resize(addition_extension_count + count); - - if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, extensions.data() + addition_extension_count) != SDL_TRUE) { - throw love::Exception("couldn't retrieve sdl vulkan extensions"); - } - - createInfo.enabledExtensionCount = static_cast(extensions.size()); - createInfo.ppEnabledExtensionNames = extensions.data(); - - if (enableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - } - else { - createInfo.enabledLayerCount = 0; - createInfo.ppEnabledLayerNames = nullptr; - } - - if (vkCreateInstance( - &createInfo, - nullptr, - &instance) != VK_SUCCESS) { - throw love::Exception("couldn't create vulkan instance"); - } - } - - bool Graphics::checkValidationSupport() { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const char* layerName : validationLayers) { - bool layerFound = false; - - for (const auto& layerProperties : availableLayers) { - if (strcmp(layerName, layerProperties.layerName) == 0) { - layerFound = true; - break; - } - } - - if (!layerFound) { - return false; - } - } - - return true; - } - - void Graphics::pickPhysicalDevice() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - - if (deviceCount == 0) { - throw love::Exception("failed to find GPUs with Vulkan support"); - } - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - - std::multimap candidates; - - for (const auto& device : devices) { - int score = rateDeviceSuitability(device); - candidates.insert(std::make_pair(score, device)); - } - - if (candidates.rbegin()->first > 0) { - physicalDevice = candidates.rbegin()->second; - } - else { - throw love::Exception("failed to find a suitable gpu"); - } - - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - minUniformBufferOffsetAlignment = properties.limits.minUniformBufferOffsetAlignment; - } - - bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) { - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); - - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - - std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); - - for (const auto& extension : availableExtensions) { - requiredExtensions.erase(extension.extensionName); - } - - return requiredExtensions.empty(); - } - - // if the score is nonzero then the device is suitable. - // A higher rating means generally better performance - // if the score is 0 the device is unsuitable - int Graphics::rateDeviceSuitability(VkPhysicalDevice device) { - VkPhysicalDeviceProperties deviceProperties; - VkPhysicalDeviceFeatures deviceFeatures; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - - int score = 1; - - // optional - - 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 - - QueueFamilyIndices indices = findQueueFamilies(device); - if (!indices.isComplete()) { - score = 0; - } - - bool extensionsSupported = checkDeviceExtensionSupport(device); - if (!extensionsSupported) { - score = 0; - } - - if (extensionsSupported) { - auto swapChainSupport = querySwapChainSupport(device); - bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); - if (!swapChainAdequate) { - score = 0; - } - } - - if (!deviceFeatures.samplerAnisotropy) { - score = 0; - } - - if (!deviceFeatures.fillModeNonSolid) { - score = 0; - } - - return score; - } - - QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { - QueueFamilyIndices indices; - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - int i = 0; - for (const auto& queueFamily : queueFamilies) { - if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphicsFamily = i; - } - - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - - if (presentSupport) { - indices.presentFamily = i; - } - - if (indices.isComplete()) { - break; - } - - i++; - } - - return indices; - } - - void Graphics::createLogicalDevice() { - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - - std::vector queueCreateInfos; - std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; - - float queuePriority = 1.0f; - for (uint32_t queueFamily : uniqueQueueFamilies) { - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamily; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.push_back(queueCreateInfo); - } - - VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature{}; - dynamicRenderingFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; - dynamicRenderingFeature.dynamicRendering = VK_TRUE; - - VkPhysicalDeviceFeatures deviceFeatures{}; - deviceFeatures.samplerAnisotropy = VK_TRUE; - - 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 = &dynamicRenderingFeature; - - createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); - - // can this be removed? - if (enableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - } - else { - createInfo.enabledLayerCount = 0; - } - - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { - throw love::Exception("failed to create logical device"); - } - - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); - - vkCmdPushDescriptorSet = (PFN_vkCmdPushDescriptorSetKHR) vkGetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR"); - if (!vkCmdPushDescriptorSet) { - // fixme: how widely adopted is this extension? - throw love::Exception("could not get a valid function pointer for vkCmdPushDescriptorSetKHR"); - } - } - - void Graphics::initVMA() { - VmaVulkanFunctions vulkanFunctions = {}; - vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; - vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; - - VmaAllocatorCreateInfo allocatorCreateInfo = {}; - allocatorCreateInfo.vulkanApiVersion = vulkanApiVersion; - allocatorCreateInfo.physicalDevice = physicalDevice; - allocatorCreateInfo.device = device; - allocatorCreateInfo.instance = instance; - allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; - - if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS) { - throw love::Exception("failed to create vma allocator"); - } - } - - void Graphics::createSurface() { - auto window = Module::getInstance(M_WINDOW); - const void* handle = window->getHandle(); - if (SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, &surface) != SDL_TRUE) { - throw love::Exception("failed to create window surface"); - } - } - - SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { - SwapChainSupportDetails details; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - - if (formatCount != 0) { - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); - } - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - - if (presentModeCount != 0) { - details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); - } - - return details; - } - - void Graphics::createSwapChain() { - SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); - - VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); - VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); - VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); - - uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; - if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { - imageCount = swapChainSupport.capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageColorSpace = surfaceFormat.colorSpace; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; - - if (indices.graphicsFamily != indices.presentFamily) { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices; - } - else { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = nullptr; - } - - createInfo.preTransform = swapChainSupport.capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = presentMode; - createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; - - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { - throw love::Exception("failed to create swap chain"); - } - - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); - swapChainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); - - swapChainImageFormat = surfaceFormat.format; - swapChainExtent = extent; - } - - VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector& availableFormats) { - for (const auto& availableFormat : availableFormats) { - // fixme: what if this format and colorspace is not available? - if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - return availableFormat; - } - } - - return availableFormats[0]; - } - - VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector& availablePresentModes) { - // needed ? - for (const auto& availablePresentMode : availablePresentModes) { - if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { - return availablePresentMode; - } - } - - return VK_PRESENT_MODE_FIFO_KHR; - } - - VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { - if (capabilities.currentExtent.width != UINT32_MAX) { - return capabilities.currentExtent; - } - else { - auto window = Module::getInstance(M_WINDOW); - const void* handle = window->getHandle(); - - int width, height; - // is this the equivalent of glfwGetFramebufferSize ? - SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height); - - VkExtent2D actualExtent = { - static_cast(width), - static_cast(height) - }; - - actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); - - return actualExtent; - } - } - - void Graphics::createImageViews() { - swapChainImageViews.resize(swapChainImages.size()); - - for (size_t i = 0; i < swapChainImages.size(); i++) { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapChainImages.at(i); - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChainImageFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i)) != VK_SUCCESS) { - throw love::Exception("failed to create image views"); - } - } - } - - void Graphics::createDefaultShaders() { - for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { - auto stype = (Shader::StandardShader)i; - - if (!Shader::standardShaders[i]) { - std::vector stages; - stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); - stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); - Shader::standardShaders[i] = newShader(stages, {}); - } - } - } - - void Graphics::createVulkanVertexFormat( - VertexAttributes vertexAttributes, - bool& useConstantVertexColor, - GraphicsPipelineConfiguration& configuration) { - std::set usedBuffers; - std::vector bindingDescriptions; - std::vector attributeDescriptions; - - auto allBits = vertexAttributes.enableBits; - - bool usesColor = false; - - uint8_t highestBufferBinding = 0; - - for (uint32_t i = 0; i < VertexAttributes::MAX; i++) { // change to loop like in opengl implementation ? - uint32 bit = 1u << i; - if (allBits & bit) { - if (i == ATTRIB_COLOR) { - usesColor = true; - } - - auto attrib = vertexAttributes.attribs[i]; - auto bufferBinding = attrib.bufferIndex; - if (usedBuffers.find(bufferBinding) == usedBuffers.end()) { // use .contains() when c++20 is enabled - usedBuffers.insert(bufferBinding); - - VkVertexInputBindingDescription bindingDescription{}; - bindingDescription.binding = bufferBinding; - if (vertexAttributes.instanceBits & (1u << bufferBinding)) { - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; - } - else { - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - } - bindingDescription.stride = vertexAttributes.bufferLayouts[bufferBinding].stride; - bindingDescriptions.push_back(bindingDescription); - - highestBufferBinding = std::max(highestBufferBinding, bufferBinding); - } - - VkVertexInputAttributeDescription attributeDescription{}; - attributeDescription.location = i; - attributeDescription.binding = bufferBinding; - attributeDescription.offset = attrib.offsetFromVertex; - attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format); - - attributeDescriptions.push_back(attributeDescription); - } - } - - // do we need to use a constant VertexColor? - if (!usesColor) { - // FIXME: is there a case where gaps happen between buffer bindings? - // then this doesn't work. We might need to enable null buffers again. - const auto constantColorBufferBinding = highestBufferBinding + 1; - - VkVertexInputBindingDescription bindingDescription{}; - bindingDescription.binding = constantColorBufferBinding; - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - bindingDescription.stride = 0; // no stride, will always read the same color multiple times. - bindingDescriptions.push_back(bindingDescription); - - VkVertexInputAttributeDescription attributeDescription{}; - attributeDescription.binding = constantColorBufferBinding; - attributeDescription.location = ATTRIB_COLOR; - attributeDescription.offset = 0; - attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT; - attributeDescriptions.push_back(attributeDescription); - - useConstantVertexColor = true; - } - else { - useConstantVertexColor = false; - } - - configuration.vertexInputBindingDescriptions = bindingDescriptions; - configuration.vertexInputAttributeDescriptions = attributeDescriptions; - } - - void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType primitiveType, CullMode cullmode) { - GraphicsPipelineConfiguration configuration; - configuration.shader = (Shader*)Shader::current; - configuration.primitiveType = primitiveType; - configuration.polygonMode = currentPolygonMode; - configuration.blendState = states.back().blend; - configuration.winding = states.back().winding; - configuration.cullmode = cullmode; - configuration.framebufferFormat = currentFramebufferOutputFormat; - configuration.viewportWidth = currentViewportWidth; - configuration.viewportHeight = currentViewportHeight; - if (states.back().scissor) { - configuration.scissorRect = states.back().scissorRect; - } - else { - configuration.scissorRect = std::nullopt; - } - std::vector bufferVector; - - std::vector offsets; - - bool useConstantColorBuffer; - createVulkanVertexFormat(attributes, useConstantColorBuffer, configuration); - - for (uint32_t i = 0; i < VertexAttributes::MAX; i++) { - if (buffers.useBits & (1u << i)) { - bufferVector.push_back((VkBuffer)buffers.info[i].buffer->getHandle()); - offsets.push_back((VkDeviceSize)buffers.info[i].offset); - } - } - - if (useConstantColorBuffer) { - bufferVector.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle()); - offsets.push_back((VkDeviceSize)0); - } - - auto currentUniformData = getCurrentBuiltinUniformData(); - configuration.shader->setUniformData(currentUniformData); - if (texture == nullptr) { - configuration.shader->setMainTex(standardTexture.get()); - } - else { - configuration.shader->setMainTex(texture); - } - - ensureGraphicsPipelineConfiguration(configuration); - - configuration.shader->cmdPushDescriptorSets(commandBuffers.at(imageIndex), static_cast(currentFrame)); - vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast(bufferVector.size()), bufferVector.data(), offsets.data()); - } - - void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) { - VkRenderingAttachmentInfo colorAttachmentInfo{}; - colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - if (texture) { - colorAttachmentInfo.imageView = texture->getImageView(); - auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat()); - currentFramebufferOutputFormat = vulkanFormat.internalFormat; - - renderTargetTexture = texture; - } else { - colorAttachmentInfo.imageView = swapChainImageViews[imageIndex]; - currentFramebufferOutputFormat = swapChainImageFormat; - - renderTargetTexture = nullptr; - } - colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - - VkRenderingInfo renderingInfo{}; - renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - renderingInfo.renderArea.extent.width = w; - renderingInfo.renderArea.extent.height = h; - renderingInfo.layerCount = 1; - renderingInfo.colorAttachmentCount = 1; - renderingInfo.pColorAttachments = &colorAttachmentInfo; - - currentViewportWidth = (float)w; - currentViewportHeight = (float)h; - - if (renderTargetTexture) { - Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)texture->getHandle(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } - - vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo); - - currentGraphicsPipeline = VK_NULL_HANDLE; - } - - void Graphics::endRenderPass() { - vkCmdEndRendering(commandBuffers.at(imageIndex)); - - if (renderTargetTexture) { - Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)renderTargetTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); - renderTargetTexture = nullptr; - } - } - - VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) { - auto &shaderStages = configuration.shader->getShaderStages(); - - VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = static_cast(configuration.vertexInputBindingDescriptions.size()); - vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data(); - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(configuration.vertexInputAttributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data(); - - VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = Vulkan::getPrimitiveTypeTopology(configuration.primitiveType); - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = configuration.viewportWidth; - viewport.height = configuration.viewportHeight; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor{}; - if (configuration.scissorRect.has_value()) { - scissor.offset.x = configuration.scissorRect.value().x; - scissor.offset.y = configuration.scissorRect.value().y; - scissor.extent.width = static_cast(configuration.scissorRect.value().w); - scissor.extent.height = static_cast(configuration.scissorRect.value().h); - } - else { - scissor.offset = { 0, 0 }; - scissor.extent = swapChainExtent; - } - - VkPipelineViewportStateCreateInfo viewportState{}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizer{}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = configuration.polygonMode; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = Vulkan::getCullMode(configuration.cullmode); - rasterizer.frontFace = Vulkan::getFrontFace(configuration.winding); - rasterizer.depthBiasEnable = VK_FALSE; - rasterizer.depthBiasConstantFactor = 0.0f; - rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 0.0f; - - VkPipelineMultisampleStateCreateInfo multisampling{}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampling.minSampleShading = 1.0f; // Optional - multisampling.pSampleMask = nullptr; // Optional - multisampling.alphaToCoverageEnable = VK_FALSE; // Optional - multisampling.alphaToOneEnable = VK_FALSE; // Optional - - VkPipelineColorBlendAttachmentState colorBlendAttachment{}; - colorBlendAttachment.colorWriteMask = Vulkan::getColorMask(configuration.colorChannelMask); - colorBlendAttachment.blendEnable = Vulkan::getBool(configuration.blendState.enable); - colorBlendAttachment.srcColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorRGB); - colorBlendAttachment.dstColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorRGB); - colorBlendAttachment.colorBlendOp = Vulkan::getBlendOp(configuration.blendState.operationRGB); - colorBlendAttachment.srcAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorA); - colorBlendAttachment.dstAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorA); - colorBlendAttachment.alphaBlendOp = Vulkan::getBlendOp(configuration.blendState.operationA); - - VkPipelineColorBlendStateCreateInfo colorBlending{}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkFormat framebufferOutputFormat = configuration.framebufferFormat; - - VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{}; - pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; - pipelineRenderingCreateInfo.colorAttachmentCount = 1; - pipelineRenderingCreateInfo.pColorAttachmentFormats = &framebufferOutputFormat; - - VkGraphicsPipelineCreateInfo pipelineInfo{}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = static_cast(shaderStages.size()); - pipelineInfo.pStages = shaderStages.data(); - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pDepthStencilState = nullptr; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = configuration.shader->getGraphicsPipelineLayout(); - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - pipelineInfo.pNext = &pipelineRenderingCreateInfo; - - VkPipeline graphicsPipeline; - if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { - throw love::Exception("failed to create graphics pipeline"); - } - return graphicsPipeline; - } - - void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) { - VkPipeline pipeline = VK_NULL_HANDLE; - for (auto const& p : graphicsPipelines) { - if (p.first == configuration) { - pipeline = p.second; - break; - } - } - if (pipeline != VK_NULL_HANDLE) { - if (currentGraphicsPipeline != pipeline) { - vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - currentGraphicsPipeline = pipeline; - } - } else { - VkPipeline newPipeLine = createGraphicsPipeline(configuration); - graphicsPipelines.push_back(std::make_pair(configuration, newPipeLine)); - vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, newPipeLine); - currentGraphicsPipeline = newPipeLine; - } - } - - void Graphics::createCommandPool() { - QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); - - VkCommandPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; - - if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { - throw love::Exception("failed to create command pool"); - } - } - - void Graphics::createCommandBuffers() { - commandBuffers.resize(swapChainImages.size()); - dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = commandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); - - if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { - throw love::Exception("failed to allocate command buffers"); - } - - VkCommandBufferAllocateInfo dataTransferAllocInfo{}; - dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - dataTransferAllocInfo.commandPool = commandPool; - dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT; - - if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) { - throw love::Exception("failed to allocate data transfer command buffers"); - } - } - - void Graphics::createSyncObjects() { - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); - - VkSemaphoreCreateInfo semaphoreInfo{}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores.at(i)) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores.at(i)) != VK_SUCCESS || - vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences.at(i)) != VK_SUCCESS) { - throw love::Exception("failed to create synchronization objects for a frame!"); - } - } - } - - void Graphics::createDefaultTexture() { - Texture::Settings settings; - standardTexture.reset((Texture*)newTexture(settings)); - } - - void Graphics::createQuadIndexBuffer() { - if (quadIndexBuffer != nullptr) - return; - - size_t size = sizeof(uint16) * getIndexCount(TRIANGLEINDEX_QUADS, LOVE_UINT16_MAX); - quadIndexBuffer.reset((StreamBuffer*)newStreamBuffer(BUFFERUSAGE_INDEX, size)); - auto map = quadIndexBuffer->map(size); - fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16*)map.data); - quadIndexBuffer->unmap(size); - } - - bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) { - if (first.scissorRect != other.scissorRect) { - return false; - } - if (first.viewportHeight != other.viewportHeight) { - return false; - } - if (first.viewportWidth != other.viewportWidth) { - return false; - } - if (first.framebufferFormat != other.framebufferFormat) { - return false; - } - if (first.cullmode != other.cullmode) { - return false; - } - if (first.winding != other.winding) { - return false; - } - if (first.colorChannelMask != other.colorChannelMask) { - return false; - } - if (!(first.blendState == other.blendState)) { // not sure why != doesn't work - return false; - } - if (first.polygonMode != other.polygonMode) { - return false; - } - if (first.primitiveType != other.primitiveType) { - return false; - } - if (first.shader != other.shader) { - return false; - } - if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) { - return false; - } - if (first.vertexInputBindingDescriptions.size() != other.vertexInputBindingDescriptions.size()) { - return false; - } - for (uint32_t i = 0; i < first.vertexInputAttributeDescriptions.size(); i++) { - const VkVertexInputAttributeDescription& x = first.vertexInputAttributeDescriptions[i]; - const VkVertexInputAttributeDescription& y = other.vertexInputAttributeDescriptions[i]; - if (x.binding != y.binding) { - return false; - } - if (x.location != y.location) { - return false; - } - if (x.offset != y.offset) { - return false; - } - if (x.format != y.format) { - return false; - } - } - for (uint32_t i = 0; i < first.vertexInputBindingDescriptions.size(); i++) { - const VkVertexInputBindingDescription& x = first.vertexInputBindingDescriptions[i]; - const VkVertexInputBindingDescription& y = other.vertexInputBindingDescriptions[i]; - if (x.binding != y.binding) { - return false; - } - if (x.inputRate != y.inputRate) { - return false; - } - if (x.stride != y.stride) { - return false; - } - } - return true; - } - - void Graphics::cleanup() { - cleanupSwapChain(); - - for (auto &cleanUpFns : cleanUpFunctions) { - for (auto cleanUpFn : cleanUpFns) { - cleanUpFn(); - } - } - cleanUpFunctions.clear(); - - vmaDestroyAllocator(vmaAllocator); - batchedDrawBuffers.clear(); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - - vkDestroyCommandPool(device, commandPool, nullptr); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - } - - void Graphics::cleanupSwapChain() { - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data()); - for (auto const& p : graphicsPipelines) { - vkDestroyPipeline(device, p.second, nullptr); - } - graphicsPipelines.clear(); - currentGraphicsPipeline = VK_NULL_HANDLE; - for (size_t i = 0; i < swapChainImageViews.size(); i++) { - vkDestroyImageView(device, swapChainImageViews[i], nullptr); - } - vkDestroySwapchainKHR(device, swapChain, nullptr); - } - - void Graphics::recreateSwapChain() { - vkDeviceWaitIdle(device); - - cleanupSwapChain(); - - createSwapChain(); - createImageViews(); - createCommandBuffers(); - startRecordingGraphicsCommands(); - } - - love::graphics::Graphics* createInstance() { - love::graphics::Graphics* instance = nullptr; - - try { - instance = new Graphics(); - } - catch (love::Exception& e) { - printf("Cannot create Vulkan renderer: %s\n", e.what()); - } - - return instance; - } +love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { + return new Buffer(this, settings, format, data, size, arraylength); +} + +// FIXME: clear stencil and depth missing. +void Graphics::clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) { + VkClearAttachment attachment{}; + + if (color.hasValue) { + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.clearValue.color.float32[0] = static_cast(color.value.r); + attachment.clearValue.color.float32[1] = static_cast(color.value.g); + attachment.clearValue.color.float32[2] = static_cast(color.value.b); + attachment.clearValue.color.float32[3] = static_cast(color.value.a); + } + + VkClearRect rect{}; + rect.layerCount = 1; + rect.rect.extent.width = static_cast(currentViewportWidth); + rect.rect.extent.height = static_cast(currentViewportHeight); + + vkCmdClearAttachments(commandBuffers[imageIndex], 1, &attachment, 1, &rect); +} + +void Graphics::clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) { + std::vector attachments; + for (const auto& color : colors) { + VkClearAttachment attachment{}; + if (color.hasValue) { + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.clearValue.color.float32[0] = static_cast(color.value.r); + attachment.clearValue.color.float32[1] = static_cast(color.value.g); + attachment.clearValue.color.float32[2] = static_cast(color.value.b); + attachment.clearValue.color.float32[3] = static_cast(color.value.a); + } + attachments.push_back(attachment); + } + + VkClearRect rect{}; + rect.layerCount = 1; + rect.rect.extent.width = static_cast(currentViewportWidth); + rect.rect.extent.height = static_cast(currentViewportHeight); + + vkCmdClearAttachments(commandBuffers[imageIndex], static_cast(attachments.size()), attachments.data(), 1, &rect); +} + +void Graphics::present(void* screenshotCallbackdata) { + if (!isActive()) { + return; + } + + flushBatchedDraws(); + + endRecordingGraphicsCommands(); + + if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { + vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); + } + imagesInFlight[imageIndex] = inFlightFences[currentFrame]; + + // all data transfers should happen before any draw calls. + std::vector submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(imageIndex) }; + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = static_cast(submitCommandbuffers.size()); + submitInfo.pCommandBuffers = submitCommandbuffers.data(); + + VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) { + throw love::Exception("failed to submit draw command buffer"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = { swapChain }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } + else if (result != VK_SUCCESS) { + throw love::Exception("failed to present swap chain image"); + } + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + updatedBatchedDrawBuffers(); + startRecordingGraphicsCommands(); +} + +void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + this->width = width; + this->height = height; + this->pixelWidth = pixelwidth; + this->pixelHeight = pixelheight; + + resetProjection(); +} + +bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) { + cleanUpFunctions.clear(); + cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT); + + createVulkanInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + initVMA(); + initCapabilities(); + createSwapChain(); + createImageViews(); + createCommandPool(); + createCommandBuffers(); + createDefaultTexture(); + createDefaultShaders(); + createQuadIndexBuffer(); + createSyncObjects(); + startRecordingGraphicsCommands(); + currentFrame = 0; + + created = true; + + float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + batchedDrawBuffers.clear(); + batchedDrawBuffers.reserve(MAX_FRAMES_IN_FLIGHT); + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + batchedDrawBuffers.emplace_back(); + // Initial sizes that should be good enough for most cases. It will + // resize to fit if needed, later. + batchedDrawBuffers[i].vertexBuffer1 = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); + batchedDrawBuffers[i].vertexBuffer2 = new StreamBuffer(this, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); + batchedDrawBuffers[i].indexBuffer = new StreamBuffer(this, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + + // sometimes the VertexColor is not set, so we manually adjust it to white color + batchedDrawBuffers[i].constantColorBuffer = new StreamBuffer(this, BUFFERUSAGE_VERTEX, sizeof(whiteColor)); + auto mapInfo = batchedDrawBuffers[i].constantColorBuffer->map(sizeof(whiteColor)); + memcpy(mapInfo.data, whiteColor, sizeof(whiteColor)); + batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor)); + batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor)); + } + + updatedBatchedDrawBuffers(); + + Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT]; + currentPolygonMode = VK_POLYGON_MODE_FILL; + restoreState(states.back()); + + setViewportSize(width, height, pixelwidth, pixelheight); + renderTargetTexture = nullptr; + currentViewportWidth = 0.0f; + currentViewportHeight = 0.0f; + + Vulkan::resetShaderSwitches(); + + return true; +} + +void Graphics::initCapabilities() { + // todo + capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false; + capabilities.features[FEATURE_CLAMP_ZERO] = false; + capabilities.features[FEATURE_CLAMP_ONE] = false; + capabilities.features[FEATURE_BLEND_MINMAX] = false; + capabilities.features[FEATURE_LIGHTEN] = false; + capabilities.features[FEATURE_FULL_NPOT] = false; + capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = true; + capabilities.features[FEATURE_SHADER_DERIVATIVES] = false; + capabilities.features[FEATURE_GLSL3] = true; + capabilities.features[FEATURE_GLSL4] = true; + capabilities.features[FEATURE_INSTANCING] = false; + capabilities.features[FEATURE_TEXEL_BUFFER] = false; + capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true; + capabilities.features[FEATURE_COPY_BUFFER] = false; + capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = false; + capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = false; + capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false; + static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); + + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + + capabilities.limits[LIMIT_POINT_SIZE] = properties.limits.pointSizeRange[1]; + capabilities.limits[LIMIT_TEXTURE_SIZE] = properties.limits.maxImageDimension2D; + capabilities.limits[LIMIT_TEXTURE_LAYERS] = properties.limits.maxImageArrayLayers; + capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = properties.limits.maxImageDimension3D; + capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = properties.limits.maxImageDimensionCube; + capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = properties.limits.maxTexelBufferElements; // ? + capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = properties.limits.maxStorageBufferRange; // ? + capabilities.limits[LIMIT_THREADGROUPS_X] = 0; // todo + capabilities.limits[LIMIT_THREADGROUPS_Y] = 0; // todo + capabilities.limits[LIMIT_THREADGROUPS_Z] = 0; // todo + capabilities.limits[LIMIT_RENDER_TARGETS] = 1; // todo + capabilities.limits[LIMIT_TEXTURE_MSAA] = 1; // todo + capabilities.limits[LIMIT_ANISOTROPY] = 1.0f; // todo + static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!"); + + capabilities.textureTypes[TEXTURE_2D] = true; + capabilities.textureTypes[TEXTURE_VOLUME] = false; + capabilities.textureTypes[TEXTURE_2D_ARRAY] = false; + capabilities.textureTypes[TEXTURE_CUBE] = false; +} + +void Graphics::getAPIStats(int& shaderswitches) const { + shaderswitches = Vulkan::getNumShaderSwitches(); +} + +void Graphics::unSetMode() { + created = false; + vkDeviceWaitIdle(device); + Volatile::unloadAll(); + cleanup(); +} + +void Graphics::setActive(bool enable) { + flushBatchedDraws(); + active = enable; +} + +void Graphics::setFrontFaceWinding(Winding winding) { + const auto& currentState = states.back(); + + if (currentState.winding == winding) { + return; + } + + flushBatchedDraws(); + + states.back().winding = winding; +} + +void Graphics::setColorMask(ColorChannelMask mask) { + flushBatchedDraws(); + + states.back().colorMask = mask; +} + +void Graphics::setBlendState(const BlendState& blend) { + flushBatchedDraws(); + + states.back().blend = blend; +} + +void Graphics::setPointSize(float size) { + if (size != states.back().pointSize) + flushBatchedDraws(); + + states.back().pointSize = size; +} + +bool Graphics::usesGLSLES() const { + return false; +} + +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 DrawCommand& cmd) { + prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode); + + vkCmdDraw(commandBuffers.at(imageIndex), static_cast(cmd.vertexCount), static_cast(cmd.instanceCount), static_cast(cmd.vertexStart), 0); +} + +void Graphics::draw(const DrawIndexedCommand& cmd) { + prepareDraw(*cmd.attributes, *cmd.buffers, cmd.texture, cmd.primitiveType, cmd.cullMode); + + vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), static_cast(cmd.indexBufferOffset), getVulkanIndexBufferType(cmd.indexType)); + vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(cmd.indexCount), static_cast(cmd.instanceCount), 0, 0, 0); +} + +void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) { + const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; + const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; + + prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK); + + vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16)); + + int baseVertex = start * 4; + + for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) { + int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); + + vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(quadcount * 6), 1, 0, baseVertex, 0); + baseVertex += quadcount * 4; + } +} + +void Graphics::setColor(Colorf c) { + c.r = std::min(std::max(c.r, 0.0f), 1.0f); + c.g = std::min(std::max(c.g, 0.0f), 1.0f); + c.b = std::min(std::max(c.b, 0.0f), 1.0f); + c.a = std::min(std::max(c.a, 0.0f), 1.0f); + + states.back().color = c; +} + +void Graphics::setScissor(const Rect& rect) { + flushBatchedDraws(); + + states.back().scissor = true; + states.back().scissorRect = rect; +} + +void Graphics::setScissor() { + flushBatchedDraws(); + + states.back().scissor = false; +} + +void Graphics::setWireframe(bool enable) { + flushBatchedDraws(); + + if (enable) { + currentPolygonMode = VK_POLYGON_MODE_LINE; + } + else { + currentPolygonMode = VK_POLYGON_MODE_FILL; + } + + states.back().wireframe = enable; +} + +PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const { + switch (format) { + case PIXELFORMAT_NORMAL: + if (isGammaCorrect()) { + return PIXELFORMAT_RGBA8_UNORM_sRGB; + } + else { + return PIXELFORMAT_RGBA8_UNORM; + } + case PIXELFORMAT_HDR: + return PIXELFORMAT_RGBA16_FLOAT; + default: + return format; + } +} + +bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { + return true; +} + +Renderer Graphics::getRenderer() const { + return RENDERER_VULKAN; +} + +graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { + return new StreamBuffer(this, type, size); +} + +Matrix4 Graphics::computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const { + uint32 flags = DEVICE_PROJECTION_DEFAULT; + return calculateDeviceProjection(projection, flags); +} + +void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) { + endRenderPass(); + + if (rts.colors.size() == 0) { + startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height); + } else { + auto& firstRenderTarget = rts.getFirstTarget(); + startRenderPass(static_cast(firstRenderTarget.texture), pixelw, pixelh); + } +} + +// END IMPLEMENTATION OVERRIDDEN FUNCTIONS + +void Graphics::startRecordingGraphicsCommands() { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + while (true) { + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + continue; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw love::Exception("failed to acquire swap chain image"); + } + + break; + } + + for (auto& cleanUpFn : cleanUpFunctions.at(currentFrame)) { + cleanUpFn(); + } + cleanUpFunctions.at(currentFrame).clear(); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording command buffer"); + } + if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording data transfer command buffer"); + } + + Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height); + + Vulkan::resetShaderSwitches(); +} + +void Graphics::endRecordingGraphicsCommands() { + endRenderPass(); + + Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) { + throw love::Exception("failed to record command buffer"); + } + if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) { + throw love::Exception("failed to record data transfer command buffer"); + } +} + +void Graphics::updatedBatchedDrawBuffers() { + batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1; + batchedDrawState.vb[0]->nextFrame(); + batchedDrawState.vb[1] = batchedDrawBuffers[currentFrame].vertexBuffer2; + batchedDrawState.vb[1]->nextFrame(); + batchedDrawState.indexBuffer = batchedDrawBuffers[currentFrame].indexBuffer; + batchedDrawState.indexBuffer->nextFrame(); +} + +uint32_t Graphics::getNumImagesInFlight() const { + return MAX_FRAMES_IN_FLIGHT; +} + +const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const { + return minUniformBufferOffsetAlignment; +} + +graphics::Texture* Graphics::getDefaultTexture() const { + return dynamic_cast(standardTexture.get()); +} + +const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const { + return vkCmdPushDescriptorSet; +} + +void Graphics::queueDatatransfer(std::function command, std::function cleanUp) { + command(dataTransferCommandBuffers.at(currentFrame)); + cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); +} + +void Graphics::queueCleanUp(std::function cleanUp) { + cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); +} + +VkCommandBuffer Graphics::beginSingleTimeCommands() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + return commandBuffer; +} + +void Graphics::endSingleTimeCommands(VkCommandBuffer commandBuffer) { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(graphicsQueue); + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); +} + +graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData() { + love::graphics::Shader::BuiltinUniformData data; + + data.transformMatrix = getTransform(); + data.projectionMatrix = getDeviceProjection(); + + // The normal matrix is the transpose of the inverse of the rotation portion + // (top-left 3x3) of the transform matrix. + { + Matrix3 normalmatrix = Matrix3(data.transformMatrix).transposedInverse(); + const float* e = normalmatrix.getElements(); + for (int i = 0; i < 3; i++) + { + data.normalMatrix[i].x = e[i * 3 + 0]; + data.normalMatrix[i].y = e[i * 3 + 1]; + data.normalMatrix[i].z = e[i * 3 + 2]; + data.normalMatrix[i].w = 0.0f; + } + } + + // Store DPI scale in an unused component of another vector. + data.normalMatrix[0].w = (float)getCurrentDPIScale(); + + // Same with point size. + data.normalMatrix[1].w = getPointSize(); + + data.screenSizeParams.x = static_cast(swapChainExtent.width); + data.screenSizeParams.y = static_cast(swapChainExtent.height); + + data.screenSizeParams.z = 1.0f; + data.screenSizeParams.w = 0.0f; + + data.constantColor = getColor(); + gammaCorrectColor(data.constantColor); + + return data; +} + +void Graphics::createVulkanInstance() { + if (enableValidationLayers && !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_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 = vulkanApiVersion; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.pNext = nullptr; + + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + unsigned int count; + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, nullptr) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + std::vector extensions = {}; // can add more here + size_t addition_extension_count = extensions.size(); + extensions.resize(addition_extension_count + count); + + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, extensions.data() + addition_extension_count) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + createInfo.ppEnabledLayerNames = nullptr; + } + + if (vkCreateInstance( + &createInfo, + nullptr, + &instance) != VK_SUCCESS) { + throw love::Exception("couldn't create vulkan instance"); + } +} + +bool Graphics::checkValidationSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { + return false; + } + } + + return true; +} + +void Graphics::pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw love::Exception("failed to find GPUs with Vulkan support"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + std::multimap candidates; + + for (const auto& device : devices) { + int score = rateDeviceSuitability(device); + candidates.insert(std::make_pair(score, device)); + } + + if (candidates.rbegin()->first > 0) { + physicalDevice = candidates.rbegin()->second; + } + else { + throw love::Exception("failed to find a suitable gpu"); + } + + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + minUniformBufferOffsetAlignment = properties.limits.minUniformBufferOffsetAlignment; +} + +bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); +} + +// if the score is nonzero then the device is suitable. +// A higher rating means generally better performance +// if the score is 0 the device is unsuitable +int Graphics::rateDeviceSuitability(VkPhysicalDevice device) { + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + int score = 1; + + // optional + + 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 + + QueueFamilyIndices indices = findQueueFamilies(device); + if (!indices.isComplete()) { + score = 0; + } + + bool extensionsSupported = checkDeviceExtensionSupport(device); + if (!extensionsSupported) { + score = 0; + } + + if (extensionsSupported) { + auto swapChainSupport = querySwapChainSupport(device); + bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + if (!swapChainAdequate) { + score = 0; + } + } + + if (!deviceFeatures.samplerAnisotropy) { + score = 0; + } + + if (!deviceFeatures.fillModeNonSolid) { + score = 0; + } + + return score; +} + +QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (presentSupport) { + indices.presentFamily = i; + } + + if (indices.isComplete()) { + break; + } + + i++; + } + + return indices; +} + +void Graphics::createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature{}; + dynamicRenderingFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; + dynamicRenderingFeature.dynamicRendering = VK_TRUE; + + VkPhysicalDeviceFeatures deviceFeatures{}; + deviceFeatures.samplerAnisotropy = VK_TRUE; + + 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 = &dynamicRenderingFeature; + + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + // can this be removed? + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw love::Exception("failed to create logical device"); + } + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + + vkCmdPushDescriptorSet = (PFN_vkCmdPushDescriptorSetKHR) vkGetDeviceProcAddr(device, "vkCmdPushDescriptorSetKHR"); + if (!vkCmdPushDescriptorSet) { + // fixme: how widely adopted is this extension? + throw love::Exception("could not get a valid function pointer for vkCmdPushDescriptorSetKHR"); + } +} + +void Graphics::initVMA() { + VmaVulkanFunctions vulkanFunctions = {}; + vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; + vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo allocatorCreateInfo = {}; + allocatorCreateInfo.vulkanApiVersion = vulkanApiVersion; + allocatorCreateInfo.physicalDevice = physicalDevice; + allocatorCreateInfo.device = device; + allocatorCreateInfo.instance = instance; + allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + + if (vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator) != VK_SUCCESS) { + throw love::Exception("failed to create vma allocator"); + } +} + +void Graphics::createSurface() { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + if (SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, &surface) != SDL_TRUE) { + throw love::Exception("failed to create window surface"); + } +} + +SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; +} + +void Graphics::createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { + throw love::Exception("failed to create swap chain"); + } + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; +} + +VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + // fixme: what if this format and colorspace is not available? + if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; +} + +VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector& availablePresentModes) { + // needed ? + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != UINT32_MAX) { + return capabilities.currentExtent; + } + else { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + int width, height; + // is this the equivalent of glfwGetFramebufferSize ? + SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } +} + +void Graphics::createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages.at(i); + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i)) != VK_SUCCESS) { + throw love::Exception("failed to create image views"); } } } + +void Graphics::createDefaultShaders() { + for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { + auto stype = (Shader::StandardShader)i; + + if (!Shader::standardShaders[i]) { + std::vector stages; + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); + Shader::standardShaders[i] = newShader(stages, {}); + } + } +} + +void Graphics::createVulkanVertexFormat( + VertexAttributes vertexAttributes, + bool& useConstantVertexColor, + GraphicsPipelineConfiguration& configuration) { + std::set usedBuffers; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + + auto allBits = vertexAttributes.enableBits; + + bool usesColor = false; + + uint8_t highestBufferBinding = 0; + + for (uint32_t i = 0; i < VertexAttributes::MAX; i++) { // change to loop like in opengl implementation ? + uint32 bit = 1u << i; + if (allBits & bit) { + if (i == ATTRIB_COLOR) { + usesColor = true; + } + + auto attrib = vertexAttributes.attribs[i]; + auto bufferBinding = attrib.bufferIndex; + if (usedBuffers.find(bufferBinding) == usedBuffers.end()) { // use .contains() when c++20 is enabled + usedBuffers.insert(bufferBinding); + + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = bufferBinding; + if (vertexAttributes.instanceBits & (1u << bufferBinding)) { + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; + } + else { + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + } + bindingDescription.stride = vertexAttributes.bufferLayouts[bufferBinding].stride; + bindingDescriptions.push_back(bindingDescription); + + highestBufferBinding = std::max(highestBufferBinding, bufferBinding); + } + + VkVertexInputAttributeDescription attributeDescription{}; + attributeDescription.location = i; + attributeDescription.binding = bufferBinding; + attributeDescription.offset = attrib.offsetFromVertex; + attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format); + + attributeDescriptions.push_back(attributeDescription); + } + } + + // do we need to use a constant VertexColor? + if (!usesColor) { + // FIXME: is there a case where gaps happen between buffer bindings? + // then this doesn't work. We might need to enable null buffers again. + const auto constantColorBufferBinding = highestBufferBinding + 1; + + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = constantColorBufferBinding; + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindingDescription.stride = 0; // no stride, will always read the same color multiple times. + bindingDescriptions.push_back(bindingDescription); + + VkVertexInputAttributeDescription attributeDescription{}; + attributeDescription.binding = constantColorBufferBinding; + attributeDescription.location = ATTRIB_COLOR; + attributeDescription.offset = 0; + attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT; + attributeDescriptions.push_back(attributeDescription); + + useConstantVertexColor = true; + } + else { + useConstantVertexColor = false; + } + + configuration.vertexInputBindingDescriptions = bindingDescriptions; + configuration.vertexInputAttributeDescriptions = attributeDescriptions; +} + +void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType primitiveType, CullMode cullmode) { + GraphicsPipelineConfiguration configuration; + configuration.shader = (Shader*)Shader::current; + configuration.primitiveType = primitiveType; + configuration.polygonMode = currentPolygonMode; + configuration.blendState = states.back().blend; + configuration.winding = states.back().winding; + configuration.cullmode = cullmode; + configuration.framebufferFormat = currentFramebufferOutputFormat; + configuration.viewportWidth = currentViewportWidth; + configuration.viewportHeight = currentViewportHeight; + if (states.back().scissor) { + configuration.scissorRect = states.back().scissorRect; + } + else { + configuration.scissorRect = std::nullopt; + } + std::vector bufferVector; + + std::vector offsets; + + bool useConstantColorBuffer; + createVulkanVertexFormat(attributes, useConstantColorBuffer, configuration); + + for (uint32_t i = 0; i < VertexAttributes::MAX; i++) { + if (buffers.useBits & (1u << i)) { + bufferVector.push_back((VkBuffer)buffers.info[i].buffer->getHandle()); + offsets.push_back((VkDeviceSize)buffers.info[i].offset); + } + } + + if (useConstantColorBuffer) { + bufferVector.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle()); + offsets.push_back((VkDeviceSize)0); + } + + auto currentUniformData = getCurrentBuiltinUniformData(); + configuration.shader->setUniformData(currentUniformData); + if (texture == nullptr) { + configuration.shader->setMainTex(standardTexture.get()); + } + else { + configuration.shader->setMainTex(texture); + } + + ensureGraphicsPipelineConfiguration(configuration); + + configuration.shader->cmdPushDescriptorSets(commandBuffers.at(imageIndex), static_cast(currentFrame)); + vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast(bufferVector.size()), bufferVector.data(), offsets.data()); +} + +void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) { + VkRenderingAttachmentInfo colorAttachmentInfo{}; + colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + if (texture) { + colorAttachmentInfo.imageView = texture->getImageView(); + auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat()); + currentFramebufferOutputFormat = vulkanFormat.internalFormat; + + renderTargetTexture = texture; + } else { + colorAttachmentInfo.imageView = swapChainImageViews[imageIndex]; + currentFramebufferOutputFormat = swapChainImageFormat; + + renderTargetTexture = nullptr; + } + colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + VkRenderingInfo renderingInfo{}; + renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + renderingInfo.renderArea.extent.width = w; + renderingInfo.renderArea.extent.height = h; + renderingInfo.layerCount = 1; + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachments = &colorAttachmentInfo; + + currentViewportWidth = (float)w; + currentViewportHeight = (float)h; + + if (renderTargetTexture) { + Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)texture->getHandle(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } + + vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo); + + currentGraphicsPipeline = VK_NULL_HANDLE; +} + +void Graphics::endRenderPass() { + vkCmdEndRendering(commandBuffers.at(imageIndex)); + + if (renderTargetTexture) { + Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)renderTargetTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + renderTargetTexture = nullptr; + } +} + +VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) { + auto &shaderStages = configuration.shader->getShaderStages(); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = static_cast(configuration.vertexInputBindingDescriptions.size()); + vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(configuration.vertexInputAttributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = Vulkan::getPrimitiveTypeTopology(configuration.primitiveType); + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = configuration.viewportWidth; + viewport.height = configuration.viewportHeight; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + if (configuration.scissorRect.has_value()) { + scissor.offset.x = configuration.scissorRect.value().x; + scissor.offset.y = configuration.scissorRect.value().y; + scissor.extent.width = static_cast(configuration.scissorRect.value().w); + scissor.extent.height = static_cast(configuration.scissorRect.value().h); + } + else { + scissor.offset = { 0, 0 }; + scissor.extent = swapChainExtent; + } + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = configuration.polygonMode; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = Vulkan::getCullMode(configuration.cullmode); + rasterizer.frontFace = Vulkan::getFrontFace(configuration.winding); + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = nullptr; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = Vulkan::getColorMask(configuration.colorChannelMask); + colorBlendAttachment.blendEnable = Vulkan::getBool(configuration.blendState.enable); + colorBlendAttachment.srcColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorRGB); + colorBlendAttachment.dstColorBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorRGB); + colorBlendAttachment.colorBlendOp = Vulkan::getBlendOp(configuration.blendState.operationRGB); + colorBlendAttachment.srcAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.srcFactorA); + colorBlendAttachment.dstAlphaBlendFactor = Vulkan::getBlendFactor(configuration.blendState.dstFactorA); + colorBlendAttachment.alphaBlendOp = Vulkan::getBlendOp(configuration.blendState.operationA); + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkFormat framebufferOutputFormat = configuration.framebufferFormat; + + VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{}; + pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + pipelineRenderingCreateInfo.colorAttachmentCount = 1; + pipelineRenderingCreateInfo.pColorAttachmentFormats = &framebufferOutputFormat; + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = static_cast(shaderStages.size()); + pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = configuration.shader->getGraphicsPipelineLayout(); + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + pipelineInfo.pNext = &pipelineRenderingCreateInfo; + + VkPipeline graphicsPipeline; + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw love::Exception("failed to create graphics pipeline"); + } + return graphicsPipeline; +} + +void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) { + VkPipeline pipeline = VK_NULL_HANDLE; + for (auto const& p : graphicsPipelines) { + if (p.first == configuration) { + pipeline = p.second; + break; + } + } + if (pipeline != VK_NULL_HANDLE) { + if (currentGraphicsPipeline != pipeline) { + vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + currentGraphicsPipeline = pipeline; + } + } else { + VkPipeline newPipeLine = createGraphicsPipeline(configuration); + graphicsPipelines.push_back(std::make_pair(configuration, newPipeLine)); + vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, newPipeLine); + currentGraphicsPipeline = newPipeLine; + } +} + +void Graphics::createCommandPool() { + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw love::Exception("failed to create command pool"); + } +} + +void Graphics::createCommandBuffers() { + commandBuffers.resize(swapChainImages.size()); + dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate command buffers"); + } + + VkCommandBufferAllocateInfo dataTransferAllocInfo{}; + dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + dataTransferAllocInfo.commandPool = commandPool; + dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT; + + if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate data transfer command buffers"); + } +} + +void Graphics::createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores.at(i)) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores.at(i)) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences.at(i)) != VK_SUCCESS) { + throw love::Exception("failed to create synchronization objects for a frame!"); + } + } +} + +void Graphics::createDefaultTexture() { + Texture::Settings settings; + standardTexture.reset((Texture*)newTexture(settings)); +} + +void Graphics::createQuadIndexBuffer() { + if (quadIndexBuffer != nullptr) + return; + + size_t size = sizeof(uint16) * getIndexCount(TRIANGLEINDEX_QUADS, LOVE_UINT16_MAX); + quadIndexBuffer.reset((StreamBuffer*)newStreamBuffer(BUFFERUSAGE_INDEX, size)); + auto map = quadIndexBuffer->map(size); + fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16*)map.data); + quadIndexBuffer->unmap(size); +} + +bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) { + if (first.scissorRect != other.scissorRect) { + return false; + } + if (first.viewportHeight != other.viewportHeight) { + return false; + } + if (first.viewportWidth != other.viewportWidth) { + return false; + } + if (first.framebufferFormat != other.framebufferFormat) { + return false; + } + if (first.cullmode != other.cullmode) { + return false; + } + if (first.winding != other.winding) { + return false; + } + if (first.colorChannelMask != other.colorChannelMask) { + return false; + } + if (!(first.blendState == other.blendState)) { // not sure why != doesn't work + return false; + } + if (first.polygonMode != other.polygonMode) { + return false; + } + if (first.primitiveType != other.primitiveType) { + return false; + } + if (first.shader != other.shader) { + return false; + } + if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) { + return false; + } + if (first.vertexInputBindingDescriptions.size() != other.vertexInputBindingDescriptions.size()) { + return false; + } + for (uint32_t i = 0; i < first.vertexInputAttributeDescriptions.size(); i++) { + const VkVertexInputAttributeDescription& x = first.vertexInputAttributeDescriptions[i]; + const VkVertexInputAttributeDescription& y = other.vertexInputAttributeDescriptions[i]; + if (x.binding != y.binding) { + return false; + } + if (x.location != y.location) { + return false; + } + if (x.offset != y.offset) { + return false; + } + if (x.format != y.format) { + return false; + } + } + for (uint32_t i = 0; i < first.vertexInputBindingDescriptions.size(); i++) { + const VkVertexInputBindingDescription& x = first.vertexInputBindingDescriptions[i]; + const VkVertexInputBindingDescription& y = other.vertexInputBindingDescriptions[i]; + if (x.binding != y.binding) { + return false; + } + if (x.inputRate != y.inputRate) { + return false; + } + if (x.stride != y.stride) { + return false; + } + } + return true; +} + +void Graphics::cleanup() { + cleanupSwapChain(); + + for (auto &cleanUpFns : cleanUpFunctions) { + for (auto &cleanUpFn : cleanUpFns) { + cleanUpFn(); + } + } + cleanUpFunctions.clear(); + + vmaDestroyAllocator(vmaAllocator); + batchedDrawBuffers.clear(); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + + vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); +} + +void Graphics::cleanupSwapChain() { + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data()); + for (auto const& p : graphicsPipelines) { + vkDestroyPipeline(device, p.second, nullptr); + } + graphicsPipelines.clear(); + currentGraphicsPipeline = VK_NULL_HANDLE; + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); +} + +void Graphics::recreateSwapChain() { + vkDeviceWaitIdle(device); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createCommandBuffers(); + startRecordingGraphicsCommands(); +} + +love::graphics::Graphics* createInstance() { + love::graphics::Graphics* instance = nullptr; + + try { + instance = new Graphics(); + } + catch (love::Exception& e) { + printf("Cannot create Vulkan renderer: %s\n", e.what()); + } + + return instance; +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 5cfbcb36c..f089beead 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -18,209 +18,209 @@ namespace love { - namespace graphics { - namespace vulkan { - struct GraphicsPipelineConfiguration { - std::vector vertexInputBindingDescriptions; - std::vector vertexInputAttributeDescriptions; - Shader* shader = nullptr; - PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM; - VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; - BlendState blendState; - ColorChannelMask colorChannelMask; - Winding winding; - CullMode cullmode; - VkFormat framebufferFormat; - float viewportWidth; - float viewportHeight; - std::optional scissorRect; +namespace graphics { +namespace vulkan { +struct GraphicsPipelineConfiguration { + std::vector vertexInputBindingDescriptions; + std::vector vertexInputAttributeDescriptions; + Shader* shader = nullptr; + PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM; + VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; + BlendState blendState; + ColorChannelMask colorChannelMask; + Winding winding; + CullMode cullmode; + VkFormat framebufferFormat; + float viewportWidth; + float viewportHeight; + std::optional scissorRect; - friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other); - }; + friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other); +}; - struct BatchedDrawBuffers { - StreamBuffer* vertexBuffer1; - StreamBuffer* vertexBuffer2; - StreamBuffer* indexBuffer; - StreamBuffer* constantColorBuffer; +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() = default; - - virtual ~Graphics(); - - const char* getName() const override; - 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; - love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; - void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override; - void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override; - Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override; - void discard(const std::vector& colorbuffers, bool depthstencil) override { } - void present(void* screenshotCallbackdata) override; - void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; - bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override; - void unSetMode() override; - void setActive(bool active) override; - int getRequestedBackbufferMSAA() const override { return 0; } - int getBackbufferMSAA() const override { return 0; } - void setColor(Colorf c) override; - void setScissor(const Rect& rect) override; - void setScissor() override; - void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { } - void setDepthMode(CompareMode compare, bool write) override { } - void setFrontFaceWinding(Winding winding) override; - void setColorMask(ColorChannelMask mask) override; - void setBlendState(const BlendState& blend) override; - void setPointSize(float size) override; - void setWireframe(bool enable) override; - PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; - bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override; - Renderer getRenderer() const override; - bool usesGLSLES() const override; - RendererInfo getRendererInfo() const override; - void draw(const DrawCommand& cmd) override; - void draw(const DrawIndexedCommand& cmd) override; - void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override; - - GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; }; - GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; } - - void queueDatatransfer(std::function command, std::function cleanUp); - void queueCleanUp(std::function cleanUp); - - VkCommandBuffer beginSingleTimeCommands(); - void endSingleTimeCommands(VkCommandBuffer); - - uint32_t getNumImagesInFlight() const; - const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const; - const VkDeviceSize getMinUniformBufferOffsetAlignment() const; - graphics::Texture* getDefaultTexture() const; - - protected: - graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { - return new ShaderStage(this, stage, source, gles, cachekey); - } - graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { - return new Shader(stages); - } - graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; - bool dispatch(int x, int y, int z) override { return false; } - void initCapabilities() override; - void getAPIStats(int& shaderswitches) const override; - void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override; - - private: - void createVulkanInstance(); - bool checkValidationSupport(); - void pickPhysicalDevice(); - int rateDeviceSuitability(VkPhysicalDevice device); - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); - void createLogicalDevice(); - void initVMA(); - void createSurface(); - bool checkDeviceExtensionSupport(VkPhysicalDevice device); - SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); - VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); - VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); - VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); - void createSwapChain(); - void createImageViews(); - void createDefaultShaders(); - VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration); - void createCommandPool(); - void createCommandBuffers(); - void createSyncObjects(); - void createDefaultTexture(); - void createQuadIndexBuffer(); - void cleanup(); - void cleanupSwapChain(); - void recreateSwapChain(); - void startRecordingGraphicsCommands(); - void endRecordingGraphicsCommands(); - void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration); - graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData(); - void updatedBatchedDrawBuffers(); - void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration); - void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode); - void startRenderPass(Texture*, uint32_t w, uint32_t h); - void endRenderPass(); - - VkInstance instance = VK_NULL_HANDLE; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - 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 = VK_FORMAT_UNDEFINED; - VkExtent2D swapChainExtent = VkExtent2D(); - std::vector swapChainImageViews; - VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; - VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE; - std::vector> graphicsPipelines; // FIXME improve performance by using a hash map - VkCommandPool commandPool = VK_NULL_HANDLE; - std::vector commandBuffers; - std::vector dataTransferCommandBuffers; - VkDescriptorPool descriptorPool = VK_NULL_HANDLE; - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; - std::vector imagesInFlight; - VkDeviceSize minUniformBufferOffsetAlignment; - PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; - size_t currentFrame = 0; - uint32_t imageIndex = 0; - bool framebufferResized = false; - VmaAllocator vmaAllocator = VK_NULL_HANDLE; - std::unique_ptr standardTexture = nullptr; - std::unique_ptr quadIndexBuffer = 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; - // functions that need to be called to cleanup objects that were needed for rendering a frame. - // just like batchedDrawBuffers we need a vector for each frame in flight. - std::vector>> cleanUpFunctions; - graphics::Texture* currentTexture = nullptr; - VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL; - - // render pass variables. - VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED; - Texture* renderTargetTexture; - float currentViewportWidth = 0; - float currentViewportHeight = 0; - }; - } + ~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() = default; + + virtual ~Graphics(); + + const char* getName() const override; + 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; + love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override; + void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override; + Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override; + void discard(const std::vector& colorbuffers, bool depthstencil) override { } + void present(void* screenshotCallbackdata) override; + void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; + bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override; + void unSetMode() override; + void setActive(bool active) override; + int getRequestedBackbufferMSAA() const override { return 0; } + int getBackbufferMSAA() const override { return 0; } + void setColor(Colorf c) override; + void setScissor(const Rect& rect) override; + void setScissor() override; + void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { } + void setDepthMode(CompareMode compare, bool write) override { } + void setFrontFaceWinding(Winding winding) override; + void setColorMask(ColorChannelMask mask) override; + void setBlendState(const BlendState& blend) override; + void setPointSize(float size) override; + void setWireframe(bool enable) override; + PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; + bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override; + Renderer getRenderer() const override; + bool usesGLSLES() const override; + RendererInfo getRendererInfo() const override; + void draw(const DrawCommand& cmd) override; + void draw(const DrawIndexedCommand& cmd) override; + void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override; + + GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; }; + GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; } + + void queueDatatransfer(std::function command, std::function cleanUp); + void queueCleanUp(std::function cleanUp); + + VkCommandBuffer beginSingleTimeCommands(); + void endSingleTimeCommands(VkCommandBuffer); + + uint32_t getNumImagesInFlight() const; + const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const; + const VkDeviceSize getMinUniformBufferOffsetAlignment() const; + graphics::Texture* getDefaultTexture() const; + +protected: + graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { + return new ShaderStage(this, stage, source, gles, cachekey); + } + graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { + return new Shader(stages); + } + graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; + bool dispatch(int x, int y, int z) override { return false; } + void initCapabilities() override; + void getAPIStats(int& shaderswitches) const override; + void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override; + +private: + void createVulkanInstance(); + bool checkValidationSupport(); + void pickPhysicalDevice(); + int rateDeviceSuitability(VkPhysicalDevice device); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + void createLogicalDevice(); + void initVMA(); + void createSurface(); + bool checkDeviceExtensionSupport(VkPhysicalDevice device); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void createSwapChain(); + void createImageViews(); + void createDefaultShaders(); + VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration); + void createCommandPool(); + void createCommandBuffers(); + void createSyncObjects(); + void createDefaultTexture(); + void createQuadIndexBuffer(); + void cleanup(); + void cleanupSwapChain(); + void recreateSwapChain(); + void startRecordingGraphicsCommands(); + void endRecordingGraphicsCommands(); + void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration); + graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData(); + void updatedBatchedDrawBuffers(); + void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration); + void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode); + void startRenderPass(Texture*, uint32_t w, uint32_t h); + void endRenderPass(); + + VkInstance instance = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + 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 = VK_FORMAT_UNDEFINED; + VkExtent2D swapChainExtent = VkExtent2D(); + std::vector swapChainImageViews; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE; + std::vector> graphicsPipelines; // FIXME improve performance by using a hash map + VkCommandPool commandPool = VK_NULL_HANDLE; + std::vector commandBuffers; + std::vector dataTransferCommandBuffers; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + std::vector imagesInFlight; + VkDeviceSize minUniformBufferOffsetAlignment; + PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; + size_t currentFrame = 0; + uint32_t imageIndex = 0; + bool framebufferResized = false; + VmaAllocator vmaAllocator = VK_NULL_HANDLE; + std::unique_ptr standardTexture = nullptr; + std::unique_ptr quadIndexBuffer = 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; + // functions that need to be called to cleanup objects that were needed for rendering a frame. + // just like batchedDrawBuffers we need a vector for each frame in flight. + std::vector>> cleanUpFunctions; + graphics::Texture* currentTexture = nullptr; + VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL; + + // render pass variables. + VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED; + Texture* renderTargetTexture; + float currentViewportWidth = 0; + float currentViewportHeight = 0; +}; +} // vulkan +} // graphics +} // love #endif diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 52d0b5195..c0be10781 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -8,486 +8,486 @@ #include namespace love { - namespace graphics { - namespace vulkan { - static const TBuiltInResource defaultTBuiltInResource = { - /* .MaxLights = */ 32, - /* .MaxClipPlanes = */ 6, - /* .MaxTextureUnits = */ 32, - /* .MaxTextureCoords = */ 32, - /* .MaxVertexAttribs = */ 64, - /* .MaxVertexUniformComponents = */ 16384, - /* .MaxVaryingFloats = */ 128, - /* .MaxVertexTextureImageUnits = */ 32, - /* .MaxCombinedTextureImageUnits = */ 80, - /* .MaxTextureImageUnits = */ 32, - /* .MaxFragmentUniformComponents = */ 16384, - /* .MaxDrawBuffers = */ 8, - /* .MaxVertexUniformVectors = */ 4096, - /* .MaxVaryingVectors = */ 32, - /* .MaxFragmentUniformVectors = */ 4096, - /* .MaxVertexOutputVectors = */ 32, - /* .MaxFragmentInputVectors = */ 31, - /* .MinProgramTexelOffset = */ -8, - /* .MaxProgramTexelOffset = */ 7, - /* .MaxClipDistances = */ 8, - /* .MaxComputeWorkGroupCountX = */ 65535, - /* .MaxComputeWorkGroupCountY = */ 65535, - /* .MaxComputeWorkGroupCountZ = */ 65535, - /* .MaxComputeWorkGroupSizeX = */ 1024, - /* .MaxComputeWorkGroupSizeY = */ 1024, - /* .MaxComputeWorkGroupSizeZ = */ 64, - /* .MaxComputeUniformComponents = */ 1024, - /* .MaxComputeTextureImageUnits = */ 32, - /* .MaxComputeImageUniforms = */ 16, - /* .MaxComputeAtomicCounters = */ 4096, - /* .MaxComputeAtomicCounterBuffers = */ 8, - /* .MaxVaryingComponents = */ 128, - /* .MaxVertexOutputComponents = */ 128, - /* .MaxGeometryInputComponents = */ 128, - /* .MaxGeometryOutputComponents = */ 128, - /* .MaxFragmentInputComponents = */ 128, - /* .MaxImageUnits = */ 192, - /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, - /* .MaxCombinedShaderOutputResources = */ 144, - /* .MaxImageSamples = */ 32, - /* .MaxVertexImageUniforms = */ 16, - /* .MaxTessControlImageUniforms = */ 16, - /* .MaxTessEvaluationImageUniforms = */ 16, - /* .MaxGeometryImageUniforms = */ 16, - /* .MaxFragmentImageUniforms = */ 16, - /* .MaxCombinedImageUniforms = */ 80, - /* .MaxGeometryTextureImageUnits = */ 16, - /* .MaxGeometryOutputVertices = */ 256, - /* .MaxGeometryTotalOutputComponents = */ 1024, - /* .MaxGeometryUniformComponents = */ 1024, - /* .MaxGeometryVaryingComponents = */ 64, - /* .MaxTessControlInputComponents = */ 128, - /* .MaxTessControlOutputComponents = */ 128, - /* .MaxTessControlTextureImageUnits = */ 16, - /* .MaxTessControlUniformComponents = */ 1024, - /* .MaxTessControlTotalOutputComponents = */ 4096, - /* .MaxTessEvaluationInputComponents = */ 128, - /* .MaxTessEvaluationOutputComponents = */ 128, - /* .MaxTessEvaluationTextureImageUnits = */ 16, - /* .MaxTessEvaluationUniformComponents = */ 1024, - /* .MaxTessPatchComponents = */ 120, - /* .MaxPatchVertices = */ 32, - /* .MaxTessGenLevel = */ 64, - /* .MaxViewports = */ 16, - /* .MaxVertexAtomicCounters = */ 4096, - /* .MaxTessControlAtomicCounters = */ 4096, - /* .MaxTessEvaluationAtomicCounters = */ 4096, - /* .MaxGeometryAtomicCounters = */ 4096, - /* .MaxFragmentAtomicCounters = */ 4096, - /* .MaxCombinedAtomicCounters = */ 4096, - /* .MaxAtomicCounterBindings = */ 8, - /* .MaxVertexAtomicCounterBuffers = */ 8, - /* .MaxTessControlAtomicCounterBuffers = */ 8, - /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, - /* .MaxGeometryAtomicCounterBuffers = */ 8, - /* .MaxFragmentAtomicCounterBuffers = */ 8, - /* .MaxCombinedAtomicCounterBuffers = */ 8, - /* .MaxAtomicCounterBufferSize = */ 16384, - /* .MaxTransformFeedbackBuffers = */ 4, - /* .MaxTransformFeedbackInterleavedComponents = */ 64, - /* .MaxCullDistances = */ 8, - /* .MaxCombinedClipAndCullDistances = */ 8, - /* .MaxSamples = */ 32, - /* .maxMeshOutputVerticesNV = */ 256, - /* .maxMeshOutputPrimitivesNV = */ 512, - /* .maxMeshWorkGroupSizeX_NV = */ 32, - /* .maxMeshWorkGroupSizeY_NV = */ 1, - /* .maxMeshWorkGroupSizeZ_NV = */ 1, - /* .maxTaskWorkGroupSizeX_NV = */ 32, - /* .maxTaskWorkGroupSizeY_NV = */ 1, - /* .maxTaskWorkGroupSizeZ_NV = */ 1, - /* .maxMeshViewCountNV = */ 4, - /* .maxDualSourceDrawBuffersEXT = */ 1, - /* .limits = */ { - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, - } - }; +namespace graphics { +namespace vulkan { +static const TBuiltInResource defaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 16384, + /* .MaxVaryingFloats = */ 128, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 16384, + /* .MaxDrawBuffers = */ 8, + /* .MaxVertexUniformVectors = */ 4096, + /* .MaxVaryingVectors = */ 32, + /* .MaxFragmentUniformVectors = */ 4096, + /* .MaxVertexOutputVectors = */ 32, + /* .MaxFragmentInputVectors = */ 31, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 32, + /* .MaxComputeImageUniforms = */ 16, + /* .MaxComputeAtomicCounters = */ 4096, + /* .MaxComputeAtomicCounterBuffers = */ 8, + /* .MaxVaryingComponents = */ 128, + /* .MaxVertexOutputComponents = */ 128, + /* .MaxGeometryInputComponents = */ 128, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 192, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, + /* .MaxCombinedShaderOutputResources = */ 144, + /* .MaxImageSamples = */ 32, + /* .MaxVertexImageUniforms = */ 16, + /* .MaxTessControlImageUniforms = */ 16, + /* .MaxTessEvaluationImageUniforms = */ 16, + /* .MaxGeometryImageUniforms = */ 16, + /* .MaxFragmentImageUniforms = */ 16, + /* .MaxCombinedImageUniforms = */ 80, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 4096, + /* .MaxTessControlAtomicCounters = */ 4096, + /* .MaxTessEvaluationAtomicCounters = */ 4096, + /* .MaxGeometryAtomicCounters = */ 4096, + /* .MaxFragmentAtomicCounters = */ 4096, + /* .MaxCombinedAtomicCounters = */ 4096, + /* .MaxAtomicCounterBindings = */ 8, + /* .MaxVertexAtomicCounterBuffers = */ 8, + /* .MaxTessControlAtomicCounterBuffers = */ 8, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, + /* .MaxGeometryAtomicCounterBuffers = */ 8, + /* .MaxFragmentAtomicCounterBuffers = */ 8, + /* .MaxCombinedAtomicCounterBuffers = */ 8, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 32, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; - static const uint32_t STREAMBUFFER_SIZE = 1024; +static const uint32_t STREAMBUFFER_SIZE = 1024; - static VkShaderStageFlagBits getStageBit(ShaderStageType type) { - switch (type) { - case SHADERSTAGE_VERTEX: - return VK_SHADER_STAGE_VERTEX_BIT; - case SHADERSTAGE_PIXEL: - return VK_SHADER_STAGE_FRAGMENT_BIT; - case SHADERSTAGE_COMPUTE: - return VK_SHADER_STAGE_COMPUTE_BIT; - } - throw love::Exception("invalid type"); - } +static VkShaderStageFlagBits getStageBit(ShaderStageType type) { + switch (type) { + case SHADERSTAGE_VERTEX: + return VK_SHADER_STAGE_VERTEX_BIT; + case SHADERSTAGE_PIXEL: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case SHADERSTAGE_COMPUTE: + return VK_SHADER_STAGE_COMPUTE_BIT; + } + throw love::Exception("invalid type"); +} - static EShLanguage getGlslShaderType(ShaderStageType stage) { - switch (stage) { - case SHADERSTAGE_VERTEX: - return EShLangVertex; - case SHADERSTAGE_PIXEL: - return EShLangFragment; - case SHADERSTAGE_COMPUTE: - return EShLangCompute; - default: - throw love::Exception("unkonwn shader stage type"); - } - } - - Shader::Shader(StrongRef stages[]) - : graphics::Shader(stages) { - loadVolatile(); - } - - bool Shader::loadVolatile() { - calculateUniformBufferSizeAligned(); - compileShaders(); - createDescriptorSetLayout(); - createPipelineLayout(); - createStreamBuffers(); - currentImage = 0; - count = 0; - - return true; - } - - void Shader::unloadVolatile() { - if (shaderModules.size() == 0) { - return; - } - - auto gfx = Module::getInstance(Module::M_GRAPHICS); - gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout](){ - for (const auto shaderModule : shaderModules) { - vkDestroyShaderModule(device, shaderModule, nullptr); - } - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - }); - for (const auto streamBuffer : streamBuffers) { - delete streamBuffer; - } - shaderModules.clear(); - shaderStages.clear(); - streamBuffers.clear(); - } - - const std::vector& Shader::getShaderStages() const { - return shaderStages; - } - - const VkPipelineLayout Shader::getGraphicsPipelineLayout() const { - return pipelineLayout; - } - - static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) { - VkDescriptorImageInfo imageInfo{}; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - Texture* vkTexture = (Texture*)texture; - imageInfo.imageView = vkTexture->getImageView(); - imageInfo.sampler = vkTexture->getSampler(); - return imageInfo; - } - - void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) { - if (currentImage != imageIndex) { - currentImage = imageIndex; - count = 0; - streamBuffers[currentImage]->nextFrame(); - } - else { - if (count >= STREAMBUFFER_SIZE) { - throw love::Exception("uniform stream buffer: out of memory (fixme: resize)"); - } - } - - auto mapInfo = streamBuffers[currentImage]->map(uniformBufferSizeAligned); - memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned); - streamBuffers[currentImage]->unmap(uniformBufferSizeAligned); - streamBuffers[currentImage]->markUsed(uniformBufferSizeAligned); - - VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle(); - bufferInfo.offset = count * uniformBufferSizeAligned; - bufferInfo.range = sizeof(BuiltinUniformData); - - auto mainTexImageInfo = createDescriptorImageInfo(mainTex); - auto ytextureImageInfo = createDescriptorImageInfo(ytexture); - auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture); - auto crtextureImageInfo = createDescriptorImageInfo(crtexture); - - std::array descriptorWrite{}; - descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[0].dstSet = 0; - descriptorWrite[0].dstBinding = 0; - descriptorWrite[0].dstArrayElement = 0; - descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrite[0].descriptorCount = 1; - descriptorWrite[0].pBufferInfo = &bufferInfo; - - descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[1].dstSet = 0; - descriptorWrite[1].dstBinding = 1; - descriptorWrite[1].dstArrayElement = 0; - descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[1].descriptorCount = 1; - descriptorWrite[1].pImageInfo = &mainTexImageInfo; - - descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[2].dstSet = 0; - descriptorWrite[2].dstBinding = 2; - descriptorWrite[2].dstArrayElement = 0; - descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[2].descriptorCount = 1; - descriptorWrite[2].pImageInfo = &ytextureImageInfo; - - descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[3].dstSet = 0; - descriptorWrite[3].dstBinding = 3; - descriptorWrite[3].dstArrayElement = 0; - descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[3].descriptorCount = 1; - descriptorWrite[3].pImageInfo = &cbtextureImageInfo; - - descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite[4].dstSet = 0; - descriptorWrite[4].dstBinding = 4; - descriptorWrite[4].dstArrayElement = 0; - descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrite[4].descriptorCount = 1; - descriptorWrite[4].pImageInfo = &crtextureImageInfo; - - vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast(descriptorWrite.size()), descriptorWrite.data()); - - count++; - } - - Shader::~Shader() { - unloadVolatile(); - } - - void Shader::attach() { - if (Shader::current != this) { - Graphics::flushBatchedDrawsGlobal(); - Shader::current = this; - Vulkan::shaderSwitch(); - } - } - - int Shader::getVertexAttributeIndex(const std::string& name) { - return vertexAttributeIndices.at(name); - } - - void Shader::calculateUniformBufferSizeAligned() { - gfx = Module::getInstance(Module::ModuleType::M_GRAPHICS); - auto vgfx = (Graphics*)gfx; - auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment(); - uniformBufferSizeAligned = - static_cast( - std::ceil( - static_cast(sizeof(BuiltinUniformData)) / static_cast(minAlignment) - ) - ) - * minAlignment; - } - - void Shader::compileShaders() { - using namespace glslang; - using namespace spirv_cross; - - TProgram* program = new TProgram(); - - gfx = Module::getInstance(Module::ModuleType::M_GRAPHICS); - auto vgfx = (Graphics*)gfx; - device = vgfx->getDevice(); - - mainTex = vgfx->getDefaultTexture(); - ytexture = vgfx->getDefaultTexture(); - crtexture = vgfx->getDefaultTexture(); - cbtexture = vgfx->getDefaultTexture(); - - for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { - if (!stages[i]) - continue; - - auto stage = (ShaderStageType)i; - auto glslangShaderStage = getGlslShaderType(stage); - auto tshader = new TShader(glslangShaderStage); - - tshader->setEnvInput(EShSourceGlsl, glslangShaderStage, EShClientVulkan, 450); - tshader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); - tshader->setEnvTarget(EshTargetSpv, EShTargetSpv_1_5); - tshader->setAutoMapLocations(true); - tshader->setAutoMapBindings(true); - tshader->setEnvInputVulkanRulesRelaxed(); - tshader->setGlobalUniformBinding(0); - tshader->setGlobalUniformSet(0); - - auto& glsl = stages[i]->getSource(); - const char* csrc = glsl.c_str(); - const int sourceLength = static_cast(glsl.length()); - tshader->setStringsWithLengths(&csrc, &sourceLength, 1); - - int defaultVersio = 450; - EProfile defaultProfile = ECoreProfile; - bool forceDefault = false; - bool forwardCompat = true; - - if (!tshader->parse(&defaultTBuiltInResource, defaultVersio, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings)) { - const char* msg1 = tshader->getInfoLog(); - const char* msg2 = tshader->getInfoDebugLog(); - - throw love::Exception("error while parsing shader"); - } - - program->addShader(tshader); - } - - if (!program->link(EShMsgDefault)) { - throw love::Exception("link failed! %s\n", program->getInfoLog()); - } - - if (!program->mapIO()) { - throw love::Exception("mapIO failed"); - } - - for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { - auto glslangStage = getGlslShaderType((ShaderStageType)i); - auto intermediate = program->getIntermediate(glslangStage); - if (intermediate == nullptr) { - continue; - } - - spv::SpvBuildLogger logger; - glslang::SpvOptions opt; - opt.validate = true; - - std::vector spirv; - GlslangToSpv(*intermediate, spirv, &logger, &opt); - - std::string msgs = logger.getAllMessages(); - - VkShaderModuleCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = spirv.size() * sizeof(uint32_t); - createInfo.pCode = spirv.data(); - - Graphics* vkGfx = (Graphics*)gfx; - auto device = vkGfx->getDevice(); - - VkShaderModule shaderModule; - - if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { - throw love::Exception("failed to create shader module"); - } - - shaderModules.push_back(shaderModule); - - VkPipelineShaderStageCreateInfo shaderStageInfo{}; - shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageInfo.stage = getStageBit((ShaderStageType)i); - shaderStageInfo.module = shaderModule; - shaderStageInfo.pName = "main"; - - shaderStages.push_back(shaderStageInfo); - } - } - - // fixme: should generate this dynamically. - void Shader::createDescriptorSetLayout() { - auto vgfx = (Graphics*)gfx; - vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer(); - - VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding samplerLayoutBinding{}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.pImmutableSamplers = nullptr; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding videoYBinding{}; - videoYBinding.binding = 2; - videoYBinding.descriptorCount = 1; - videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoYBinding.pImmutableSamplers = nullptr; - videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding videoCBBinding{}; - videoCBBinding.binding = 3; - videoCBBinding.descriptorCount = 1; - videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoCBBinding.pImmutableSamplers = nullptr; - videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding videoCRinding{}; - videoCRinding.binding = 4; - videoCRinding.descriptorCount = 1; - videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - videoCRinding.pImmutableSamplers = nullptr; - videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - std::array bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding }; - VkDescriptorSetLayoutCreateInfo layoutInfo{}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { - throw love::Exception("failed to create descriptor set layout"); - } - } - - void Shader::createPipelineLayout() { - VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; - pipelineLayoutInfo.pushConstantRangeCount = 0; - - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { - throw love::Exception("failed to create pipeline layout"); - } - } - - void Shader::createStreamBuffers() { - auto vgfx = (Graphics*)gfx; - const auto numImagesInFlight = vgfx->getNumImagesInFlight(); - streamBuffers.resize(numImagesInFlight); - for (uint32_t i = 0; i < numImagesInFlight; i++) { - streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * uniformBufferSizeAligned); - } - } - - void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) { - this->ytexture = ytexture; - this->cbtexture = cbtexture; - this->crtexture = crtexture; - } - - void Shader::setUniformData(BuiltinUniformData& data) { - uniformData = data; - } - - void Shader::setMainTex(graphics::Texture* texture) { - mainTex = texture; - } - } +static EShLanguage getGlslShaderType(ShaderStageType stage) { + switch (stage) { + case SHADERSTAGE_VERTEX: + return EShLangVertex; + case SHADERSTAGE_PIXEL: + return EShLangFragment; + case SHADERSTAGE_COMPUTE: + return EShLangCompute; + default: + throw love::Exception("unkonwn shader stage type"); } } + +Shader::Shader(StrongRef stages[]) + : graphics::Shader(stages) { + loadVolatile(); +} + +bool Shader::loadVolatile() { + calculateUniformBufferSizeAligned(); + compileShaders(); + createDescriptorSetLayout(); + createPipelineLayout(); + createStreamBuffers(); + currentImage = 0; + count = 0; + + return true; +} + +void Shader::unloadVolatile() { + if (shaderModules.size() == 0) { + return; + } + + auto gfx = Module::getInstance(Module::M_GRAPHICS); + gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout](){ + for (const auto shaderModule : shaderModules) { + vkDestroyShaderModule(device, shaderModule, nullptr); + } + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + }); + for (const auto streamBuffer : streamBuffers) { + delete streamBuffer; + } + shaderModules.clear(); + shaderStages.clear(); + streamBuffers.clear(); +} + +const std::vector& Shader::getShaderStages() const { + return shaderStages; +} + +const VkPipelineLayout Shader::getGraphicsPipelineLayout() const { + return pipelineLayout; +} + +static VkDescriptorImageInfo createDescriptorImageInfo(graphics::Texture* texture) { + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + Texture* vkTexture = (Texture*)texture; + imageInfo.imageView = vkTexture->getImageView(); + imageInfo.sampler = vkTexture->getSampler(); + return imageInfo; +} + +void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + if (currentImage != imageIndex) { + currentImage = imageIndex; + count = 0; + streamBuffers[currentImage]->nextFrame(); + } + else { + if (count >= STREAMBUFFER_SIZE) { + throw love::Exception("uniform stream buffer: out of memory (fixme: resize)"); + } + } + + auto mapInfo = streamBuffers[currentImage]->map(uniformBufferSizeAligned); + memcpy(mapInfo.data, &uniformData, uniformBufferSizeAligned); + streamBuffers[currentImage]->unmap(uniformBufferSizeAligned); + streamBuffers[currentImage]->markUsed(uniformBufferSizeAligned); + + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = (VkBuffer)streamBuffers[currentImage]->getHandle(); + bufferInfo.offset = count * uniformBufferSizeAligned; + bufferInfo.range = sizeof(BuiltinUniformData); + + auto mainTexImageInfo = createDescriptorImageInfo(mainTex); + auto ytextureImageInfo = createDescriptorImageInfo(ytexture); + auto cbtextureImageInfo = createDescriptorImageInfo(cbtexture); + auto crtextureImageInfo = createDescriptorImageInfo(crtexture); + + std::array descriptorWrite{}; + descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite[0].dstSet = 0; + descriptorWrite[0].dstBinding = 0; + descriptorWrite[0].dstArrayElement = 0; + descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite[0].descriptorCount = 1; + descriptorWrite[0].pBufferInfo = &bufferInfo; + + descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite[1].dstSet = 0; + descriptorWrite[1].dstBinding = 1; + descriptorWrite[1].dstArrayElement = 0; + descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite[1].descriptorCount = 1; + descriptorWrite[1].pImageInfo = &mainTexImageInfo; + + descriptorWrite[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite[2].dstSet = 0; + descriptorWrite[2].dstBinding = 2; + descriptorWrite[2].dstArrayElement = 0; + descriptorWrite[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite[2].descriptorCount = 1; + descriptorWrite[2].pImageInfo = &ytextureImageInfo; + + descriptorWrite[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite[3].dstSet = 0; + descriptorWrite[3].dstBinding = 3; + descriptorWrite[3].dstArrayElement = 0; + descriptorWrite[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite[3].descriptorCount = 1; + descriptorWrite[3].pImageInfo = &cbtextureImageInfo; + + descriptorWrite[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite[4].dstSet = 0; + descriptorWrite[4].dstBinding = 4; + descriptorWrite[4].dstArrayElement = 0; + descriptorWrite[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite[4].descriptorCount = 1; + descriptorWrite[4].pImageInfo = &crtextureImageInfo; + + vkCmdPushDescriptorSet(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast(descriptorWrite.size()), descriptorWrite.data()); + + count++; +} + +Shader::~Shader() { + unloadVolatile(); +} + +void Shader::attach() { + if (Shader::current != this) { + Graphics::flushBatchedDrawsGlobal(); + Shader::current = this; + Vulkan::shaderSwitch(); + } +} + +int Shader::getVertexAttributeIndex(const std::string& name) { + return vertexAttributeIndices.at(name); +} + +void Shader::calculateUniformBufferSizeAligned() { + gfx = Module::getInstance(Module::ModuleType::M_GRAPHICS); + auto vgfx = (Graphics*)gfx; + auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment(); + uniformBufferSizeAligned = + static_cast( + std::ceil( + static_cast(sizeof(BuiltinUniformData)) / static_cast(minAlignment) + ) + ) + * minAlignment; +} + +void Shader::compileShaders() { + using namespace glslang; + using namespace spirv_cross; + + TProgram* program = new TProgram(); + + gfx = Module::getInstance(Module::ModuleType::M_GRAPHICS); + auto vgfx = (Graphics*)gfx; + device = vgfx->getDevice(); + + mainTex = vgfx->getDefaultTexture(); + ytexture = vgfx->getDefaultTexture(); + crtexture = vgfx->getDefaultTexture(); + cbtexture = vgfx->getDefaultTexture(); + + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { + if (!stages[i]) + continue; + + auto stage = (ShaderStageType)i; + auto glslangShaderStage = getGlslShaderType(stage); + auto tshader = new TShader(glslangShaderStage); + + tshader->setEnvInput(EShSourceGlsl, glslangShaderStage, EShClientVulkan, 450); + tshader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); + tshader->setEnvTarget(EshTargetSpv, EShTargetSpv_1_5); + tshader->setAutoMapLocations(true); + tshader->setAutoMapBindings(true); + tshader->setEnvInputVulkanRulesRelaxed(); + tshader->setGlobalUniformBinding(0); + tshader->setGlobalUniformSet(0); + + auto& glsl = stages[i]->getSource(); + const char* csrc = glsl.c_str(); + const int sourceLength = static_cast(glsl.length()); + tshader->setStringsWithLengths(&csrc, &sourceLength, 1); + + int defaultVersio = 450; + EProfile defaultProfile = ECoreProfile; + bool forceDefault = false; + bool forwardCompat = true; + + if (!tshader->parse(&defaultTBuiltInResource, defaultVersio, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings)) { + const char* msg1 = tshader->getInfoLog(); + const char* msg2 = tshader->getInfoDebugLog(); + + throw love::Exception("error while parsing shader"); + } + + program->addShader(tshader); + } + + if (!program->link(EShMsgDefault)) { + throw love::Exception("link failed! %s\n", program->getInfoLog()); + } + + if (!program->mapIO()) { + throw love::Exception("mapIO failed"); + } + + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { + auto glslangStage = getGlslShaderType((ShaderStageType)i); + auto intermediate = program->getIntermediate(glslangStage); + if (intermediate == nullptr) { + continue; + } + + spv::SpvBuildLogger logger; + glslang::SpvOptions opt; + opt.validate = true; + + std::vector spirv; + GlslangToSpv(*intermediate, spirv, &logger, &opt); + + std::string msgs = logger.getAllMessages(); + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = spirv.size() * sizeof(uint32_t); + createInfo.pCode = spirv.data(); + + Graphics* vkGfx = (Graphics*)gfx; + auto device = vkGfx->getDevice(); + + VkShaderModule shaderModule; + + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); + } + + shaderModules.push_back(shaderModule); + + VkPipelineShaderStageCreateInfo shaderStageInfo{}; + shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageInfo.stage = getStageBit((ShaderStageType)i); + shaderStageInfo.module = shaderModule; + shaderStageInfo.pName = "main"; + + shaderStages.push_back(shaderStageInfo); + } +} + +// fixme: should generate this dynamically. +void Shader::createDescriptorSetLayout() { + auto vgfx = (Graphics*)gfx; + vkCmdPushDescriptorSet = vgfx->getVkCmdPushDescriptorSetKHRFunctionPointer(); + + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.pImmutableSamplers = nullptr; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding videoYBinding{}; + videoYBinding.binding = 2; + videoYBinding.descriptorCount = 1; + videoYBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + videoYBinding.pImmutableSamplers = nullptr; + videoYBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding videoCBBinding{}; + videoCBBinding.binding = 3; + videoCBBinding.descriptorCount = 1; + videoCBBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + videoCBBinding.pImmutableSamplers = nullptr; + videoCBBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutBinding videoCRinding{}; + videoCRinding.binding = 4; + videoCRinding.descriptorCount = 1; + videoCRinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + videoCRinding.pImmutableSamplers = nullptr; + videoCRinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + std::array bindings = { uboLayoutBinding, samplerLayoutBinding, videoYBinding, videoCBBinding, videoCRinding }; + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { + throw love::Exception("failed to create descriptor set layout"); + } +} + +void Shader::createPipelineLayout() { + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; + pipelineLayoutInfo.pushConstantRangeCount = 0; + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw love::Exception("failed to create pipeline layout"); + } +} + +void Shader::createStreamBuffers() { + auto vgfx = (Graphics*)gfx; + const auto numImagesInFlight = vgfx->getNumImagesInFlight(); + streamBuffers.resize(numImagesInFlight); + for (uint32_t i = 0; i < numImagesInFlight; i++) { + streamBuffers[i] = new StreamBuffer(gfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_SIZE * uniformBufferSizeAligned); + } +} + +void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) { + this->ytexture = ytexture; + this->cbtexture = cbtexture; + this->crtexture = crtexture; +} + +void Shader::setUniformData(BuiltinUniformData& data) { + uniformData = data; +} + +void Shader::setMainTex(graphics::Texture* texture) { + mainTex = texture; +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 956c01cd0..4ca37b0ec 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -11,91 +11,91 @@ namespace love { - namespace graphics { - namespace vulkan { - class Shader final : public graphics::Shader, public Volatile { - public: - Shader(StrongRef stages[]); - virtual ~Shader(); +namespace graphics { +namespace vulkan { +class Shader final : public graphics::Shader, public Volatile { +public: + Shader(StrongRef stages[]); + virtual ~Shader(); - bool loadVolatile() override; - void unloadVolatile() override; + bool loadVolatile() override; + void unloadVolatile() override; - const std::vector& getShaderStages() const; + const std::vector& getShaderStages() const; - const VkPipelineLayout getGraphicsPipelineLayout() const; + const VkPipelineLayout getGraphicsPipelineLayout() const; - void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage); + void cmdPushDescriptorSets(VkCommandBuffer, uint32_t currentImage); - void attach() override; + void attach() override; - ptrdiff_t getHandle() const { return 0; } + ptrdiff_t getHandle() const { return 0; } - std::string getWarnings() const override { return ""; } + std::string getWarnings() const override { return ""; } - int getVertexAttributeIndex(const std::string& name) override; + int getVertexAttributeIndex(const std::string& name) override; - const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } - const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } + const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } + const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } - void updateUniform(const UniformInfo* info, int count) override {} + void updateUniform(const UniformInfo* info, int count) override {} - void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override {} - void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {} + void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override {} + void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {} - bool hasUniform(const std::string& name) const override { return false; } + bool hasUniform(const std::string& name) const override { return false; } - void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override; + void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override; - // fixme: use normal methods for this in the future. - void setUniformData(BuiltinUniformData& data); - void setMainTex(graphics::Texture* texture); + // fixme: use normal methods for this in the future. + void setUniformData(BuiltinUniformData& data); + void setMainTex(graphics::Texture* texture); - private: - void calculateUniformBufferSizeAligned(); - void compileShaders(); - void createDescriptorSetLayout(); - void createPipelineLayout(); - void createStreamBuffers(); +private: + void calculateUniformBufferSizeAligned(); + void compileShaders(); + void createDescriptorSetLayout(); + void createPipelineLayout(); + void createStreamBuffers(); - VkDeviceSize uniformBufferSizeAligned; - PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; + VkDeviceSize uniformBufferSizeAligned; + PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; - VkDescriptorSetLayout descriptorSetLayout; - VkPipelineLayout pipelineLayout; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; - std::vector streamBuffers; + std::vector streamBuffers; - std::vector shaderStages; - std::vector shaderModules; - Graphics* gfx; - VkDevice device; + std::vector shaderStages; + std::vector shaderModules; + Graphics* gfx; + VkDevice device; - std::map vertexAttributeIndices = { - { "VertexPosition", 0 }, - { "VertexTexCoord", 1 }, - { "VertexColor", 2 } - }; + std::map vertexAttributeIndices = { + { "VertexPosition", 0 }, + { "VertexTexCoord", 1 }, + { "VertexColor", 2 } + }; - std::map uniformBindings = { - { "love_UniformsPerDraw", 0 }, - { "love_VideoYChannel", 1 }, - { "love_VideoCbChannel", 2 }, - { "love_VideoCrChannel", 3 }, - { "MainTex", 4 } - }; + std::map uniformBindings = { + { "love_UniformsPerDraw", 0 }, + { "love_VideoYChannel", 1 }, + { "love_VideoCbChannel", 2 }, + { "love_VideoCrChannel", 3 }, + { "MainTex", 4 } + }; - BuiltinUniformData uniformData; - graphics::Texture* mainTex; - graphics::Texture* ytexture; - graphics::Texture* cbtexture; - graphics::Texture* crtexture; + BuiltinUniformData uniformData; + graphics::Texture* mainTex; + graphics::Texture* ytexture; + graphics::Texture* cbtexture; + graphics::Texture* crtexture; - uint32_t currentImage; - uint32_t count; - }; - } - } + uint32_t currentImage; + uint32_t count; +}; +} +} } #endif diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp index 9919db7f5..b8a313d77 100644 --- a/src/modules/graphics/vulkan/ShaderStage.cpp +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -12,12 +12,12 @@ namespace love { - namespace graphics { - namespace vulkan { - ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) - : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { - // the compilation is done in Shader. - } - } - } +namespace graphics { +namespace vulkan { +ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) + : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { + // the compilation is done in Shader. } +} // love +} // graphics +} // vulkan diff --git a/src/modules/graphics/vulkan/ShaderStage.h b/src/modules/graphics/vulkan/ShaderStage.h index 4652ef7dc..a67f8efe2 100644 --- a/src/modules/graphics/vulkan/ShaderStage.h +++ b/src/modules/graphics/vulkan/ShaderStage.h @@ -6,18 +6,18 @@ #include namespace love { - namespace graphics { - namespace vulkan { - class ShaderStage final : public graphics::ShaderStage { - public: - ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey); +namespace graphics { +namespace vulkan { +class ShaderStage final : public graphics::ShaderStage { +public: + ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey); - ptrdiff_t getHandle() const { - return 0; - } - }; - } + ptrdiff_t getHandle() const { + return 0; } +}; +} +} } #endif diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp index 7c49d679d..29e86b499 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.cpp +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -4,75 +4,75 @@ namespace love { - namespace graphics { - namespace vulkan { - static VkBufferUsageFlags getUsageFlags(BufferUsage mode) { - switch (mode) { - case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - default: - throw love::Exception("unsupported BufferUsage mode"); - } - } - - StreamBuffer::StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size) - : love::graphics::StreamBuffer(mode, size), gfx(gfx) { - loadVolatile(); - } - - bool StreamBuffer::loadVolatile() { - Graphics* vgfx = (Graphics*)gfx; - allocator = vgfx->getVmaAllocator(); - - VkBufferCreateInfo bufferInfo{}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = getSize(); - bufferInfo.usage = getUsageFlags(mode); - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VmaAllocationCreateInfo allocCreateInfo = {}; - allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; - allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; // always mapped - - vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo); - - usedGPUMemory = 0; - - return true; - } - - void StreamBuffer::unloadVolatile() { - if (buffer == VK_NULL_HANDLE) - return; - - auto vgfx = (Graphics*)gfx; - vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){ - vmaDestroyBuffer(allocator, buffer, allocation); - }); - buffer = VK_NULL_HANDLE; - } - - StreamBuffer::~StreamBuffer() { - unloadVolatile(); - } - - love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { - (void)minsize; - return love::graphics::StreamBuffer::MapInfo((uint8*) allocInfo.pMappedData + usedGPUMemory, getSize()); - } - - size_t StreamBuffer::unmap(size_t usedSize) { - return usedGPUMemory; - } - - void StreamBuffer::markUsed(size_t usedSize) { - usedGPUMemory += usedSize; - } - - void StreamBuffer::nextFrame() { - usedGPUMemory = 0; - } - } +namespace graphics { +namespace vulkan { +static VkBufferUsageFlags getUsageFlags(BufferUsage mode) { + switch (mode) { + case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + default: + throw love::Exception("unsupported BufferUsage mode"); } } + +StreamBuffer::StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size) + : love::graphics::StreamBuffer(mode, size), gfx(gfx) { + loadVolatile(); +} + +bool StreamBuffer::loadVolatile() { + Graphics* vgfx = (Graphics*)gfx; + allocator = vgfx->getVmaAllocator(); + + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = getUsageFlags(mode); + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; // always mapped + + vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, &allocInfo); + + usedGPUMemory = 0; + + return true; +} + +void StreamBuffer::unloadVolatile() { + if (buffer == VK_NULL_HANDLE) + return; + + auto vgfx = (Graphics*)gfx; + vgfx->queueCleanUp([allocator=allocator, buffer=buffer, allocation=allocation](){ + vmaDestroyBuffer(allocator, buffer, allocation); + }); + buffer = VK_NULL_HANDLE; +} + +StreamBuffer::~StreamBuffer() { + unloadVolatile(); +} + +love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { + (void)minsize; + return love::graphics::StreamBuffer::MapInfo((uint8*) allocInfo.pMappedData + usedGPUMemory, getSize()); +} + +size_t StreamBuffer::unmap(size_t usedSize) { + return usedGPUMemory; +} + +void StreamBuffer::markUsed(size_t usedSize) { + usedGPUMemory += usedSize; +} + +void StreamBuffer::nextFrame() { + usedGPUMemory = 0; +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/StreamBuffer.h b/src/modules/graphics/vulkan/StreamBuffer.h index ab02966e7..11202f2c8 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.h +++ b/src/modules/graphics/vulkan/StreamBuffer.h @@ -9,38 +9,38 @@ #include "vk_mem_alloc.h" namespace love { - namespace graphics { - namespace vulkan { - class StreamBuffer : public love::graphics::StreamBuffer, public graphics::Volatile { - public: - StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size); - virtual ~StreamBuffer(); +namespace graphics { +namespace vulkan { +class StreamBuffer : public love::graphics::StreamBuffer, public graphics::Volatile { +public: + StreamBuffer(graphics::Graphics* gfx, BufferUsage mode, size_t size); + virtual ~StreamBuffer(); - virtual bool loadVolatile() override; + virtual bool loadVolatile() override; - virtual void unloadVolatile() override; + virtual void unloadVolatile() override; - MapInfo map(size_t minsize) override; - size_t unmap(size_t usedSize) override; - void markUsed(size_t usedSize) override; + MapInfo map(size_t minsize) override; + size_t unmap(size_t usedSize) override; + void markUsed(size_t usedSize) override; - void nextFrame() override; + void nextFrame() override; - ptrdiff_t getHandle() const override { - return (ptrdiff_t) buffer; - } - - private: - graphics::Graphics* gfx; - VmaAllocator allocator; - VmaAllocation allocation; - VmaAllocationInfo allocInfo; - VkBuffer buffer = VK_NULL_HANDLE; - size_t usedGPUMemory; - - }; - } + ptrdiff_t getHandle() const override { + return (ptrdiff_t) buffer; } -} + +private: + graphics::Graphics* gfx; + VmaAllocator allocator; + VmaAllocation allocation; + VmaAllocationInfo allocInfo; + VkBuffer buffer = VK_NULL_HANDLE; + size_t usedGPUMemory; + +}; +} // vulkan +} // graphics +} // love #endif diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index d27a13a2c..75cd58b9c 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -8,272 +8,272 @@ #define vgfx ((Graphics*)gfx) namespace love { - namespace graphics { - namespace vulkan { - Texture::Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data) - : love::graphics::Texture(gfx, settings, data), gfx(gfx), data(data) { - loadVolatile(); - } +namespace graphics { +namespace vulkan { +Texture::Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data) + : love::graphics::Texture(gfx, settings, data), gfx(gfx), data(data) { + loadVolatile(); +} - bool Texture::loadVolatile() { - allocator = vgfx->getVmaAllocator(); - device = vgfx->getDevice(); +bool Texture::loadVolatile() { + allocator = vgfx->getVmaAllocator(); + device = vgfx->getDevice(); - auto vulkanFormat = Vulkan::getTextureFormat(format); + auto vulkanFormat = Vulkan::getTextureFormat(format); - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - VkImageCreateInfo imageInfo{}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent.width = static_cast(width); - imageInfo.extent.height = static_cast(height); - imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; - imageInfo.format = vulkanFormat.internalFormat; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = usageFlags; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = static_cast(width); + imageInfo.extent.height = static_cast(height); + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = vulkanFormat.internalFormat; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = usageFlags; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - VmaAllocationCreateInfo imageAllocationCreateInfo{}; + VmaAllocationCreateInfo imageAllocationCreateInfo{}; - if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) { - throw love::Exception("failed to create image"); - } - // fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL. - transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) { + throw love::Exception("failed to create image"); + } + // fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL. + transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); - if (data) { - auto sliceData = data->get(0, 0); - auto size = sliceData->getSize(); - auto dataPtr = sliceData->getData(); - Rect rect{}; - rect.x = 0; - rect.y = 0; - rect.w = sliceData->getWidth(); - rect.h = sliceData->getHeight(); + if (data) { + auto sliceData = data->get(0, 0); + auto size = sliceData->getSize(); + auto dataPtr = sliceData->getData(); + Rect rect{}; + rect.x = 0; + rect.y = 0; + rect.w = sliceData->getWidth(); + rect.h = sliceData->getHeight(); - uploadByteData(format, dataPtr, size, 0, 0, rect); - } else { - if (isRenderTarget()) { - clear(false); - } - else { - clear(true); - } - } - createTextureImageView(); - createTextureSampler(); - - return true; - } - - void Texture::unloadVolatile() { - if (textureImage == VK_NULL_HANDLE) - return; - - vgfx->queueCleanUp([ - device = device, - textureSampler = textureSampler, - textureImageView = textureImageView, - allocator = allocator, - textureImage = textureImage, - textureImageAllocation = textureImageAllocation] () { - vkDestroySampler(device, textureSampler, nullptr); - vkDestroyImageView(device, textureImageView, nullptr); - vmaDestroyImage(allocator, textureImage, textureImageAllocation); - }); - - textureImage = VK_NULL_HANDLE; - } - - Texture::~Texture() { - unloadVolatile(); - } - - void Texture::createTextureImageView() { - auto vulkanFormat = Vulkan::getTextureFormat(format); - - VkImageViewCreateInfo viewInfo{}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureImage; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = vulkanFormat.internalFormat; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = vulkanFormat.swizzleR; - viewInfo.components.g = vulkanFormat.swizzleG; - viewInfo.components.b = vulkanFormat.swizzleB; - viewInfo.components.a = vulkanFormat.swizzleA; - - if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) { - throw love::Exception("could not create texture image view"); - } - } - - void Texture::createTextureSampler() { - auto physicalDevice = vgfx->getPhysicalDevice(); - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - - VkSamplerCreateInfo samplerInfo{}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerInfo.mipLodBias = 0.0f; - samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = 0.0f; - - if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { - throw love::Exception("failed to create texture sampler"); - } - } - - void Texture::clear(bool white) { - auto commandBuffer = vgfx->beginSingleTimeCommands(); - - auto clearColor = getClearValue(white); - - VkImageSubresourceRange range{}; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.layerCount = getMipmapCount(); - range.levelCount = 1; - - vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range); - - vgfx->endSingleTimeCommands(commandBuffer); - } - - VkClearColorValue Texture::getClearValue(bool white) { - auto vulkanFormat = Vulkan::getTextureFormat(format); - - VkClearColorValue clearColor{}; - if (white) { - switch (vulkanFormat.internalFormatRepresentation) { - case FORMATREPRESENTATION_FLOAT: - clearColor.float32[0] = 1.0f; - clearColor.float32[1] = 1.0f; - clearColor.float32[2] = 1.0f; - clearColor.float32[3] = 1.0f; - break; - case FORMATREPRESENTATION_SINT: - clearColor.int32[0] = std::numeric_limits::max(); - clearColor.int32[1] = std::numeric_limits::max(); - clearColor.int32[2] = std::numeric_limits::max(); - clearColor.int32[3] = std::numeric_limits::max(); - break; - case FORMATREPRESENTATION_UINT: - clearColor.uint32[0] = std::numeric_limits::max(); - clearColor.uint32[1] = std::numeric_limits::max(); - clearColor.uint32[2] = std::numeric_limits::max(); - clearColor.uint32[3] = std::numeric_limits::max(); - break; - } - } - else { - switch (vulkanFormat.internalFormatRepresentation) { - case FORMATREPRESENTATION_FLOAT: - clearColor.float32[0] = 0.0f; - clearColor.float32[1] = 0.0f; - clearColor.float32[2] = 0.0f; - clearColor.float32[3] = 0.0f; - break; - case FORMATREPRESENTATION_SINT: - clearColor.int32[0] = 0; - clearColor.int32[1] = 0; - clearColor.int32[2] = 0; - clearColor.int32[3] = 0; - break; - case FORMATREPRESENTATION_UINT: - clearColor.uint32[0] = 0; - clearColor.uint32[1] = 0; - clearColor.uint32[2] = 0; - clearColor.uint32[3] = 0; - break; - } - } - return clearColor; - } - - void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { - auto commandBuffer = vgfx->beginSingleTimeCommands(); - - Vulkan::cmdTransitionImageLayout(commandBuffer, image, oldLayout, newLayout); - - vgfx->endSingleTimeCommands(commandBuffer); - } - - void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) { - VkBuffer stagingBuffer; - VmaAllocation vmaAllocation; - - VkBufferCreateInfo bufferCreateInfo{}; - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.size = size; - bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - - VmaAllocationCreateInfo allocCreateInfo = {}; - allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; - allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; - - VmaAllocationInfo allocInfo; - vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo); - - memcpy(allocInfo.pMappedData, data, size); - - auto command = [buffer = stagingBuffer, image = textureImage, r = r](VkCommandBuffer commandBuffer) { - VkBufferImageCopy region{}; - region.bufferOffset = 0; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; - - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; - region.imageSubresource.layerCount = 1; - - region.imageOffset = { r.x, r.y, 0 }; - region.imageExtent = { - static_cast(r.w), - static_cast(r.h), 1 - }; - - Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkCmdCopyBufferToImage( - commandBuffer, - buffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ®ion - ); - - Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); - }; - - auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() { - vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation); - }; - - vgfx->queueDatatransfer(command, cleanUp); - } + uploadByteData(format, dataPtr, size, 0, 0, rect); + } else { + if (isRenderTarget()) { + clear(false); + } + else { + clear(true); } } + createTextureImageView(); + createTextureSampler(); + + return true; } + +void Texture::unloadVolatile() { + if (textureImage == VK_NULL_HANDLE) + return; + + vgfx->queueCleanUp([ + device = device, + textureSampler = textureSampler, + textureImageView = textureImageView, + allocator = allocator, + textureImage = textureImage, + textureImageAllocation = textureImageAllocation] () { + vkDestroySampler(device, textureSampler, nullptr); + vkDestroyImageView(device, textureImageView, nullptr); + vmaDestroyImage(allocator, textureImage, textureImageAllocation); + }); + + textureImage = VK_NULL_HANDLE; +} + +Texture::~Texture() { + unloadVolatile(); +} + +void Texture::createTextureImageView() { + auto vulkanFormat = Vulkan::getTextureFormat(format); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = textureImage; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = vulkanFormat.internalFormat; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + viewInfo.components.r = vulkanFormat.swizzleR; + viewInfo.components.g = vulkanFormat.swizzleG; + viewInfo.components.b = vulkanFormat.swizzleB; + viewInfo.components.a = vulkanFormat.swizzleA; + + if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) { + throw love::Exception("could not create texture image view"); + } +} + +void Texture::createTextureSampler() { + auto physicalDevice = vgfx->getPhysicalDevice(); + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + + if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { + throw love::Exception("failed to create texture sampler"); + } +} + +void Texture::clear(bool white) { + auto commandBuffer = vgfx->beginSingleTimeCommands(); + + auto clearColor = getClearValue(white); + + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.layerCount = getMipmapCount(); + range.levelCount = 1; + + vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &range); + + vgfx->endSingleTimeCommands(commandBuffer); +} + +VkClearColorValue Texture::getClearValue(bool white) { + auto vulkanFormat = Vulkan::getTextureFormat(format); + + VkClearColorValue clearColor{}; + if (white) { + switch (vulkanFormat.internalFormatRepresentation) { + case FORMATREPRESENTATION_FLOAT: + clearColor.float32[0] = 1.0f; + clearColor.float32[1] = 1.0f; + clearColor.float32[2] = 1.0f; + clearColor.float32[3] = 1.0f; + break; + case FORMATREPRESENTATION_SINT: + clearColor.int32[0] = std::numeric_limits::max(); + clearColor.int32[1] = std::numeric_limits::max(); + clearColor.int32[2] = std::numeric_limits::max(); + clearColor.int32[3] = std::numeric_limits::max(); + break; + case FORMATREPRESENTATION_UINT: + clearColor.uint32[0] = std::numeric_limits::max(); + clearColor.uint32[1] = std::numeric_limits::max(); + clearColor.uint32[2] = std::numeric_limits::max(); + clearColor.uint32[3] = std::numeric_limits::max(); + break; + } + } + else { + switch (vulkanFormat.internalFormatRepresentation) { + case FORMATREPRESENTATION_FLOAT: + clearColor.float32[0] = 0.0f; + clearColor.float32[1] = 0.0f; + clearColor.float32[2] = 0.0f; + clearColor.float32[3] = 0.0f; + break; + case FORMATREPRESENTATION_SINT: + clearColor.int32[0] = 0; + clearColor.int32[1] = 0; + clearColor.int32[2] = 0; + clearColor.int32[3] = 0; + break; + case FORMATREPRESENTATION_UINT: + clearColor.uint32[0] = 0; + clearColor.uint32[1] = 0; + clearColor.uint32[2] = 0; + clearColor.uint32[3] = 0; + break; + } + } + return clearColor; +} + +void Texture::transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { + auto commandBuffer = vgfx->beginSingleTimeCommands(); + + Vulkan::cmdTransitionImageLayout(commandBuffer, image, oldLayout, newLayout); + + vgfx->endSingleTimeCommands(commandBuffer); +} + +void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) { + VkBuffer stagingBuffer; + VmaAllocation vmaAllocation; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = size; + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VmaAllocationInfo allocInfo; + vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &stagingBuffer, &vmaAllocation, &allocInfo); + + memcpy(allocInfo.pMappedData, data, size); + + auto command = [buffer = stagingBuffer, image = textureImage, r = r](VkCommandBuffer commandBuffer) { + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = { r.x, r.y, 0 }; + region.imageExtent = { + static_cast(r.w), + static_cast(r.h), 1 + }; + + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkCmdCopyBufferToImage( + commandBuffer, + buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion + ); + + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + }; + + auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() { + vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation); + }; + + vgfx->queueDatatransfer(command, cleanUp); +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h index 9162dbd1b..27353dd64 100644 --- a/src/modules/graphics/vulkan/Texture.h +++ b/src/modules/graphics/vulkan/Texture.h @@ -10,50 +10,50 @@ namespace love { - namespace graphics { - namespace vulkan { - class Texture : public graphics::Texture, public Volatile { - public: - Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data); - ~Texture(); +namespace graphics { +namespace vulkan { +class Texture : public graphics::Texture, public Volatile { +public: + Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data); + ~Texture(); - virtual bool loadVolatile() override; - virtual void unloadVolatile() override; + virtual bool loadVolatile() override; + virtual void unloadVolatile() override; - void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { }; - void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { }; + void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override { }; + void copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override { }; - ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; }; - ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; }; + ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t)textureImage; }; + ptrdiff_t getSamplerHandle() const override { return (ptrdiff_t)textureSampler; }; - void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override; + void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override; - void generateMipmapsInternal() override { }; + void generateMipmapsInternal() override { }; - int getMSAA() const override { return 0; }; - ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; } - VkImageView getImageView() const { return textureImageView; } - VkSampler getSampler() const { return textureSampler; } + int getMSAA() const override { return 0; }; + ptrdiff_t getHandle() const override { return (ptrdiff_t)textureImage; } + VkImageView getImageView() const { return textureImageView; } + VkSampler getSampler() const { return textureSampler; } - private: - void transitionImageLayout(VkImage, VkImageLayout oldLayout, VkImageLayout newLayout); - void createTextureImageView(); - void createTextureSampler(); - void clear(bool white); +private: + void transitionImageLayout(VkImage, VkImageLayout oldLayout, VkImageLayout newLayout); + void createTextureImageView(); + void createTextureSampler(); + void clear(bool white); - VkClearColorValue getClearValue(bool white); + VkClearColorValue getClearValue(bool white); - graphics::Graphics* gfx; - VkDevice device; - VmaAllocator allocator; - VkImage textureImage = VK_NULL_HANDLE; - VmaAllocation textureImageAllocation; - VkImageView textureImageView; - VkSampler textureSampler; - const Slices* data; - }; - } - } -} + graphics::Graphics* gfx; + VkDevice device; + VmaAllocator allocator; + VkImage textureImage = VK_NULL_HANDLE; + VmaAllocation textureImageAllocation; + VkImageView textureImageView; + VkSampler textureSampler; + const Slices* data; +}; +} // vulkan +} // graphics +} // love #endif diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 5a8dec341..18a656696 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -4,548 +4,548 @@ namespace love { - namespace graphics { - namespace vulkan { - static uint32_t numShaderSwitches; +namespace graphics { +namespace vulkan { +static uint32_t numShaderSwitches; - void Vulkan::shaderSwitch() { - numShaderSwitches++; - } +void Vulkan::shaderSwitch() { + numShaderSwitches++; +} - uint32_t Vulkan::getNumShaderSwitches() { - return numShaderSwitches; - } - - void Vulkan::resetShaderSwitches() { - numShaderSwitches = 0; - } +uint32_t Vulkan::getNumShaderSwitches() { + return numShaderSwitches; +} - VkFormat Vulkan::getVulkanVertexFormat(DataFormat format) { - switch (format) { - case DATAFORMAT_FLOAT: - return VK_FORMAT_R32_SFLOAT; - case DATAFORMAT_FLOAT_VEC2: - return VK_FORMAT_R32G32_SFLOAT; - case DATAFORMAT_FLOAT_VEC3: - return VK_FORMAT_R32G32B32_SFLOAT; - case DATAFORMAT_FLOAT_VEC4: - return VK_FORMAT_R32G32B32A32_SFLOAT; +void Vulkan::resetShaderSwitches() { + numShaderSwitches = 0; +} - case DATAFORMAT_FLOAT_MAT2X2: - case DATAFORMAT_FLOAT_MAT2X3: - case DATAFORMAT_FLOAT_MAT2X4: - case DATAFORMAT_FLOAT_MAT3X2: - case DATAFORMAT_FLOAT_MAT3X3: - case DATAFORMAT_FLOAT_MAT3X4: - case DATAFORMAT_FLOAT_MAT4X2: - case DATAFORMAT_FLOAT_MAT4X3: - case DATAFORMAT_FLOAT_MAT4X4: - throw love::Exception("unimplemented data format (matnxm)"); +VkFormat Vulkan::getVulkanVertexFormat(DataFormat format) { + switch (format) { + case DATAFORMAT_FLOAT: + return VK_FORMAT_R32_SFLOAT; + case DATAFORMAT_FLOAT_VEC2: + return VK_FORMAT_R32G32_SFLOAT; + case DATAFORMAT_FLOAT_VEC3: + return VK_FORMAT_R32G32B32_SFLOAT; + case DATAFORMAT_FLOAT_VEC4: + return VK_FORMAT_R32G32B32A32_SFLOAT; - case DATAFORMAT_INT32: - return VK_FORMAT_R32_SINT; - case DATAFORMAT_INT32_VEC2: - return VK_FORMAT_R32G32_SINT; - case DATAFORMAT_INT32_VEC3: - return VK_FORMAT_R32G32B32_SINT; - case DATAFORMAT_INT32_VEC4: - return VK_FORMAT_R32G32B32A32_SINT; + case DATAFORMAT_FLOAT_MAT2X2: + case DATAFORMAT_FLOAT_MAT2X3: + case DATAFORMAT_FLOAT_MAT2X4: + case DATAFORMAT_FLOAT_MAT3X2: + case DATAFORMAT_FLOAT_MAT3X3: + case DATAFORMAT_FLOAT_MAT3X4: + case DATAFORMAT_FLOAT_MAT4X2: + case DATAFORMAT_FLOAT_MAT4X3: + case DATAFORMAT_FLOAT_MAT4X4: + throw love::Exception("unimplemented data format (matnxm)"); - case DATAFORMAT_UINT32: - return VK_FORMAT_R32_UINT; - case DATAFORMAT_UINT32_VEC2: - return VK_FORMAT_R32G32_UINT; - case DATAFORMAT_UINT32_VEC3: - return VK_FORMAT_R32G32B32_UINT; - case DATAFORMAT_UINT32_VEC4: - return VK_FORMAT_R32G32B32A32_UINT; + case DATAFORMAT_INT32: + return VK_FORMAT_R32_SINT; + case DATAFORMAT_INT32_VEC2: + return VK_FORMAT_R32G32_SINT; + case DATAFORMAT_INT32_VEC3: + return VK_FORMAT_R32G32B32_SINT; + case DATAFORMAT_INT32_VEC4: + return VK_FORMAT_R32G32B32A32_SINT; - case DATAFORMAT_SNORM8_VEC4: - return VK_FORMAT_R8G8B8A8_SNORM; - case DATAFORMAT_UNORM8_VEC4: - return VK_FORMAT_R8G8B8A8_UNORM; - case DATAFORMAT_INT8_VEC4: - return VK_FORMAT_R8G8B8A8_SINT; - case DATAFORMAT_UINT8_VEC4: - return VK_FORMAT_R8G8B8A8_UINT; + case DATAFORMAT_UINT32: + return VK_FORMAT_R32_UINT; + case DATAFORMAT_UINT32_VEC2: + return VK_FORMAT_R32G32_UINT; + case DATAFORMAT_UINT32_VEC3: + return VK_FORMAT_R32G32B32_UINT; + case DATAFORMAT_UINT32_VEC4: + return VK_FORMAT_R32G32B32A32_UINT; - case DATAFORMAT_SNORM16_VEC2: - return VK_FORMAT_R16G16_SNORM; - case DATAFORMAT_SNORM16_VEC4: - return VK_FORMAT_R16G16B16A16_SNORM; - case DATAFORMAT_UNORM16_VEC2: - return VK_FORMAT_R16G16_UNORM; - case DATAFORMAT_UNORM16_VEC4: - return VK_FORMAT_R16G16B16A16_UNORM; + case DATAFORMAT_SNORM8_VEC4: + return VK_FORMAT_R8G8B8A8_SNORM; + case DATAFORMAT_UNORM8_VEC4: + return VK_FORMAT_R8G8B8A8_UNORM; + case DATAFORMAT_INT8_VEC4: + return VK_FORMAT_R8G8B8A8_SINT; + case DATAFORMAT_UINT8_VEC4: + return VK_FORMAT_R8G8B8A8_UINT; - case DATAFORMAT_INT16_VEC2: - return VK_FORMAT_R16G16_SINT; - case DATAFORMAT_INT16_VEC4: - return VK_FORMAT_R16G16B16A16_SINT; + case DATAFORMAT_SNORM16_VEC2: + return VK_FORMAT_R16G16_SNORM; + case DATAFORMAT_SNORM16_VEC4: + return VK_FORMAT_R16G16B16A16_SNORM; + case DATAFORMAT_UNORM16_VEC2: + return VK_FORMAT_R16G16_UNORM; + case DATAFORMAT_UNORM16_VEC4: + return VK_FORMAT_R16G16B16A16_UNORM; - case DATAFORMAT_UINT16: - return VK_FORMAT_R16_UINT; - case DATAFORMAT_UINT16_VEC2: - return VK_FORMAT_R16G16_UINT; - case DATAFORMAT_UINT16_VEC4: - return VK_FORMAT_R16G16B16A16_UINT; + case DATAFORMAT_INT16_VEC2: + return VK_FORMAT_R16G16_SINT; + case DATAFORMAT_INT16_VEC4: + return VK_FORMAT_R16G16B16A16_SINT; - case DATAFORMAT_BOOL: - case DATAFORMAT_BOOL_VEC2: - case DATAFORMAT_BOOL_VEC3: - case DATAFORMAT_BOOL_VEC4: - throw love::Exception("unimplemented data format (bool)"); + case DATAFORMAT_UINT16: + return VK_FORMAT_R16_UINT; + case DATAFORMAT_UINT16_VEC2: + return VK_FORMAT_R16G16_UINT; + case DATAFORMAT_UINT16_VEC4: + return VK_FORMAT_R16G16B16A16_UINT; - default: - throw love::Exception("unknown data format"); - } - } + case DATAFORMAT_BOOL: + case DATAFORMAT_BOOL_VEC2: + case DATAFORMAT_BOOL_VEC3: + case DATAFORMAT_BOOL_VEC4: + throw love::Exception("unimplemented data format (bool)"); - TextureFormat Vulkan::getTextureFormat(PixelFormat format) { - TextureFormat textureFormat{}; - - switch (format) { - case PIXELFORMAT_UNKNOWN: - throw love::Exception("unknown pixel format"); - case PIXELFORMAT_NORMAL: - textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_HDR: - throw love::Exception("unimplemented pixel format: hdr"); - case PIXELFORMAT_R8_UNORM: - textureFormat.internalFormat = VK_FORMAT_R8_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_R8_INT: - textureFormat.internalFormat = VK_FORMAT_R8_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_R8_UINT: - textureFormat.internalFormat = VK_FORMAT_R8_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_R16_UNORM: - textureFormat.internalFormat = VK_FORMAT_R16_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_R16_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R16_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_R16_INT: - textureFormat.internalFormat = VK_FORMAT_R16_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_R16_UINT: - textureFormat.internalFormat = VK_FORMAT_R16_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_R32_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R32_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_R32_INT: - textureFormat.internalFormat = VK_FORMAT_R32_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_R32_UINT: - textureFormat.internalFormat = VK_FORMAT_R32_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RG8_UNORM: - textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RG8_INT: - textureFormat.internalFormat = VK_FORMAT_R8G8_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RG8_UINT: - textureFormat.internalFormat = VK_FORMAT_R8G8_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_LA8_UNORM: // Same as RG8: but accessed as (L: L: L: A) - textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM; - textureFormat.swizzleR = VK_COMPONENT_SWIZZLE_R; - textureFormat.swizzleG = VK_COMPONENT_SWIZZLE_R; - textureFormat.swizzleB = VK_COMPONENT_SWIZZLE_R; - textureFormat.swizzleA = VK_COMPONENT_SWIZZLE_G; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RG16_UNORM: - textureFormat.internalFormat = VK_FORMAT_R16G16_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RG16_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R16G16_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RG16_INT: - textureFormat.internalFormat = VK_FORMAT_R16G16_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RG16_UINT: - textureFormat.internalFormat = VK_FORMAT_R16G16_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RG32_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R32G32_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RG32_INT: - textureFormat.internalFormat = VK_FORMAT_R32G32_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RG32_UINT: - textureFormat.internalFormat = VK_FORMAT_R32G32_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RGBA8_UNORM: - textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RGBA8_UNORM_sRGB: - textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_BGRA8_UNORM: - textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_BGRA8_UNORM_sRGB: - textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_SRGB; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RGBA8_INT: - textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RGBA8_UINT: - textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RGBA16_UNORM: - textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UNORM; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RGBA16_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RGBA16_INT: - textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RGBA16_UINT: - textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RGBA32_FLOAT: - textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SFLOAT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; - break; - case PIXELFORMAT_RGBA32_INT: - textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; - break; - case PIXELFORMAT_RGBA32_UINT: - textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_UINT; - textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; - break; - case PIXELFORMAT_RGBA4_UNORM: // LSB->MSB: [a: b: g: r] - case PIXELFORMAT_RGB5A1_UNORM: // LSB->MSB: [a: b: g: r] - case PIXELFORMAT_RGB565_UNORM: // LSB->MSB: [b: g: r] - case PIXELFORMAT_RGB10A2_UNORM: // LSB->MSB: [r: g: b: a] - case PIXELFORMAT_RG11B10_FLOAT: // LSB->MSB: [r: g: b] - case PIXELFORMAT_STENCIL8: - case PIXELFORMAT_DEPTH16_UNORM: - case PIXELFORMAT_DEPTH24_UNORM: - case PIXELFORMAT_DEPTH32_FLOAT: - case PIXELFORMAT_DEPTH24_UNORM_STENCIL8: - case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8: - case PIXELFORMAT_DXT1_UNORM: - case PIXELFORMAT_DXT3_UNORM: - case PIXELFORMAT_DXT5_UNORM: - case PIXELFORMAT_BC4_UNORM: - case PIXELFORMAT_BC4_SNORM: - case PIXELFORMAT_BC5_UNORM: - case PIXELFORMAT_BC5_SNORM: - case PIXELFORMAT_BC6H_UFLOAT: - case PIXELFORMAT_BC6H_FLOAT: - case PIXELFORMAT_BC7_UNORM: - case PIXELFORMAT_PVR1_RGB2_UNORM: - case PIXELFORMAT_PVR1_RGB4_UNORM: - case PIXELFORMAT_PVR1_RGBA2_UNORM: - case PIXELFORMAT_PVR1_RGBA4_UNORM: - case PIXELFORMAT_ETC1_UNORM: - case PIXELFORMAT_ETC2_RGB_UNORM: - case PIXELFORMAT_ETC2_RGBA_UNORM: - case PIXELFORMAT_ETC2_RGBA1_UNORM: - case PIXELFORMAT_EAC_R_UNORM: - case PIXELFORMAT_EAC_R_SNORM: - case PIXELFORMAT_EAC_RG_UNORM: - case PIXELFORMAT_EAC_RG_SNORM: - case PIXELFORMAT_ASTC_4x4: - case PIXELFORMAT_ASTC_5x4: - case PIXELFORMAT_ASTC_5x5: - case PIXELFORMAT_ASTC_6x5: - case PIXELFORMAT_ASTC_6x6: - case PIXELFORMAT_ASTC_8x5: - case PIXELFORMAT_ASTC_8x6: - case PIXELFORMAT_ASTC_8x8: - case PIXELFORMAT_ASTC_10x5: - case PIXELFORMAT_ASTC_10x6: - case PIXELFORMAT_ASTC_10x8: - case PIXELFORMAT_ASTC_10x10: - case PIXELFORMAT_ASTC_12x10: - case PIXELFORMAT_ASTC_12x12: - throw love::Exception("unimplemented pixel format"); - } - - 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 4130: - return "AMD"; - case 4318: - return "Nvidia"; - case 8086: - return "Intel"; - case 4203: - return "Apple"; - case 5140: - return "Microsoft"; - case 5045: - return "ARM"; - case 20803: - return "Qualcomm"; - case 5348: - return "Broadcom"; - default: - return "unknown"; - } - } - - std::string Vulkan::getVulkanApiVersion(uint32_t version) { - std::stringstream ss; - - ss << VK_API_VERSION_MAJOR(version) - << "." << VK_API_VERSION_MINOR(version) - << "." << VK_API_VERSION_PATCH(version); - - return ss.str(); - } - - VkPrimitiveTopology Vulkan::getPrimitiveTypeTopology(graphics::PrimitiveType primitiveType) { - switch (primitiveType) { - case PRIMITIVE_POINTS: - return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; - case PRIMITIVE_TRIANGLES: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - case PRIMITIVE_TRIANGLE_FAN: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; - case PRIMITIVE_TRIANGLE_STRIP: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - default: - throw love::Exception("unknown primitive type"); - } - } - - VkBlendFactor Vulkan::getBlendFactor(BlendFactor blendFactor) { - switch (blendFactor) { - case BLENDFACTOR_ZERO: - return VK_BLEND_FACTOR_ZERO; - case BLENDFACTOR_ONE: - return VK_BLEND_FACTOR_ONE; - case BLENDFACTOR_SRC_COLOR: - return VK_BLEND_FACTOR_SRC_COLOR; - case BLENDFACTOR_ONE_MINUS_SRC_COLOR: - return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - case BLENDFACTOR_SRC_ALPHA: - return VK_BLEND_FACTOR_SRC_ALPHA; - case BLENDFACTOR_ONE_MINUS_SRC_ALPHA: - return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case BLENDFACTOR_DST_COLOR: - return VK_BLEND_FACTOR_DST_COLOR; - case BLENDFACTOR_ONE_MINUS_DST_COLOR: - return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; - case BLENDFACTOR_DST_ALPHA: - return VK_BLEND_FACTOR_DST_ALPHA; - case BLENDFACTOR_ONE_MINUS_DST_ALPHA: - return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - case BLENDFACTOR_SRC_ALPHA_SATURATED: - return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; - default: - throw love::Exception("unknown blend factor"); - } - } - - VkBlendOp Vulkan::getBlendOp(BlendOperation op) { - switch (op) { - case BLENDOP_ADD: - return VK_BLEND_OP_ADD; - case BLENDOP_MAX: - return VK_BLEND_OP_MAX; - case BLENDOP_MIN: - return VK_BLEND_OP_MIN; - case BLENDOP_SUBTRACT: - return VK_BLEND_OP_SUBTRACT; - case BLENDOP_REVERSE_SUBTRACT: - return VK_BLEND_OP_REVERSE_SUBTRACT; - default: - throw love::Exception("unknown blend operation"); - } - } - - VkBool32 Vulkan::getBool(bool b) { - if (b) { - return VK_TRUE; - } else { - return VK_FALSE; - } - } - - VkColorComponentFlags Vulkan::getColorMask(ColorChannelMask mask) { - VkColorComponentFlags flags = 0; - - if (mask.r) { - flags |= VK_COLOR_COMPONENT_R_BIT; - } - if (mask.g) { - flags |= VK_COLOR_COMPONENT_G_BIT; - } - if (mask.b) { - flags |= VK_COLOR_COMPONENT_B_BIT; - } - if (mask.a) { - flags |= VK_COLOR_COMPONENT_A_BIT; - } - - return flags; - } - - VkFrontFace Vulkan::getFrontFace(Winding winding) { - switch (winding) { - case WINDING_CW: - return VK_FRONT_FACE_CLOCKWISE; - case WINDING_CCW: - return VK_FRONT_FACE_COUNTER_CLOCKWISE; - default: - throw love::Exception("unknown winding"); - } - } - - VkCullModeFlags Vulkan::getCullMode(CullMode cullmode) { - switch (cullmode) { - case CULL_BACK: - return VK_CULL_MODE_BACK_BIT; - case CULL_FRONT: - return VK_CULL_MODE_FRONT_BIT; - case CULL_NONE: - return VK_CULL_MODE_NONE; - default: - throw love::Exception("unknown cull mode"); - } - } - - void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - - VkPipelineStageFlags sourceStage; - VkPipelineStageFlags destinationStage; - - if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; - - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.dstAccessMask = 0; - - sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) { - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.dstAccessMask = 0; - - sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - else { - throw std::invalid_argument("unsupported layout transition!"); - } - - vkCmdPipelineBarrier( - commandBuffer, - sourceStage, destinationStage, - 0, - 0, nullptr, - 0, nullptr, - 1, &barrier - ); - } - } + default: + throw love::Exception("unknown data format"); } } + +TextureFormat Vulkan::getTextureFormat(PixelFormat format) { + TextureFormat textureFormat{}; + + switch (format) { + case PIXELFORMAT_UNKNOWN: + throw love::Exception("unknown pixel format"); + case PIXELFORMAT_NORMAL: + textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_HDR: + throw love::Exception("unimplemented pixel format: hdr"); + case PIXELFORMAT_R8_UNORM: + textureFormat.internalFormat = VK_FORMAT_R8_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_R8_INT: + textureFormat.internalFormat = VK_FORMAT_R8_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_R8_UINT: + textureFormat.internalFormat = VK_FORMAT_R8_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_R16_UNORM: + textureFormat.internalFormat = VK_FORMAT_R16_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_R16_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R16_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_R16_INT: + textureFormat.internalFormat = VK_FORMAT_R16_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_R16_UINT: + textureFormat.internalFormat = VK_FORMAT_R16_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_R32_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R32_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_R32_INT: + textureFormat.internalFormat = VK_FORMAT_R32_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_R32_UINT: + textureFormat.internalFormat = VK_FORMAT_R32_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RG8_UNORM: + textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RG8_INT: + textureFormat.internalFormat = VK_FORMAT_R8G8_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RG8_UINT: + textureFormat.internalFormat = VK_FORMAT_R8G8_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_LA8_UNORM: // Same as RG8: but accessed as (L: L: L: A) + textureFormat.internalFormat = VK_FORMAT_R8G8_UNORM; + textureFormat.swizzleR = VK_COMPONENT_SWIZZLE_R; + textureFormat.swizzleG = VK_COMPONENT_SWIZZLE_R; + textureFormat.swizzleB = VK_COMPONENT_SWIZZLE_R; + textureFormat.swizzleA = VK_COMPONENT_SWIZZLE_G; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RG16_UNORM: + textureFormat.internalFormat = VK_FORMAT_R16G16_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RG16_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R16G16_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RG16_INT: + textureFormat.internalFormat = VK_FORMAT_R16G16_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RG16_UINT: + textureFormat.internalFormat = VK_FORMAT_R16G16_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RG32_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R32G32_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RG32_INT: + textureFormat.internalFormat = VK_FORMAT_R32G32_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RG32_UINT: + textureFormat.internalFormat = VK_FORMAT_R32G32_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RGBA8_UNORM: + textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RGBA8_UNORM_sRGB: + textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SRGB; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_BGRA8_UNORM: + textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_BGRA8_UNORM_sRGB: + textureFormat.internalFormat = VK_FORMAT_B8G8R8A8_SRGB; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RGBA8_INT: + textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RGBA8_UINT: + textureFormat.internalFormat = VK_FORMAT_R8G8B8A8_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RGBA16_UNORM: + textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UNORM; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RGBA16_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RGBA16_INT: + textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RGBA16_UINT: + textureFormat.internalFormat = VK_FORMAT_R16G16B16A16_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RGBA32_FLOAT: + textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_FLOAT; + break; + case PIXELFORMAT_RGBA32_INT: + textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_SINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_SINT; + break; + case PIXELFORMAT_RGBA32_UINT: + textureFormat.internalFormat = VK_FORMAT_R32G32B32A32_UINT; + textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT; + break; + case PIXELFORMAT_RGBA4_UNORM: // LSB->MSB: [a: b: g: r] + case PIXELFORMAT_RGB5A1_UNORM: // LSB->MSB: [a: b: g: r] + case PIXELFORMAT_RGB565_UNORM: // LSB->MSB: [b: g: r] + case PIXELFORMAT_RGB10A2_UNORM: // LSB->MSB: [r: g: b: a] + case PIXELFORMAT_RG11B10_FLOAT: // LSB->MSB: [r: g: b] + case PIXELFORMAT_STENCIL8: + case PIXELFORMAT_DEPTH16_UNORM: + case PIXELFORMAT_DEPTH24_UNORM: + case PIXELFORMAT_DEPTH32_FLOAT: + case PIXELFORMAT_DEPTH24_UNORM_STENCIL8: + case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8: + case PIXELFORMAT_DXT1_UNORM: + case PIXELFORMAT_DXT3_UNORM: + case PIXELFORMAT_DXT5_UNORM: + case PIXELFORMAT_BC4_UNORM: + case PIXELFORMAT_BC4_SNORM: + case PIXELFORMAT_BC5_UNORM: + case PIXELFORMAT_BC5_SNORM: + case PIXELFORMAT_BC6H_UFLOAT: + case PIXELFORMAT_BC6H_FLOAT: + case PIXELFORMAT_BC7_UNORM: + case PIXELFORMAT_PVR1_RGB2_UNORM: + case PIXELFORMAT_PVR1_RGB4_UNORM: + case PIXELFORMAT_PVR1_RGBA2_UNORM: + case PIXELFORMAT_PVR1_RGBA4_UNORM: + case PIXELFORMAT_ETC1_UNORM: + case PIXELFORMAT_ETC2_RGB_UNORM: + case PIXELFORMAT_ETC2_RGBA_UNORM: + case PIXELFORMAT_ETC2_RGBA1_UNORM: + case PIXELFORMAT_EAC_R_UNORM: + case PIXELFORMAT_EAC_R_SNORM: + case PIXELFORMAT_EAC_RG_UNORM: + case PIXELFORMAT_EAC_RG_SNORM: + case PIXELFORMAT_ASTC_4x4: + case PIXELFORMAT_ASTC_5x4: + case PIXELFORMAT_ASTC_5x5: + case PIXELFORMAT_ASTC_6x5: + case PIXELFORMAT_ASTC_6x6: + case PIXELFORMAT_ASTC_8x5: + case PIXELFORMAT_ASTC_8x6: + case PIXELFORMAT_ASTC_8x8: + case PIXELFORMAT_ASTC_10x5: + case PIXELFORMAT_ASTC_10x6: + case PIXELFORMAT_ASTC_10x8: + case PIXELFORMAT_ASTC_10x10: + case PIXELFORMAT_ASTC_12x10: + case PIXELFORMAT_ASTC_12x12: + throw love::Exception("unimplemented pixel format"); + } + + 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 4130: + return "AMD"; + case 4318: + return "Nvidia"; + case 8086: + return "Intel"; + case 4203: + return "Apple"; + case 5140: + return "Microsoft"; + case 5045: + return "ARM"; + case 20803: + return "Qualcomm"; + case 5348: + return "Broadcom"; + default: + return "unknown"; + } +} + +std::string Vulkan::getVulkanApiVersion(uint32_t version) { + std::stringstream ss; + + ss << VK_API_VERSION_MAJOR(version) + << "." << VK_API_VERSION_MINOR(version) + << "." << VK_API_VERSION_PATCH(version); + + return ss.str(); +} + +VkPrimitiveTopology Vulkan::getPrimitiveTypeTopology(graphics::PrimitiveType primitiveType) { + switch (primitiveType) { + case PRIMITIVE_POINTS: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case PRIMITIVE_TRIANGLES: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case PRIMITIVE_TRIANGLE_FAN: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case PRIMITIVE_TRIANGLE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + default: + throw love::Exception("unknown primitive type"); + } +} + +VkBlendFactor Vulkan::getBlendFactor(BlendFactor blendFactor) { + switch (blendFactor) { + case BLENDFACTOR_ZERO: + return VK_BLEND_FACTOR_ZERO; + case BLENDFACTOR_ONE: + return VK_BLEND_FACTOR_ONE; + case BLENDFACTOR_SRC_COLOR: + return VK_BLEND_FACTOR_SRC_COLOR; + case BLENDFACTOR_ONE_MINUS_SRC_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case BLENDFACTOR_SRC_ALPHA: + return VK_BLEND_FACTOR_SRC_ALPHA; + case BLENDFACTOR_ONE_MINUS_SRC_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case BLENDFACTOR_DST_COLOR: + return VK_BLEND_FACTOR_DST_COLOR; + case BLENDFACTOR_ONE_MINUS_DST_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case BLENDFACTOR_DST_ALPHA: + return VK_BLEND_FACTOR_DST_ALPHA; + case BLENDFACTOR_ONE_MINUS_DST_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case BLENDFACTOR_SRC_ALPHA_SATURATED: + return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + default: + throw love::Exception("unknown blend factor"); + } +} + +VkBlendOp Vulkan::getBlendOp(BlendOperation op) { + switch (op) { + case BLENDOP_ADD: + return VK_BLEND_OP_ADD; + case BLENDOP_MAX: + return VK_BLEND_OP_MAX; + case BLENDOP_MIN: + return VK_BLEND_OP_MIN; + case BLENDOP_SUBTRACT: + return VK_BLEND_OP_SUBTRACT; + case BLENDOP_REVERSE_SUBTRACT: + return VK_BLEND_OP_REVERSE_SUBTRACT; + default: + throw love::Exception("unknown blend operation"); + } +} + +VkBool32 Vulkan::getBool(bool b) { + if (b) { + return VK_TRUE; + } else { + return VK_FALSE; + } +} + +VkColorComponentFlags Vulkan::getColorMask(ColorChannelMask mask) { + VkColorComponentFlags flags = 0; + + if (mask.r) { + flags |= VK_COLOR_COMPONENT_R_BIT; + } + if (mask.g) { + flags |= VK_COLOR_COMPONENT_G_BIT; + } + if (mask.b) { + flags |= VK_COLOR_COMPONENT_B_BIT; + } + if (mask.a) { + flags |= VK_COLOR_COMPONENT_A_BIT; + } + + return flags; +} + +VkFrontFace Vulkan::getFrontFace(Winding winding) { + switch (winding) { + case WINDING_CW: + return VK_FRONT_FACE_CLOCKWISE; + case WINDING_CCW: + return VK_FRONT_FACE_COUNTER_CLOCKWISE; + default: + throw love::Exception("unknown winding"); + } +} + +VkCullModeFlags Vulkan::getCullMode(CullMode cullmode) { + switch (cullmode) { + case CULL_BACK: + return VK_CULL_MODE_BACK_BIT; + case CULL_FRONT: + return VK_CULL_MODE_FRONT_BIT; + case CULL_NONE: + return VK_CULL_MODE_NONE; + default: + throw love::Exception("unknown cull mode"); + } +} + +void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = 0; + + sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) { + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = 0; + + sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + else { + throw std::invalid_argument("unsupported layout transition!"); + } + + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); +} +} // vulkan +} // graphics +} // love diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index c8c438f74..e45d609ff 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/src/modules/graphics/vulkan/Vulkan.h @@ -5,47 +5,47 @@ #include "vulkan/vulkan.h" namespace love { - namespace graphics { - namespace vulkan { - enum InternalFormatRepresentation { - FORMATREPRESENTATION_FLOAT, - FORMATREPRESENTATION_UINT, - FORMATREPRESENTATION_SINT, - FORMATREPRESENTATION_MAX_ENUM - }; +namespace graphics { +namespace vulkan { +enum InternalFormatRepresentation { + FORMATREPRESENTATION_FLOAT, + FORMATREPRESENTATION_UINT, + FORMATREPRESENTATION_SINT, + FORMATREPRESENTATION_MAX_ENUM +}; - struct TextureFormat { - InternalFormatRepresentation internalFormatRepresentation; - VkFormat internalFormat = VK_FORMAT_UNDEFINED; +struct TextureFormat { + InternalFormatRepresentation internalFormatRepresentation; + VkFormat internalFormat = VK_FORMAT_UNDEFINED; - VkComponentSwizzle swizzleR = VK_COMPONENT_SWIZZLE_IDENTITY; - VkComponentSwizzle swizzleG = VK_COMPONENT_SWIZZLE_IDENTITY; - VkComponentSwizzle swizzleB = VK_COMPONENT_SWIZZLE_IDENTITY; - VkComponentSwizzle swizzleA = VK_COMPONENT_SWIZZLE_IDENTITY; - }; + VkComponentSwizzle swizzleR = VK_COMPONENT_SWIZZLE_IDENTITY; + VkComponentSwizzle swizzleG = VK_COMPONENT_SWIZZLE_IDENTITY; + VkComponentSwizzle swizzleB = VK_COMPONENT_SWIZZLE_IDENTITY; + VkComponentSwizzle swizzleA = VK_COMPONENT_SWIZZLE_IDENTITY; +}; - class Vulkan { - public: - static void shaderSwitch(); - static uint32_t getNumShaderSwitches(); - static void resetShaderSwitches(); +class Vulkan { +public: + static void shaderSwitch(); + static uint32_t getNumShaderSwitches(); + static void resetShaderSwitches(); - static VkFormat getVulkanVertexFormat(DataFormat format); - static TextureFormat getTextureFormat(PixelFormat); - static std::string getVendorName(uint32_t vendorId); - static std::string getVulkanApiVersion(uint32_t apiVersion); - static VkPrimitiveTopology getPrimitiveTypeTopology(graphics::PrimitiveType); - static VkBlendFactor getBlendFactor(BlendFactor); - static VkBlendOp getBlendOp(BlendOperation); - static VkBool32 getBool(bool); - static VkColorComponentFlags getColorMask(ColorChannelMask); - static VkFrontFace getFrontFace(Winding); - static VkCullModeFlags getCullMode(CullMode); + static VkFormat getVulkanVertexFormat(DataFormat format); + static TextureFormat getTextureFormat(PixelFormat); + static std::string getVendorName(uint32_t vendorId); + static std::string getVulkanApiVersion(uint32_t apiVersion); + static VkPrimitiveTopology getPrimitiveTypeTopology(graphics::PrimitiveType); + static VkBlendFactor getBlendFactor(BlendFactor); + static VkBlendOp getBlendOp(BlendOperation); + static VkBool32 getBool(bool); + static VkColorComponentFlags getColorMask(ColorChannelMask); + static VkFrontFace getFrontFace(Winding); + static VkCullModeFlags getCullMode(CullMode); - static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout); - }; - } - } -} + static void cmdTransitionImageLayout(VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout); +}; +} // vulkan +} // graphics +} // love #endif