diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 7121dcf0b7..51a017b741 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -344,4 +344,88 @@ void DXStagingTexture::Flush() m_needs_flush = false; } +DXFramebuffer::DXFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, + u32 width, u32 height, u32 layers, u32 samples, + ID3D11RenderTargetView* rtv, ID3D11DepthStencilView* dsv) + : AbstractFramebuffer(color_format, depth_format, width, height, layers, samples), m_rtv(rtv), + m_dsv(dsv) +{ +} + +DXFramebuffer::~DXFramebuffer() +{ + if (m_rtv) + m_rtv->Release(); + if (m_dsv) + m_dsv->Release(); +} + +std::unique_ptr DXFramebuffer::Create(const DXTexture* color_attachment, + const DXTexture* depth_attachment) +{ + if (!ValidateConfig(color_attachment, depth_attachment)) + return nullptr; + + const AbstractTextureFormat color_format = + color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const AbstractTextureFormat depth_format = + depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const DXTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; + const u32 width = either_attachment->GetWidth(); + const u32 height = either_attachment->GetHeight(); + const u32 layers = either_attachment->GetLayers(); + const u32 samples = either_attachment->GetSamples(); + + ID3D11RenderTargetView* rtv = nullptr; + if (color_attachment) + { + D3D11_RENDER_TARGET_VIEW_DESC desc; + desc.Format = GetDXGIFormatForHostFormat(color_attachment->GetConfig().format); + if (color_attachment->GetConfig().IsMultisampled()) + { + desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + desc.Texture2DMSArray.ArraySize = color_attachment->GetConfig().layers; + desc.Texture2DMSArray.FirstArraySlice = 0; + } + else + { + desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.ArraySize = color_attachment->GetConfig().layers; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.MipSlice = 0; + } + + HRESULT hr = D3D::device->CreateRenderTargetView( + color_attachment->GetRawTexIdentifier()->GetTex(), &desc, &rtv); + CHECK(SUCCEEDED(hr), "Create render target view for framebuffer"); + } + + ID3D11DepthStencilView* dsv = nullptr; + if (depth_attachment) + { + D3D11_DEPTH_STENCIL_VIEW_DESC desc; + desc.Format = GetDXGIFormatForHostFormat(depth_attachment->GetConfig().format); + if (depth_attachment->GetConfig().IsMultisampled()) + { + desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + desc.Texture2DMSArray.ArraySize = depth_attachment->GetConfig().layers; + desc.Texture2DMSArray.FirstArraySlice = 0; + } + else + { + desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.ArraySize = depth_attachment->GetConfig().layers; + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.MipSlice = 0; + } + + HRESULT hr = D3D::device->CreateDepthStencilView( + depth_attachment->GetRawTexIdentifier()->GetTex(), &desc, &dsv); + CHECK(SUCCEEDED(hr), "Create depth stencil view for framebuffer"); + } + + return std::make_unique(color_format, depth_format, width, height, layers, samples, + rtv, dsv); +} + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index 0810471cad..96d8f13919 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -4,8 +4,10 @@ #pragma once +#include #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" @@ -63,4 +65,23 @@ private: ID3D11Texture2D* m_tex = nullptr; }; +class DXFramebuffer final : public AbstractFramebuffer +{ +public: + DXFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, u32 width, + u32 height, u32 layers, u32 samples, ID3D11RenderTargetView* rtv, + ID3D11DepthStencilView* dsv); + ~DXFramebuffer() override; + + ID3D11RenderTargetView* const* GetRTVArray() const { return &m_rtv; } + UINT GetNumRTVs() const { return m_rtv ? 1 : 0; } + ID3D11DepthStencilView* GetDSV() const { return m_dsv; } + static std::unique_ptr Create(const DXTexture* color_attachment, + const DXTexture* depth_attachment); + +protected: + ID3D11RenderTargetView* m_rtv; + ID3D11DepthStencilView* m_dsv; +}; + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 5cc4832c56..79fa51a504 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -90,6 +90,8 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height); D3D::context->RSSetViewports(1, &vp); FramebufferManager::BindEFBRenderTarget(); + m_current_framebuffer_width = m_target_width; + m_current_framebuffer_height = m_target_height; } Renderer::~Renderer() @@ -243,6 +245,14 @@ std::unique_ptr Renderer::CreateStagingTexture(StagingTe return DXStagingTexture::Create(type, config); } +std::unique_ptr +Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + return DXFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { D3D::DrawTextScaled(static_cast(left + 1), static_cast(top + 1), 20.f, 0.0f, @@ -648,6 +658,9 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region static constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data()); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_backbuffer_width; + m_current_framebuffer_height = m_backbuffer_height; // activate linear filtering for the buffer copies D3D::SetLinearCopySampler(); @@ -763,6 +776,9 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { // Gets us back into a more game-like state. + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_target_width; + m_current_framebuffer_height = m_target_height; FramebufferManager::BindEFBRenderTarget(); BPFunctions::SetViewport(); BPFunctions::SetScissor(); @@ -792,6 +808,36 @@ void Renderer::RestoreState() { } +void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer) +{ + const DXFramebuffer* fb = static_cast(framebuffer); + D3D::context->OMSetRenderTargets(fb->GetNumRTVs(), fb->GetRTVArray(), fb->GetDSV()); + m_current_framebuffer = fb; + m_current_framebuffer_width = fb->GetWidth(); + m_current_framebuffer_height = fb->GetHeight(); +} + +void Renderer::SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) +{ + SetFramebuffer(framebuffer); +} + +void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value, float depth_value) +{ + SetFramebuffer(framebuffer); + if (framebuffer->GetColorFormat() != AbstractTextureFormat::Undefined) + { + D3D::context->ClearRenderTargetView( + static_cast(framebuffer)->GetRTVArray()[0], color_value.data()); + } + if (framebuffer->GetDepthFormat() != AbstractTextureFormat::Undefined) + { + D3D::context->ClearDepthStencilView(static_cast(framebuffer)->GetDSV(), + D3D11_CLEAR_DEPTH, depth_value, 0); + } +} + void Renderer::SetRasterizationState(const RasterizationState& state) { m_gx_state.raster.hex = state.hex; diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index e06a6c30e2..7706cbe56d 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -31,8 +31,16 @@ public: std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length) override; std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override; + std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) override; void SetPipeline(const AbstractPipeline* pipeline) override; + void SetFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value = {}, + float depth_value = 0.0f) override; void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp index 55abc0521c..2a21afb4b7 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.cpp +++ b/Source/Core/VideoBackends/Null/NullTexture.cpp @@ -70,4 +70,31 @@ void NullStagingTexture::Flush() m_needs_flush = false; } +NullFramebuffer::NullFramebuffer(AbstractTextureFormat color_format, + AbstractTextureFormat depth_format, u32 width, u32 height, + u32 layers, u32 samples) + : AbstractFramebuffer(color_format, depth_format, width, height, layers, samples) +{ +} + +std::unique_ptr NullFramebuffer::Create(const NullTexture* color_attachment, + const NullTexture* depth_attachment) +{ + if (!ValidateConfig(color_attachment, depth_attachment)) + return nullptr; + + const AbstractTextureFormat color_format = + color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const AbstractTextureFormat depth_format = + depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const NullTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; + const u32 width = either_attachment->GetWidth(); + const u32 height = either_attachment->GetHeight(); + const u32 layers = either_attachment->GetLayers(); + const u32 samples = either_attachment->GetSamples(); + + return std::make_unique(color_format, depth_format, width, height, layers, + samples); +} + } // namespace Null diff --git a/Source/Core/VideoBackends/Null/NullTexture.h b/Source/Core/VideoBackends/Null/NullTexture.h index c80685c1e9..5a48ff652c 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.h +++ b/Source/Core/VideoBackends/Null/NullTexture.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" @@ -53,4 +55,15 @@ private: std::vector m_texture_buf; }; +class NullFramebuffer final : public AbstractFramebuffer +{ +public: + explicit NullFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, + u32 width, u32 height, u32 layers, u32 samples); + ~NullFramebuffer() override = default; + + static std::unique_ptr Create(const NullTexture* color_attachment, + const NullTexture* depth_attachment); +}; + } // namespace Null diff --git a/Source/Core/VideoBackends/Null/Render.cpp b/Source/Core/VideoBackends/Null/Render.cpp index 6c804bd346..4e9cf655c2 100644 --- a/Source/Core/VideoBackends/Null/Render.cpp +++ b/Source/Core/VideoBackends/Null/Render.cpp @@ -69,6 +69,14 @@ std::unique_ptr Renderer::CreatePipeline(const AbstractPipelin return std::make_unique(); } +std::unique_ptr +Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + return NullFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str()); diff --git a/Source/Core/VideoBackends/Null/Render.h b/Source/Core/VideoBackends/Null/Render.h index f7c05ed8c8..0c0092554f 100644 --- a/Source/Core/VideoBackends/Null/Render.h +++ b/Source/Core/VideoBackends/Null/Render.h @@ -17,6 +17,9 @@ public: std::unique_ptr CreateTexture(const TextureConfig& config) override; std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; + std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) override; std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) override; diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 60b33886c6..c57506d233 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -593,4 +593,72 @@ void OGLStagingTexture::Unmap() m_map_pointer = nullptr; } +OGLFramebuffer::OGLFramebuffer(AbstractTextureFormat color_format, + AbstractTextureFormat depth_format, u32 width, u32 height, + u32 layers, u32 samples, GLuint fbo) + : AbstractFramebuffer(color_format, depth_format, width, height, layers, samples), m_fbo(fbo) +{ +} + +OGLFramebuffer::~OGLFramebuffer() +{ + glDeleteFramebuffers(1, &m_fbo); +} + +std::unique_ptr OGLFramebuffer::Create(const OGLTexture* color_attachment, + const OGLTexture* depth_attachment) +{ + if (!ValidateConfig(color_attachment, depth_attachment)) + return nullptr; + + const AbstractTextureFormat color_format = + color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const AbstractTextureFormat depth_format = + depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const OGLTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; + const u32 width = either_attachment->GetWidth(); + const u32 height = either_attachment->GetHeight(); + const u32 layers = either_attachment->GetLayers(); + const u32 samples = either_attachment->GetSamples(); + + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + if (color_attachment) + { + if (color_attachment->GetConfig().layers > 1) + { + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + color_attachment->GetRawTexIdentifier(), 0); + } + else + { + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + color_attachment->GetRawTexIdentifier(), 0, 0); + } + } + + if (depth_attachment) + { + GLenum attachment = AbstractTexture::IsStencilFormat(depth_format) ? + GL_DEPTH_STENCIL_ATTACHMENT : + GL_DEPTH_ATTACHMENT; + if (depth_attachment->GetConfig().layers > 1) + { + glFramebufferTexture(GL_FRAMEBUFFER, attachment, depth_attachment->GetRawTexIdentifier(), 0); + } + else + { + glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, depth_attachment->GetRawTexIdentifier(), + 0, 0); + } + } + + _dbg_assert_(VIDEO, glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + FramebufferManager::SetFramebuffer(0); + return std::make_unique(color_format, depth_format, width, height, layers, + samples, fbo); +} + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h index 555d1968d8..60c5c932b3 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.h +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include "Common/GL/GLUtil.h" +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" @@ -74,4 +76,19 @@ private: GLsync m_fence = 0; }; +class OGLFramebuffer final : public AbstractFramebuffer +{ +public: + OGLFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, u32 width, + u32 height, u32 layers, u32 samples, GLuint fbo); + ~OGLFramebuffer() override; + + GLuint GetFBO() const { return m_fbo; } + static std::unique_ptr Create(const OGLTexture* color_attachment, + const OGLTexture* depth_attachment); + +protected: + GLuint m_fbo; +}; + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index fab642e71b..e7c15782ec 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -827,6 +827,8 @@ void Renderer::Init() // Initialize the FramebufferManager g_framebuffer_manager = std::make_unique( m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer()); + m_current_framebuffer_width = m_target_width; + m_current_framebuffer_height = m_target_height; m_post_processor = std::make_unique(); s_raster_font = std::make_unique(); @@ -843,6 +845,14 @@ std::unique_ptr Renderer::CreateStagingTexture(StagingTe return OGLStagingTexture::Create(type, config); } +std::unique_ptr +Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + return OGLFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + void Renderer::RenderText(const std::string& text, int left, int top, u32 color) { s_raster_font->printMultilineText(text, @@ -1145,7 +1155,7 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne { // The x/y parameters here assume a upper-left origin. glViewport takes an offset from the // lower-left of the framebuffer, so we must set y to the distance from the lower-left. - y = static_cast(m_target_height) - y - height; + y = static_cast(m_current_framebuffer_height) - y - height; if (g_ogl_config.bSupportViewportFloat) { glViewportIndexedf(0, x, y, width, height); @@ -1238,6 +1248,44 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) } } +void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer) +{ + glBindFramebuffer(GL_FRAMEBUFFER, static_cast(framebuffer)->GetFBO()); + m_current_framebuffer = framebuffer; + m_current_framebuffer_width = framebuffer->GetWidth(); + m_current_framebuffer_height = framebuffer->GetHeight(); +} + +void Renderer::SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) +{ + // EXT_discard_framebuffer could be used here to save bandwidth on tilers. + SetFramebuffer(framebuffer); +} + +void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value, float depth_value) +{ + SetFramebuffer(framebuffer); + + // NOTE: This disturbs the current scissor/mask setting. + // This won't be an issue when we implement proper state tracking. + glDisable(GL_SCISSOR_TEST); + GLbitfield clear_mask = 0; + if (framebuffer->HasColorBuffer()) + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(color_value[0], color_value[1], color_value[2], color_value[3]); + clear_mask |= GL_COLOR_BUFFER_BIT; + } + if (framebuffer->HasDepthBuffer()) + { + glDepthMask(GL_TRUE); + glClearDepth(depth_value); + clear_mask |= GL_DEPTH_BUFFER_BIT; + } + glClear(clear_mask); +} + void Renderer::ApplyBlendingState(const BlendingState& state) { bool useDualSource = @@ -1351,6 +1399,9 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_backbuffer_width; + m_current_framebuffer_height = m_backbuffer_height; // Copy the framebuffer to screen. BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(), @@ -1496,6 +1547,9 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { + m_current_framebuffer = nullptr; + m_current_framebuffer_width = m_target_width; + m_current_framebuffer_height = m_target_height; FramebufferManager::SetFramebuffer(0); // Gets us back into a more game-like state. diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index e8e385ed7b..e28f2b52d1 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -95,8 +95,16 @@ public: std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length) override; std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) override; + std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) override; void SetPipeline(const AbstractPipeline* pipeline) override; + void SetFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value = {}, + float depth_value = 0.0f) override; void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index cde52b4449..51b035dd82 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -39,6 +39,14 @@ SWRenderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& c return std::make_unique(type, config); } +std::unique_ptr +SWRenderer::CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + return SW::SWFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color) { SWOGLWindow::s_instance->PrintText(pstr, left, top, color); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 0631cbdf00..2c4a5aeef6 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -16,6 +16,9 @@ public: std::unique_ptr CreateTexture(const TextureConfig& config) override; std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; + std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) override; std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) override; diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index 967474916f..78f083f768 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -152,4 +152,31 @@ void SWStagingTexture::Flush() { m_needs_flush = false; } + +SWFramebuffer::SWFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, + u32 width, u32 height, u32 layers, u32 samples) + : AbstractFramebuffer(color_format, depth_format, width, height, layers, samples) +{ +} + +std::unique_ptr SWFramebuffer::Create(const SWTexture* color_attachment, + const SWTexture* depth_attachment) +{ + if (!ValidateConfig(color_attachment, depth_attachment)) + return nullptr; + + const AbstractTextureFormat color_format = + color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const AbstractTextureFormat depth_format = + depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined; + const SWTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; + const u32 width = either_attachment->GetWidth(); + const u32 height = either_attachment->GetHeight(); + const u32 layers = either_attachment->GetLayers(); + const u32 samples = either_attachment->GetSamples(); + + return std::make_unique(color_format, depth_format, width, height, layers, + samples); +} + } // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h index 26c869da62..5c403f54ba 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.h +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" @@ -59,4 +61,15 @@ private: std::vector m_data; }; +class SWFramebuffer final : public AbstractFramebuffer +{ +public: + explicit SWFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, + u32 width, u32 height, u32 layers, u32 samples); + ~SWFramebuffer() override = default; + + static std::unique_ptr Create(const SWTexture* color_attachment, + const SWTexture* depth_attachment); +}; + } // namespace SW diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 12de136162..588e4b0919 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -192,6 +192,14 @@ std::unique_ptr Renderer::CreatePipeline(const AbstractPipelin return VKPipeline::Create(config); } +std::unique_ptr +Renderer::CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + return VKFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + std::tuple Renderer::UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size) { @@ -593,8 +601,9 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha // Fastest path: Use a render pass to clear the buffers. if (use_clear_render_pass) { - VkClearValue clear_values[2] = {clear_color_value, clear_depth_value}; - StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values); + const std::array clear_values = {{clear_color_value, clear_depth_value}}; + StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values.data(), + static_cast(clear_values.size())); return; } @@ -743,6 +752,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Restore the EFB color texture to color attachment ready for rendering the next frame. FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout( g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + RestoreAPIState(); // Determine what (if anything) has changed in the config. CheckForConfigChanges(); @@ -792,6 +802,9 @@ void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region backbuffer->OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED); backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = backbuffer->GetWidth(); + m_current_framebuffer_height = backbuffer->GetHeight(); // Begin render pass for rendering to the swap chain. VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; @@ -1010,6 +1023,9 @@ void Renderer::BindEFBToStateTracker() FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size); StateTracker::GetInstance()->SetMultisamplingstate( FramebufferManager::GetInstance()->GetEFBMultisamplingState()); + m_current_framebuffer = nullptr; + m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth(); + m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight(); } void Renderer::RecreateEFBFramebuffer() @@ -1037,12 +1053,78 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { StateTracker::GetInstance()->EndRenderPass(); + if (m_current_framebuffer) + static_cast(m_current_framebuffer)->TransitionForSample(); + BindEFBToStateTracker(); // Instruct the state tracker to re-bind everything before the next draw StateTracker::GetInstance()->SetPendingRebind(); } +void Renderer::BindFramebuffer(const VKFramebuffer* fb) +{ + const VkRect2D render_area = {static_cast(fb->GetWidth()), + static_cast(fb->GetHeight())}; + + StateTracker::GetInstance()->EndRenderPass(); + if (m_current_framebuffer) + static_cast(m_current_framebuffer)->TransitionForSample(); + + fb->TransitionForRender(); + StateTracker::GetInstance()->SetFramebuffer(fb->GetFB(), render_area); + StateTracker::GetInstance()->SetRenderPass(fb->GetLoadRenderPass(), fb->GetClearRenderPass()); + m_current_framebuffer = fb; + m_current_framebuffer_width = fb->GetWidth(); + m_current_framebuffer_height = fb->GetHeight(); +} + +void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer) +{ + const VKFramebuffer* vkfb = static_cast(framebuffer); + BindFramebuffer(vkfb); + StateTracker::GetInstance()->BeginRenderPass(); +} + +void Renderer::SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) +{ + const VKFramebuffer* vkfb = static_cast(framebuffer); + BindFramebuffer(vkfb); + + // If we're discarding, begin the discard pass, then switch to a load pass. + // This way if the command buffer is flushed, we don't start another discard pass. + StateTracker::GetInstance()->SetRenderPass(vkfb->GetDiscardRenderPass(), + vkfb->GetClearRenderPass()); + StateTracker::GetInstance()->BeginRenderPass(); + StateTracker::GetInstance()->SetRenderPass(vkfb->GetLoadRenderPass(), vkfb->GetClearRenderPass()); +} + +void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value, float depth_value) +{ + const VKFramebuffer* vkfb = static_cast(framebuffer); + BindFramebuffer(vkfb); + + const VkRect2D render_area = {static_cast(vkfb->GetWidth()), + static_cast(vkfb->GetHeight())}; + std::array clear_values; + u32 num_clear_values = 0; + if (vkfb->GetColorFormat() != AbstractTextureFormat::Undefined) + { + std::memcpy(clear_values[num_clear_values].color.float32, color_value.data(), + sizeof(clear_values[num_clear_values].color.float32)); + num_clear_values++; + } + if (vkfb->GetDepthFormat() != AbstractTextureFormat::Undefined) + { + clear_values[num_clear_values].depthStencil.depth = depth_value; + clear_values[num_clear_values].depthStencil.stencil = 0; + num_clear_values++; + } + StateTracker::GetInstance()->BeginClearRenderPass(render_area, clear_values.data(), + num_clear_values); +} + void Renderer::SetRasterizationState(const RasterizationState& state) { StateTracker::GetInstance()->SetRasterizationState(state); diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index a0bc35536c..e62849ac19 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -23,6 +23,7 @@ class SwapChain; class StagingTexture2D; class Texture2D; class RasterFont; +class VKFramebuffer; class VKPipeline; class VKTexture; @@ -37,6 +38,9 @@ public: std::unique_ptr CreateTexture(const TextureConfig& config) override; std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; + std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) override; std::unique_ptr CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) override; @@ -68,6 +72,11 @@ public: void RestoreAPIState() override; void SetPipeline(const AbstractPipeline* pipeline) override; + void SetFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) override; + void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value = {}, + float depth_value = 0.0f) override; void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; @@ -99,6 +108,7 @@ private: void OnSwapChainResized(); void BindEFBToStateTracker(); void RecreateEFBFramebuffer(); + void BindFramebuffer(const VKFramebuffer* fb); void RecompileShaders(); bool CompileShaders(); diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index d902c1ea97..e5be3e2c1e 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -703,7 +703,8 @@ void StateTracker::EndRenderPass() m_current_render_pass = VK_NULL_HANDLE; } -void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue clear_values[2]) +void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue* clear_values, + u32 num_clear_values) { _assert_(!InRenderPass()); @@ -715,7 +716,7 @@ void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue m_current_render_pass, m_framebuffer, m_framebuffer_render_area, - 2, + num_clear_values, clear_values}; vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info, diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index 06f9f3e94f..92771504a9 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -45,6 +45,7 @@ public: { return m_bindings.ps_samplers; } + VkFramebuffer GetFramebuffer() const { return m_framebuffer; } void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset); void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type); @@ -90,7 +91,8 @@ public: void EndRenderPass(); // Ends the current render pass if it was a clear render pass. - void BeginClearRenderPass(const VkRect2D& area, const VkClearValue clear_values[2]); + void BeginClearRenderPass(const VkRect2D& area, const VkClearValue* clear_values, + u32 num_clear_values); void EndClearRenderPass(); void SetViewport(const VkViewport& viewport); diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index f51a9150de..a812e87602 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -566,4 +566,118 @@ void VKStagingTexture::Flush() m_staging_buffer->InvalidateCPUCache(); } +VKFramebuffer::VKFramebuffer(const VKTexture* color_attachment, const VKTexture* depth_attachment, + u32 width, u32 height, u32 layers, u32 samples, VkFramebuffer fb, + VkRenderPass load_render_pass, VkRenderPass discard_render_pass, + VkRenderPass clear_render_pass) + : AbstractFramebuffer( + color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined, + depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined, + width, height, layers, samples), + m_color_attachment(color_attachment), m_depth_attachment(depth_attachment), m_fb(fb), + m_load_render_pass(load_render_pass), m_discard_render_pass(discard_render_pass), + m_clear_render_pass(clear_render_pass) +{ +} + +VKFramebuffer::~VKFramebuffer() +{ + g_command_buffer_mgr->DeferFramebufferDestruction(m_fb); +} + +std::unique_ptr VKFramebuffer::Create(const VKTexture* color_attachment, + const VKTexture* depth_attachment) +{ + if (!ValidateConfig(color_attachment, depth_attachment)) + return nullptr; + + const VkFormat vk_color_format = + color_attachment ? color_attachment->GetRawTexIdentifier()->GetFormat() : VK_FORMAT_UNDEFINED; + const VkFormat vk_depth_format = + depth_attachment ? depth_attachment->GetRawTexIdentifier()->GetFormat() : VK_FORMAT_UNDEFINED; + const VKTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; + const u32 width = either_attachment->GetWidth(); + const u32 height = either_attachment->GetHeight(); + const u32 layers = either_attachment->GetLayers(); + const u32 samples = either_attachment->GetSamples(); + + std::array attachment_views{}; + u32 num_attachments = 0; + + if (color_attachment) + attachment_views[num_attachments++] = color_attachment->GetRawTexIdentifier()->GetView(); + + if (depth_attachment) + attachment_views[num_attachments++] = depth_attachment->GetRawTexIdentifier()->GetView(); + + VkRenderPass load_render_pass = g_object_cache->GetRenderPass( + vk_color_format, vk_depth_format, samples, VK_ATTACHMENT_LOAD_OP_LOAD); + VkRenderPass discard_render_pass = g_object_cache->GetRenderPass( + vk_color_format, vk_depth_format, samples, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + VkRenderPass clear_render_pass = g_object_cache->GetRenderPass( + vk_color_format, vk_depth_format, samples, VK_ATTACHMENT_LOAD_OP_CLEAR); + if (load_render_pass == VK_NULL_HANDLE || discard_render_pass == VK_NULL_HANDLE || + clear_render_pass == VK_NULL_HANDLE) + { + return nullptr; + } + + VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + nullptr, + 0, + load_render_pass, + num_attachments, + attachment_views.data(), + width, + height, + layers}; + + VkFramebuffer fb; + VkResult res = + vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, &fb); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); + return nullptr; + } + + return std::make_unique(color_attachment, depth_attachment, width, height, layers, + samples, fb, load_render_pass, discard_render_pass, + clear_render_pass); +} + +void VKFramebuffer::TransitionForRender() const +{ + if (m_color_attachment) + { + m_color_attachment->GetRawTexIdentifier()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } + + if (m_depth_attachment) + { + m_depth_attachment->GetRawTexIdentifier()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + } +} + +void VKFramebuffer::TransitionForSample() const +{ + if (StateTracker::GetInstance()->GetFramebuffer() == m_fb) + StateTracker::GetInstance()->EndRenderPass(); + + if (m_color_attachment) + { + m_color_attachment->GetRawTexIdentifier()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + if (m_depth_attachment) + { + m_depth_attachment->GetRawTexIdentifier()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } +} + } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 21531e50f4..3a5c8cadc8 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -7,6 +7,7 @@ #include #include +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" @@ -80,4 +81,32 @@ private: VkFence m_flush_fence = VK_NULL_HANDLE; }; +class VKFramebuffer final : public AbstractFramebuffer +{ +public: + VKFramebuffer(const VKTexture* color_attachment, const VKTexture* depth_attachment, u32 width, + u32 height, u32 layers, u32 samples, VkFramebuffer fb, + VkRenderPass load_render_pass, VkRenderPass discard_render_pass, + VkRenderPass clear_render_pass); + ~VKFramebuffer() override; + + VkFramebuffer GetFB() const { return m_fb; } + VkRenderPass GetLoadRenderPass() const { return m_load_render_pass; } + VkRenderPass GetDiscardRenderPass() const { return m_discard_render_pass; } + VkRenderPass GetClearRenderPass() const { return m_clear_render_pass; } + void TransitionForRender() const; + void TransitionForSample() const; + + static std::unique_ptr Create(const VKTexture* color_attachments, + const VKTexture* depth_attachment); + +protected: + const VKTexture* m_color_attachment; + const VKTexture* m_depth_attachment; + VkFramebuffer m_fb; + VkRenderPass m_load_render_pass; + VkRenderPass m_discard_render_pass; + VkRenderPass m_clear_render_pass; +}; + } // namespace Vulkan diff --git a/Source/Core/VideoCommon/AbstractFramebuffer.cpp b/Source/Core/VideoCommon/AbstractFramebuffer.cpp new file mode 100644 index 0000000000..8a1adb7018 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractFramebuffer.cpp @@ -0,0 +1,55 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractTexture.h" + +AbstractFramebuffer::AbstractFramebuffer(AbstractTextureFormat color_format, + AbstractTextureFormat depth_format, u32 width, u32 height, + u32 layers, u32 samples) + : m_color_format(color_format), m_depth_format(depth_format), m_width(width), m_height(height), + m_layers(layers), m_samples(samples) +{ +} + +AbstractFramebuffer::~AbstractFramebuffer() = default; + +bool AbstractFramebuffer::ValidateConfig(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) +{ + // Must have at least a color or depth attachment. + if (!color_attachment && !depth_attachment) + return false; + + // Currently we only expose a single mip level for render target textures. + // MSAA textures are not supported with mip levels on most backends, and it simplifies our + // handling of framebuffers. + auto CheckAttachment = [](const AbstractTexture* tex) { + return tex->GetConfig().rendertarget && tex->GetConfig().levels == 1; + }; + if ((color_attachment && !CheckAttachment(color_attachment)) || + depth_attachment && !CheckAttachment(depth_attachment)) + { + return false; + } + + // If both color and depth are present, their attributes must match. + if (color_attachment && depth_attachment) + { + if (color_attachment->GetConfig().width != depth_attachment->GetConfig().width || + color_attachment->GetConfig().height != depth_attachment->GetConfig().height || + color_attachment->GetConfig().layers != depth_attachment->GetConfig().layers || + color_attachment->GetConfig().samples != depth_attachment->GetConfig().samples) + { + return false; + } + } + + return true; +} + +MathUtil::Rectangle AbstractFramebuffer::GetRect() const +{ + return MathUtil::Rectangle(0, 0, static_cast(m_width), static_cast(m_height)); +} diff --git a/Source/Core/VideoCommon/AbstractFramebuffer.h b/Source/Core/VideoCommon/AbstractFramebuffer.h new file mode 100644 index 0000000000..9d4b2d29cd --- /dev/null +++ b/Source/Core/VideoCommon/AbstractFramebuffer.h @@ -0,0 +1,45 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/MathUtil.h" +#include "VideoCommon/TextureConfig.h" + +class AbstractTexture; + +// An abstract framebuffer wraps a backend framebuffer/view object, which can be used to +// draw onto a texture. Currently, only single-level textures are supported. Multi-layer +// textures will render by default only to the first layer, however, multiple layers +// be rendered in parallel using geometry shaders and layer variable. + +class AbstractFramebuffer +{ +public: + AbstractFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, + u32 width, u32 height, u32 layers, u32 samples); + virtual ~AbstractFramebuffer(); + + static bool ValidateConfig(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment); + + AbstractTextureFormat GetColorFormat() const { return m_color_format; } + AbstractTextureFormat GetDepthFormat() const { return m_depth_format; } + bool HasColorBuffer() const { return m_color_format != AbstractTextureFormat::Undefined; } + bool HasDepthBuffer() const { return m_depth_format != AbstractTextureFormat::Undefined; } + u32 GetWidth() const { return m_width; } + u32 GetHeight() const { return m_height; } + u32 GetLayers() const { return m_layers; } + u32 GetSamples() const { return m_samples; } + MathUtil::Rectangle GetRect() const; + +protected: + AbstractTextureFormat m_color_format; + AbstractTextureFormat m_depth_format; + u32 m_width; + u32 m_height; + u32 m_layers; + u32 m_samples; +}; diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 0f7602a0df..8a870de4bb 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -78,6 +78,11 @@ bool AbstractTexture::IsDepthFormat(AbstractTextureFormat format) } } +bool AbstractTexture::IsStencilFormat(AbstractTextureFormat format) +{ + return format == AbstractTextureFormat::D32F_S8; +} + size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length) { switch (format) diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index b7de94820d..b193e5459e 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -40,6 +40,7 @@ public: static bool IsCompressedFormat(AbstractTextureFormat format); static bool IsDepthFormat(AbstractTextureFormat format); + static bool IsStencilFormat(AbstractTextureFormat format); static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length); static size_t GetTexelSizeForFormat(AbstractTextureFormat format); diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index e6dd1f769d..42de08e9f1 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + AbstractFramebuffer.cpp AbstractStagingTexture.cpp AbstractTexture.cpp AsyncRequests.cpp diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index eea3c0e1db..8aea2f098d 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -43,6 +43,7 @@ #include "Core/Movie.h" #include "VideoCommon/AVIDump.h" +#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index a4bc818295..a51f1b2813 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -14,6 +14,7 @@ #pragma once +#include #include #include #include @@ -32,6 +33,7 @@ #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoCommon.h" +class AbstractFramebuffer; class AbstractPipeline; class AbstractShader; class AbstractTexture; @@ -63,6 +65,8 @@ public: Renderer(int backbuffer_width, int backbuffer_height); virtual ~Renderer(); + using ClearColor = std::array; + enum PixelPerfQuery { PP_ZCOMP_INPUT_ZCOMPLOC, @@ -95,6 +99,17 @@ public: virtual std::unique_ptr CreateTexture(const TextureConfig& config) = 0; virtual std::unique_ptr CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0; + virtual std::unique_ptr + CreateFramebuffer(const AbstractTexture* color_attachment, + const AbstractTexture* depth_attachment) = 0; + + // Framebuffer operations. + virtual void SetFramebuffer(const AbstractFramebuffer* framebuffer) {} + virtual void SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) {} + virtual void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, + const ClearColor& color_value = {}, float depth_value = 0.0f) + { + } // Shader modules/objects. virtual std::unique_ptr @@ -104,6 +119,9 @@ public: virtual std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config) = 0; + const AbstractFramebuffer* GetCurrentFramebuffer() const { return m_current_framebuffer; } + u32 GetCurrentFramebufferWidth() const { return m_current_framebuffer_width; } + u32 GetCurrentFramebufferHeight() const { return m_current_framebuffer_height; } // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } @@ -192,6 +210,11 @@ protected: void CheckFifoRecording(); void RecordVideoMemory(); + // TODO: Remove the width/height parameters once we make the EFB an abstract framebuffer. + const AbstractFramebuffer* m_current_framebuffer = nullptr; + u32 m_current_framebuffer_width = 1; + u32 m_current_framebuffer_height = 1; + Common::Flag m_screenshot_request; Common::Event m_screenshot_completed; std::mutex m_screenshot_lock; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index a90312f270..33931bdb17 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -36,6 +36,7 @@ + @@ -98,6 +99,7 @@ + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index 71e04d3bba..cc7d80508c 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -194,6 +194,9 @@ Base + + Base + @@ -263,9 +266,6 @@ Shader Generators - - Shader Generators - Shader Generators @@ -374,6 +374,10 @@ Base + + + Base +