mirror of
https://github.com/love2d/love.git
synced 2026-08-19 12:14:20 +02:00
vulkan: fix video rendering
This commit is contained in:
@@ -142,6 +142,9 @@ namespace love {
|
|||||||
}
|
}
|
||||||
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
|
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
|
||||||
|
|
||||||
|
// all data transfers should happen before any draw calls.
|
||||||
|
std::vector<VkCommandBuffer> submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(imageIndex) };
|
||||||
|
|
||||||
VkSubmitInfo submitInfo{};
|
VkSubmitInfo submitInfo{};
|
||||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
|
||||||
@@ -151,8 +154,8 @@ namespace love {
|
|||||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||||
submitInfo.pWaitDstStageMask = waitStages;
|
submitInfo.pWaitDstStageMask = waitStages;
|
||||||
|
|
||||||
submitInfo.commandBufferCount = 1;
|
submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size());
|
||||||
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
|
submitInfo.pCommandBuffers = submitCommandbuffers.data();
|
||||||
|
|
||||||
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
|
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
|
||||||
submitInfo.signalSemaphoreCount = 1;
|
submitInfo.signalSemaphoreCount = 1;
|
||||||
@@ -213,10 +216,10 @@ namespace love {
|
|||||||
initCapabilities();
|
initCapabilities();
|
||||||
createSwapChain();
|
createSwapChain();
|
||||||
createImageViews();
|
createImageViews();
|
||||||
createDefaultShaders();
|
|
||||||
createCommandPool();
|
createCommandPool();
|
||||||
createCommandBuffers();
|
createCommandBuffers();
|
||||||
createDefaultTexture();
|
createDefaultTexture();
|
||||||
|
createDefaultShaders();
|
||||||
createQuadIndexBuffer();
|
createQuadIndexBuffer();
|
||||||
createSyncObjects();
|
createSyncObjects();
|
||||||
startRecordingGraphicsCommands();
|
startRecordingGraphicsCommands();
|
||||||
@@ -510,6 +513,9 @@ namespace love {
|
|||||||
if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
|
if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
|
||||||
throw love::Exception("failed to begin recording command buffer");
|
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);
|
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
@@ -519,8 +525,6 @@ namespace love {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::endRecordingGraphicsCommands() {
|
void Graphics::endRecordingGraphicsCommands() {
|
||||||
const auto& commandBuffer = commandBuffers.at(imageIndex);
|
|
||||||
|
|
||||||
endRenderPass();
|
endRenderPass();
|
||||||
|
|
||||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||||
@@ -528,6 +532,9 @@ namespace love {
|
|||||||
if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
|
if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
|
||||||
throw love::Exception("failed to record command buffer");
|
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() {
|
void Graphics::updatedBatchedDrawBuffers() {
|
||||||
@@ -547,12 +554,16 @@ namespace love {
|
|||||||
return minUniformBufferOffsetAlignment;
|
return minUniformBufferOffsetAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graphics::Texture* Graphics::getDefaultTexture() const {
|
||||||
|
return dynamic_cast<graphics::Texture*>(standardTexture.get());
|
||||||
|
}
|
||||||
|
|
||||||
const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const {
|
const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const {
|
||||||
return vkCmdPushDescriptorSet;
|
return vkCmdPushDescriptorSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::executeCommand(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp) {
|
void Graphics::queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp) {
|
||||||
command(commandBuffers.at(imageIndex));
|
command(dataTransferCommandBuffers.at(currentFrame));
|
||||||
cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp));
|
cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1207,7 +1218,6 @@ namespace love {
|
|||||||
else {
|
else {
|
||||||
configuration.shader->setMainTex(texture);
|
configuration.shader->setMainTex(texture);
|
||||||
}
|
}
|
||||||
configuration.shader->setVideoTextures(standardTexture.get(), standardTexture.get(), standardTexture.get());
|
|
||||||
|
|
||||||
ensureGraphicsPipelineConfiguration(configuration);
|
ensureGraphicsPipelineConfiguration(configuration);
|
||||||
|
|
||||||
@@ -1416,6 +1426,7 @@ namespace love {
|
|||||||
|
|
||||||
void Graphics::createCommandBuffers() {
|
void Graphics::createCommandBuffers() {
|
||||||
commandBuffers.resize(swapChainImages.size());
|
commandBuffers.resize(swapChainImages.size());
|
||||||
|
dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
VkCommandBufferAllocateInfo allocInfo{};
|
VkCommandBufferAllocateInfo allocInfo{};
|
||||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
@@ -1426,6 +1437,16 @@ namespace love {
|
|||||||
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
|
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
|
||||||
throw love::Exception("failed to allocate command buffers");
|
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() {
|
void Graphics::createSyncObjects() {
|
||||||
@@ -1565,6 +1586,7 @@ namespace love {
|
|||||||
void Graphics::cleanupSwapChain() {
|
void Graphics::cleanupSwapChain() {
|
||||||
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
|
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
|
||||||
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
|
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
|
||||||
|
vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data());
|
||||||
for (auto const& p : graphicsPipelines) {
|
for (auto const& p : graphicsPipelines) {
|
||||||
vkDestroyPipeline(device, p.second, nullptr);
|
vkDestroyPipeline(device, p.second, nullptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,8 +114,7 @@ namespace love {
|
|||||||
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::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; }
|
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; }
|
||||||
|
|
||||||
// fixme: better naming for these two functions?
|
void queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
|
||||||
void executeCommand(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
|
|
||||||
void queueCleanUp(std::function<void()> cleanUp);
|
void queueCleanUp(std::function<void()> cleanUp);
|
||||||
|
|
||||||
VkCommandBuffer beginSingleTimeCommands();
|
VkCommandBuffer beginSingleTimeCommands();
|
||||||
@@ -124,6 +123,7 @@ namespace love {
|
|||||||
uint32_t getNumImagesInFlight() const;
|
uint32_t getNumImagesInFlight() const;
|
||||||
const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
|
const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
|
||||||
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
|
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
|
||||||
|
graphics::Texture* getDefaultTexture() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
|
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
|
||||||
@@ -190,6 +190,7 @@ namespace love {
|
|||||||
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
|
std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines; // FIXME improve performance by using a hash map
|
||||||
VkCommandPool commandPool = VK_NULL_HANDLE;
|
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||||
std::vector<VkCommandBuffer> commandBuffers;
|
std::vector<VkCommandBuffer> commandBuffers;
|
||||||
|
std::vector<VkCommandBuffer> dataTransferCommandBuffers;
|
||||||
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||||
|
|||||||
@@ -311,6 +311,11 @@ namespace love {
|
|||||||
auto vgfx = (Graphics*)gfx;
|
auto vgfx = (Graphics*)gfx;
|
||||||
device = vgfx->getDevice();
|
device = vgfx->getDevice();
|
||||||
|
|
||||||
|
mainTex = vgfx->getDefaultTexture();
|
||||||
|
ytexture = vgfx->getDefaultTexture();
|
||||||
|
crtexture = vgfx->getDefaultTexture();
|
||||||
|
cbtexture = vgfx->getDefaultTexture();
|
||||||
|
|
||||||
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
|
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
|
||||||
if (!stages[i])
|
if (!stages[i])
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace love {
|
|||||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
|
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
|
||||||
throw love::Exception("failed to create image");
|
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);
|
transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -253,22 +254,25 @@ namespace love {
|
|||||||
static_cast<uint32_t>(r.h), 1
|
static_cast<uint32_t>(r.h), 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// fixme: use VK_IMAGE_LAYOUT_DST_OPTIMAL
|
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||||
|
|
||||||
vkCmdCopyBufferToImage(
|
vkCmdCopyBufferToImage(
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
buffer,
|
buffer,
|
||||||
image,
|
image,
|
||||||
VK_IMAGE_LAYOUT_GENERAL,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
1,
|
1,
|
||||||
®ion
|
®ion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() {
|
auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() {
|
||||||
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
|
vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
|
||||||
};
|
};
|
||||||
|
|
||||||
vgfx->executeCommand(command, cleanUp);
|
vgfx->queueDatatransfer(command, cleanUp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -491,6 +491,13 @@ namespace love {
|
|||||||
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||||
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_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) {
|
else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
|
||||||
barrier.srcAccessMask = 0;
|
barrier.srcAccessMask = 0;
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
|||||||
Reference in New Issue
Block a user