Permalink
Browse files

Merge pull request #10037 from hrydgard/vulkan-sync-readback

Vulkan: Implement synchronous framebuffer readback
  • Loading branch information...
hrydgard committed Oct 29, 2017
2 parents 872b030 + 97dced5 commit 0c7ae95df56df6de99b2ef76de2550feb094afb2
View
@@ -808,7 +808,8 @@ if(VULKAN)
ext/native/thin3d/VulkanRenderManager.cpp
ext/native/thin3d/VulkanRenderManager.h
ext/native/thin3d/VulkanQueueRunner.cpp
ext/native/thin3d/VulkanQueueRunner.h)
ext/native/thin3d/VulkanQueueRunner.h
ext/native/thin3d/DataFormat.h)
endif()
if(WIN32)
set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS}
@@ -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) {
@@ -61,8 +61,7 @@ void CGEDebugger::Init() {
}
CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent)
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent), primaryWindow(nullptr), secondWindow(nullptr),
textureLevel_(0), showClut_(false), primaryBuffer_(nullptr), secondBuffer_(nullptr) {
: Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) {
GPUBreakpoints::Init();
Core_ListenShutdown(ForceUnpause);
@@ -85,25 +85,25 @@ class CGEDebugger : public Dialog {
u32 TexturePreviewFlags(const GPUgstate &state);
CtrlDisplayListView *displayList;
TabDisplayLists *lists;
TabStateFlags *flags;
TabStateLighting *lighting;
TabStateTexture *textureState;
TabStateSettings *settings;
TabVertices *vertices;
TabMatrices *matrices;
SimpleGLWindow *primaryWindow;
SimpleGLWindow *secondWindow;
TabStateWatch *watch;
TabControl *tabs;
TabControl *fbTabs;
int textureLevel_;
bool showClut_;
bool forceOpaque_;
CtrlDisplayListView *displayList = nullptr;
TabDisplayLists *lists = nullptr;
TabStateFlags *flags = nullptr;
TabStateLighting *lighting = nullptr;
TabStateTexture *textureState = nullptr;
TabStateSettings *settings = nullptr;
TabVertices *vertices = nullptr;
TabMatrices *matrices = nullptr;
SimpleGLWindow *primaryWindow = nullptr;
SimpleGLWindow *secondWindow = nullptr;
TabStateWatch *watch = nullptr;
TabControl *tabs = nullptr;
TabControl *fbTabs = nullptr;
int textureLevel_ = 0;
bool showClut_ = false;
bool forceOpaque_ = false;
// The most recent primary/framebuffer and texture buffers.
const GPUDebugBuffer *primaryBuffer_;
const GPUDebugBuffer *secondBuffer_;
const GPUDebugBuffer *primaryBuffer_ = nullptr;
const GPUDebugBuffer *secondBuffer_ = nullptr;
int minWidth_;
int minHeight_;
@@ -240,6 +240,7 @@
<ClInclude Include="gfx_es2\draw_text_qt.h" />
<ClInclude Include="gfx_es2\draw_text_win.h" />
<ClInclude Include="thin3d\d3d11_loader.h" />
<ClInclude Include="thin3d\DataFormat.h" />
<ClInclude Include="thin3d\VulkanQueueRunner.h" />
<ClInclude Include="thin3d\VulkanRenderManager.h" />
<ClInclude Include="util\text\wrap_text.h" />
@@ -760,4 +761,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
@@ -326,6 +326,9 @@
<ClInclude Include="thin3d\VulkanRenderManager.h">
<Filter>thin3d</Filter>
</ClInclude>
<ClInclude Include="thin3d\DataFormat.h">
<Filter>thin3d</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
@@ -865,4 +868,4 @@
<UniqueIdentifier>{06c6305a-a646-485b-85b9-645a24dd6553}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>
@@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
namespace Draw {
enum class DataFormat : uint8_t {
UNDEFINED,
R8_UNORM,
R8G8_UNORM,
R8G8B8_UNORM,
R8G8B8A8_UNORM,
R8G8B8A8_UNORM_SRGB,
B8G8R8A8_UNORM, // D3D style
B8G8R8A8_UNORM_SRGB, // D3D style
R8G8B8A8_SNORM,
R8G8B8A8_UINT,
R8G8B8A8_SINT,
R4G4_UNORM_PACK8,
A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit
B4G4R4A4_UNORM_PACK16,
R4G4B4A4_UNORM_PACK16,
R5G6B5_UNORM_PACK16,
B5G6R5_UNORM_PACK16,
R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit
B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit
A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit.
R16_FLOAT,
R16G16_FLOAT,
R16G16B16A16_FLOAT,
R32_FLOAT,
R32G32_FLOAT,
R32G32B32_FLOAT,
R32G32B32A32_FLOAT,
// Block compression formats.
// These are modern names for DXT and friends, now patent free.
// https://msdn.microsoft.com/en-us/library/bb694531.aspx
BC1_RGBA_UNORM_BLOCK,
BC1_RGBA_SRGB_BLOCK,
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using
BC2_SRGB_BLOCK,
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color
BC3_SRGB_BLOCK,
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha
BC4_SNORM_BLOCK,
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha
BC5_SNORM_BLOCK,
BC6H_UFLOAT_BLOCK, // TODO
BC6H_SFLOAT_BLOCK,
BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality.
BC7_SRGB_BLOCK,
ETC1,
S8,
D16,
D24_S8,
D32F,
D32F_S8,
};
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
} // namespace
@@ -1,13 +1,41 @@
#include "DataFormat.h"
#include "VulkanQueueRunner.h"
#include "VulkanRenderManager.h"
const uint32_t readbackBufferSize = 2048 * 2048 * 4;
void VulkanQueueRunner::CreateDeviceObjects() {
InitBackbufferRenderPass();
InitRenderpasses();
VkDevice device = vulkan_->GetDevice();
VkBufferCreateInfo buf{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
buf.size = readbackBufferSize;
buf.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
vkCreateBuffer(device, &buf, nullptr, &readbackBuffer_);
VkMemoryRequirements reqs{};
vkGetBufferMemoryRequirements(device, readbackBuffer_, &reqs);
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
alloc.allocationSize = reqs.size;
VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &alloc.memoryTypeIndex);
assert(success);
vkAllocateMemory(device, &alloc, nullptr, &readbackMemory_);
uint32_t offset = 0;
vkBindBufferMemory(device, readbackBuffer_, readbackMemory_, offset);
}
void VulkanQueueRunner::DestroyDeviceObjects() {
VkDevice device = vulkan_->GetDevice();
vkFreeMemory(device, readbackMemory_, nullptr);
vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_);
for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) {
assert(renderPasses_[i] != VK_NULL_HANDLE);
vkDestroyRenderPass(device, renderPasses_[i], nullptr);
@@ -16,7 +44,6 @@ void VulkanQueueRunner::DestroyDeviceObjects() {
vkDestroyRenderPass(device, backbufferRenderPass_, nullptr);
}
void VulkanQueueRunner::InitBackbufferRenderPass() {
VkResult U_ASSERT_ONLY res;
@@ -167,7 +194,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep
PerformBlit(step, cmd);
break;
case VKRStepType::READBACK:
// PerformReadback
PerformReadback(step, cmd);
break;
}
delete steps[i];
@@ -688,3 +715,44 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemor
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
img.layout = barrier.newLayout;
}
void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) {
VKRImage *srcImage;
if (step.readback.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
srcImage = &step.readback.src->color;
} else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
srcImage = &step.readback.src->depth;
} else {
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 = 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(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
// Read back to the requested address in ram from buffer.
void *mappedData;
const int srcPixelSize = 4; // TODO: Fix.
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_);
}
@@ -4,7 +4,7 @@
#include "Common/Vulkan/VulkanContext.h"
#include "math/dataconv.h"
#include "thin3d/thin3d.h"
#include "thin3d/DataFormat.h"
class VKRFramebuffer;
struct VKRImage;
@@ -127,8 +127,8 @@ struct VKRStep {
VkFilter filter;
} blit;
struct {
int aspectMask;
VKRFramebuffer *src;
void *destPtr;
VkRect2D srcRect;
} readback;
};
@@ -156,6 +156,8 @@ class VulkanQueueRunner {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
private:
void InitBackbufferRenderPass();
void InitRenderpasses();
@@ -164,6 +166,7 @@ class VulkanQueueRunner {
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd);
static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
@@ -177,4 +180,9 @@ class VulkanQueueRunner {
// Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
// TODO: Create these on demand.
VkRenderPass renderPasses_[9]{};
};
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
// We size it generously.
VkDeviceMemory readbackMemory_;
VkBuffer readbackBuffer_;
};
Oops, something went wrong.

2 comments on commit 0c7ae95

@tausifj15

This comment has been minimized.

Show comment
Hide comment
@tausifj15

tausifj15 Nov 3, 2017

Android build kab ayega ?

tausifj15 replied Nov 3, 2017

Android build kab ayega ?

@tausifj15

This comment has been minimized.

Show comment
Hide comment
@tausifj15

tausifj15 Nov 3, 2017

Bhool gayle bada kaho?

tausifj15 replied Nov 3, 2017

Bhool gayle bada kaho?

Please sign in to comment.