Permalink
Browse files

Merge pull request #10060 from unknownbrackets/debugger

Show texture, depth, and stencil preview in Vulkan
  • Loading branch information...
hrydgard committed Nov 6, 2017
2 parents 392adde + 3a84b63 commit b280e30a2732f1cae00cba3cb27bed1bc19958d9
@@ -35,7 +35,6 @@ class VulkanTexture {
void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void EndCreate(VkCommandBuffer cmd);
int GetNumMips() const { return numMips_; }
void Destroy();
// Used in image copies, etc.
@@ -46,6 +45,8 @@ class VulkanTexture {
int32_t GetWidth() const { return tex_width; }
int32_t GetHeight() const { return tex_height; }
int32_t GetNumMips() const { return numMips_; }
VkFormat GetFormat() const { return format_; }
private:
void CreateMappableImage();
@@ -705,7 +705,7 @@ bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, Vi
}
// Check if it's in bufferWidth (which might be higher than width and may indicate the framebuffer includes the data.)
if (fbInfo.xOffset >= framebuffer->bufferWidth && fbInfo.xOffset + w <= framebuffer->fb_stride) {
if (fbInfo.xOffset >= framebuffer->bufferWidth && fbInfo.xOffset + w <= (u32)framebuffer->fb_stride) {
// This happens in Brave Story, see #10045 - the texture is in the space between strides, with matching stride.
DetachFramebuffer(entry, address, framebuffer);
return false;
@@ -819,6 +819,20 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level
if (!nextTexture_)
return false;
// TODO: Centralize.
if (nextTexture_->framebuffer) {
VirtualFramebuffer *vfb = nextTexture_->framebuffer;
bool flipY = g_Config.iGPUBackend == GPU_BACKEND_OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY);
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
// We may have blitted to a temp FBO.
framebufferManager_->RebindFramebuffer();
return retval;
}
ID3D11Texture2D *texture = (ID3D11Texture2D *)nextTexture_->texturePtr;
if (!texture)
return false;
View
@@ -255,7 +255,6 @@
<ClInclude Include="D3D11\D3D11Util.h">
<Filter>D3D11</Filter>
</ClInclude>
<ClInclude Include="D3D11\StateMappingD3D11.h" />
<ClInclude Include="Common\ShaderTranslation.h">
<Filter>Common</Filter>
</ClInclude>
@@ -268,6 +267,9 @@
<ClInclude Include="Common\StencilCommon.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="D3D11\StateMappingD3D11.h">
<Filter>D3D11</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@@ -809,6 +809,62 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt
}
bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
// TODO
return false;
ApplyTexture();
SetTexture(false);
if (!nextTexture_)
return false;
// TODO: Centralize?
if (nextTexture_->framebuffer) {
VirtualFramebuffer *vfb = nextTexture_->framebuffer;
bool flipY = g_Config.iGPUBackend == GPU_BACKEND_OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY);
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
// We may have blitted to a temp FBO.
framebufferManager_->RebindFramebuffer();
return retval;
}
if (!nextTexture_->vkTex || !nextTexture_->vkTex->texture_)
return false;
VulkanTexture *texture = nextTexture_->vkTex->texture_;
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
GPUDebugBufferFormat bufferFormat;
Draw::DataFormat drawFormat;
switch (texture->GetFormat()) {
case VULKAN_565_FORMAT:
bufferFormat = GPU_DBG_FORMAT_565;
drawFormat = Draw::DataFormat::B5G6R5_UNORM_PACK16;
break;
case VULKAN_1555_FORMAT:
bufferFormat = GPU_DBG_FORMAT_5551;
drawFormat = Draw::DataFormat::B5G5R5A1_UNORM_PACK16;
break;
case VULKAN_4444_FORMAT:
bufferFormat = GPU_DBG_FORMAT_4444;
drawFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
break;
case VULKAN_8888_FORMAT:
default:
bufferFormat = GPU_DBG_FORMAT_8888;
drawFormat = Draw::DataFormat::R8G8B8A8_UNORM;
break;
}
int w = texture->GetWidth();
int h = texture->GetHeight();
buffer.Allocate(w, h, bufferFormat);
renderManager->CopyImageToMemorySync(texture->GetImage(), level, 0, 0, w, h, drawFormat, (uint8_t *)buffer.GetData(), w);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
framebufferManager_->RebindFramebuffer();
return true;
}
@@ -66,6 +66,13 @@ enum class DataFormat : uint8_t {
D32F_S8,
};
size_t DataFormatSizeInBytes(DataFormat fmt);
bool DataFormatIsDepthStencil(DataFormat fmt);
inline bool DataFormatIsColor(DataFormat fmt) {
return !DataFormatIsDepthStencil(fmt);
}
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
} // namespace
@@ -2,6 +2,8 @@
#include "VulkanQueueRunner.h"
#include "VulkanRenderManager.h"
// TODO: This is only enough for 4x render resolution / 4x texture upscale for debugger.
// Maybe we should use a dynamically allocated one for larger?
const uint32_t readbackBufferSize = 2048 * 2048 * 4;
void VulkanQueueRunner::CreateDeviceObjects() {
@@ -205,6 +207,9 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep
case VKRStepType::READBACK:
PerformReadback(step, cmd);
break;
case VKRStepType::READBACK_IMAGE:
PerformReadbackImage(step, cmd);
break;
}
delete steps[i];
}
@@ -227,6 +232,9 @@ void VulkanQueueRunner::LogSteps(const std::vector<VKRStep *> &steps) {
case VKRStepType::READBACK:
LogReadback(step);
break;
case VKRStepType::READBACK_IMAGE:
LogReadbackImage(step);
break;
}
}
}
@@ -280,6 +288,10 @@ void VulkanQueueRunner::LogReadback(const VKRStep &pass) {
ILOG("Readback");
}
void VulkanQueueRunner::LogReadbackImage(const VKRStep &pass) {
ILOG("ReadbackImage");
}
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) {
// TODO: If there are multiple, we can transition them together.
for (const auto &iter : step.preTransitions) {
@@ -825,8 +837,6 @@ 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;
if (srcImage->layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
@@ -849,13 +859,60 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
step.readback.src->Release();
}
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffer cmd) {
// TODO: Clean this up - just reusing `SetupTransitionToTransferSrc`.
VKRImage srcImage;
srcImage.image = step.readback_image.image;
srcImage.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
VkPipelineStageFlags stage = 0;
SetupTransitionToTransferSrc(srcImage, barrier, stage, VK_IMAGE_ASPECT_COLOR_BIT);
vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
VkBufferImageCopy region{};
region.imageOffset = { step.readback_image.srcRect.offset.x, step.readback_image.srcRect.offset.y, 0 };
region.imageExtent = { step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height, 1 };
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageSubresource.mipLevel = step.readback_image.mipLevel;
region.bufferOffset = 0;
region.bufferRowLength = step.readback_image.srcRect.extent.width;
region.bufferImageHeight = step.readback_image.srcRect.extent.height;
vkCmdCopyImageToBuffer(cmd, step.readback_image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuffer_, 1, &region);
// Now transfer it back to a texture.
TransitionImageLayout2(cmd, step.readback_image.image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT);
// NOTE: Can't read the buffer using the CPU here - need to sync first.
}
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, 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.
const size_t srcPixelSize = DataFormatSizeInBytes(srcFormat);
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);
if (srcFormat == Draw::DataFormat::R8G8B8A8_UNORM) {
ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat);
} else if (srcFormat == destFormat) {
uint8_t *dst = pixels;
const uint8_t *src = (const uint8_t *)mappedData;
for (int y = 0; y < height; ++y) {
memcpy(dst, src, width * srcPixelSize);
src += width * srcPixelSize;
dst += pixelStride * srcPixelSize;
}
} else if (destFormat == Draw::DataFormat::D32F) {
ConvertToD32F(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat);
} else {
// TODO: Maybe a depth conversion or something?
assert(false);
}
vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_);
}
@@ -84,6 +84,7 @@ enum class VKRStepType : uint8_t {
COPY,
BLIT,
READBACK,
READBACK_IMAGE,
};
enum class VKRRenderPassAction {
@@ -133,6 +134,11 @@ struct VKRStep {
VKRFramebuffer *src;
VkRect2D srcRect;
} readback;
struct {
VkImage image;
VkRect2D srcRect;
int mipLevel;
} readback_image;
};
};
@@ -159,7 +165,7 @@ class VulkanQueueRunner {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
private:
void InitBackbufferRenderPass();
@@ -170,11 +176,13 @@ class VulkanQueueRunner {
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd);
void PerformReadbackImage(const VKRStep &pass, VkCommandBuffer cmd);
void LogRenderPass(const VKRStep &pass);
void LogCopy(const VKRStep &pass);
void LogBlit(const VKRStep &pass);
void LogReadback(const VKRStep &pass);
void LogReadbackImage(const VKRStep &pass);
static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
@@ -401,8 +401,43 @@ 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, destFormat, pixelStride, pixels);
Draw::DataFormat srcFormat;
if (aspectBits & VK_IMAGE_ASPECT_COLOR_BIT) {
switch (src->color.format) {
case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break;
default: assert(false);
}
} else if (aspectBits & VK_IMAGE_ASPECT_STENCIL_BIT) {
// Copies from stencil are always S8.
srcFormat = Draw::DataFormat::S8;
} else if (aspectBits & VK_IMAGE_ASPECT_DEPTH_BIT) {
switch (src->depth.format) {
case VK_FORMAT_D24_UNORM_S8_UINT: srcFormat = Draw::DataFormat::D24_S8; break;
case VK_FORMAT_D32_SFLOAT_S8_UINT: srcFormat = Draw::DataFormat::D32F; break;
case VK_FORMAT_D16_UNORM_S8_UINT: srcFormat = Draw::DataFormat::D16; break;
default: assert(false);
}
} else {
assert(false);
}
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
}
void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
VKRStep *step = new VKRStep{ VKRStepType::READBACK_IMAGE };
step->readback_image.image = image;
step->readback_image.srcRect.offset = { x, y };
step->readback_image.srcRect.extent = { (uint32_t)w, (uint32_t)h };
step->readback_image.mipLevel = mipLevel;
steps_.push_back(step);
curRenderStep_ = nullptr;
FlushSync();
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
queueRunner_.CopyReadbackBuffer(w, h, destFormat, destFormat, pixelStride, pixels);
}
void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
@@ -635,6 +670,10 @@ void VulkanRenderManager::Wipe() {
case VKRStepType::READBACK:
step->readback.src->Release();
break;
case VKRStepType::READBACK_IMAGE:
break;
default:
assert(false);
}
delete step;
}
@@ -102,6 +102,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, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat 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);
Oops, something went wrong.

0 comments on commit b280e30

Please sign in to comment.