Permalink
Browse files

Fix Vulkan framebuffer readbacks, as long as they're in R8G8B8A8.

  • Loading branch information...
hrydgard committed Oct 28, 2017
1 parent b98d4e5 commit 34b65c0ca907fef92f315ff39114237c94918423
@@ -725,28 +725,37 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
assert(false);
}
assert(srcImage->format == VK_FORMAT_R8G8B8A8_UNORM);
VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
VkPipelineStageFlags stage = 0;
SetupTransitionToTransferSrc(*srcImage, barrier, stage, step.readback.aspectMask);
vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
VkBufferImageCopy region{};
region.imageOffset = { step.readback.srcRect.offset.x, step.readback.srcRect.offset.y, 0 };
region.imageExtent = { step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, 1 };
region.imageSubresource.aspectMask = step.readback.aspectMask;
region.imageSubresource.layerCount = 1;
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferRowLength = step.readback.srcRect.extent.width;
region.bufferImageHeight = step.readback.srcRect.extent.height;
vkCmdCopyImageToBuffer(cmd, srcImage->image, srcImage->layout, readbackBuffer_, 1, &region);
// NOTE: Can't read the buffer using the CPU here - need to sync first.
}
void VulkanQueueRunner::CopyReadbackBuffer(const VKRStep &step) {
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels) {
// Read back to the requested address in ram from buffer.
void *mappedData;
VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, step.readback.srcRect.extent.width * step.readback.srcRect.extent.height * 4, 0, &mappedData);
const int pixelSize = 4; // TODO: Fix.
VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * pixelSize, 0, &mappedData);
assert(res == VK_SUCCESS);
const int pixelSize = 4; // TODO: Fix.
for (int y = 0; y < step.readback.srcRect.extent.height; y++) {
const uint8_t *src = (const uint8_t *)mappedData + step.readback.srcRect.extent.width * y;
uint8_t *dst = (uint8_t *)step.readback.destPtr + step.readback.pixelStride * pixelSize * y;
for (int y = 0; y < height; y++) {
const uint8_t *src = (const uint8_t *)mappedData + width * y;
uint8_t *dst = pixels + pixelStride * pixelSize * y;
memcpy(dst, src, width * pixelSize);
}
vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_);
}
@@ -129,8 +129,6 @@ struct VKRStep {
struct {
int aspectMask;
VKRFramebuffer *src;
uint8_t *destPtr;
int pixelStride;
VkRect2D srcRect;
} readback;
};
@@ -158,7 +156,7 @@ class VulkanQueueRunner {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(const VKRStep &step);
void CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels);
private:
void InitBackbufferRenderPass();
@@ -5,7 +5,7 @@
#include "thin3d/VulkanRenderManager.h"
#include "thread/threadutil.h"
#ifdef _DEBUG
#if 0 // def _DEBUG
#define VLOG ILOG
#else
#define VLOG(...)
@@ -92,6 +92,8 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage,
0, dstAccessMask);
img.layout = initialLayout;
img.format = format;
}
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(vulkan) {
@@ -328,8 +330,6 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride) {
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
step->readback.aspectMask = aspectBits;
step->readback.destPtr = (uint8_t *)pixels;
step->readback.pixelStride = pixelStride;
step->readback.src = src;
step->readback.srcRect.offset = { x, y };
step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h };
@@ -339,8 +339,8 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a
FlushSync();
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(*step);
// Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(w, h, pixelStride, pixels);
}
@@ -569,7 +569,7 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) {
}
}
void VulkanRenderManager::Submit(int frame) {
void VulkanRenderManager::Submit(int frame, bool triggerFence) {
FrameData &frameData = frameData_[frame];
if (frameData.hasInitCommands) {
VkResult res = vkEndCommandBuffer(frameData.initCmd);
@@ -590,16 +590,20 @@ void VulkanRenderManager::Submit(int frame) {
cmdBufs.push_back(frameData.mainCmd);
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &acquireSemaphore_;
if (triggerFence) {
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &acquireSemaphore_;
}
VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT };
submit_info.pWaitDstStageMask = waitStage;
submit_info.commandBufferCount = (uint32_t)cmdBufs.size();
submit_info.pCommandBuffers = cmdBufs.data();
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &renderingCompleteSemaphore_;
res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence);
if (triggerFence) {
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &renderingCompleteSemaphore_;
}
res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE);
assert(res == VK_SUCCESS);
if (useThread) {
@@ -617,7 +621,7 @@ void VulkanRenderManager::EndSubmitFrame(int frame) {
TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image);
Submit(frame);
Submit(frame, true);
VkSwapchainKHR swapchain = vulkan_->GetSwapchain();
VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
@@ -660,22 +664,25 @@ void VulkanRenderManager::FlushSync() {
BeginSubmitFrame(frame);
FrameData &frameData = frameData_[frame];
frameData.steps = std::move(steps_);
auto &stepsOnThread = frameData_[frame].steps;
VkCommandBuffer cmd = frameData.mainCmd;
queueRunner_.RunSteps(cmd, stepsOnThread);
stepsOnThread.clear();
Submit(frame);
Submit(frame, false);
vkDeviceWaitIdle(vulkan_->GetDevice());
// At this point we can resume filling the command buffers for the current frame since
// we know the device is idle - and thus all previously enqueued command buffers have been processed.
// No need to switch to the next frame number.
VkCommandBufferBeginInfo begin = {
VkCommandBufferBeginInfo begin{
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
nullptr,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
};
vkBeginCommandBuffer(frameData_->mainCmd, &begin);
VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin);
assert(res == VK_SUCCESS);
}
@@ -21,6 +21,7 @@ struct VKRImage {
VkImageView imageView;
VkDeviceMemory memory;
VkImageLayout layout;
VkFormat format;
};
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color);
@@ -186,7 +187,7 @@ class VulkanRenderManager {
void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
void Submit(int frame);
void Submit(int frame, bool triggerFence);
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync();
@@ -1288,8 +1288,13 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr
bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) {
VKFramebuffer *src = (VKFramebuffer *)srcfb;
VkFormat vkFormat = DataFormatToVulkan(format);
int aspectMask = 0;
if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
if (channelBits & FBChannel::FB_COLOR_BIT) {
assert(vkFormat == src->GetFB()->color.format);
aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
}
if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;

0 comments on commit 34b65c0

Please sign in to comment.