diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 1ce0db314..bff4881ac 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -563,38 +563,6 @@ 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; @@ -641,9 +609,9 @@ void Graphics::createVulkanInstance() { 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.applicationVersion = VK_MAKE_API_VERSION(0, 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.engineVersion = VK_MAKE_API_VERSION(0, VERSION_MAJOR, VERSION_MINOR, VERSION_REV); appInfo.apiVersion = vulkanApiVersion; VkInstanceCreateInfo createInfo{}; @@ -1283,8 +1251,8 @@ VkSampler Graphics::createSampler(const SamplerState& samplerState) { } samplerInfo.mipmapMode = Vulkan::getMipMapMode(samplerState.mipmapFilter); samplerInfo.mipLodBias = samplerState.lodBias; - samplerInfo.minLod = 0.0f; // fixme: samplerState.minLod - samplerInfo.maxLod = 0.0f; // fixme: samplerState.maxLod + samplerInfo.minLod = static_cast(samplerState.minLod); + samplerInfo.maxLod = static_cast(samplerState.maxLod); VkSampler sampler; if (vkCreateSampler(device, &samplerInfo, nullptr, &sampler) != VK_SUCCESS) { @@ -1505,6 +1473,8 @@ void Graphics::createSyncObjects() { void Graphics::createDefaultTexture() { Texture::Settings settings; standardTexture.reset((Texture*)newTexture(settings)); + uint8_t whitePixels[] = {255, 255, 255, 255}; + standardTexture->replacePixels(whitePixels, sizeof(whitePixels), 0, 0, { 0, 0, 1, 1 }, false); } void Graphics::cleanup() { diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 027004fe0..360692464 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -138,9 +138,6 @@ public: VkCommandBuffer getDataTransferCommandBuffer(); void queueCleanUp(std::function cleanUp); - VkCommandBuffer beginSingleTimeCommands(); - void endSingleTimeCommands(VkCommandBuffer); - uint32_t getNumImagesInFlight() const; const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const; const VkDeviceSize getMinUniformBufferOffsetAlignment() const; @@ -222,8 +219,8 @@ private: std::vector renderFinishedSemaphores; std::vector inFlightFences; std::vector imagesInFlight; - VkDeviceSize minUniformBufferOffsetAlignment; - PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet; + VkDeviceSize minUniformBufferOffsetAlignment = 0; + PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSet = nullptr; size_t currentFrame = 0; uint32_t imageIndex = 0; bool framebufferResized = false; @@ -239,7 +236,7 @@ private: // render pass variables. VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED; - Texture* renderTargetTexture; + Texture* renderTargetTexture = nullptr; float currentViewportWidth = 0; float currentViewportHeight = 0; }; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index eecfb8813..db42763fc 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -174,7 +174,7 @@ void Shader::unloadVolatile() { vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); }); - for (const auto streamBufferVector : streamBuffers) { + for (const auto &streamBufferVector : streamBuffers) { for (const auto streamBuffer : streamBufferVector) { delete streamBuffer; } diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 76b9dc421..eeea2aa48 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -11,7 +11,11 @@ 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) { + : love::graphics::Texture(gfx, settings, data), gfx(gfx), slices(settings.type) { + if (data) { + slices = *data; + } + loadVolatile(); } @@ -42,8 +46,8 @@ bool Texture::loadVolatile() { imageInfo.extent.width = static_cast(width); imageInfo.extent.height = static_cast(height); imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = layerCount; + imageInfo.mipLevels = static_cast(getMipmapCount()); + imageInfo.arrayLayers = static_cast(layerCount); imageInfo.format = vulkanFormat.internalFormat; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -60,32 +64,34 @@ bool Texture::loadVolatile() { auto commandBuffer = vgfx->getDataTransferCommandBuffer(); // fixme: we probably should select a different default layout when the texture is not readable, instead of VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, 1, 0, layerCount); + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 0, static_cast(getMipmapCount()), + 0, static_cast(layerCount)); - if (data) { - for (int slice = 0; slice < layerCount; slice++) { - auto sliceData = data->get(slice, 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(); + bool hasdata = slices.get(0, 0) != nullptr; - uploadByteData(format, dataPtr, size, 0, slice, rect); + if (hasdata) { + for (int mip = 0; mip < layerCount; mip++) { + // fixme: deal with compressed images. + + for (int slice = 0; slice < slices.getSliceCount(mip); slice++) { + auto* id = slices.get(slice, mip); + if (id != nullptr) { + uploadImageData(id, mip, slice, 0, 0); + } + } } } else { - if (isRenderTarget()) { - clear(false); - } - else { - clear(true); - } + clear(); } createTextureImageView(); textureSampler = vgfx->getCachedSampler(samplerState); + if (slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE) { + generateMipmaps(); + } + return true; } @@ -138,21 +144,25 @@ void Texture::createTextureImageView() { } } -void Texture::clear(bool white) { +void Texture::clear() { auto commandBuffer = vgfx->getDataTransferCommandBuffer(); - auto clearColor = getClearValue(white); + auto clearColor = getClearValue(false); VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.layerCount = layerCount; - range.levelCount = 1; + range.layerCount = static_cast(layerCount); + range.levelCount = static_cast(getMipmapCount()); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, 1, 0, layerCount); + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, range.levelCount, 0, range.layerCount); vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, 1, 0, layerCount); + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 0, range.levelCount, 0, range.layerCount); } VkClearColorValue Texture::getClearValue(bool white) { @@ -206,6 +216,83 @@ VkClearColorValue Texture::getClearValue(bool white) { return clearColor; } +void Texture::generateMipmapsInternal() { + auto commandBuffer = vgfx->getDataTransferCommandBuffer(); + + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, static_cast(getMipmapCount()), 0, static_cast(layerCount)); + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = textureImage; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = static_cast(layerCount); + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1u; + + uint32_t mipLevels = static_cast(getMipmapCount()); + + for (uint32_t i = 1; i < mipLevels; i++) { + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + VkImageBlit blit{}; + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { getWidth(i - 1), getHeight(i - 1), 1 }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = static_cast(layerCount); + + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { getWidth(i), getHeight(i), 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = static_cast(layerCount); + + vkCmdBlitImage(commandBuffer, + textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, + VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + } + + barrier.subresourceRange.baseMipLevel = mipLevels - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); +} + void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) { VkBuffer stagingBuffer; VmaAllocation vmaAllocation; @@ -242,7 +329,9 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t s auto commandBuffer = vgfx->getDataTransferCommandBuffer(); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, level, 1, slice, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + level, 1, slice, 1); vkCmdCopyBufferToImage( commandBuffer, @@ -253,7 +342,9 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void* data, size_t s ®ion ); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, level, 1, slice, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + level, 1, slice, 1); vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() { vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation); diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h index 108fd3a85..8b0ba8ad8 100644 --- a/src/modules/graphics/vulkan/Texture.h +++ b/src/modules/graphics/vulkan/Texture.h @@ -19,7 +19,7 @@ public: virtual bool loadVolatile() override; virtual void unloadVolatile() override; - + void setSamplerState(const SamplerState &s) override; void copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override; @@ -30,25 +30,25 @@ public: 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; } private: void createTextureImageView(); - void clear(bool white); + void clear(); VkClearColorValue getClearValue(bool white); - graphics::Graphics* gfx; - VkDevice device; - VmaAllocator allocator; + graphics::Graphics* gfx = nullptr; + VkDevice device = VK_NULL_HANDLE; + VmaAllocator allocator = VK_NULL_HANDLE; VkImage textureImage = VK_NULL_HANDLE; - VmaAllocation textureImageAllocation; - VkImageView textureImageView; - VkSampler textureSampler; - const Slices* data; + VmaAllocation textureImageAllocation = VK_NULL_HANDLE; + VkImageView textureImageView = VK_NULL_HANDLE; + VkSampler textureSampler = VK_NULL_HANDLE; + Slices slices; int layerCount = 0; }; } // vulkan