Permalink
Browse files

Color-convert directly during the readback, saves a copy. Like we alr…

…eady do in D3D11.
  • Loading branch information...
hrydgard committed Oct 29, 2017
1 parent 9c254ea commit 93c785b76d41d838efabfaa5858e0128d28080cd
@@ -387,6 +387,7 @@ void FramebufferManagerVulkan::RebindFramebuffer() {
// Should this even happen?
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
}
bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) {
@@ -1,3 +1,5 @@
#include "Common/ColorConv.h"
#include "VulkanQueueRunner.h"
#include "VulkanRenderManager.h"
@@ -745,17 +747,66 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
// NOTE: Can't read the buffer using the CPU here - need to sync first.
}
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels) {
// Would love to share this with D3D11 but don't have a shared format enum we can use... as we don't want
// to depend on thin3d here.
// TODO: SSE/NEON
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
// Strides are in pixels.
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, VkFormat format) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
const u32 *src32 = (const u32 *)src;
if (format == VK_FORMAT_R8G8B8A8_UNORM) {
u32 *dst32 = (u32 *)dst;
if (src == dst) {
return;
} else {
for (u32 y = 0; y < height; ++y) {
memcpy(dst32, src32, width * 4);
src32 += srcStride;
dst32 += dstStride;
}
}
} else {
// But here it shouldn't matter if they do intersect
u16 *dst16 = (u16 *)dst;
switch (format) {
case VK_FORMAT_B5G6R5_UNORM_PACK16: // BGR 565
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGB565(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: // ABGR 1555
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: // ABGR 4444
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
default:
Crash();
// Not possible.
break;
}
}
}
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels) {
// Read back to the requested address in ram from buffer.
void *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 srcPixelSize = 4; // TODO: Fix.
for (int y = 0; y < height; y++) {
const uint8_t *src = (const uint8_t *)mappedData + width * pixelSize * y;
uint8_t *dst = pixels + pixelStride * pixelSize * y;
memcpy(dst, src, width * pixelSize);
}
VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * srcPixelSize, 0, &mappedData);
assert(res == VK_SUCCESS);
ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat);
vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_);
}
@@ -156,7 +156,7 @@ class VulkanQueueRunner {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels);
void CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels);
private:
void InitBackbufferRenderPass();
@@ -327,7 +327,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight();
}
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride) {
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride) {
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
step->readback.aspectMask = aspectBits;
step->readback.src = src;
@@ -340,10 +340,9 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a
FlushSync();
// Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(w, h, pixelStride, pixels);
queueRunner_.CopyReadbackBuffer(w, h, destFormat, pixelStride, pixels);
}
void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
VkResult U_ASSERT_ONLY res;
// We share the same depth buffer but have multiple color buffers, see the loop below.
@@ -85,7 +85,7 @@ class VulkanRenderManager {
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment);
void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride);
void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask);
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter);
@@ -1292,13 +1292,12 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits,
int aspectMask = 0;
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;
renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, (uint8_t *)pixels, pixelStride);
renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, vkFormat, (uint8_t *)pixels, pixelStride);
return true;
}

0 comments on commit 93c785b

Please sign in to comment.