diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 29bea57c32..9a436a2c73 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -1325,7 +1325,17 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) { // Texture arrays are supported but we don't have any other use cases yet. _dbg_assert_(desc.numLayers == 1); - fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + DXGI_FORMAT colorFormat; + switch (desc.colorFormat) { + case DataFormat::R8G8B8A8_UNORM: + colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + default: + _assert_msg_(false, "Framebuffer format not supported"); + return nullptr; + } + + fb->colorFormat = colorFormat; D3D11_TEXTURE2D_DESC descColor{}; descColor.Width = desc.width; descColor.Height = desc.height; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index b7e920d604..04889807f3 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -1264,7 +1264,18 @@ Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) { D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height); fbo->depthstenciltex = nullptr; - HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, nullptr); + D3DFORMAT colorFormat; + switch (desc.colorFormat) { + case DataFormat::R8G8B8A8_UNORM: + // We pretend to support this, although in reality we use the reverse format. + colorFormat = D3DFMT_A8R8G8B8; + break; + default: + _assert_msg_(false, "Framebuffer format not supported"); + return nullptr; + } + + HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, colorFormat, D3DPOOL_DEFAULT, &fbo->tex, nullptr); if (FAILED(rtResult)) { ERROR_LOG(G3D, "Failed to create render target"); fbo->Release(); diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 63f75af330..dfb36a1246 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -517,7 +517,22 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { // Color texture is same everywhere glGenFramebuffers(1, &fbo->handle); - initFBOTexture(fbo->color_texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true); + + GLint colorInternalFormat; + GLint colorFormat; + GLint colorElementType; + switch (fbo->colorFormat) { + case Draw::DataFormat::R8G8B8A8_UNORM: + colorInternalFormat = GL_RGBA; + colorFormat = GL_RGBA; + colorElementType = GL_UNSIGNED_BYTE; + break; + default: + _assert_msg_(false, "Data format not supported"); + return; + } + + initFBOTexture(fbo->color_texture, colorInternalFormat, colorFormat, colorElementType, true); retry_depth: if (!fbo->z_stencil_) { diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index f0a1cce354..0d81b1a5e6 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -106,6 +106,7 @@ void GLDeleter::Perform(GLRenderManager *renderManager, bool skipGLCalls) { framebuffer->z_stencil_texture.texture = 0; framebuffer->z_buffer = 0; framebuffer->stencil_buffer = 0; + framebuffer->colorFormat = Draw::DataFormat::UNDEFINED; } delete framebuffer; } diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 2d213be8b8..afaf8e7c90 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -51,10 +51,9 @@ public: class GLRFramebuffer { public: - GLRFramebuffer(const Draw::DeviceCaps &caps, int _width, int _height, bool z_stencil) + GLRFramebuffer(const Draw::DeviceCaps &caps, Draw::DataFormat _colorFormat, int _width, int _height, bool z_stencil) : color_texture(caps, _width, _height, 1, 1), z_stencil_texture(caps, _width, _height, 1, 1), - width(_width), height(_height), z_stencil_(z_stencil) { - } + colorFormat(_colorFormat), width(_width), height(_height), z_stencil_(z_stencil) {} ~GLRFramebuffer(); @@ -65,6 +64,7 @@ public: GLRTexture z_stencil_texture; GLuint z_buffer = 0; GLuint stencil_buffer = 0; + Draw::DataFormat colorFormat; int width; int height; @@ -464,9 +464,9 @@ public: return step.create_shader.shader; } - GLRFramebuffer *CreateFramebuffer(int width, int height, bool z_stencil) { + GLRFramebuffer *CreateFramebuffer(Draw::DataFormat colorFormat, int width, int height, bool z_stencil) { GLRInitStep step{ GLRInitStepType::CREATE_FRAMEBUFFER }; - step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, width, height, z_stencil); + step.create_framebuffer.framebuffer = new GLRFramebuffer(caps_, colorFormat, width, height, z_stencil); initSteps_.push_back(step); return step.create_framebuffer.framebuffer; } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 480f0cc3bb..546ca17c06 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -1437,7 +1437,7 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { // TODO: Support multiview later. (It's our only use case for multi layers). _dbg_assert_(desc.numLayers == 1); - GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil); + GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.colorFormat, desc.width, desc.height, desc.z_stencil); OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer); return fbo; } diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.cpp b/Common/GPU/Vulkan/VulkanFramebuffer.cpp index 17167e36eb..b74a0f32d9 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.cpp +++ b/Common/GPU/Vulkan/VulkanFramebuffer.cpp @@ -35,12 +35,12 @@ void VKRImage::Delete(VulkanContext *vulkan) { } } -VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag) +VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, VkFormat colorFormat, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag) : vulkan_(vk), tag_(tag), width(_width), height(_height), numLayers(_numLayers) { _dbg_assert_(tag); - CreateImage(vulkan_, initCmd, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); + CreateImage(vulkan_, initCmd, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, colorFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); if (createDepthStencilBuffer) { CreateImage(vulkan_, initCmd, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); } diff --git a/Common/GPU/Vulkan/VulkanFramebuffer.h b/Common/GPU/Vulkan/VulkanFramebuffer.h index fc584f4b8c..04da60ac32 100644 --- a/Common/GPU/Vulkan/VulkanFramebuffer.h +++ b/Common/GPU/Vulkan/VulkanFramebuffer.h @@ -58,7 +58,7 @@ struct VKRImage { class VKRFramebuffer { public: - VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag); + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, VkFormat colorFormat, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag); ~VKRFramebuffer(); VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType); diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 7046121f20..551ed2130b 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -415,7 +415,8 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override; - DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) override; + DataFormat PreferredColorReadbackFormat(Framebuffer *src) override; + DataFormat PreferredDepthReadbackFormat(Framebuffer *src) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override; @@ -1602,8 +1603,12 @@ Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { _assert_(desc.width > 0); _assert_(desc.height > 0); + _assert_(desc.colorFormat == DataFormat::R8G8B8A8_UNORM || desc.colorFormat == DataFormat::R16_UNORM); + + VkFormat colorFormat = DataFormatToVulkan(desc.colorFormat); + VkCommandBuffer cmd = renderManager_.GetInitCmd(); - VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.multiSampleLevel, desc.z_stencil, desc.tag); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), colorFormat, desc.width, desc.height, desc.numLayers, desc.multiSampleLevel, desc.z_stencil, desc.tag); return new VKFramebuffer(vkrfb, desc.multiSampleLevel); } @@ -1643,15 +1648,19 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, return renderManager_.CopyFramebufferToMemorySync(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride, tag); } -DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) { +DataFormat VKContext::PreferredColorReadbackFormat(Framebuffer *src) { if (src) { - return DrawContext::PreferredFramebufferReadbackFormat(src); + return DrawContext::PreferredColorReadbackFormat(src); } if (vulkan_->GetSwapchainFormat() == VK_FORMAT_B8G8R8A8_UNORM) { return Draw::DataFormat::B8G8R8A8_UNORM; } - return DrawContext::PreferredFramebufferReadbackFormat(src); + return DrawContext::PreferredColorReadbackFormat(src); +} + +DataFormat VKContext::PreferredDepthReadbackFormat(Framebuffer *src) { + return Draw::DataFormat::R16_UNORM; } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) { diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index bf10cb3801..695d3e8596 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -295,6 +295,7 @@ enum class Event { constexpr uint32_t MAX_TEXTURE_SLOTS = 3; struct FramebufferDesc { + DataFormat colorFormat; int width; int height; int depth; @@ -696,9 +697,13 @@ public: virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) { return false; } - virtual DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) { + virtual DataFormat PreferredColorReadbackFormat(Framebuffer *src) { return DataFormat::R8G8B8A8_UNORM; } + virtual DataFormat PreferredDepthReadbackFormat(Framebuffer *src) { + // We use a shader to read depth and write color, while scaling. + return DataFormat::R16_UNORM; + } // These functions should be self explanatory. // Binding a zero render target means binding the backbuffer. diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index c0a061f5f4..6d0000b922 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1689,7 +1689,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, char tag[128]; size_t len = FormatFramebufferName(vfb, tag, sizeof(tag)); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), msaaLevel_, true, tag }); + vfb->fbo = draw_->CreateFramebuffer({ colorFormat_, vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), msaaLevel_, true, tag }); if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) { NotifyMemInfo(MemBlockFlags::ALLOC, vfb->fb_address, ColorBufferByteSize(vfb), tag, len); } @@ -2060,7 +2060,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd char name[64]; snprintf(name, sizeof(name), "%08x_color_RAM", vfb->fb_address); textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, name }); + vfb->fbo = draw_->CreateFramebuffer({ colorFormat_, vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, name }); vfbs_.push_back(vfb); u32 byteSize = ColorBufferByteSize(vfb); @@ -2112,8 +2112,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram char name[64]; snprintf(name, sizeof(name), "download_temp"); // TODO: We don't have a way to create a depth-only framebuffer yet. - // Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses. - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, 0, channel == RASTER_DEPTH ? true : false, name }); + nvfb->fbo = draw_->CreateFramebuffer({ colorFormat_, nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, 0, channel == RASTER_DEPTH ? true : false, name }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight); delete nvfb; @@ -2496,7 +2495,7 @@ static const char *TempFBOReasonToString(TempFBO reason) { } Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h) { - u64 key = ((u64)reason << 48) | ((u32)w << 16) | h; + u64 key = ((u64)reason << 48) | ((u64)w << 16) | h; auto it = tempFBOs_.find(key); if (it != tempFBOs_.end()) { it->second.last_frame_used = gpuStats.numFlips; @@ -2507,7 +2506,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u char name[128]; snprintf(name, sizeof(name), "tempfbo_%s_%dx%d", TempFBOReasonToString(reason), w / renderScaleFactor_, h / renderScaleFactor_); - Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), 0, z_stencil, name }); + Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ colorFormat_, w, h, 1, GetFramebufferLayers(), 0, z_stencil, name }); if (!fbo) { return nullptr; } @@ -2699,7 +2698,7 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int w, h; draw_->GetFramebufferDimensions(nullptr, &w, &h); - Draw::DataFormat fmt = draw_->PreferredFramebufferReadbackFormat(nullptr); + Draw::DataFormat fmt = draw_->PreferredColorReadbackFormat(nullptr); // Ignore preferred formats other than BGRA. if (fmt != Draw::DataFormat::B8G8R8A8_UNORM) fmt = Draw::DataFormat::R8G8B8A8_UNORM; @@ -3202,7 +3201,7 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi char tag[128]; FormatFramebufferName(vfb, tag, sizeof(tag)); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, tag }); + vfb->fbo = draw_->CreateFramebuffer({ colorFormat_, vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 0, true, tag }); vfbs_.push_back(vfb); } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 3a8232b431..62663065ac 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -605,4 +605,6 @@ protected: Draw2D draw2D_; // The fragment shaders are "owned" by the pipelines since they're 1:1. + + const Draw::DataFormat colorFormat_ = Draw::DataFormat::R8G8B8A8_UNORM; }; diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 4341961214..1bd9478fb2 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -278,7 +278,7 @@ bool PresentationCommon::UpdatePostShader() { previousIndex_ = 0; for (int i = 0; i < FRAMES; ++i) { - previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "inter_presentation" }); + previousFramebuffers_[i] = draw_->CreateFramebuffer({ Draw::DataFormat::R8G8B8A8_UNORM, w, h, 1, 1, 0, false, "inter_presentation" }); if (!previousFramebuffers_[i]) { DestroyPostShader(); return false; @@ -394,7 +394,7 @@ bool PresentationCommon::AllocateFramebuffer(int w, int h) { } // No depth/stencil for post processing - Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "presentation" }); + Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ Draw::DataFormat::R8G8B8A8_UNORM, w, h, 1, 1, 0, false, "presentation" }); if (!fbo) { return false; }