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
+7 -2
View File
@@ -38,7 +38,12 @@ bool Buffer::loadVolatile() {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
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 = {};
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) {
Graphics* vgfx = (Graphics*)gfx;
auto commandBuffer = vgfx->getDataTransferCommandBuffer();
auto commandBuffer = vgfx->getReadbackCommandBuffer();
VkBufferCopy bufferCopy{};
bufferCopy.srcOffset = sourceoffset;
+155 -42
View File
@@ -1,5 +1,6 @@
#include "Graphics.h"
#include "Buffer.h"
#include "GraphicsReadback.h"
#include "SDL_vulkan.h"
#include "window/Window.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);
}
void Graphics::present(void* screenshotCallbackdata) {
if (!isActive()) {
return;
}
void Graphics::submitGpuCommands(bool present) {
flushBatchedDraws();
endRecordingGraphicsCommands();
endRecordingGraphicsCommands(present);
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<VkCommandBuffer> submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(currentFrame) };
std::vector<VkCommandBuffer> submitCommandbuffers = {
dataTransferCommandBuffers.at(currentFrame),
commandBuffers.at(currentFrame),
readbackCommandBuffers.at(currentFrame)};
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
if (imageRequested) {
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
imageRequested = false;
}
submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size());
submitInfo.pCommandBuffers = submitCommandbuffers.data();
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");
}
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{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
VkSemaphore waitSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
presentInfo.pWaitSemaphores = waitSemaphores;
VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1;
@@ -210,8 +242,9 @@ void Graphics::present(void* screenshotCallbackdata) {
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
beginFrame();
updatedBatchedDrawBuffers();
startRecordingGraphicsCommands();
}
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.resize(MAX_FRAMES_IN_FLIGHT);
readbackCallbacks.clear();
readbackCallbacks.resize(MAX_FRAMES_IN_FLIGHT);
createVulkanInstance();
createSurface();
pickPhysicalDevice();
@@ -244,13 +280,6 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
createDefaultFramebuffers();
createCommandPool();
createCommandBuffers();
startRecordingGraphicsCommands();
createQuadIndexBuffer();
createDefaultTexture();
createDefaultShaders();
currentFrame = 0;
created = true;
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->markUsed(sizeof(whiteColor));
}
updatedBatchedDrawBuffers();
beginFrame();
createDefaultTexture();
createDefaultShaders();
Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
createQuadIndexBuffer();
restoreState(states.back());
setViewportSize(width, height, pixelwidth, pixelheight);
currentViewportWidth = 0.0f;
currentViewportHeight = 0.0f;
currentViewportWidth = 1.0f;
currentViewportHeight = 1.0f;
Vulkan::resetShaderSwitches();
currentFrame = 0;
created = true;
return true;
}
@@ -301,10 +339,10 @@ void Graphics::initCapabilities() {
capabilities.features[FEATURE_INSTANCING] = true;
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;
capabilities.features[FEATURE_COPY_BUFFER] = true;
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
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!");
VkPhysicalDeviceProperties properties;
@@ -584,6 +622,22 @@ Renderer Graphics::getRenderer() const {
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) {
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);
while (true) {
@@ -661,11 +715,24 @@ void Graphics::startRecordingGraphicsCommands() {
break;
}
imageRequested = true;
for (auto& readbackCallback : readbackCallbacks.at(currentFrame)) {
readbackCallback();
}
readbackCallbacks.at(currentFrame).clear();
for (auto& cleanUpFn : cleanUpFunctions.at(currentFrame)) {
cleanUpFn();
}
cleanUpFunctions.at(currentFrame).clear();
startRecordingGraphicsCommands(true);
Vulkan::resetShaderSwitches();
}
void Graphics::startRecordingGraphicsCommands(bool newFrame) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
@@ -677,20 +744,25 @@ void Graphics::startRecordingGraphicsCommands() {
if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
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();
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();
Vulkan::resetShaderSwitches();
}
void Graphics::endRecordingGraphicsCommands() {
void Graphics::endRecordingGraphicsCommands(bool present) {
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) {
throw love::Exception("failed to record command buffer");
@@ -698,6 +770,9 @@ void Graphics::endRecordingGraphicsCommands() {
if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
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() {
@@ -725,10 +800,18 @@ VkCommandBuffer Graphics::getDataTransferCommandBuffer() {
return dataTransferCommandBuffers.at(currentFrame);
}
VkCommandBuffer Graphics::getReadbackCommandBuffer() {
return readbackCommandBuffers.at(currentFrame);
}
void Graphics::queueCleanUp(std::function<void()> 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() {
love::graphics::Shader::BuiltinUniformData data;
@@ -1036,7 +1119,6 @@ void Graphics::createLogicalDevice() {
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size());
createInfo.ppEnabledExtensionNames = enabledExtensions.data();
// can this be removed?
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -1520,14 +1602,24 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration configuration) {
dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_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{};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
createInfo.pAttachments = attachments.data();
createInfo.subpassCount = 1;
createInfo.pSubpasses = &subPass;
createInfo.dependencyCount = 1;
createInfo.pDependencies = &dependency;
createInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
createInfo.pDependencies = dependencies.data();
VkRenderPass renderPass;
if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) {
@@ -1681,6 +1773,16 @@ void Graphics::startDefaultRenderPass() {
currentViewportHeight = (float)swapChainExtent.height;
currentMsaaSamples = msaaSamples;
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) {
@@ -2151,12 +2253,13 @@ void Graphics::createCommandPool() {
void Graphics::createCommandBuffers() {
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
readbackCommandBuffers.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)MAX_FRAMES_IN_FLIGHT;
allocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
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.commandPool = commandPool;
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) {
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() {
+16 -11
View File
@@ -219,11 +219,16 @@ public:
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) 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;
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 getReadbackCommandBuffer();
void queueCleanUp(std::function<void()> cleanUp);
void addReadbackCallback(const std::function<void()> &callback);
void submitGpuCommands(bool present);
uint32_t getNumImagesInFlight() const;
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
@@ -231,12 +236,8 @@ public:
VkSampler getCachedSampler(const SamplerState&);
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<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override {
return new Shader(stages);
}
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override;
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
bool dispatch(int x, int y, int z) override { return false; }
void initCapabilities() override;
@@ -280,8 +281,9 @@ private:
void cleanupSwapChain();
void recreateSwapChain();
void initDynamicState();
void startRecordingGraphicsCommands();
void endRecordingGraphicsCommands();
void beginFrame();
void startRecordingGraphicsCommands(bool newFrame);
void endRecordingGraphicsCommands(bool present);
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
void updatedBatchedDrawBuffers();
@@ -329,13 +331,15 @@ private:
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers;
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> dataTransferCommandBuffers;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkCommandBuffer> readbackCommandBuffers;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
VkDeviceSize minUniformBufferOffsetAlignment = 0;
bool imageRequested = false;
size_t currentFrame = 0;
uint32_t imageIndex = 0;
bool framebufferResized = false;
@@ -347,6 +351,7 @@ private:
// 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<std::vector<std::function<void()>>> cleanUpFunctions;
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
// render pass variables.
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) {
auto commandBuffer = vgfx->getDataTransferCommandBuffer();
auto commandBuffer = vgfx->getReadbackCommandBuffer();
VkImageSubresourceLayers layers{};
layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -402,10 +402,11 @@ void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const
VkBufferImageCopy region{};
region.bufferOffset = destoffset;
region.bufferRowLength = destwidth;
region.bufferImageHeight = 1;
region.bufferImageHeight = 0;
region.imageSubresource = layers;
region.imageExtent.width = static_cast<uint32_t>(rect.w);
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);