From db0989a9d0e2d0d298a4e8a8945d4d26b878c650 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 22:41:18 -0800 Subject: [PATCH] Debugger: Fix texture preview in GLES. Also, fix issues with the first view of a texture in Vulkan / D3D11. --- GPU/D3D11/TextureCacheD3D11.cpp | 14 +++++++------ GPU/GLES/TextureCacheGLES.cpp | 29 +++++++++++++++++++++++---- GPU/Vulkan/TextureCacheVulkan.cpp | 16 ++++++++------- ext/native/thin3d/GLQueueRunner.cpp | 21 +++++++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 2 +- ext/native/thin3d/GLRenderManager.cpp | 16 +++++++++++++++ ext/native/thin3d/GLRenderManager.h | 2 +- 7 files changed, 81 insertions(+), 19 deletions(-) diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 425d8b738b..fc12d417db 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -775,16 +775,18 @@ bool TextureCacheD3D11::DecodeTexture(u8 *output, const GPUgstate &state) { } bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { - ApplyTexture(); SetTexture(false); if (!nextTexture_) return false; + // Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer. + TexCacheEntry *entry = nextTexture_; + ApplyTexture(); + // TODO: Centralize. - if (nextTexture_->framebuffer) { - VirtualFramebuffer *vfb = nextTexture_->framebuffer; - bool flipY = GetGPUBackend() == GPUBackend::OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; - buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY); + if (entry->framebuffer) { + VirtualFramebuffer *vfb = entry->framebuffer; + buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false); 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. @@ -794,7 +796,7 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level return retval; } - ID3D11Texture2D *texture = (ID3D11Texture2D *)nextTexture_->texturePtr; + ID3D11Texture2D *texture = (ID3D11Texture2D *)entry->texturePtr; if (!texture) return false; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 8560438e04..39b3c7ede1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -907,19 +907,40 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) } SetTexture(true); + if (!nextTexture_) + return false; + + // Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer. + TexCacheEntry *entry = nextTexture_; ApplyTexture(); + + // TODO: Centralize? + if (entry->framebuffer) { + VirtualFramebuffer *vfb = entry->framebuffer; + buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false); + 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; + } + + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + // Not a framebuffer, so let's assume these are right. int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (level != 0) { gstate = saved; } buffer.Allocate(w, h, GE_FORMAT_8888, false); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); + renderManager->CopyImageToMemorySync(entry->textureName, level, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, (uint8_t *)buffer.GetData(), w); + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + framebufferManager_->RebindFramebuffer(); return true; #else diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 4b844b192b..041ed911e8 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -784,16 +784,18 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt } bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { - ApplyTexture(); SetTexture(false); if (!nextTexture_) return false; + // Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer. + TexCacheEntry *entry = nextTexture_; + ApplyTexture(); + // TODO: Centralize? - if (nextTexture_->framebuffer) { - VirtualFramebuffer *vfb = nextTexture_->framebuffer; - bool flipY = GetGPUBackend() == GPUBackend::OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; - buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY); + if (entry->framebuffer) { + VirtualFramebuffer *vfb = entry->framebuffer; + buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false); 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. @@ -803,9 +805,9 @@ bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int leve return retval; } - if (!nextTexture_->vkTex || !nextTexture_->vkTex->texture_) + if (!entry->vkTex || !entry->vkTex->texture_) return false; - VulkanTexture *texture = nextTexture_->vkTex->texture_; + VulkanTexture *texture = entry->vkTex->texture_; VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); GPUDebugBufferFormat bufferFormat; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 381934224b..d7a724ae25 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -947,7 +947,28 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { } void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { + GLRTexture *tex = pass.readback_image.texture; + glBindTexture(GL_TEXTURE_2D, tex->texture); + + CHECK_GL_ERROR_IF_DEBUG(); + + int pixelStride = pass.readback_image.srcRect.w; + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + GLRect2D rect = pass.readback.srcRect; + + int size = 4 * rect.w * rect.h; + if (size > readbackBufferSize_) { + delete[] readbackBuffer_; + readbackBuffer_ = new uint8_t[size]; + readbackBufferSize_ = size; + } + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetTexImage(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_RGBA, GL_UNSIGNED_BYTE, readbackBuffer_); + + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index a4175405f2..362d3e30f3 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -301,7 +301,7 @@ struct GLRStep { Draw::DataFormat dstFormat; } readback; struct { - GLint texture; + GLRTexture *texture; GLRect2D srcRect; int mipLevel; } readback_image; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 63c9ecbb8f..33990e3db1 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -276,6 +276,22 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec return true; } +void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { + GLRStep *step = new GLRStep{ GLRStepType::READBACK_IMAGE }; + step->readback_image.texture = texture; + step->readback_image.mipLevel = mipLevel; + step->readback_image.srcRect = { x, y, w, h }; + steps_.push_back(step); + + // Every step clears this state. + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE); + + curRenderStep_ = nullptr; + FlushSync(); + + queueRunner_.CopyReadbackBuffer(w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels); +} + void GLRenderManager::BeginFrame() { VLOG("BeginFrame"); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 345b8ad6f2..b8933ed074 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -313,7 +313,7 @@ public: void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil); void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); bool CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); - void CopyImageToMemorySync(GLuint texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); + void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask); void BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter);