vulkan: implement graphics readback

This commit is contained in:
niki
2022-08-30 17:11:59 +02:00
parent a69d4a38dd
commit ec5a3a50f9
7 changed files with 287 additions and 57 deletions
+2
View File
@@ -599,6 +599,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
set(LOVE_SRC_MODULE_GRAPHICS_VULKAN set(LOVE_SRC_MODULE_GRAPHICS_VULKAN
src/modules/graphics/vulkan/Graphics.h src/modules/graphics/vulkan/Graphics.h
src/modules/graphics/vulkan/Graphics.cpp src/modules/graphics/vulkan/Graphics.cpp
src/modules/graphics/vulkan/GraphicsReadback.h
src/modules/graphics/vulkan/GraphicsReadback.cpp
src/modules/graphics/vulkan/Shader.h src/modules/graphics/vulkan/Shader.h
src/modules/graphics/vulkan/Shader.cpp src/modules/graphics/vulkan/Shader.cpp
src/modules/graphics/vulkan/ShaderStage.h src/modules/graphics/vulkan/ShaderStage.h
+7 -2
View File
@@ -38,7 +38,12 @@ bool Buffer::loadVolatile() {
VkBufferCreateInfo bufferInfo{}; VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = getSize(); bufferInfo.size = getSize();
bufferInfo.usage = getVulkanUsageFlags(usageFlags); if (dataUsage == BUFFERDATAUSAGE_READBACK) {
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
}
else {
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
}
VmaAllocationCreateInfo allocCreateInfo = {}; VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
@@ -88,7 +93,7 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) {
void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) { void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) {
Graphics* vgfx = (Graphics*)gfx; Graphics* vgfx = (Graphics*)gfx;
auto commandBuffer = vgfx->getDataTransferCommandBuffer(); auto commandBuffer = vgfx->getReadbackCommandBuffer();
VkBufferCopy bufferCopy{}; VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = sourceoffset; bufferCopy.srcOffset = sourceoffset;
+155 -42
View File
@@ -1,5 +1,6 @@
#include "Graphics.h" #include "Graphics.h"
#include "Buffer.h" #include "Buffer.h"
#include "GraphicsReadback.h"
#include "SDL_vulkan.h" #include "SDL_vulkan.h"
#include "window/Window.h" #include "window/Window.h"
#include "common/Exception.h" #include "common/Exception.h"
@@ -147,50 +148,81 @@ void Graphics::clear(const std::vector<OptionalColorD>& colors, OptionalInt sten
vkCmdClearAttachments(commandBuffers[currentFrame], static_cast<uint32_t>(attachments.size()), attachments.data(), 1, &rect); vkCmdClearAttachments(commandBuffers[currentFrame], static_cast<uint32_t>(attachments.size()), attachments.data(), 1, &rect);
} }
void Graphics::present(void* screenshotCallbackdata) { void Graphics::submitGpuCommands(bool present) {
if (!isActive()) {
return;
}
flushBatchedDraws(); flushBatchedDraws();
endRecordingGraphicsCommands(); endRecordingGraphicsCommands(present);
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX);
} }
imagesInFlight[imageIndex] = inFlightFences[currentFrame]; imagesInFlight[imageIndex] = inFlightFences[currentFrame];
// all data transfers should happen before any draw calls. std::vector<VkCommandBuffer> submitCommandbuffers = {
std::vector<VkCommandBuffer> submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(currentFrame) }; dataTransferCommandBuffers.at(currentFrame),
commandBuffers.at(currentFrame),
readbackCommandBuffers.at(currentFrame)};
VkSubmitInfo submitInfo{}; VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) }; VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT }; VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores; if (imageRequested) {
submitInfo.pWaitDstStageMask = waitStages; submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
imageRequested = false;
}
submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size()); submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size());
submitInfo.pCommandBuffers = submitCommandbuffers.data(); submitInfo.pCommandBuffers = submitCommandbuffers.data();
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) }; VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]); VkFence fence = VK_NULL_HANDLE;
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) { if (present) {
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]);
fence = inFlightFences[currentFrame];
}
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS) {
throw love::Exception("failed to submit draw command buffer"); throw love::Exception("failed to submit draw command buffer");
} }
if (!present) {
vkQueueWaitIdle(graphicsQueue);
for (auto& callbacks : readbackCallbacks) {
for (const auto& callback : callbacks) {
callback();
}
callbacks.clear();
}
startRecordingGraphicsCommands(false);
}
}
void Graphics::present(void* screenshotCallbackdata) {
if (!isActive()) {
return;
}
submitGpuCommands(true);
VkPresentInfoKHR presentInfo{}; VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
VkSemaphore waitSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
presentInfo.waitSemaphoreCount = 1; presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores; presentInfo.pWaitSemaphores = waitSemaphores;
VkSwapchainKHR swapChains[] = { swapChain }; VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
@@ -210,8 +242,9 @@ void Graphics::present(void* screenshotCallbackdata) {
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
beginFrame();
updatedBatchedDrawBuffers(); updatedBatchedDrawBuffers();
startRecordingGraphicsCommands();
} }
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) {
@@ -229,6 +262,9 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
cleanUpFunctions.clear(); cleanUpFunctions.clear();
cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT); cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT);
readbackCallbacks.clear();
readbackCallbacks.resize(MAX_FRAMES_IN_FLIGHT);
createVulkanInstance(); createVulkanInstance();
createSurface(); createSurface();
pickPhysicalDevice(); pickPhysicalDevice();
@@ -244,13 +280,6 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
createDefaultFramebuffers(); createDefaultFramebuffers();
createCommandPool(); createCommandPool();
createCommandBuffers(); createCommandBuffers();
startRecordingGraphicsCommands();
createQuadIndexBuffer();
createDefaultTexture();
createDefaultShaders();
currentFrame = 0;
created = true;
float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
@@ -271,18 +300,27 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor)); batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor));
batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor)); batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor));
} }
updatedBatchedDrawBuffers(); updatedBatchedDrawBuffers();
beginFrame();
createDefaultTexture();
createDefaultShaders();
Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT]; Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
createQuadIndexBuffer();
restoreState(states.back()); restoreState(states.back());
setViewportSize(width, height, pixelwidth, pixelheight); setViewportSize(width, height, pixelwidth, pixelheight);
currentViewportWidth = 0.0f; currentViewportWidth = 1.0f;
currentViewportHeight = 0.0f; currentViewportHeight = 1.0f;
Vulkan::resetShaderSwitches(); Vulkan::resetShaderSwitches();
currentFrame = 0;
created = true;
return true; return true;
} }
@@ -301,10 +339,10 @@ void Graphics::initCapabilities() {
capabilities.features[FEATURE_INSTANCING] = true; capabilities.features[FEATURE_INSTANCING] = true;
capabilities.features[FEATURE_TEXEL_BUFFER] = false; capabilities.features[FEATURE_TEXEL_BUFFER] = false;
capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true; capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true;
capabilities.features[FEATURE_COPY_BUFFER] = false; capabilities.features[FEATURE_COPY_BUFFER] = true;
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = false; capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = false; capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false; capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = true;
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
VkPhysicalDeviceProperties properties; VkPhysicalDeviceProperties properties;
@@ -584,6 +622,22 @@ Renderer Graphics::getRenderer() const {
return RENDERER_VULKAN; return RENDERER_VULKAN;
} }
graphics::GraphicsReadback* Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) {
return new GraphicsReadback(this, method, buffer, offset, size, dest, destoffset);
}
graphics::GraphicsReadback* Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) {
return new GraphicsReadback(this, method, texture, slice, mipmap, rect, dest, destx, desty);
}
graphics::ShaderStage* Graphics::newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) {
return new ShaderStage(this, stage, source, gles, cachekey);
}
graphics::Shader* Graphics::newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) {
return new Shader(stages);
}
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
return new StreamBuffer(this, type, size); return new StreamBuffer(this, type, size);
} }
@@ -645,7 +699,7 @@ void Graphics::initDynamicState() {
} }
} }
void Graphics::startRecordingGraphicsCommands() { void Graphics::beginFrame() {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
while (true) { while (true) {
@@ -661,11 +715,24 @@ void Graphics::startRecordingGraphicsCommands() {
break; break;
} }
imageRequested = true;
for (auto& readbackCallback : readbackCallbacks.at(currentFrame)) {
readbackCallback();
}
readbackCallbacks.at(currentFrame).clear();
for (auto& cleanUpFn : cleanUpFunctions.at(currentFrame)) { for (auto& cleanUpFn : cleanUpFunctions.at(currentFrame)) {
cleanUpFn(); cleanUpFn();
} }
cleanUpFunctions.at(currentFrame).clear(); cleanUpFunctions.at(currentFrame).clear();
startRecordingGraphicsCommands(true);
Vulkan::resetShaderSwitches();
}
void Graphics::startRecordingGraphicsCommands(bool newFrame) {
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; beginInfo.flags = 0;
@@ -677,20 +744,25 @@ void Graphics::startRecordingGraphicsCommands() {
if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) { if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
throw love::Exception("failed to begin recording data transfer command buffer"); throw love::Exception("failed to begin recording data transfer command buffer");
} }
if (vkBeginCommandBuffer(readbackCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
throw love::Exception("failed to begin recording readback command buffer");
}
initDynamicState(); initDynamicState();
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); if (newFrame) {
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
startDefaultRenderPass(); startDefaultRenderPass();
Vulkan::resetShaderSwitches();
} }
void Graphics::endRecordingGraphicsCommands() { void Graphics::endRecordingGraphicsCommands(bool present) {
endRenderPass(); endRenderPass();
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); if (present) {
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
if (vkEndCommandBuffer(commandBuffers.at(currentFrame)) != VK_SUCCESS) { if (vkEndCommandBuffer(commandBuffers.at(currentFrame)) != VK_SUCCESS) {
throw love::Exception("failed to record command buffer"); throw love::Exception("failed to record command buffer");
@@ -698,6 +770,9 @@ void Graphics::endRecordingGraphicsCommands() {
if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) { if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
throw love::Exception("failed to record data transfer command buffer"); throw love::Exception("failed to record data transfer command buffer");
} }
if (vkEndCommandBuffer(readbackCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
throw love::Exception("failed to record read back command buffer");
}
} }
void Graphics::updatedBatchedDrawBuffers() { void Graphics::updatedBatchedDrawBuffers() {
@@ -725,10 +800,18 @@ VkCommandBuffer Graphics::getDataTransferCommandBuffer() {
return dataTransferCommandBuffers.at(currentFrame); return dataTransferCommandBuffers.at(currentFrame);
} }
VkCommandBuffer Graphics::getReadbackCommandBuffer() {
return readbackCommandBuffers.at(currentFrame);
}
void Graphics::queueCleanUp(std::function<void()> cleanUp) { void Graphics::queueCleanUp(std::function<void()> cleanUp) {
cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp));
} }
void Graphics::addReadbackCallback(const std::function<void()>& callback) {
readbackCallbacks.at(currentFrame).push_back(std::move(callback));
}
graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData() { graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData() {
love::graphics::Shader::BuiltinUniformData data; love::graphics::Shader::BuiltinUniformData data;
@@ -1036,7 +1119,6 @@ void Graphics::createLogicalDevice() {
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size()); createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size());
createInfo.ppEnabledExtensionNames = enabledExtensions.data(); createInfo.ppEnabledExtensionNames = enabledExtensions.data();
// can this be removed?
if (enableValidationLayers) { if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data(); createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -1520,14 +1602,24 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration configuration) {
dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
VkSubpassDependency readbackDependency{};
readbackDependency.srcSubpass = 0;
readbackDependency.dstSubpass = VK_SUBPASS_EXTERNAL;
readbackDependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
readbackDependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
readbackDependency.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
readbackDependency.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
std::array<VkSubpassDependency, 2> dependencies = { dependency, readbackDependency };
VkRenderPassCreateInfo createInfo{}; VkRenderPassCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size()); createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
createInfo.pAttachments = attachments.data(); createInfo.pAttachments = attachments.data();
createInfo.subpassCount = 1; createInfo.subpassCount = 1;
createInfo.pSubpasses = &subPass; createInfo.pSubpasses = &subPass;
createInfo.dependencyCount = 1; createInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
createInfo.pDependencies = &dependency; createInfo.pDependencies = dependencies.data();
VkRenderPass renderPass; VkRenderPass renderPass;
if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) { if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) {
@@ -1681,6 +1773,16 @@ void Graphics::startDefaultRenderPass() {
currentViewportHeight = (float)swapChainExtent.height; currentViewportHeight = (float)swapChainExtent.height;
currentMsaaSamples = msaaSamples; currentMsaaSamples = msaaSamples;
currentNumColorAttachments = 1; currentNumColorAttachments = 1;
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = currentViewportWidth;
viewport.height = currentViewportHeight;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport);
} }
void Graphics::startRenderPass(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) { void Graphics::startRenderPass(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
@@ -2151,12 +2253,13 @@ void Graphics::createCommandPool() {
void Graphics::createCommandBuffers() { void Graphics::createCommandBuffers() {
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
readbackCommandBuffers.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;
allocInfo.commandPool = commandPool; allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT; allocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
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");
@@ -2166,11 +2269,21 @@ void Graphics::createCommandBuffers() {
dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
dataTransferAllocInfo.commandPool = commandPool; dataTransferAllocInfo.commandPool = commandPool;
dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT; dataTransferAllocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) { if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) {
throw love::Exception("failed to allocate data transfer command buffers"); throw love::Exception("failed to allocate data transfer command buffers");
} }
VkCommandBufferAllocateInfo readbackAllocInfo{};
readbackAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
readbackAllocInfo.commandPool = commandPool;
readbackAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
readbackAllocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
if (vkAllocateCommandBuffers(device, &readbackAllocInfo, readbackCommandBuffers.data()) != VK_SUCCESS) {
throw love::Exception("failed to allocate readback command buffers");
}
} }
void Graphics::createSyncObjects() { void Graphics::createSyncObjects() {
+16 -11
View File
@@ -219,11 +219,16 @@ public:
void draw(const DrawIndexedCommand& cmd) override; void draw(const DrawIndexedCommand& cmd) override;
void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) 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; }; graphics::GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override;
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) override { return nullptr; } graphics::GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) override;
VkCommandBuffer getDataTransferCommandBuffer(); VkCommandBuffer getDataTransferCommandBuffer();
VkCommandBuffer getReadbackCommandBuffer();
void queueCleanUp(std::function<void()> cleanUp); void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(const std::function<void()> &callback);
void submitGpuCommands(bool present);
uint32_t getNumImagesInFlight() const; uint32_t getNumImagesInFlight() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const; const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
@@ -231,12 +236,8 @@ public:
VkSampler getCachedSampler(const SamplerState&); VkSampler getCachedSampler(const SamplerState&);
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;
return new ShaderStage(this, stage, source, gles, cachekey); graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
}
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override {
return new Shader(stages);
}
graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override { return false; } bool dispatch(int x, int y, int z) override { return false; }
void initCapabilities() override; void initCapabilities() override;
@@ -280,8 +281,9 @@ private:
void cleanupSwapChain(); void cleanupSwapChain();
void recreateSwapChain(); void recreateSwapChain();
void initDynamicState(); void initDynamicState();
void startRecordingGraphicsCommands(); void beginFrame();
void endRecordingGraphicsCommands(); void startRecordingGraphicsCommands(bool newFrame);
void endRecordingGraphicsCommands(bool present);
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration); void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData(); graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
void updatedBatchedDrawBuffers(); void updatedBatchedDrawBuffers();
@@ -329,13 +331,15 @@ private:
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines; std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers; std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers;
VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> dataTransferCommandBuffers; std::vector<VkCommandBuffer> dataTransferCommandBuffers;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> readbackCommandBuffers;
std::vector<VkSemaphore> imageAvailableSemaphores; std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores; std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences; std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight; std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment = 0; VkDeviceSize minUniformBufferOffsetAlignment = 0;
bool imageRequested = false;
size_t currentFrame = 0; size_t currentFrame = 0;
uint32_t imageIndex = 0; uint32_t imageIndex = 0;
bool framebufferResized = false; bool framebufferResized = false;
@@ -347,6 +351,7 @@ private:
// functions that need to be called to cleanup objects that were needed for rendering a frame. // 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. // just like batchedDrawBuffers we need a vector for each frame in flight.
std::vector<std::vector<std::function<void()>>> cleanUpFunctions; std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
// render pass variables. // render pass variables.
std::optional<std::function<void()>> postRenderPass; std::optional<std::function<void()>> postRenderPass;
@@ -0,0 +1,73 @@
#include "GraphicsReadback.h"
#include "Buffer.h"
#include "Texture.h"
#include "Graphics.h"
#include "data/ByteData.h"
namespace love {
namespace graphics {
namespace vulkan {
GraphicsReadback::GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset)
: graphics::GraphicsReadback(gfx, method, buffer, offset, size, dest, destoffset) {
vgfx = dynamic_cast<Graphics*>(gfx);
// Immediate readback of readback-type buffers doesn't need a staging buffer.
if (method != READBACK_IMMEDIATE || buffer->getDataUsage() != BUFFERDATAUSAGE_READBACK) {
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
gfx->copyBuffer(buffer, stagingBuffer, offset, 0, size);
}
if (method == READBACK_IMMEDIATE) {
vgfx->submitGpuCommands(false);
if (stagingBuffer.get()) {
status = readbackBuffer(stagingBuffer, 0, size);
gfx->releaseTemporaryBuffer(stagingBuffer);
}
else
status = readbackBuffer(buffer, offset, size);
}
else
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
}
GraphicsReadback::GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty)
: graphics::GraphicsReadback(gfx, method, texture, slice, mipmap, rect, dest, destx, desty) {
vgfx = dynamic_cast<Graphics*>(gfx);
size_t size = getPixelFormatSliceSize(textureFormat, rect.w, rect.h);
stagingBuffer = vgfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
vgfx->copyTextureToBuffer(texture, stagingBuffer, slice, mipmap, rect, 0, 0);
vgfx->addReadbackCallback([&]() {
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
vgfx->releaseTemporaryBuffer(stagingBuffer);
stagingBuffer.set(nullptr);
});
if (method == READBACK_IMMEDIATE)
vgfx->submitGpuCommands(false);
}
GraphicsReadback::~GraphicsReadback() {
}
void GraphicsReadback::wait() {
if (status == STATUS_WAITING)
vgfx->submitGpuCommands(false);
}
void GraphicsReadback::update() {
}
} // vulkan
} // graphics
} // love
@@ -0,0 +1,31 @@
#ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_READBACK_H
#define LOVE_GRAPHICS_VULKAN_GRAPHICS_READBACK_H
#include "graphics/GraphicsReadback.h"
namespace love {
namespace graphics {
namespace vulkan {
class Graphics;
class GraphicsReadback : public graphics::GraphicsReadback {
public:
GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset);
GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty);
virtual ~GraphicsReadback();
void wait() override;
void update() override;
private:
Graphics* vgfx;
StrongRef<love::graphics::Buffer> stagingBuffer;
};
} // vulkan
} // graphics
} // love
#endif
+3 -2
View File
@@ -391,7 +391,7 @@ void Texture::copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int
} }
void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) { void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) {
auto commandBuffer = vgfx->getDataTransferCommandBuffer(); auto commandBuffer = vgfx->getReadbackCommandBuffer();
VkImageSubresourceLayers layers{}; VkImageSubresourceLayers layers{};
layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -402,10 +402,11 @@ void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const
VkBufferImageCopy region{}; VkBufferImageCopy region{};
region.bufferOffset = destoffset; region.bufferOffset = destoffset;
region.bufferRowLength = destwidth; region.bufferRowLength = destwidth;
region.bufferImageHeight = 1; region.bufferImageHeight = 0;
region.imageSubresource = layers; region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w); region.imageExtent.width = static_cast<uint32_t>(rect.w);
region.imageExtent.height = static_cast<uint32_t>(rect.h); region.imageExtent.height = static_cast<uint32_t>(rect.h);
region.imageExtent.depth = 1;
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);