From 887e3830bac95b0418201af337756564c89963d0 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 9 Feb 2018 21:41:31 +1000 Subject: [PATCH 1/6] VideoBackends: Restore the framebuffer as part of the API state It's not often we switch out to draw to the EFB anyway. --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 1 - Source/Core/VideoBackends/D3D/FramebufferManager.cpp | 2 -- Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp | 2 -- Source/Core/VideoBackends/D3D/Render.cpp | 9 ++------- Source/Core/VideoBackends/D3D/TextureCache.cpp | 2 -- Source/Core/VideoBackends/OGL/OGLTexture.cpp | 1 - Source/Core/VideoBackends/OGL/Render.cpp | 5 ++--- Source/Core/VideoBackends/OGL/TextureCache.cpp | 2 -- Source/Core/VideoBackends/OGL/TextureConverter.cpp | 3 --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 3 +++ 10 files changed, 7 insertions(+), 23 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 7be3264a74..ac21d3041c 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -144,7 +144,6 @@ void DXTexture::ScaleRectangleFromTexture(const AbstractTexture* source, VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader(), 1.0, 0); - FramebufferManager::BindEFBRenderTarget(); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp index 30fbde261e..e7e8fa9ddc 100644 --- a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp @@ -87,9 +87,7 @@ D3DTexture2D*& FramebufferManager::GetResolvedEFBDepthTexture() PixelShaderCache::GetDepthResolveProgram(), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader()); - BindEFBRenderTarget(); g_renderer->RestoreAPIState(); - return m_efb.resolved_depth_tex; } else diff --git a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp index b1ccefd8f0..e4d981bcc4 100644 --- a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp +++ b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp @@ -131,8 +131,6 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w } } - // Restore API - FramebufferManager::BindEFBRenderTarget(); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index f1751e44f2..5cc4832c56 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -450,7 +450,6 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) VertexShaderCache::GetSimpleInputLayout()); // Restore expected game state. - FramebufferManager::BindEFBRenderTarget(); RestoreAPIState(); // Copy the pixel from the renderable to cpu-readable buffer. @@ -525,7 +524,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetTargetWidth(), (float)GetTargetHeight()); D3D::context->RSSetViewports(1, &vp); - FramebufferManager::BindEFBRenderTarget(false); } else // if (type == EFBAccessType::PokeZ) { @@ -536,7 +534,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetTargetWidth(), (float)GetTargetHeight()); D3D::context->RSSetViewports(1, &vp); - FramebufferManager::BindEFBRenderTarget(); } D3D::DrawEFBPokeQuads(type, points, num_points); @@ -627,10 +624,8 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) GetTargetHeight(), pixel_shader, VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader()); - RestoreAPIState(); - FramebufferManager::SwapReinterpretTexture(); - FramebufferManager::BindEFBRenderTarget(); + RestoreAPIState(); } void Renderer::SetBlendingState(const BlendingState& state) @@ -707,7 +702,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // begin next frame RestoreAPIState(); - FramebufferManager::BindEFBRenderTarget(); } void Renderer::CheckForSurfaceChange() @@ -769,6 +763,7 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { // Gets us back into a more game-like state. + FramebufferManager::BindEFBRenderTarget(); BPFunctions::SetViewport(); BPFunctions::SetScissor(); } diff --git a/Source/Core/VideoBackends/D3D/TextureCache.cpp b/Source/Core/VideoBackends/D3D/TextureCache.cpp index e9ad781dcf..b0974f3cc3 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/TextureCache.cpp @@ -160,7 +160,6 @@ void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader()); - FramebufferManager::BindEFBRenderTarget(); g_renderer->RestoreAPIState(); } @@ -275,7 +274,6 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, pixel_shader, VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader()); - FramebufferManager::BindEFBRenderTarget(); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 680e1aceae..db0ffd8e5a 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -219,7 +219,6 @@ void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source, glUniform4f(TextureCache::GetInstance()->GetColorCopyPositionUniform(), float(srcrect.left), float(srcrect.top), float(srcrect.GetWidth()), float(srcrect.GetHeight())); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - FramebufferManager::SetFramebuffer(0); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 99666ed531..fab642e71b 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1416,9 +1416,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_texture_cache->Cleanup(frameCount); ProgramShaderCache::RetrieveAsyncShaders(); - // Render to the framebuffer. - FramebufferManager::SetFramebuffer(0); - RestoreAPIState(); g_Config.iSaveTargetId = 0; @@ -1499,6 +1496,8 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { + FramebufferManager::SetFramebuffer(0); + // Gets us back into a more game-like state. glEnable(GL_SCISSOR_TEST); if (g_ActiveConfig.backend_info.bSupportsDepthClamp) diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index 65324c608b..bd2e1cfcd1 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -323,7 +323,6 @@ void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, ProgramShaderCache::BindVertexFormat(nullptr); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - FramebufferManager::SetFramebuffer(0); g_renderer->RestoreAPIState(); } @@ -540,7 +539,6 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, ProgramShaderCache::BindVertexFormat(nullptr); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - FramebufferManager::SetFramebuffer(0); g_renderer->RestoreAPIState(); } } diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp index e7fa050e3f..a1b12db772 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp +++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp @@ -133,8 +133,6 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line s_encoding_readback_texture->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0, copy_rect); s_encoding_readback_texture->ReadTexels(copy_rect, destAddr, writeStride); - - FramebufferManager::SetFramebuffer(0); } void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 native_width, @@ -157,7 +155,6 @@ void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 nativ EncodeToRamUsingShader(read_texture, dest_ptr, bytes_per_row, num_blocks_y, memory_stride, scale_by_half && !params.depth, params.y_scale); - FramebufferManager::SetFramebuffer(0); g_renderer->RestoreAPIState(); } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index e4a7401c54..12de136162 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -1036,6 +1036,9 @@ void Renderer::ResetAPIState() void Renderer::RestoreAPIState() { + StateTracker::GetInstance()->EndRenderPass(); + BindEFBToStateTracker(); + // Instruct the state tracker to re-bind everything before the next draw StateTracker::GetInstance()->SetPendingRebind(); } From e125eaa237f2844d54474c3b3e6e1bdf4bbc1149 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 21 Jan 2018 14:14:24 +1000 Subject: [PATCH 2/6] VideoCommon: Drop references to AbstractRawTexture --- Source/Core/VideoCommon/RenderBase.h | 1 - Source/Core/VideoCommon/VideoCommon.vcxproj.filters | 6 ------ 2 files changed, 7 deletions(-) diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 3ec537e831..a4bc818295 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -32,7 +32,6 @@ #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoCommon.h" -class AbstractRawTexture; class AbstractPipeline; class AbstractShader; class AbstractTexture; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index 2dafb43246..71e04d3bba 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -176,9 +176,6 @@ Base - - Base - Shader Generators @@ -356,9 +353,6 @@ Base - - Base - Util From 4316f5f56b9f3fdba89a52224a058b0fcd8b8c55 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 22 Jan 2018 00:28:34 +1000 Subject: [PATCH 3/6] AbstractTexture: Add property/attribute accessor helpers --- Source/Core/VideoCommon/AbstractTexture.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index faf6076156..f28055f212 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -27,6 +27,13 @@ public: virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) = 0; + u32 GetWidth() const { return m_config.width; } + u32 GetHeight() const { return m_config.height; } + u32 GetLevels() const { return m_config.levels; } + u32 GetLayers() const { return m_config.layers; } + u32 GetSamples() const { return m_config.samples; } + AbstractTextureFormat GetFormat() const { return m_config.format; } + bool IsMultisampled() const { return m_config.IsMultisampled(); } bool Save(const std::string& filename, unsigned int level); static bool IsCompressedFormat(AbstractTextureFormat format); From 6374a4c4a80e88eea2e5914fb84e393c91ec65af Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 21 Jan 2018 15:03:06 +1000 Subject: [PATCH 4/6] AbstractTexture: Support multisampled abstract texture --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 57 ++++--- Source/Core/VideoBackends/D3D/DXTexture.h | 2 + .../VideoBackends/D3D/PSTextureEncoder.cpp | 2 +- .../Core/VideoBackends/Null/NullTexture.cpp | 4 + Source/Core/VideoBackends/Null/NullTexture.h | 2 + Source/Core/VideoBackends/OGL/OGLTexture.cpp | 139 +++++++++++------- Source/Core/VideoBackends/OGL/OGLTexture.h | 6 + .../VideoBackends/OGL/TextureConverter.cpp | 2 +- .../Core/VideoBackends/Software/SWTexture.cpp | 4 + .../Core/VideoBackends/Software/SWTexture.h | 2 + .../Vulkan/FramebufferManager.cpp | 4 +- .../VideoBackends/Vulkan/TextureConverter.cpp | 2 +- .../Core/VideoBackends/Vulkan/VKTexture.cpp | 49 +++++- Source/Core/VideoBackends/Vulkan/VKTexture.h | 2 + Source/Core/VideoCommon/AbstractTexture.cpp | 2 +- Source/Core/VideoCommon/AbstractTexture.h | 2 + Source/Core/VideoCommon/RenderBase.cpp | 2 +- Source/Core/VideoCommon/TextureConfig.cpp | 9 +- Source/Core/VideoCommon/TextureConfig.h | 8 +- 19 files changed, 209 insertions(+), 91 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index ac21d3041c..0431155549 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -52,33 +52,28 @@ DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format) DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) { - DXGI_FORMAT dxgi_format = GetDXGIFormatForHostFormat(m_config.format); - if (m_config.rendertarget) - { - m_texture = D3DTexture2D::Create( - m_config.width, m_config.height, - (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE), - D3D11_USAGE_DEFAULT, dxgi_format, 1, m_config.layers); - } - else - { - const D3D11_TEXTURE2D_DESC texdesc = - CD3D11_TEXTURE2D_DESC(dxgi_format, m_config.width, m_config.height, 1, m_config.levels, - D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0); + DXGI_FORMAT tex_format = GetDXGIFormatForHostFormat(m_config.format); + UINT bind_flags = D3D11_BIND_SHADER_RESOURCE; + if (tex_config.rendertarget) + bind_flags |= D3D11_BIND_RENDER_TARGET; - ID3D11Texture2D* pTexture; - const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); - CHECK(SUCCEEDED(hr), "Create texture of the TextureCache"); + CD3D11_TEXTURE2D_DESC texdesc(tex_format, tex_config.width, tex_config.height, tex_config.layers, + tex_config.levels, bind_flags, D3D11_USAGE_DEFAULT, 0, + tex_config.samples, 0, 0); - m_texture = new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE); + ID3D11Texture2D* pTexture; + HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); + CHECK(SUCCEEDED(hr), "Create backing DXTexture"); - // TODO: better debug names - D3D::SetDebugObjectName(m_texture->GetTex(), "a texture of the TextureCache"); - D3D::SetDebugObjectName(m_texture->GetSRV(), - "shader resource view of a texture of the TextureCache"); + m_texture = new D3DTexture2D( + pTexture, static_cast(bind_flags), tex_format, DXGI_FORMAT_UNKNOWN, + tex_config.rendertarget ? tex_format : DXGI_FORMAT_UNKNOWN, tex_config.samples > 1); - SAFE_RELEASE(pTexture); - } + D3D::SetDebugObjectName(m_texture->GetTex(), "a texture of the TextureCache"); + D3D::SetDebugObjectName(m_texture->GetSRV(), + "shader resource view of a texture of the TextureCache"); + + SAFE_RELEASE(pTexture); } DXTexture::~DXTexture() @@ -147,6 +142,22 @@ void DXTexture::ScaleRectangleFromTexture(const AbstractTexture* source, g_renderer->RestoreAPIState(); } +void DXTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) +{ + const DXTexture* srcentry = static_cast(src); + _dbg_assert_(VIDEO, m_config.samples > 1 && m_config.width == srcentry->m_config.width && + m_config.height == srcentry->m_config.height && m_config.samples == 1); + _dbg_assert_(VIDEO, + rect.left + rect.GetWidth() <= static_cast(srcentry->m_config.width) && + rect.top + rect.GetHeight() <= static_cast(srcentry->m_config.height)); + + D3D::context->ResolveSubresource( + m_texture->GetTex(), D3D11CalcSubresource(level, layer, m_config.levels), + srcentry->m_texture->GetTex(), D3D11CalcSubresource(level, layer, srcentry->m_config.levels), + GetDXGIFormatForHostFormat(m_config.format)); +} + void DXTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) { diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index fe8c71dfb1..0810471cad 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -26,6 +26,8 @@ public: void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) override; + void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp index e4d981bcc4..ad13894331 100644 --- a/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp +++ b/Source/Core/VideoBackends/D3D/PSTextureEncoder.cpp @@ -43,7 +43,7 @@ PSTextureEncoder::~PSTextureEncoder() = default; void PSTextureEncoder::Init() { // TODO: Move this to a constant somewhere in common. - TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, AbstractTextureFormat::BGRA8, + TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8, true); m_encoding_render_texture = g_renderer->CreateTexture(encoding_texture_config); m_encoding_readback_texture = diff --git a/Source/Core/VideoBackends/Null/NullTexture.cpp b/Source/Core/VideoBackends/Null/NullTexture.cpp index 1bf3ae1470..55abc0521c 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.cpp +++ b/Source/Core/VideoBackends/Null/NullTexture.cpp @@ -21,6 +21,10 @@ void NullTexture::ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& dstrect) { } +void NullTexture::ResolveFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& rect, u32 layer, u32 level) +{ +} void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) diff --git a/Source/Core/VideoBackends/Null/NullTexture.h b/Source/Core/VideoBackends/Null/NullTexture.h index b1cc0273ad..c80685c1e9 100644 --- a/Source/Core/VideoBackends/Null/NullTexture.h +++ b/Source/Core/VideoBackends/Null/NullTexture.h @@ -26,6 +26,8 @@ public: void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) override; + void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; }; diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index db0ffd8e5a..b42852d499 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -80,38 +80,50 @@ bool UsePersistentStagingBuffers() OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) { + _dbg_assert_msg_(VIDEO, !tex_config.IsMultisampled() || tex_config.levels == 1, + "OpenGL does not support multisampled textures with mip levels"); + + GLenum target = + tex_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY; glGenTextures(1, &m_texId); - glActiveTexture(GL_TEXTURE9); - glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId); + glBindTexture(target, m_texId); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, m_config.levels - 1); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, m_config.levels - 1); - if (g_ogl_config.bSupportsTextureStorage) + GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, true); + if (tex_config.IsMultisampled()) { - GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, true); - glTexStorage3D(GL_TEXTURE_2D_ARRAY, m_config.levels, gl_internal_format, m_config.width, - m_config.height, m_config.layers); + if (g_ogl_config.bSupportsTextureStorage) + glTexStorage3DMultisample(target, tex_config.samples, gl_internal_format, m_config.width, + m_config.height, m_config.layers, GL_FALSE); + else + glTexImage3DMultisample(target, tex_config.samples, gl_internal_format, m_config.width, + m_config.height, m_config.layers, GL_FALSE); + } + else if (g_ogl_config.bSupportsTextureStorage) + { + glTexStorage3D(target, m_config.levels, gl_internal_format, m_config.width, m_config.height, + m_config.layers); } if (m_config.rendertarget) { // We can't render to compressed formats. _assert_(!IsCompressedFormat(m_config.format)); - - if (!g_ogl_config.bSupportsTextureStorage) + if (!g_ogl_config.bSupportsTextureStorage && !tex_config.IsMultisampled()) { for (u32 level = 0; level < m_config.levels; level++) { - glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, std::max(m_config.width >> level, 1u), + glTexImage3D(target, level, GL_RGBA, std::max(m_config.width >> level, 1u), std::max(m_config.height >> level, 1u), m_config.layers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } } glGenFramebuffers(1, &m_framebuffer); FramebufferManager::SetFramebuffer(m_framebuffer); - FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_ARRAY, m_texId, 0); + FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, m_texId, + 0); // We broke the framebuffer binding here, and need to restore it, as the CreateTexture // method is in the base renderer class and can be called by VideoCommon. @@ -156,47 +168,60 @@ void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src, } else { - // If it isn't a single leveled/layered texture, we need to update the framebuffer. - bool update_src_framebuffer = - srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0; - bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0; - if (!m_framebuffer) - glGenFramebuffers(1, &m_framebuffer); - if (!srcentry->m_framebuffer) - glGenFramebuffers(1, &const_cast(srcentry)->m_framebuffer); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer); - if (update_src_framebuffer) - { - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, - src_level, src_layer); - } - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer); - if (update_dst_framebuffer) - { - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level, - dst_layer); - } - - glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, - dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, - GL_NEAREST); - - if (update_src_framebuffer) - { - FramebufferManager::FramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_ARRAY, srcentry->m_texId, 0); - } - if (update_dst_framebuffer) - { - FramebufferManager::FramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_ARRAY, m_texId, 0); - } - - FramebufferManager::SetFramebuffer(0); + BlitFramebuffer(const_cast(srcentry), src_rect, src_layer, src_level, dst_rect, + dst_layer, dst_level); } } + +void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, + const MathUtil::Rectangle& dst_rect, u32 dst_layer, + u32 dst_level) +{ + // If it isn't a single leveled/layered texture, we need to update the framebuffer. + bool update_src_framebuffer = + srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0; + bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0; + if (!m_framebuffer) + glGenFramebuffers(1, &m_framebuffer); + if (!srcentry->m_framebuffer) + glGenFramebuffers(1, &const_cast(srcentry)->m_framebuffer); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer); + if (update_src_framebuffer) + { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, + src_level, src_layer); + } + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer); + if (update_dst_framebuffer) + { + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level, + dst_layer); + } + + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, + dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (update_src_framebuffer) + { + FramebufferManager::FramebufferTexture( + GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + srcentry->m_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY, + srcentry->m_texId, 0); + } + if (update_dst_framebuffer) + { + FramebufferManager::FramebufferTexture( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + m_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY, m_texId, + 0); + } + + FramebufferManager::SetFramebuffer(0); +} + void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) @@ -222,6 +247,18 @@ void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source, g_renderer->RestoreAPIState(); } +void OGLTexture::ResolveFromTexture(const AbstractTexture* src, + const MathUtil::Rectangle& rect, u32 layer, u32 level) +{ + const OGLTexture* srcentry = static_cast(src); + _dbg_assert_(VIDEO, m_config.samples > 1 && m_config.width == srcentry->m_config.width && + m_config.height == srcentry->m_config.height && m_config.samples == 1); + _dbg_assert_(VIDEO, + rect.left + rect.GetWidth() <= static_cast(srcentry->m_config.width) && + rect.top + rect.GetHeight() <= static_cast(srcentry->m_config.height)); + BlitFramebuffer(const_cast(srcentry), rect, layer, level, rect, layer, level); +} + void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) { diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.h b/Source/Core/VideoBackends/OGL/OGLTexture.h index 8991791393..555d1968d8 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.h +++ b/Source/Core/VideoBackends/OGL/OGLTexture.h @@ -26,6 +26,8 @@ public: void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) override; + void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; @@ -33,6 +35,10 @@ public: GLuint GetFramebuffer() const; private: + void BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle& src_rect, + u32 src_layer, u32 src_level, const MathUtil::Rectangle& dst_rect, + u32 dst_layer, u32 dst_level); + GLuint m_texId; GLuint m_framebuffer = 0; }; diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp index a1b12db772..e1ec911a65 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp +++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp @@ -86,7 +86,7 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params) void Init() { - TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, AbstractTextureFormat::BGRA8, + TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, 1, AbstractTextureFormat::BGRA8, true); s_encoding_render_texture = g_renderer->CreateTexture(config); s_encoding_readback_texture = diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index 5fbd816bad..967474916f 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -86,6 +86,10 @@ void SWTexture::ScaleRectangleFromTexture(const AbstractTexture* source, memcpy(GetData(), destination_pixels.data(), destination_pixels.size()); } } +void SWTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) +{ +} void SWTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) diff --git a/Source/Core/VideoBackends/Software/SWTexture.h b/Source/Core/VideoBackends/Software/SWTexture.h index 885ee28ca6..26c869da62 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.h +++ b/Source/Core/VideoBackends/Software/SWTexture.h @@ -26,6 +26,8 @@ public: void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) override; + void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 9c2c712837..27a1fd30f8 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -846,8 +846,8 @@ bool FramebufferManager::CreateReadbackTextures() return false; } - TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8, - false); + TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, + AbstractTextureFormat::RGBA8, false); m_color_readback_texture = g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config); m_depth_readback_texture = diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index 80efbf3d45..74e7e12baf 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -596,7 +596,7 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params) bool TextureConverter::CreateEncodingTexture() { - TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, + TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, 1, ENCODING_TEXTURE_FORMAT, true); m_encoding_render_texture = g_renderer->CreateTexture(config); diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 4a4f70c2cb..974a8578a1 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -42,9 +42,10 @@ std::unique_ptr VKTexture::Create(const TextureConfig& tex_config) // Allocate texture object VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(tex_config.format); - auto texture = Texture2D::Create(tex_config.width, tex_config.height, tex_config.levels, - tex_config.layers, vk_format, VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); + auto texture = + Texture2D::Create(tex_config.width, tex_config.height, tex_config.levels, tex_config.layers, + vk_format, static_cast(tex_config.samples), + VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); if (!texture) { @@ -56,8 +57,9 @@ std::unique_ptr VKTexture::Create(const TextureConfig& tex_config) if (tex_config.rendertarget) { VkImageView framebuffer_attachments[] = {texture->GetView()}; - VkRenderPass render_pass = g_object_cache->GetRenderPass( - texture->GetFormat(), VK_FORMAT_UNDEFINED, 1, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + VkRenderPass render_pass = + g_object_cache->GetRenderPass(texture->GetFormat(), VK_FORMAT_UNDEFINED, tex_config.samples, + VK_ATTACHMENT_LOAD_OP_DONT_CARE); VkFramebufferCreateInfo framebuffer_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, @@ -195,6 +197,43 @@ void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } +void VKTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) +{ + const VKTexture* srcentry = static_cast(src); + _dbg_assert_(VIDEO, m_config.samples == 1 && m_config.width == srcentry->m_config.width && + m_config.height == srcentry->m_config.height && + srcentry->m_config.samples > 1); + _dbg_assert_(VIDEO, + rect.left + rect.GetWidth() <= static_cast(srcentry->m_config.width) && + rect.top + rect.GetHeight() <= static_cast(srcentry->m_config.height)); + + // Resolving is considered to be a transfer operation. + StateTracker::GetInstance()->EndRenderPass(); + VkImageLayout old_src_layout = srcentry->m_texture->GetLayout(); + srcentry->m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkImageResolve resolve = { + {VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1}, // srcSubresource + {rect.left, rect.top, 0}, // srcOffset + {VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1}, // dstSubresource + {rect.left, rect.top, 0}, // dstOffset + {static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), 1} // extent + }; + vkCmdResolveImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), + srcentry->m_texture->GetImage(), srcentry->m_texture->GetLayout(), + m_texture->GetImage(), m_texture->GetLayout(), 1, &resolve); + + // Restore old source texture layout. Destination is assumed to be bound as a shader resource. + srcentry->m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + old_src_layout); + m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) { diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index 407d950149..21531e50f4 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -28,6 +28,8 @@ public: void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& src_rect, const MathUtil::Rectangle& dst_rect) override; + void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) override; void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) override; diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index e4a42ad62b..98872f5c59 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -29,7 +29,7 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level) // Use a temporary staging texture for the download. Certainly not optimal, // but this is not a frequently-executed code path.. - TextureConfig readback_texture_config(level_width, level_height, 1, 1, + TextureConfig readback_texture_config(level_width, level_height, 1, 1, 1, AbstractTextureFormat::RGBA8, false); auto readback_texture = g_renderer->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config); diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index f28055f212..01b56faa18 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -24,6 +24,8 @@ public: virtual void ScaleRectangleFromTexture(const AbstractTexture* source, const MathUtil::Rectangle& srcrect, const MathUtil::Rectangle& dstrect) = 0; + virtual void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle& rect, + u32 layer, u32 level) = 0; virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) = 0; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 1f0da14269..eea3c0e1db 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -739,7 +739,7 @@ void Renderer::RenderFrameDump() m_frame_dump_render_texture->GetConfig().height == static_cast(target_height)) { // Recreate texture objects. Release before creating so we don't temporarily use twice the RAM. - TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true); + TextureConfig config(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, true); m_frame_dump_render_texture.reset(); m_frame_dump_render_texture = CreateTexture(config); _assert_(m_frame_dump_render_texture); diff --git a/Source/Core/VideoCommon/TextureConfig.cpp b/Source/Core/VideoCommon/TextureConfig.cpp index d8155d59b1..0407576af6 100644 --- a/Source/Core/VideoCommon/TextureConfig.cpp +++ b/Source/Core/VideoCommon/TextureConfig.cpp @@ -9,8 +9,8 @@ bool TextureConfig::operator==(const TextureConfig& o) const { - return std::tie(width, height, levels, layers, format, rendertarget) == - std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); + return std::tie(width, height, levels, layers, samples, format, rendertarget) == + std::tie(o.width, o.height, o.levels, o.layers, o.samples, o.format, o.rendertarget); } bool TextureConfig::operator!=(const TextureConfig& o) const @@ -38,3 +38,8 @@ size_t TextureConfig::GetMipStride(u32 level) const { return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u)); } + +bool TextureConfig::IsMultisampled() const +{ + return samples > 1; +} diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h index a212c7f86a..3d24bf3b93 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -31,10 +31,10 @@ enum class StagingTextureType struct TextureConfig { constexpr TextureConfig() = default; - constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_, + constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_, u32 samples_, AbstractTextureFormat format_, bool rendertarget_) - : width(width_), height(height_), levels(levels_), layers(layers_), format(format_), - rendertarget(rendertarget_) + : width(width_), height(height_), levels(levels_), layers(layers_), samples(samples_), + format(format_), rendertarget(rendertarget_) { } @@ -44,11 +44,13 @@ struct TextureConfig MathUtil::Rectangle GetMipRect(u32 level) const; size_t GetStride() const; size_t GetMipStride(u32 level) const; + bool IsMultisampled() const; u32 width = 0; u32 height = 0; u32 levels = 1; u32 layers = 1; + u32 samples = 1; AbstractTextureFormat format = AbstractTextureFormat::RGBA8; bool rendertarget = false; }; From 2a6d9e4713a9a540684795cb10ee6e6462178ba1 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 21 Jan 2018 23:59:19 +1000 Subject: [PATCH 5/6] AbstractTexture: Add support for depth textures/formats --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 63 ++++++++++++++++--- Source/Core/VideoBackends/OGL/OGLTexture.cpp | 28 +++++++++ Source/Core/VideoBackends/Vulkan/Util.cpp | 15 +++++ .../Core/VideoBackends/Vulkan/VKTexture.cpp | 31 ++++++--- Source/Core/VideoCommon/AbstractTexture.cpp | 28 +++++++++ Source/Core/VideoCommon/AbstractTexture.h | 1 + Source/Core/VideoCommon/TextureConfig.h | 5 ++ 7 files changed, 155 insertions(+), 16 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/DXTexture.cpp b/Source/Core/VideoBackends/D3D/DXTexture.cpp index 0431155549..7121dcf0b7 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.cpp +++ b/Source/Core/VideoBackends/D3D/DXTexture.cpp @@ -43,19 +43,71 @@ DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format) return DXGI_FORMAT_R8G8B8A8_UNORM; case AbstractTextureFormat::BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; + case AbstractTextureFormat::R16: + return DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::R32F: + return DXGI_FORMAT_R32_FLOAT; + case AbstractTextureFormat::D16: + return DXGI_FORMAT_R16_TYPELESS; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_R32_TYPELESS; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; default: PanicAlert("Unhandled texture format."); return DXGI_FORMAT_R8G8B8A8_UNORM; } } +DXGI_FORMAT GetSRVFormatForHostFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::D16: + return DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_R32_FLOAT; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + default: + return GetDXGIFormatForHostFormat(format); + } +} +DXGI_FORMAT GetDSVFormatForHostFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::D16: + return DXGI_FORMAT_D16_UNORM; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_D32_FLOAT; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; + default: + return GetDXGIFormatForHostFormat(format); + } +} } // Anonymous namespace DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config) { DXGI_FORMAT tex_format = GetDXGIFormatForHostFormat(m_config.format); + DXGI_FORMAT srv_format = GetSRVFormatForHostFormat(m_config.format); + DXGI_FORMAT rtv_format = DXGI_FORMAT_UNKNOWN; + DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN; UINT bind_flags = D3D11_BIND_SHADER_RESOURCE; if (tex_config.rendertarget) - bind_flags |= D3D11_BIND_RENDER_TARGET; + { + if (IsDepthFormat(tex_config.format)) + { + bind_flags |= D3D11_BIND_DEPTH_STENCIL; + dsv_format = GetDSVFormatForHostFormat(m_config.format); + } + else + { + bind_flags |= D3D11_BIND_RENDER_TARGET; + rtv_format = tex_format; + } + } CD3D11_TEXTURE2D_DESC texdesc(tex_format, tex_config.width, tex_config.height, tex_config.layers, tex_config.levels, bind_flags, D3D11_USAGE_DEFAULT, 0, @@ -65,13 +117,8 @@ DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_conf HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); CHECK(SUCCEEDED(hr), "Create backing DXTexture"); - m_texture = new D3DTexture2D( - pTexture, static_cast(bind_flags), tex_format, DXGI_FORMAT_UNKNOWN, - tex_config.rendertarget ? tex_format : DXGI_FORMAT_UNKNOWN, tex_config.samples > 1); - - D3D::SetDebugObjectName(m_texture->GetTex(), "a texture of the TextureCache"); - D3D::SetDebugObjectName(m_texture->GetSRV(), - "shader resource view of a texture of the TextureCache"); + m_texture = new D3DTexture2D(pTexture, static_cast(bind_flags), srv_format, + dsv_format, rtv_format, tex_config.samples > 1); SAFE_RELEASE(pTexture); } diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index b42852d499..60b33886c6 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -35,6 +35,16 @@ GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool st return storage ? GL_RGBA8 : GL_RGBA; case AbstractTextureFormat::BGRA8: return storage ? GL_RGBA8 : GL_BGRA; + case AbstractTextureFormat::R16: + return GL_R16; + case AbstractTextureFormat::R32F: + return GL_R32F; + case AbstractTextureFormat::D16: + return GL_DEPTH_COMPONENT16; + case AbstractTextureFormat::D32F: + return GL_DEPTH_COMPONENT32F; + case AbstractTextureFormat::D32F_S8: + return GL_DEPTH32F_STENCIL8; default: PanicAlert("Unhandled texture format."); return storage ? GL_RGBA8 : GL_RGBA; @@ -49,6 +59,14 @@ GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format) return GL_RGBA; case AbstractTextureFormat::BGRA8: return GL_BGRA; + case AbstractTextureFormat::R16: + case AbstractTextureFormat::R32F: + return GL_RED; + case AbstractTextureFormat::D16: + case AbstractTextureFormat::D32F: + return GL_DEPTH_COMPONENT; + case AbstractTextureFormat::D32F_S8: + return GL_DEPTH_STENCIL; // Compressed texture formats don't use this parameter. default: return GL_UNSIGNED_BYTE; @@ -62,6 +80,16 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format) case AbstractTextureFormat::RGBA8: case AbstractTextureFormat::BGRA8: return GL_UNSIGNED_BYTE; + case AbstractTextureFormat::R16: + return GL_UNSIGNED_SHORT; + case AbstractTextureFormat::R32F: + return GL_FLOAT; + case AbstractTextureFormat::D16: + return GL_UNSIGNED_SHORT; + case AbstractTextureFormat::D32F: + return GL_FLOAT; + case AbstractTextureFormat::D32F_S8: + return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; // Compressed texture formats don't use this parameter. default: return GL_UNSIGNED_BYTE; diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 8fbdfbb72c..4977a270a3 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -112,6 +112,21 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format) case AbstractTextureFormat::BGRA8: return VK_FORMAT_B8G8R8A8_UNORM; + case AbstractTextureFormat::R16: + return VK_FORMAT_R16_UNORM; + + case AbstractTextureFormat::D16: + return VK_FORMAT_D16_UNORM; + + case AbstractTextureFormat::R32F: + return VK_FORMAT_R32_SFLOAT; + + case AbstractTextureFormat::D32F: + return VK_FORMAT_D32_SFLOAT; + + case AbstractTextureFormat::D32F_S8: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + default: PanicAlert("Unhandled texture format."); return VK_FORMAT_R8G8B8A8_UNORM; diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 974a8578a1..f51a9150de 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -79,14 +79,29 @@ std::unique_ptr VKTexture::Create(const TextureConfig& tex_config) return nullptr; } - // Clear render targets before use to prevent reading uninitialized memory. - VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; - VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, tex_config.levels, 0, - tex_config.layers}; - texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), - texture->GetLayout(), &clear_value, 1, &clear_range); + if (!IsDepthFormat(tex_config.format)) + { + // Clear render targets before use to prevent reading uninitialized memory. + VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; + VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, tex_config.levels, 0, + tex_config.layers}; + texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), + texture->GetLayout(), &clear_value, 1, &clear_range); + } + else + { + // Clear render targets before use to prevent reading uninitialized memory. + VkClearDepthStencilValue clear_value = {0.0f, 0}; + VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, tex_config.levels, 0, + tex_config.layers}; + texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdClearDepthStencilImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + texture->GetImage(), texture->GetLayout(), &clear_value, 1, + &clear_range); + } } return std::unique_ptr(new VKTexture(tex_config, std::move(texture), framebuffer)); diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index 98872f5c59..0f7602a0df 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -64,6 +64,20 @@ bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format) } } +bool AbstractTexture::IsDepthFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::D16: + case AbstractTextureFormat::D32F: + case AbstractTextureFormat::D32F_S8: + return true; + + default: + return false; + } +} + size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length) { switch (format) @@ -74,9 +88,16 @@ size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u case AbstractTextureFormat::DXT5: case AbstractTextureFormat::BPTC: return static_cast(std::max(1u, row_length / 4)) * 16; + case AbstractTextureFormat::R16: + case AbstractTextureFormat::D16: + return static_cast(row_length) * 2; case AbstractTextureFormat::RGBA8: case AbstractTextureFormat::BGRA8: + case AbstractTextureFormat::R32F: + case AbstractTextureFormat::D32F: return static_cast(row_length) * 4; + case AbstractTextureFormat::D32F_S8: + return static_cast(row_length) * 8; default: PanicAlert("Unhandled texture format."); return 0; @@ -93,9 +114,16 @@ size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format) case AbstractTextureFormat::DXT5: case AbstractTextureFormat::BPTC: return 16; + case AbstractTextureFormat::R16: + case AbstractTextureFormat::D16: + return 2; case AbstractTextureFormat::RGBA8: case AbstractTextureFormat::BGRA8: + case AbstractTextureFormat::R32F: + case AbstractTextureFormat::D32F: return 4; + case AbstractTextureFormat::D32F_S8: + return 8; default: PanicAlert("Unhandled texture format."); return 0; diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h index 01b56faa18..b7de94820d 100644 --- a/Source/Core/VideoCommon/AbstractTexture.h +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -39,6 +39,7 @@ public: bool Save(const std::string& filename, unsigned int level); static bool IsCompressedFormat(AbstractTextureFormat format); + static bool IsDepthFormat(AbstractTextureFormat format); static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length); static size_t GetTexelSizeForFormat(AbstractTextureFormat format); diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h index 3d24bf3b93..1aa27cca4c 100644 --- a/Source/Core/VideoCommon/TextureConfig.h +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -18,6 +18,11 @@ enum class AbstractTextureFormat : u32 DXT3, DXT5, BPTC, + R16, + D16, + R32F, + D32F, + D32F_S8, Undefined }; From 4c24a697106471b33973da619a43167c947276aa Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 21 Jan 2018 20:22:45 +1000 Subject: [PATCH 6/6] VideoCommon: Add support for Abstract Framebuffers --- Source/Core/VideoBackends/D3D/DXTexture.cpp | 84 +++++++++++++ Source/Core/VideoBackends/D3D/DXTexture.h | 21 ++++ Source/Core/VideoBackends/D3D/Render.cpp | 46 +++++++ Source/Core/VideoBackends/D3D/Render.h | 8 ++ .../Core/VideoBackends/Null/NullTexture.cpp | 27 +++++ Source/Core/VideoBackends/Null/NullTexture.h | 13 ++ Source/Core/VideoBackends/Null/Render.cpp | 8 ++ Source/Core/VideoBackends/Null/Render.h | 3 + Source/Core/VideoBackends/OGL/OGLTexture.cpp | 68 +++++++++++ Source/Core/VideoBackends/OGL/OGLTexture.h | 17 +++ Source/Core/VideoBackends/OGL/Render.cpp | 56 ++++++++- Source/Core/VideoBackends/OGL/Render.h | 8 ++ .../VideoBackends/Software/SWRenderer.cpp | 8 ++ .../Core/VideoBackends/Software/SWRenderer.h | 3 + .../Core/VideoBackends/Software/SWTexture.cpp | 27 +++++ .../Core/VideoBackends/Software/SWTexture.h | 13 ++ Source/Core/VideoBackends/Vulkan/Renderer.cpp | 86 ++++++++++++- Source/Core/VideoBackends/Vulkan/Renderer.h | 10 ++ .../VideoBackends/Vulkan/StateTracker.cpp | 5 +- .../Core/VideoBackends/Vulkan/StateTracker.h | 4 +- .../Core/VideoBackends/Vulkan/VKTexture.cpp | 114 ++++++++++++++++++ Source/Core/VideoBackends/Vulkan/VKTexture.h | 29 +++++ .../Core/VideoCommon/AbstractFramebuffer.cpp | 55 +++++++++ Source/Core/VideoCommon/AbstractFramebuffer.h | 45 +++++++ Source/Core/VideoCommon/AbstractTexture.cpp | 5 + Source/Core/VideoCommon/AbstractTexture.h | 1 + Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/RenderBase.cpp | 1 + Source/Core/VideoCommon/RenderBase.h | 23 ++++ Source/Core/VideoCommon/VideoCommon.vcxproj | 2 + .../VideoCommon/VideoCommon.vcxproj.filters | 10 +- 31 files changed, 792 insertions(+), 9 deletions(-) create mode 100644 Source/Core/VideoCommon/AbstractFramebuffer.cpp create mode 100644 Source/Core/VideoCommon/AbstractFramebuffer.h 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 +