From c1fd4a2013d90c65f1f12d83ffbb9720ef463376 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 24 Jul 2022 10:46:05 +1200 Subject: [PATCH 01/60] Move TCacheEntry out of TextureCacheBase Allows for fowards declaration --- Source/Core/VideoCommon/TextureCacheBase.cpp | 46 ++-- Source/Core/VideoCommon/TextureCacheBase.h | 223 +++++++++---------- 2 files changed, 134 insertions(+), 135 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 14cf207034..865868bd4c 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -62,13 +62,13 @@ static int xfb_count = 0; std::unique_ptr g_texture_cache; -TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr tex, +TCacheEntry::TCacheEntry(std::unique_ptr tex, std::unique_ptr fb) : texture(std::move(tex)), framebuffer(std::move(fb)) { } -TextureCacheBase::TCacheEntry::~TCacheEntry() +TCacheEntry::~TCacheEntry() { for (auto& reference : references) reference->references.erase(this); @@ -240,7 +240,7 @@ void TextureCacheBase::Cleanup(int _frameCount) } } -bool TextureCacheBase::TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const +bool TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const { if (addr + size_in_bytes <= range_address) return false; @@ -268,7 +268,7 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config) config.graphics_mod_config ? config.graphics_mod_config->GetChangeCount() : 0; } -TextureCacheBase::TCacheEntry* +TCacheEntry* TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt) { DEBUG_ASSERT(g_ActiveConfig.backend_info.bSupportsPaletteConversion); @@ -337,7 +337,7 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU return decoded_entry; } -TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entry, +TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entry, TextureFormat new_format) { const AbstractPipeline* pipeline = @@ -383,7 +383,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEn return reinterpreted_entry; } -void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* entry, u32 new_width, +void TextureCacheBase::ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height) { if (entry->GetWidth() == new_width && entry->GetHeight() == new_height) @@ -746,7 +746,7 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) } } -void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p) +void TCacheEntry::DoState(PointerWrap& p) { p.Do(addr); p.Do(size_in_bytes); @@ -770,7 +770,7 @@ void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p) p.Do(frameCount); } -TextureCacheBase::TCacheEntry* +TCacheEntry* TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette, TLUTFormat tlutfmt) { @@ -1224,7 +1224,7 @@ private: std::vector levels; }; -TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) +TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) { // if this stage was not invalidated by changes to texture registers, keep the current texture if (TMEM::IsValid(texture_info.GetStage()) && bound_textures[texture_info.GetStage()]) @@ -1272,7 +1272,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture return entry; } -TextureCacheBase::TCacheEntry* +TCacheEntry* TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, const TextureInfo& texture_info) { @@ -1729,7 +1729,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, return entry; } -static void GetDisplayRectForXFBEntry(TextureCacheBase::TCacheEntry* entry, u32 width, u32 height, +static void GetDisplayRectForXFBEntry(TCacheEntry* entry, u32 width, u32 height, MathUtil::Rectangle* display_rect) { // Scale the sub-rectangle to the full resolution of the texture. @@ -1739,7 +1739,7 @@ static void GetDisplayRectForXFBEntry(TextureCacheBase::TCacheEntry* entry, u32 display_rect->bottom = static_cast(height * entry->GetHeight() / entry->native_height); } -TextureCacheBase::TCacheEntry* +TCacheEntry* TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, MathUtil::Rectangle* display_rect) { @@ -1822,7 +1822,7 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, return entry; } -TextureCacheBase::TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height, +TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride) { auto iter_range = textures_by_address.equal_range(address); @@ -2555,7 +2555,7 @@ void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_ } } -TextureCacheBase::TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) +TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) { std::optional alloc = AllocateTexture(config); if (!alloc) @@ -2619,7 +2619,7 @@ TextureCacheBase::FindMatchingTextureFromPool(const TextureConfig& config) } TextureCacheBase::TexAddrCache::iterator -TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntry* entry) +TextureCacheBase::GetTexCacheIter(TCacheEntry* entry) { auto iter_range = textures_by_address.equal_range(entry->addr); TexAddrCache::iterator iter = iter_range.first; @@ -2967,7 +2967,7 @@ bool TextureCacheBase::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, con return true; } -u32 TextureCacheBase::TCacheEntry::BytesPerRow() const +u32 TCacheEntry::BytesPerRow() const { // RGBA takes two cache lines per block; all others take one const u32 bytes_per_block = format == TextureFormat::RGBA8 ? 64 : 32; @@ -2975,7 +2975,7 @@ u32 TextureCacheBase::TCacheEntry::BytesPerRow() const return NumBlocksX() * bytes_per_block; } -u32 TextureCacheBase::TCacheEntry::NumBlocksX() const +u32 TCacheEntry::NumBlocksX() const { const u32 blockW = TexDecoder_GetBlockWidthInTexels(format.texfmt); @@ -2985,7 +2985,7 @@ u32 TextureCacheBase::TCacheEntry::NumBlocksX() const return actualWidth / blockW; } -u32 TextureCacheBase::TCacheEntry::NumBlocksY() const +u32 TCacheEntry::NumBlocksY() const { u32 blockH = TexDecoder_GetBlockHeightInTexels(format.texfmt); // Round up source height to multiple of block size @@ -2994,7 +2994,7 @@ u32 TextureCacheBase::TCacheEntry::NumBlocksY() const return actualHeight / blockH; } -void TextureCacheBase::TCacheEntry::SetXfbCopy(u32 stride) +void TCacheEntry::SetXfbCopy(u32 stride) { is_efb_copy = false; is_xfb_copy = true; @@ -3006,7 +3006,7 @@ void TextureCacheBase::TCacheEntry::SetXfbCopy(u32 stride) size_in_bytes = memory_stride * NumBlocksY(); } -void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride) +void TCacheEntry::SetEfbCopy(u32 stride) { is_efb_copy = true; is_xfb_copy = false; @@ -3018,14 +3018,14 @@ void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride) size_in_bytes = memory_stride * NumBlocksY(); } -void TextureCacheBase::TCacheEntry::SetNotCopy() +void TCacheEntry::SetNotCopy() { is_efb_copy = false; is_xfb_copy = false; is_xfb_container = false; } -int TextureCacheBase::TCacheEntry::HashSampleSize() const +int TCacheEntry::HashSampleSize() const { if (should_force_safe_hashing) { @@ -3035,7 +3035,7 @@ int TextureCacheBase::TCacheEntry::HashSampleSize() const return g_ActiveConfig.iSafeTextureCache_ColorSamples; } -u64 TextureCacheBase::TCacheEntry::CalculateHash() const +u64 TCacheEntry::CalculateHash() const { const u32 bytes_per_row = BytesPerRow(); const u32 hash_sample_size = HashSampleSize(); diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 4fe11a64f4..cfcb4afe9e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -33,6 +33,8 @@ struct VideoConfig; constexpr std::string_view EFB_DUMP_PREFIX = "efb1"; constexpr std::string_view XFB_DUMP_PREFIX = "xfb1"; +static constexpr int FRAMECOUNT_INVALID = 0; + struct TextureAndTLUTFormat { TextureAndTLUTFormat(TextureFormat texfmt_ = TextureFormat::I4, @@ -103,121 +105,118 @@ struct fmt::formatter } }; +struct TCacheEntry +{ + // common members + std::unique_ptr texture; + std::unique_ptr framebuffer; + u32 addr = 0; + u32 size_in_bytes = 0; + u64 base_hash = 0; + u64 hash = 0; // for paletted textures, hash = base_hash ^ palette_hash + TextureAndTLUTFormat format; + u32 memory_stride = 0; + bool is_efb_copy = false; + bool is_custom_tex = false; + bool may_have_overlapping_textures = true; + bool tmem_only = false; // indicates that this texture only exists in the tmem cache + bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary + // content, aren't just downscaled + bool should_force_safe_hashing = false; // for XFB + bool is_xfb_copy = false; + bool is_xfb_container = false; + u64 id = 0; + + bool reference_changed = false; // used by xfb to determine when a reference xfb changed + + // Texture dimensions from the GameCube's point of view + u32 native_width = 0; + u32 native_height = 0; + u32 native_levels = 0; + + // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames + int frameCount = FRAMECOUNT_INVALID; + + // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when + // removing the cache entry + std::multimap::iterator textures_by_hash_iter; + + // This is used to keep track of both: + // * efb copies used by this partially updated texture + // * partially updated textures which refer to this efb copy + std::unordered_set references; + + // Pending EFB copy + std::unique_ptr pending_efb_copy; + u32 pending_efb_copy_width = 0; + u32 pending_efb_copy_height = 0; + bool pending_efb_copy_invalidated = false; + + std::string texture_info_name = ""; + + explicit TCacheEntry(std::unique_ptr tex, + std::unique_ptr fb); + + ~TCacheEntry(); + + void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format, + bool force_safe_hashing) + { + addr = _addr; + size_in_bytes = _size; + format = _format; + should_force_safe_hashing = force_safe_hashing; + } + + void SetDimensions(unsigned int _native_width, unsigned int _native_height, + unsigned int _native_levels) + { + native_width = _native_width; + native_height = _native_height; + native_levels = _native_levels; + memory_stride = _native_width; + } + + void SetHashes(u64 _base_hash, u64 _hash) + { + base_hash = _base_hash; + hash = _hash; + } + + // This texture entry is used by the other entry as a sub-texture + void CreateReference(TCacheEntry* other_entry) + { + // References are two-way, so they can easily be destroyed later + this->references.emplace(other_entry); + other_entry->references.emplace(this); + } + + void SetXfbCopy(u32 stride); + void SetEfbCopy(u32 stride); + void SetNotCopy(); + + bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; + + bool IsEfbCopy() const { return is_efb_copy; } + bool IsCopy() const { return is_xfb_copy || is_efb_copy; } + u32 NumBlocksX() const; + u32 NumBlocksY() const; + u32 BytesPerRow() const; + + u64 CalculateHash() const; + + int HashSampleSize() const; + u32 GetWidth() const { return texture->GetConfig().width; } + u32 GetHeight() const { return texture->GetConfig().height; } + u32 GetNumLevels() const { return texture->GetConfig().levels; } + u32 GetNumLayers() const { return texture->GetConfig().layers; } + AbstractTextureFormat GetFormat() const { return texture->GetConfig().format; } + void DoState(PointerWrap& p); +}; + class TextureCacheBase { -private: - static const int FRAMECOUNT_INVALID = 0; - public: - struct TCacheEntry - { - // common members - std::unique_ptr texture; - std::unique_ptr framebuffer; - u32 addr = 0; - u32 size_in_bytes = 0; - u64 base_hash = 0; - u64 hash = 0; // for paletted textures, hash = base_hash ^ palette_hash - TextureAndTLUTFormat format; - u32 memory_stride = 0; - bool is_efb_copy = false; - bool is_custom_tex = false; - bool may_have_overlapping_textures = true; - bool tmem_only = false; // indicates that this texture only exists in the tmem cache - bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary - // content, aren't just downscaled - bool should_force_safe_hashing = false; // for XFB - bool is_xfb_copy = false; - bool is_xfb_container = false; - u64 id = 0; - - bool reference_changed = false; // used by xfb to determine when a reference xfb changed - - // Texture dimensions from the GameCube's point of view - u32 native_width = 0; - u32 native_height = 0; - u32 native_levels = 0; - - // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames - int frameCount = FRAMECOUNT_INVALID; - - // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when - // removing the cache entry - std::multimap::iterator textures_by_hash_iter; - - // This is used to keep track of both: - // * efb copies used by this partially updated texture - // * partially updated textures which refer to this efb copy - std::unordered_set references; - - // Pending EFB copy - std::unique_ptr pending_efb_copy; - u32 pending_efb_copy_width = 0; - u32 pending_efb_copy_height = 0; - bool pending_efb_copy_invalidated = false; - - std::string texture_info_name = ""; - - explicit TCacheEntry(std::unique_ptr tex, - std::unique_ptr fb); - - ~TCacheEntry(); - - void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format, - bool force_safe_hashing) - { - addr = _addr; - size_in_bytes = _size; - format = _format; - should_force_safe_hashing = force_safe_hashing; - } - - void SetDimensions(unsigned int _native_width, unsigned int _native_height, - unsigned int _native_levels) - { - native_width = _native_width; - native_height = _native_height; - native_levels = _native_levels; - memory_stride = _native_width; - } - - void SetHashes(u64 _base_hash, u64 _hash) - { - base_hash = _base_hash; - hash = _hash; - } - - // This texture entry is used by the other entry as a sub-texture - void CreateReference(TCacheEntry* other_entry) - { - // References are two-way, so they can easily be destroyed later - this->references.emplace(other_entry); - other_entry->references.emplace(this); - } - - void SetXfbCopy(u32 stride); - void SetEfbCopy(u32 stride); - void SetNotCopy(); - - bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; - - bool IsEfbCopy() const { return is_efb_copy; } - bool IsCopy() const { return is_xfb_copy || is_efb_copy; } - u32 NumBlocksX() const; - u32 NumBlocksY() const; - u32 BytesPerRow() const; - - u64 CalculateHash() const; - - int HashSampleSize() const; - u32 GetWidth() const { return texture->GetConfig().width; } - u32 GetHeight() const { return texture->GetConfig().height; } - u32 GetNumLevels() const { return texture->GetConfig().levels; } - u32 GetNumLayers() const { return texture->GetConfig().layers; } - AbstractTextureFormat GetFormat() const { return texture->GetConfig().format; } - void DoState(PointerWrap& p); - }; - // Minimal version of TCacheEntry just for TexPool struct TexPoolEntry { From 606c18210dd4f651655b2c84e9e332e6c6c6e21b Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 25 Jul 2022 17:20:33 +1200 Subject: [PATCH 02/60] TextureCache: Refactor with smart pointers The whole ownership model was getting a bit of a mess, with a some of special cases to deal with. And I'm planning to make it even more complex in the future. So here is some upfront work to convert it over to reference counted pointers. --- Source/Core/VideoBackends/D3D/D3DMain.cpp | 10 - .../Core/VideoBackends/D3D12/VideoBackend.cpp | 14 +- Source/Core/VideoBackends/Metal/MTLMain.mm | 10 - .../Core/VideoBackends/Null/NullBackend.cpp | 9 - Source/Core/VideoBackends/Null/TextureCache.h | 2 +- Source/Core/VideoBackends/OGL/OGLMain.cpp | 13 +- Source/Core/VideoBackends/Software/SWmain.cpp | 12 - .../VideoBackends/Software/TextureCache.h | 2 +- Source/Core/VideoBackends/Vulkan/VKMain.cpp | 13 +- Source/Core/VideoCommon/BPStructs.cpp | 3 + Source/Core/VideoCommon/RenderBase.cpp | 2 +- Source/Core/VideoCommon/TextureCacheBase.cpp | 343 +++++++++--------- Source/Core/VideoCommon/TextureCacheBase.h | 76 ++-- Source/Core/VideoCommon/VideoBackendBase.cpp | 15 + 14 files changed, 248 insertions(+), 276 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DMain.cpp b/Source/Core/VideoBackends/D3D/D3DMain.cpp index 755f0cf590..1748a0b3fc 100644 --- a/Source/Core/VideoBackends/D3D/D3DMain.cpp +++ b/Source/Core/VideoBackends/D3D/D3DMain.cpp @@ -174,16 +174,6 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) void VideoBackend::Shutdown() { - g_shader_cache->Shutdown(); - g_renderer->Shutdown(); - - g_perf_query.reset(); - g_texture_cache.reset(); - g_framebuffer_manager.reset(); - g_shader_cache.reset(); - g_vertex_manager.reset(); - g_renderer.reset(); - ShutdownShared(); D3D::Destroy(); } diff --git a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp index 1e7483fd6a..209a74dfc9 100644 --- a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp +++ b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp @@ -157,19 +157,7 @@ void VideoBackend::Shutdown() if (g_renderer) Renderer::GetInstance()->ExecuteCommandList(true); - if (g_shader_cache) - g_shader_cache->Shutdown(); - - if (g_renderer) - g_renderer->Shutdown(); - - g_perf_query.reset(); - g_texture_cache.reset(); - g_framebuffer_manager.reset(); - g_shader_cache.reset(); - g_vertex_manager.reset(); - g_renderer.reset(); - DXContext::Destroy(); ShutdownShared(); + DXContext::Destroy(); } } // namespace DX12 diff --git a/Source/Core/VideoBackends/Metal/MTLMain.mm b/Source/Core/VideoBackends/Metal/MTLMain.mm index ad646b9bf0..a5e40bc59f 100644 --- a/Source/Core/VideoBackends/Metal/MTLMain.mm +++ b/Source/Core/VideoBackends/Metal/MTLMain.mm @@ -132,16 +132,6 @@ bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi) void Metal::VideoBackend::Shutdown() { - g_shader_cache->Shutdown(); - g_renderer->Shutdown(); - - g_shader_cache.reset(); - g_texture_cache.reset(); - g_framebuffer_manager.reset(); - g_perf_query.reset(); - g_vertex_manager.reset(); - g_renderer.reset(); - g_state_tracker.reset(); ObjectCache::Shutdown(); ShutdownShared(); } diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index 7cc8919ad0..80f8431ee7 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -93,15 +93,6 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) void VideoBackend::Shutdown() { - g_shader_cache->Shutdown(); - g_renderer->Shutdown(); - - g_texture_cache.reset(); - g_perf_query.reset(); - g_vertex_manager.reset(); - g_framebuffer_manager.reset(); - g_renderer.reset(); - ShutdownShared(); } diff --git a/Source/Core/VideoBackends/Null/TextureCache.h b/Source/Core/VideoBackends/Null/TextureCache.h index 2b95586f44..78e910b74b 100644 --- a/Source/Core/VideoBackends/Null/TextureCache.h +++ b/Source/Core/VideoBackends/Null/TextureCache.h @@ -18,7 +18,7 @@ protected: { } - void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + void CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_copy, const MathUtil::Rectangle& src_rect, bool scale_by_half, bool linear_filter, EFBCopyFormat dst_format, bool is_intensity, float gamma, bool clamp_top, bool clamp_bottom, diff --git a/Source/Core/VideoBackends/OGL/OGLMain.cpp b/Source/Core/VideoBackends/OGL/OGLMain.cpp index f6a84240e0..59d63e8b92 100644 --- a/Source/Core/VideoBackends/OGL/OGLMain.cpp +++ b/Source/Core/VideoBackends/OGL/OGLMain.cpp @@ -208,16 +208,9 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) void VideoBackend::Shutdown() { - g_shader_cache->Shutdown(); - g_renderer->Shutdown(); - g_sampler_cache.reset(); - g_texture_cache.reset(); - g_perf_query.reset(); - g_vertex_manager.reset(); - g_framebuffer_manager.reset(); - g_shader_cache.reset(); - ProgramShaderCache::Shutdown(); - g_renderer.reset(); ShutdownShared(); + + ProgramShaderCache::Shutdown(); + g_sampler_cache.reset(); } } // namespace OGL diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index b6d2005c26..b749f447bb 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -127,18 +127,6 @@ bool VideoSoftware::Initialize(const WindowSystemInfo& wsi) void VideoSoftware::Shutdown() { - if (g_shader_cache) - g_shader_cache->Shutdown(); - - if (g_renderer) - g_renderer->Shutdown(); - - g_texture_cache.reset(); - g_perf_query.reset(); - g_framebuffer_manager.reset(); - g_shader_cache.reset(); - g_vertex_manager.reset(); - g_renderer.reset(); ShutdownShared(); } } // namespace SW diff --git a/Source/Core/VideoBackends/Software/TextureCache.h b/Source/Core/VideoBackends/Software/TextureCache.h index a7d241197f..7fc38ed9e8 100644 --- a/Source/Core/VideoBackends/Software/TextureCache.h +++ b/Source/Core/VideoBackends/Software/TextureCache.h @@ -19,7 +19,7 @@ protected: TextureEncoder::Encode(dst, params, native_width, bytes_per_row, num_blocks_y, memory_stride, src_rect, scale_by_half, y_scale, gamma); } - void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + void CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_copy, const MathUtil::Rectangle& src_rect, bool scale_by_half, bool linear_filter, EFBCopyFormat dst_format, bool is_intensity, float gamma, bool clamp_top, bool clamp_bottom, diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index 46d535ce75..13379aebed 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -260,26 +260,15 @@ void VideoBackend::Shutdown() if (g_vulkan_context) vkDeviceWaitIdle(g_vulkan_context->GetDevice()); - if (g_shader_cache) - g_shader_cache->Shutdown(); - if (g_object_cache) g_object_cache->Shutdown(); - if (g_renderer) - g_renderer->Shutdown(); + ShutdownShared(); - g_perf_query.reset(); - g_texture_cache.reset(); - g_framebuffer_manager.reset(); - g_shader_cache.reset(); - g_vertex_manager.reset(); - g_renderer.reset(); g_object_cache.reset(); StateTracker::DestroyInstance(); g_command_buffer_mgr.reset(); g_vulkan_context.reset(); - ShutdownShared(); UnloadVulkanLibrary(); } diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 075e2243bf..4e1114e87d 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -185,6 +185,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, { INCSTAT(g_stats.this_frame.num_draw_done); g_texture_cache->FlushEFBCopies(); + g_texture_cache->FlushStaleBinds(); g_framebuffer_manager->InvalidatePeekCache(false); g_framebuffer_manager->RefreshPeekCache(); auto& system = Core::System::GetInstance(); @@ -203,6 +204,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, { INCSTAT(g_stats.this_frame.num_token); g_texture_cache->FlushEFBCopies(); + g_texture_cache->FlushStaleBinds(); g_framebuffer_manager->InvalidatePeekCache(false); g_framebuffer_manager->RefreshPeekCache(); auto& system = Core::System::GetInstance(); @@ -218,6 +220,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, { INCSTAT(g_stats.this_frame.num_token_int); g_texture_cache->FlushEFBCopies(); + g_texture_cache->FlushStaleBinds(); g_framebuffer_manager->InvalidatePeekCache(false); g_framebuffer_manager->RefreshPeekCache(); auto& system = Core::System::GetInstance(); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 209fa8293a..9c9e564b91 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -1361,7 +1361,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 { // Get the current XFB from texture cache MathUtil::Rectangle xfb_rect; - const auto* xfb_entry = + const auto xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 865868bd4c..84208c8094 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -63,7 +63,7 @@ static int xfb_count = 0; std::unique_ptr g_texture_cache; TCacheEntry::TCacheEntry(std::unique_ptr tex, - std::unique_ptr fb) + std::unique_ptr fb) : texture(std::move(tex)), framebuffer(std::move(fb)) { } @@ -72,6 +72,8 @@ TCacheEntry::~TCacheEntry() { for (auto& reference : references) reference->references.erase(this); + ASSERT_MSG(VIDEO, g_texture_cache, "Texture cache destroyed before TCacheEntry was destroyed"); + g_texture_cache->ReleaseToPool(this); } void TextureCacheBase::CheckTempSize(size_t required_size) @@ -99,13 +101,19 @@ TextureCacheBase::TextureCacheBase() TMEM::InvalidateAll(); } -TextureCacheBase::~TextureCacheBase() +void TextureCacheBase::Shutdown() { // Clear pending EFB copies first, so we don't try to flush them. m_pending_efb_copies.clear(); HiresTexture::Shutdown(); + + // For correctness, we need to invalidate textures before the gpu context starts shutting down. Invalidate(); +} + +TextureCacheBase::~TextureCacheBase() +{ Common::FreeAlignedMemory(temp); temp = nullptr; } @@ -126,13 +134,10 @@ void TextureCacheBase::Invalidate() FlushEFBCopies(); TMEM::InvalidateAll(); - bound_textures.fill(nullptr); - for (auto& tex : textures_by_address) - { - delete tex.second; - } - textures_by_address.clear(); + for (auto& bind : bound_textures) + bind.reset(); textures_by_hash.clear(); + textures_by_address.clear(); texture_pool.clear(); } @@ -183,11 +188,7 @@ void TextureCacheBase::Cleanup(int _frameCount) TexAddrCache::iterator tcend = textures_by_address.end(); while (iter != tcend) { - if (iter->second->tmem_only) - { - iter = InvalidateTexture(iter); - } - else if (iter->second->frameCount == FRAMECOUNT_INVALID) + if (iter->second->frameCount == FRAMECOUNT_INVALID) { iter->second->frameCount = _frameCount; ++iter; @@ -268,8 +269,8 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config) config.graphics_mod_config ? config.graphics_mod_config->GetChangeCount() : 0; } -TCacheEntry* -TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt) +RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const u8* palette, + TLUTFormat tlutfmt) { DEBUG_ASSERT(g_ActiveConfig.backend_info.bSupportsPaletteConversion); @@ -277,16 +278,16 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU if (!pipeline) { ERROR_LOG_FMT(VIDEO, "Failed to get conversion pipeline for format {}", tlutfmt); - return nullptr; + return {}; } TextureConfig new_config = entry->texture->GetConfig(); new_config.levels = 1; new_config.flags |= AbstractTextureFlag_RenderTarget; - TCacheEntry* decoded_entry = AllocateCacheEntry(new_config); + RcTcacheEntry decoded_entry = AllocateCacheEntry(new_config); if (!decoded_entry) - return nullptr; + return decoded_entry; decoded_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format, entry->should_force_safe_hashing); @@ -337,8 +338,8 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU return decoded_entry; } -TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entry, - TextureFormat new_format) +RcTcacheEntry TextureCacheBase::ReinterpretEntry(const RcTcacheEntry& existing_entry, + TextureFormat new_format) { const AbstractPipeline* pipeline = g_shader_cache->GetTextureReinterpretPipeline(existing_entry->format.texfmt, new_format); @@ -346,16 +347,16 @@ TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entr { ERROR_LOG_FMT(VIDEO, "Failed to obtain texture reinterpreting pipeline from format {} to {}", existing_entry->format.texfmt, new_format); - return nullptr; + return {}; } TextureConfig new_config = existing_entry->texture->GetConfig(); new_config.levels = 1; new_config.flags |= AbstractTextureFlag_RenderTarget; - TCacheEntry* reinterpreted_entry = AllocateCacheEntry(new_config); + RcTcacheEntry reinterpreted_entry = AllocateCacheEntry(new_config); if (!reinterpreted_entry) - return nullptr; + return {}; reinterpreted_entry->SetGeneralParameters(existing_entry->addr, existing_entry->size_in_bytes, new_format, existing_entry->should_force_safe_hashing); @@ -383,8 +384,7 @@ TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entr return reinterpreted_entry; } -void TextureCacheBase::ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, - u32 new_height) +void TextureCacheBase::ScaleTextureCacheEntryTo(RcTcacheEntry& entry, u32 new_width, u32 new_height) { if (entry->GetWidth() == new_width && entry->GetHeight() == new_height) { @@ -559,15 +559,19 @@ void TextureCacheBase::DoState(PointerWrap& p) void TextureCacheBase::DoSaveState(PointerWrap& p) { + // Flush all stale binds + FlushStaleBinds(); + std::map entry_map; std::vector entries_to_save; - auto ShouldSaveEntry = [](const TCacheEntry* entry) { + auto ShouldSaveEntry = [](const RcTcacheEntry& entry) { // We skip non-copies as they can be decoded from RAM when the state is loaded. // Storing them would duplicate data in the save state file, adding to decompression time. - return entry->IsCopy(); + // We also need to store invalidated entires, as they can't be restored from RAM. + return entry->IsCopy() || entry->invalidated; }; - auto AddCacheEntryToMap = [&entry_map, &entries_to_save](TCacheEntry* entry) -> u32 { - auto iter = entry_map.find(entry); + auto AddCacheEntryToMap = [&entry_map, &entries_to_save](const RcTcacheEntry& entry) -> u32 { + auto iter = entry_map.find(entry.get()); if (iter != entry_map.end()) return iter->second; @@ -575,8 +579,8 @@ void TextureCacheBase::DoSaveState(PointerWrap& p) // same order they were collected. This is because of iterating both the address and hash maps. // Therefore, the map is used for fast lookup, and the vector for ordering. u32 id = static_cast(entry_map.size()); - entry_map.emplace(entry, id); - entries_to_save.push_back(entry); + entry_map.emplace(entry.get(), id); + entries_to_save.push_back(entry.get()); return id; }; auto GetCacheEntryId = [&entry_map](const TCacheEntry* entry) -> std::optional { @@ -588,6 +592,7 @@ void TextureCacheBase::DoSaveState(PointerWrap& p) // of address/hash to entry ID. std::vector> textures_by_address_list; std::vector> textures_by_hash_list; + std::vector> bound_textures_list; if (Config::Get(Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE)) { for (const auto& it : textures_by_address) @@ -606,6 +611,15 @@ void TextureCacheBase::DoSaveState(PointerWrap& p) textures_by_hash_list.emplace_back(it.first, id); } } + for (u32 i = 0; i < bound_textures.size(); i++) + { + const auto& tentry = bound_textures[i]; + if (bound_textures[i] && ShouldSaveEntry(tentry)) + { + const u32 id = AddCacheEntryToMap(tentry); + bound_textures_list.emplace_back(i, id); + } + } } // Save the texture cache entries out in the order the were referenced. @@ -641,29 +655,20 @@ void TextureCacheBase::DoSaveState(PointerWrap& p) } } - size = static_cast(reference_pairs.size()); - p.Do(size); - for (const auto& it : reference_pairs) - { - p.Do(it.first); - p.Do(it.second); - } + auto doList = [&p](auto list) { + u32 size = static_cast(list.size()); + p.Do(size); + for (const auto& it : list) + { + p.Do(it.first); + p.Do(it.second); + } + }; - size = static_cast(textures_by_address_list.size()); - p.Do(size); - for (const auto& it : textures_by_address_list) - { - p.Do(it.first); - p.Do(it.second); - } - - size = static_cast(textures_by_hash_list.size()); - p.Do(size); - for (const auto& it : textures_by_hash_list) - { - p.Do(it.first); - p.Do(it.second); - } + doList(reference_pairs); + doList(textures_by_address_list); + doList(textures_by_hash_list); + doList(bound_textures_list); // Free the readback texture to potentially save host-mapped GPU memory, depending on where // the driver mapped the staging buffer. @@ -673,10 +678,11 @@ void TextureCacheBase::DoSaveState(PointerWrap& p) void TextureCacheBase::DoLoadState(PointerWrap& p) { // Helper for getting a cache entry from an ID. - std::map id_map; - auto GetEntry = [&id_map](u32 id) { + std::map id_map; + RcTcacheEntry null_entry; + auto GetEntry = [&id_map, &null_entry](u32 id) -> RcTcacheEntry& { auto iter = id_map.find(id); - return iter == id_map.end() ? nullptr : iter->second; + return iter == id_map.end() ? null_entry : iter->second; }; // Only clear out state when actually restoring/loading. @@ -694,13 +700,11 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) // Even if the texture isn't valid, we still need to create the cache entry object // to update the point in the state state. We'll just throw it away if it's invalid. auto tex = DeserializeTexture(p); - TCacheEntry* entry = new TCacheEntry(std::move(tex->texture), std::move(tex->framebuffer)); + auto entry = Common::make_rc(std::move(tex->texture), std::move(tex->framebuffer)); entry->textures_by_hash_iter = textures_by_hash.end(); entry->DoState(p); if (entry->texture && commit_state) id_map.emplace(i, entry); - else - delete entry; } p.DoMarker("TextureCacheEntries"); @@ -711,10 +715,10 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) u32 id1 = 0, id2 = 0; p.Do(id1); p.Do(id2); - TCacheEntry* e1 = GetEntry(id1); - TCacheEntry* e2 = GetEntry(id2); + auto e1 = GetEntry(id1); + auto e2 = GetEntry(id2); if (e1 && e2) - e1->CreateReference(e2); + e1->CreateReference(e2.get()); } // Fill in address map. @@ -726,7 +730,7 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) p.Do(addr); p.Do(id); - TCacheEntry* entry = GetEntry(id); + auto& entry = GetEntry(id); if (entry) textures_by_address.emplace(addr, entry); } @@ -740,10 +744,28 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) p.Do(hash); p.Do(id); - TCacheEntry* entry = GetEntry(id); + auto& entry = GetEntry(id); if (entry) entry->textures_by_hash_iter = textures_by_hash.emplace(hash, entry); } + + // Clear bound textures + for (u32 i = 0; i < bound_textures.size(); i++) + bound_textures[i].reset(); + + // Fill in bound textures + p.Do(size); + for (u32 i = 0; i < size; i++) + { + u32 index = 0; + u32 id = 0; + p.Do(index); + p.Do(id); + + auto& entry = GetEntry(id); + if (entry) + bound_textures[index] = entry; + } } void TCacheEntry::DoState(PointerWrap& p) @@ -757,7 +779,7 @@ void TCacheEntry::DoState(PointerWrap& p) p.Do(is_efb_copy); p.Do(is_custom_tex); p.Do(may_have_overlapping_textures); - p.Do(tmem_only); + p.Do(invalidated); p.Do(has_arbitrary_mips); p.Do(should_force_safe_hashing); p.Do(is_xfb_copy); @@ -770,9 +792,8 @@ void TCacheEntry::DoState(PointerWrap& p) p.Do(frameCount); } -TCacheEntry* -TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette, - TLUTFormat tlutfmt) +RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_update, + const u8* palette, TLUTFormat tlutfmt) { // If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies, // which aren't applied already. It is set for new textures, and for the affected range @@ -798,9 +819,9 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8 auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes); while (iter.first != iter.second) { - TCacheEntry* entry = iter.first->second; - if (entry != entry_to_update && entry->IsCopy() && !entry->tmem_only && - entry->references.count(entry_to_update) == 0 && + auto& entry = iter.first->second; + if (entry != entry_to_update && entry->IsCopy() && + entry->references.count(entry_to_update.get()) == 0 && entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) && entry->memory_stride == numBlocksX * block_size) { @@ -815,20 +836,19 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8 continue; } - TCacheEntry* reinterpreted_entry = - ReinterpretEntry(entry, entry_to_update->format.texfmt); + auto reinterpreted_entry = ReinterpretEntry(entry, entry_to_update->format.texfmt); if (reinterpreted_entry) entry = reinterpreted_entry; } if (isPaletteTexture) { - TCacheEntry* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); + auto decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); if (decoded_entry) { // Link the efb copy with the partially updated texture, so we won't apply this partial // update again - entry->CreateReference(entry_to_update); + entry->CreateReference(entry_to_update.get()); // Mark the texture update as used, as if it was loaded directly entry->frameCount = FRAMECOUNT_INVALID; entry = decoded_entry; @@ -929,7 +949,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8 else { // Link the two textures together, so we won't apply this partial update again - entry->CreateReference(entry_to_update); + entry->CreateReference(entry_to_update.get()); // Mark the texture update as used, as if it was loaded directly entry->frameCount = FRAMECOUNT_INVALID; } @@ -947,7 +967,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8 return entry_to_update; } -void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level, +void TextureCacheBase::DumpTexture(RcTcacheEntry& entry, std::string basename, unsigned int level, bool is_arbitrary) { std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID(); @@ -1065,7 +1085,7 @@ void TextureCacheBase::BindTextures(BitSet32 used_textures) auto& pixel_shader_manager = system.GetPixelShaderManager(); for (u32 i = 0; i < bound_textures.size(); i++) { - const TCacheEntry* tentry = bound_textures[i]; + const RcTcacheEntry& tentry = bound_textures[i]; if (used_textures[i] && tentry) { g_renderer->SetTexture(i, tentry->texture.get()); @@ -1229,9 +1249,16 @@ TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) // if this stage was not invalidated by changes to texture registers, keep the current texture if (TMEM::IsValid(texture_info.GetStage()) && bound_textures[texture_info.GetStage()]) { - TCacheEntry* entry = bound_textures[texture_info.GetStage()]; + TCacheEntry* entry = bound_textures[texture_info.GetStage()].get(); // If the TMEM configuration is such that this texture is more or less guaranteed to still // be in TMEM, then we know we can reuse the old entry without even hashing the memory + // + // It's possible this texture has already been overwritten in emulated memory and therfore + // invalidated from our texture cache, but we want to use it anyway to approximate the + // result of the game using an overwritten texture cached in TMEM. + // + // Spyro: A Hero's Tail is known for (deliberately?) using such overwritten textures + // in it's bloom effect, which breaks without giving it the invalidated texture. if (TMEM::IsCached(texture_info.GetStage())) { return entry; @@ -1239,7 +1266,7 @@ TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) // Otherwise, hash the backing memory and check it's unchanged. // FIXME: this doesn't correctly handle textures from tmem. - if (!entry->tmem_only && entry->base_hash == entry->CalculateHash()) + if (!entry->invalidated && entry->base_hash == entry->CalculateHash()) { return entry; } @@ -1269,12 +1296,11 @@ TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) TMEM::Bind(texture_info.GetStage(), entry->NumBlocksX(), entry->NumBlocksY(), entry->GetNumLevels() > 1, entry->format == TextureFormat::RGBA8); - return entry; + return entry.get(); } -TCacheEntry* -TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, - const TextureInfo& texture_info) +RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, + const TextureInfo& texture_info) { u32 expanded_width = texture_info.GetExpandedWidth(); u32 expanded_height = texture_info.GetExpandedHeight(); @@ -1291,7 +1317,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, // Reject invalid tlut format. if (texture_info.GetPaletteSize() && !IsValidTLUTFormat(texture_info.GetTlutFormat())) - return nullptr; + return {}; u32 bytes_per_block = (texture_info.GetBlockWidth() * texture_info.GetBlockHeight() * TexDecoder_GetTexelSizeInNibbles(texture_info.GetTextureFormat())) / @@ -1304,7 +1330,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, { ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}", texture_info.GetRawAddress()); - return nullptr; + return {}; } // If we are recording a FifoLog, keep track of what memory we read. FifoRecorder does @@ -1368,14 +1394,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, while (iter != iter_range.second) { - TCacheEntry* entry = iter->second; - - // Skip entries that are only left in our texture cache for the tmem cache emulation - if (entry->tmem_only) - { - ++iter; - continue; - } + RcTcacheEntry& entry = iter->second; // TODO: Some games (Rogue Squadron 3, Twin Snakes) seem to load a previously made XFB // copy as a regular texture. You can see this particularly well in RS3 whenever the @@ -1478,7 +1497,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, if (unreinterpreted_copy != textures_by_address.end()) { - TCacheEntry* decoded_entry = + auto decoded_entry = ReinterpretEntry(unreinterpreted_copy->second, texture_info.GetTextureFormat()); // It's possible to combine reinterpreted textures + palettes. @@ -1492,8 +1511,9 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, if (unconverted_copy != textures_by_address.end()) { - TCacheEntry* decoded_entry = ApplyPaletteToEntry( - unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat()); + auto decoded_entry = + ApplyPaletteToEntry(unconverted_copy->second, texture_info.GetTlutAddress(), + texture_info.GetTlutFormat()); if (decoded_entry) { @@ -1515,7 +1535,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, TexHashCache::iterator hash_iter = hash_range.first; while (hash_iter != hash_range.second) { - TCacheEntry* entry = hash_iter->second; + RcTcacheEntry& entry = hash_iter->second; // All parameters, except the address, need to match here if (entry->format == full_format && entry->native_levels >= texture_info.GetLevelCount() && entry->native_width == texture_info.GetRawWidth() && @@ -1577,9 +1597,9 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, // create the entry/texture const TextureConfig config(width, height, texLevels, 1, 1, hires_tex ? hires_tex->GetFormat() : AbstractTextureFormat::RGBA8, 0); - TCacheEntry* entry = AllocateCacheEntry(config); + RcTcacheEntry entry = AllocateCacheEntry(config); if (!entry) - return nullptr; + return entry; ArbitraryMipmapDetector arbitrary_mip_detector; if (hires_tex) @@ -1739,9 +1759,8 @@ static void GetDisplayRectForXFBEntry(TCacheEntry* entry, u32 width, u32 height, display_rect->bottom = static_cast(height * entry->GetHeight() / entry->native_height); } -TCacheEntry* -TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, - MathUtil::Rectangle* display_rect) +RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, + MathUtil::Rectangle* display_rect) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); @@ -1749,11 +1768,11 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, if (!src_data) { ERROR_LOG_FMT(VIDEO, "Trying to load XFB texture from invalid address {:#010x}", address); - return nullptr; + return {}; } // Do we currently have a version of this XFB copy in VRAM? - TCacheEntry* entry = GetXFBFromCache(address, width, height, stride); + RcTcacheEntry entry = GetXFBFromCache(address, width, height, stride); if (entry) { if (entry->is_xfb_container) @@ -1762,7 +1781,7 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, entry->texture->FinishedRendering(); } - GetDisplayRectForXFBEntry(entry, width, height, display_rect); + GetDisplayRectForXFBEntry(entry.get(), width, height, display_rect); return entry; } @@ -1818,19 +1837,18 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, } } - GetDisplayRectForXFBEntry(entry, width, height, display_rect); + GetDisplayRectForXFBEntry(entry.get(), width, height, display_rect); return entry; } -TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height, - u32 stride) +RcTcacheEntry TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride) { auto iter_range = textures_by_address.equal_range(address); TexAddrCache::iterator iter = iter_range.first; while (iter != iter_range.second) { - TCacheEntry* entry = iter->second; + auto& entry = iter->second; // The only thing which has to match exactly is the stride. We can use a partial rectangle if // the VI width/height differs from that of the XFB copy. @@ -1854,10 +1872,10 @@ TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 heigh ++iter; } - return nullptr; + return {}; } -void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry) +void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry) { // It is possible that some of the overlapping textures overlap each other. This behavior has been // seen with XFB copies in Rogue Leader. To get the correct result, we apply the texture updates @@ -1876,8 +1894,8 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry) // our force progressive hack means that an XFB copy should always have a matching stride. If // the hack is disabled, XFB2RAM should also be enabled. Should we wish to implement interlaced // stitching in the future, this would require a shader which grabs every second line. - TCacheEntry* entry = iter.first->second; - if (entry != stitched_entry && entry->IsCopy() && !entry->tmem_only && + auto& entry = iter.first->second; + if (entry != stitched_entry && entry->IsCopy() && entry->OverlapsMemoryRange(stitched_entry->addr, stitched_entry->size_in_bytes) && entry->memory_stride == stitched_entry->memory_stride) { @@ -1887,7 +1905,7 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry) if (entry->native_width != entry->GetWidth()) create_upscaled_copy = true; - candidates.emplace_back(entry); + candidates.emplace_back(entry.get()); } else { @@ -1997,7 +2015,7 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry) } // Link the two textures together, so we won't apply this partial update again - entry->CreateReference(stitched_entry); + entry->CreateReference(stitched_entry.get()); // Mark the texture update as used, as if it was loaded directly entry->frameCount = FRAMECOUNT_INVALID; @@ -2223,7 +2241,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( const bool linear_filter = !is_depth_copy && (scaleByHalf || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f); - TCacheEntry* entry = nullptr; + RcTcacheEntry entry; if (copy_to_vram) { // create the texture @@ -2247,8 +2265,8 @@ void TextureCacheBase::CopyRenderTargetToTexture( entry->may_have_overlapping_textures = false; entry->is_custom_tex = false; - CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter, dstFormat, - isIntensity, gamma, clamp_top, clamp_bottom, + CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter, + dstFormat, isIntensity, gamma, clamp_top, clamp_bottom, GetVRAMCopyFilterCoefficients(filter_coefficients)); if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods)) @@ -2314,7 +2332,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( entry->pending_efb_copy = std::move(staging_texture); entry->pending_efb_copy_width = bytes_per_row / sizeof(u32); entry->pending_efb_copy_height = num_blocks_y; - entry->pending_efb_copy_invalidated = false; m_pending_efb_copies.push_back(entry); } } @@ -2339,7 +2356,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( auto iter = FindOverlappingTextures(dstAddr, covered_range); while (iter.first != iter.second) { - TCacheEntry* overlapping_entry = iter.first->second; + RcTcacheEntry& overlapping_entry = iter.first->second; if (overlapping_entry->addr == dstAddr && overlapping_entry->is_xfb_copy) { @@ -2412,7 +2429,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( { const u64 hash = entry->CalculateHash(); entry->SetHashes(hash, hash); - textures_by_address.emplace(dstAddr, entry); + textures_by_address.emplace(dstAddr, std::move(entry)); } } @@ -2421,11 +2438,20 @@ void TextureCacheBase::FlushEFBCopies() if (m_pending_efb_copies.empty()) return; - for (TCacheEntry* entry : m_pending_efb_copies) - FlushEFBCopy(entry); + for (auto& entry : m_pending_efb_copies) + FlushEFBCopy(entry.get()); m_pending_efb_copies.clear(); } +void TextureCacheBase::FlushStaleBinds() +{ + for (u32 i = 0; i < bound_textures.size(); i++) + { + if (!TMEM::IsCached(i)) + bound_textures[i].reset(); + } +} + void TextureCacheBase::WriteEFBCopyToRAM(u8* dst_ptr, u32 width, u32 height, u32 stride, std::unique_ptr staging_texture) { @@ -2443,14 +2469,10 @@ void TextureCacheBase::FlushEFBCopy(TCacheEntry* entry) WriteEFBCopyToRAM(dst, entry->pending_efb_copy_width, entry->pending_efb_copy_height, entry->memory_stride, std::move(entry->pending_efb_copy)); - // If the EFB copy was invalidated (e.g. the bloom case mentioned in InvalidateTexture), now is - // the time to clean up the TCacheEntry. In which case, we don't need to compute the new hash of - // the RAM copy. But we need to clean up the TCacheEntry, as InvalidateTexture doesn't free it. - if (entry->pending_efb_copy_invalidated) - { - delete entry; + // If the EFB copy was invalidated (e.g. the bloom case mentioned in InvalidateTexture), we don't + // need to do anything more. The entry will be automatically deleted by smart pointers + if (entry->invalidated) return; - } // Re-hash the texture now that the guest memory is populated. // This should be safe because we'll catch any writes before the game can modify it. @@ -2465,7 +2487,7 @@ void TextureCacheBase::FlushEFBCopy(TCacheEntry* entry) auto range = FindOverlappingTextures(entry->addr, covered_range); for (auto iter = range.first; iter != range.second; ++iter) { - TCacheEntry* overlapping_entry = iter->second; + auto& overlapping_entry = iter->second; if (overlapping_entry->may_have_overlapping_textures && overlapping_entry->is_xfb_copy && overlapping_entry->OverlapsMemoryRange(entry->addr, covered_range)) { @@ -2555,14 +2577,14 @@ void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_ } } -TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) +RcTcacheEntry TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) { std::optional alloc = AllocateTexture(config); if (!alloc) - return nullptr; + return {}; - TCacheEntry* cacheEntry = - new TCacheEntry(std::move(alloc->texture), std::move(alloc->framebuffer)); + auto cacheEntry = + Common::make_rc(std::move(alloc->texture), std::move(alloc->framebuffer)); cacheEntry->textures_by_hash_iter = textures_by_hash.end(); cacheEntry->id = last_entry_id++; return cacheEntry; @@ -2618,14 +2640,13 @@ TextureCacheBase::FindMatchingTextureFromPool(const TextureConfig& config) return matching_iter != range.second ? matching_iter : texture_pool.end(); } -TextureCacheBase::TexAddrCache::iterator -TextureCacheBase::GetTexCacheIter(TCacheEntry* entry) +TextureCacheBase::TexAddrCache::iterator TextureCacheBase::GetTexCacheIter(TCacheEntry* entry) { auto iter_range = textures_by_address.equal_range(entry->addr); TexAddrCache::iterator iter = iter_range.first; while (iter != iter_range.second) { - if (iter->second == entry) + if (iter->second.get() == entry) { return iter; } @@ -2657,7 +2678,7 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe if (iter == textures_by_address.end()) return textures_by_address.end(); - TCacheEntry* entry = iter->second; + RcTcacheEntry& entry = iter->second; if (entry->textures_by_hash_iter != textures_by_hash.end()) { @@ -2665,26 +2686,6 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe entry->textures_by_hash_iter = textures_by_hash.end(); } - for (size_t i = 0; i < bound_textures.size(); ++i) - { - if (bound_textures[i] == entry) - { - if (TMEM::IsCached(static_cast(i))) - { - // If the entry is currently bound and tmem has it recorded as cached, keep it, but mark it - // as invalidated. This way it can still be used via tmem cache emulation, but nothing else. - // Spyro: A Hero's Tail is known for using such overwritten textures. - bound_textures[i]->tmem_only = true; - return ++iter; - } - else - { - // Otherwise, delete the reference to it from bound_textures - bound_textures[i] = nullptr; - } - } - } - // If this is a pending EFB copy, we don't want to flush it here. // Why? Because let's say a game is rendering a bloom-type effect, using EFB copies to essentially // downscale the framebuffer. Copy from EFB->Texture, draw texture to EFB, copy EFB->Texture, @@ -2707,19 +2708,23 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe } else { - entry->pending_efb_copy_invalidated = true; + // The texture data has already been copied into the staging texture, so it's valid to + // optimistically release the texture data. Will slightly lower VRAM usage. + ReleaseToPool(entry.get()); } } + entry->invalidated = true; + return textures_by_address.erase(iter); +} + +void TextureCacheBase::ReleaseToPool(TCacheEntry* entry) +{ + if (!entry->texture) + return; auto config = entry->texture->GetConfig(); texture_pool.emplace(config, TexPoolEntry(std::move(entry->texture), std::move(entry->framebuffer))); - - // Don't delete if there's a pending EFB copy, as we need the TCacheEntry alive. - if (!entry->pending_efb_copy) - delete entry; - - return textures_by_address.erase(iter); } bool TextureCacheBase::CreateUtilityTextures() @@ -2748,7 +2753,7 @@ bool TextureCacheBase::CreateUtilityTextures() return true; } -void TextureCacheBase::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, +void TextureCacheBase::CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_copy, const MathUtil::Rectangle& src_rect, bool scale_by_half, bool linear_filter, EFBCopyFormat dst_format, bool is_intensity, float gamma, @@ -2904,7 +2909,7 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& g_vertex_manager->OnEFBCopyToRAM(); } -bool TextureCacheBase::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, +bool TextureCacheBase::DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, const u8* data, u32 data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index cfcb4afe9e..c0540cd0df 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -19,6 +19,7 @@ #include "Common/BitSet.h" #include "Common/CommonTypes.h" #include "Common/MathUtil.h" +#include "Common/RcPtr.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureConfig.h" @@ -88,6 +89,7 @@ struct EFBCopyParams template <> struct fmt::formatter { + std::shared_ptr state; constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } template auto format(const EFBCopyParams& uid, FormatContext& ctx) const @@ -119,14 +121,16 @@ struct TCacheEntry bool is_efb_copy = false; bool is_custom_tex = false; bool may_have_overlapping_textures = true; - bool tmem_only = false; // indicates that this texture only exists in the tmem cache - bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary - // content, aren't just downscaled + bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary + // content, aren't just downscaled bool should_force_safe_hashing = false; // for XFB bool is_xfb_copy = false; bool is_xfb_container = false; u64 id = 0; + // Indicates that this TCacheEntry has been invalided from textures_by_address + bool invalidated = false; + bool reference_changed = false; // used by xfb to determine when a reference xfb changed // Texture dimensions from the GameCube's point of view @@ -139,7 +143,7 @@ struct TCacheEntry // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when // removing the cache entry - std::multimap::iterator textures_by_hash_iter; + std::multimap>::iterator textures_by_hash_iter; // This is used to keep track of both: // * efb copies used by this partially updated texture @@ -150,12 +154,11 @@ struct TCacheEntry std::unique_ptr pending_efb_copy; u32 pending_efb_copy_width = 0; u32 pending_efb_copy_height = 0; - bool pending_efb_copy_invalidated = false; std::string texture_info_name = ""; explicit TCacheEntry(std::unique_ptr tex, - std::unique_ptr fb); + std::unique_ptr fb); ~TCacheEntry(); @@ -169,7 +172,7 @@ struct TCacheEntry } void SetDimensions(unsigned int _native_width, unsigned int _native_height, - unsigned int _native_levels) + unsigned int _native_levels) { native_width = _native_width; native_height = _native_height; @@ -214,6 +217,8 @@ struct TCacheEntry void DoState(PointerWrap& p); }; +using RcTcacheEntry = Common::rc_ptr; + class TextureCacheBase { public: @@ -231,6 +236,7 @@ public: virtual ~TextureCacheBase(); bool Initialize(); + void Shutdown(); void OnConfigChanged(const VideoConfig& config); void ForceReload(); @@ -240,12 +246,13 @@ public: void Cleanup(int _frameCount); void Invalidate(); + void ReleaseToPool(TCacheEntry* entry); TCacheEntry* Load(const TextureInfo& texture_info); - TCacheEntry* GetTexture(const int textureCacheSafetyColorSampleSize, - const TextureInfo& texture_info); - TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, - MathUtil::Rectangle* display_rect); + RcTcacheEntry GetTexture(const int textureCacheSafetyColorSampleSize, + const TextureInfo& texture_info); + RcTcacheEntry GetXFBTexture(u32 address, u32 width, u32 height, u32 stride, + MathUtil::Rectangle* display_rect); virtual void BindTextures(BitSet32 used_textures); void CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 width, u32 height, @@ -255,11 +262,14 @@ public: bool clamp_bottom, const CopyFilterCoefficients::Values& filter_coefficients); - void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height); + void ScaleTextureCacheEntryTo(RcTcacheEntry& entry, u32 new_width, u32 new_height); // Flushes all pending EFB copies to emulated RAM. void FlushEFBCopies(); + // Flush any Bound textures that can't be reused + void FlushStaleBinds(); + // Texture Serialization void SerializeTexture(AbstractTexture* tex, const TextureConfig& config, PointerWrap& p); std::optional DeserializeTexture(PointerWrap& p); @@ -276,7 +286,7 @@ protected: // width, height are the size of the image in pixels. // aligned_width, aligned_height are the size of the image in pixels, aligned to the block size. // row_stride is the number of bytes for a row of blocks, not pixels. - bool DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, u32 data_size, + bool DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, const u8* data, u32 data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TLUTFormat palette_format); @@ -286,7 +296,7 @@ protected: const MathUtil::Rectangle& src_rect, bool scale_by_half, bool linear_filter, float y_scale, float gamma, bool clamp_top, bool clamp_bottom, const std::array& filter_coefficients); - virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + virtual void CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_copy, const MathUtil::Rectangle& src_rect, bool scale_by_half, bool linear_filter, EFBCopyFormat dst_format, bool is_intensity, float gamma, bool clamp_top, bool clamp_bottom, @@ -295,32 +305,31 @@ protected: alignas(16) u8* temp = nullptr; size_t temp_size = 0; - std::array bound_textures{}; - static std::bitset<8> valid_bind_points; - private: - using TexAddrCache = std::multimap; - using TexHashCache = std::multimap; + using TexAddrCache = std::multimap; + using TexHashCache = std::multimap; + using TexPool = std::unordered_multimap; bool CreateUtilityTextures(); void SetBackupConfig(const VideoConfig& config); - TCacheEntry* GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride); + RcTcacheEntry GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride); - TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt); + RcTcacheEntry ApplyPaletteToEntry(RcTcacheEntry& entry, const u8* palette, TLUTFormat tlutfmt); - TCacheEntry* ReinterpretEntry(const TCacheEntry* existing_entry, TextureFormat new_format); + RcTcacheEntry ReinterpretEntry(const RcTcacheEntry& existing_entry, TextureFormat new_format); - TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette, - TLUTFormat tlutfmt); - void StitchXFBCopy(TCacheEntry* entry_to_update); + RcTcacheEntry DoPartialTextureUpdates(RcTcacheEntry& entry_to_update, const u8* palette, + TLUTFormat tlutfmt); + void StitchXFBCopy(RcTcacheEntry& entry_to_update); - void DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level, bool is_arbitrary); + void DumpTexture(RcTcacheEntry& entry, std::string basename, unsigned int level, + bool is_arbitrary); void CheckTempSize(size_t required_size); - TCacheEntry* AllocateCacheEntry(const TextureConfig& config); + RcTcacheEntry AllocateCacheEntry(const TextureConfig& config); std::optional AllocateTexture(const TextureConfig& config); TexPool::iterator FindMatchingTextureFromPool(const TextureConfig& config); TexAddrCache::iterator GetTexCacheIter(TCacheEntry* entry); @@ -358,8 +367,18 @@ private: void DoSaveState(PointerWrap& p); void DoLoadState(PointerWrap& p); + // textures_by_address is the authoritive version of what's actually "in" the texture cache + // but it's possible for invalidated TCache entries to live on elsewhere TexAddrCache textures_by_address; + + // textures_by_hash is an alternative view of the texture cache + // All textures in here will also be in textures_by_address TexHashCache textures_by_hash; + + // bound_textures are actually active in the current draw + // It's valid for textures to be in here after they've been invalidated + std::array bound_textures{}; + TexPool texture_pool; u64 last_entry_id = 0; @@ -394,7 +413,8 @@ private: // List of pending EFB copies. It is important that the order is preserved for these, // so that overlapping textures are written to guest RAM in the order they are issued. - std::vector m_pending_efb_copies; + // It's valid for textures to live be in here after they've been invalidated + std::vector m_pending_efb_copies; // Staging texture used for readbacks. // We store this in the class so that the same staging texture can be used for multiple diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 8baa6f0be7..8ba3e14d58 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -44,6 +44,7 @@ #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/OpcodeDecoding.h" @@ -339,6 +340,20 @@ void VideoBackendBase::InitializeShared() void VideoBackendBase::ShutdownShared() { + if (g_shader_cache) + g_shader_cache->Shutdown(); + if (g_renderer) + g_renderer->Shutdown(); + if (g_texture_cache) + g_texture_cache->Shutdown(); + + g_perf_query.reset(); + g_texture_cache.reset(); + g_framebuffer_manager.reset(); + g_shader_cache.reset(); + g_vertex_manager.reset(); + g_renderer.reset(); + m_initialized = false; auto& system = Core::System::GetInstance(); From a01d5283ec1dc6a4dc50e4b8da48e6de1c6ac1dd Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Wed, 27 Jul 2022 19:35:51 +1200 Subject: [PATCH 03/60] TextureCache: Add content locking Texture cache occasionally mutates textures for efficiency. Which is awkward if we want to borrow those textures from texture cache to do something else, such as a graphics debugger, or async presentation on another thread. Content locking provides a way to signal that the contents of a texture cache entry should not change. Texture cache will be forced to use alternative strategies. --- Source/Core/VideoCommon/TextureCacheBase.cpp | 42 +++++++++++++------- Source/Core/VideoCommon/TextureCacheBase.h | 14 +++++-- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 84208c8094..6102767e70 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -700,7 +700,8 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) // Even if the texture isn't valid, we still need to create the cache entry object // to update the point in the state state. We'll just throw it away if it's invalid. auto tex = DeserializeTexture(p); - auto entry = Common::make_rc(std::move(tex->texture), std::move(tex->framebuffer)); + auto entry = + std::make_shared(std::move(tex->texture), std::move(tex->framebuffer)); entry->textures_by_hash_iter = textures_by_hash.end(); entry->DoState(p); if (entry->texture && commit_state) @@ -809,6 +810,13 @@ RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_ if (entry_to_update->IsCopy()) return entry_to_update; + if (entry_to_update->IsLocked()) + { + // TODO: Shouldn't be too hard, just need to clone the texture entry + texture contents. + PanicAlertFmt("TextureCache: PartialTextureUpdates of locked textures is not implemented"); + return {}; + } + u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format.texfmt); u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format.texfmt); u32 block_size = block_width * block_height * @@ -1474,8 +1482,11 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp { entry = DoPartialTextureUpdates(iter->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat()); - entry->texture->FinishedRendering(); - return entry; + if (entry) + { + entry->texture->FinishedRendering(); + return entry; + } } } @@ -1511,9 +1522,8 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp if (unconverted_copy != textures_by_address.end()) { - auto decoded_entry = - ApplyPaletteToEntry(unconverted_copy->second, texture_info.GetTlutAddress(), - texture_info.GetTlutFormat()); + auto decoded_entry = ApplyPaletteToEntry( + unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat()); if (decoded_entry) { @@ -1543,8 +1553,11 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp { entry = DoPartialTextureUpdates(hash_iter->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat()); - entry->texture->FinishedRendering(); - return entry; + if (entry) + { + entry->texture->FinishedRendering(); + return entry; + } } ++hash_iter; } @@ -1771,9 +1784,9 @@ RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height return {}; } - // Do we currently have a version of this XFB copy in VRAM? + // Do we currently have a mutable version of this XFB copy in VRAM? RcTcacheEntry entry = GetXFBFromCache(address, width, height, stride); - if (entry) + if (entry && !entry->IsLocked()) { if (entry->is_xfb_container) { @@ -2265,8 +2278,8 @@ void TextureCacheBase::CopyRenderTargetToTexture( entry->may_have_overlapping_textures = false; entry->is_custom_tex = false; - CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter, - dstFormat, isIntensity, gamma, clamp_top, clamp_bottom, + CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter, dstFormat, + isIntensity, gamma, clamp_top, clamp_bottom, GetVRAMCopyFilterCoefficients(filter_coefficients)); if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods)) @@ -2584,7 +2597,7 @@ RcTcacheEntry TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) return {}; auto cacheEntry = - Common::make_rc(std::move(alloc->texture), std::move(alloc->framebuffer)); + std::make_shared(std::move(alloc->texture), std::move(alloc->framebuffer)); cacheEntry->textures_by_hash_iter = textures_by_hash.end(); cacheEntry->id = last_entry_id++; return cacheEntry; @@ -2710,7 +2723,8 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe { // The texture data has already been copied into the staging texture, so it's valid to // optimistically release the texture data. Will slightly lower VRAM usage. - ReleaseToPool(entry.get()); + if (!entry->IsLocked()) + ReleaseToPool(entry.get()); } } entry->invalidated = true; diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index c0540cd0df..8b9010a50e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -19,7 +19,6 @@ #include "Common/BitSet.h" #include "Common/CommonTypes.h" #include "Common/MathUtil.h" -#include "Common/RcPtr.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureConfig.h" @@ -127,6 +126,7 @@ struct TCacheEntry bool is_xfb_copy = false; bool is_xfb_container = false; u64 id = 0; + u32 content_semaphore = 0; // Counts up // Indicates that this TCacheEntry has been invalided from textures_by_address bool invalidated = false; @@ -143,7 +143,7 @@ struct TCacheEntry // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when // removing the cache entry - std::multimap>::iterator textures_by_hash_iter; + std::multimap>::iterator textures_by_hash_iter; // This is used to keep track of both: // * efb copies used by this partially updated texture @@ -194,6 +194,14 @@ struct TCacheEntry other_entry->references.emplace(this); } + // Acquiring a content lock will lock the current contents and prevent texture cache from + // reusing the same entry for a newer version of the texture. + void AcquireContentLock() { content_semaphore++; } + void ReleaseContentLock() { content_semaphore--; } + + // Can this be mutated? + bool IsLocked() const { return content_semaphore > 0; } + void SetXfbCopy(u32 stride); void SetEfbCopy(u32 stride); void SetNotCopy(); @@ -217,7 +225,7 @@ struct TCacheEntry void DoState(PointerWrap& p); }; -using RcTcacheEntry = Common::rc_ptr; +using RcTcacheEntry = std::shared_ptr; class TextureCacheBase { From c38c76abad039c3c0ccb3f7ebb891dc62560f8a3 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Thu, 26 Jan 2023 19:06:39 +1300 Subject: [PATCH 04/60] Refactor to remove virtual from RenderXFBToScreen Slightly simplifies the upcoming refactor --- Source/Core/VideoBackends/OGL/OGLMain.cpp | 2 ++ Source/Core/VideoBackends/OGL/OGLRender.cpp | 16 ++++++-------- Source/Core/VideoBackends/OGL/OGLRender.h | 7 +++--- .../VideoBackends/Software/SWRenderer.cpp | 5 ++--- .../Core/VideoBackends/Software/SWRenderer.h | 5 ++--- Source/Core/VideoCommon/RenderBase.cpp | 22 +++++++++++++++++-- Source/Core/VideoCommon/VideoConfig.h | 1 + 7 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/OGLMain.cpp b/Source/Core/VideoBackends/OGL/OGLMain.cpp index 59d63e8b92..f8ed07dc94 100644 --- a/Source/Core/VideoBackends/OGL/OGLMain.cpp +++ b/Source/Core/VideoBackends/OGL/OGLMain.cpp @@ -115,6 +115,8 @@ void VideoBackend::InitBackendInfo() g_Config.backend_info.bSupportsTextureQueryLevels = false; g_Config.backend_info.bSupportsSettingObjectNames = false; + g_Config.backend_info.bUsesExplictQuadBuffering = true; + g_Config.backend_info.Adapters.clear(); // aamodes - 1 is to stay consistent with D3D (means no AA) diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLRender.cpp index 13549f3d72..b9864ac7a0 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLRender.cpp @@ -963,20 +963,18 @@ void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, BPFunctions::SetScissorAndViewport(); } -void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) +void Renderer::SelectLeftBuffer() { - // Quad-buffered stereo is annoying on GL. - if (g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer) - return ::Renderer::RenderXFBToScreen(target_rc, source_texture, source_rc); - glDrawBuffer(GL_BACK_LEFT); - m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); +} +void Renderer::SelectRightBuffer() +{ glDrawBuffer(GL_BACK_RIGHT); - m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); +} + void Renderer::SelectMainBuffer() +{ glDrawBuffer(GL_BACK); } diff --git a/Source/Core/VideoBackends/OGL/OGLRender.h b/Source/Core/VideoBackends/OGL/OGLRender.h index 72da03ef29..c42543a32b 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.h +++ b/Source/Core/VideoBackends/OGL/OGLRender.h @@ -135,9 +135,6 @@ public: void Flush() override; void WaitForGPUIdle() override; - void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) override; void OnConfigChanged(u32 bits) override; void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, @@ -165,6 +162,10 @@ public: protected: std::unique_ptr CreateBoundingBox() const override; + virtual void SelectLeftBuffer() override; + virtual void SelectRightBuffer() override; + virtual void SelectMainBuffer() override; + private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 5e75b57701..5d233a9b87 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -110,9 +110,8 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel } // Called on the GPU thread -void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) +void SWRenderer::ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) { if (!IsHeadless()) m_window->ShowImage(source_texture, source_rc); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 8aa9aa4af5..7444834a18 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -45,9 +45,8 @@ public: u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} - void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) override; + void ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) override; void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 9c9e564b91..fcfde79bda 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -1492,8 +1492,26 @@ void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, const AbstractTexture* source_texture, const MathUtil::Rectangle& source_rc) { - if (g_ActiveConfig.stereo_mode == StereoMode::SBS || - g_ActiveConfig.stereo_mode == StereoMode::TAB) + if (!g_ActiveConfig.backend_info.bSupportsPostProcessing) + { + ShowImage(source_texture, source_rc); + return + } + + if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && + g_ActiveConfig.backend_info.bUsesExplictQuadBuffering) + { + // Quad-buffered stereo is annoying on GL. + SelectLeftBuffer(); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); + + SelectRightBuffer(); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); + + SelectMainBuffer(); + } + else if (g_ActiveConfig.stereo_mode == StereoMode::SBS || + g_ActiveConfig.stereo_mode == StereoMode::TAB) { const auto [left_rc, right_rc] = ConvertStereoRectangle(target_rc); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index df6f608760..184e599a58 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -214,6 +214,7 @@ struct VideoConfig final u32 MaxTextureSize = 16384; bool bUsesLowerLeftOrigin = false; + bool bUsesExplictQuadBuffering = false; bool bSupportsExclusiveFullscreen = false; bool bSupportsDualSourceBlend = false; From 0d4537d60f695e50379c1542d6c4b1e643e8a5a0 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 27 Jan 2023 17:03:15 +1300 Subject: [PATCH 05/60] Move Presenting, Dumping and ImGui out of Renderer --- Source/Android/jni/MainAndroid.cpp | 11 +- Source/Core/Core/Core.cpp | 10 +- Source/Core/DolphinLib.props | 6 + Source/Core/DolphinNoGUI/PlatformWin32.cpp | 6 +- Source/Core/DolphinNoGUI/PlatformX11.cpp | 6 +- .../Graphics/PostProcessingConfigWindow.cpp | 8 +- Source/Core/DolphinQt/Host.cpp | 9 +- Source/Core/DolphinQt/RenderWidget.cpp | 35 +- Source/Core/VideoBackends/D3D/D3DRender.cpp | 8 +- .../VideoBackends/D3D12/D3D12Renderer.cpp | 8 +- .../Core/VideoBackends/Metal/MTLRenderer.mm | 8 +- Source/Core/VideoBackends/OGL/OGLRender.cpp | 25 +- .../VideoBackends/Software/SWRenderer.cpp | 6 +- .../Core/VideoBackends/Vulkan/VKRenderer.cpp | 11 +- .../Core/VideoBackends/Vulkan/VKSwapChain.cpp | 6 +- Source/Core/VideoCommon/CMakeLists.txt | 6 + Source/Core/VideoCommon/FrameDump.cpp | 29 +- Source/Core/VideoCommon/FrameDump.h | 30 +- Source/Core/VideoCommon/FrameDumper.cpp | 354 ++++++ Source/Core/VideoCommon/FrameDumper.h | 121 ++ Source/Core/VideoCommon/OnScreenUI.cpp | 386 ++++++ Source/Core/VideoCommon/OnScreenUI.h | 78 ++ Source/Core/VideoCommon/PostProcessing.cpp | 3 +- Source/Core/VideoCommon/Present.cpp | 524 ++++++++ Source/Core/VideoCommon/Present.h | 132 ++ Source/Core/VideoCommon/RenderBase.cpp | 1121 +---------------- Source/Core/VideoCommon/RenderBase.h | 196 +-- Source/Core/VideoCommon/ShaderCache.cpp | 9 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 8 + 29 files changed, 1766 insertions(+), 1394 deletions(-) create mode 100644 Source/Core/VideoCommon/FrameDumper.cpp create mode 100644 Source/Core/VideoCommon/FrameDumper.h create mode 100644 Source/Core/VideoCommon/OnScreenUI.cpp create mode 100644 Source/Core/VideoCommon/OnScreenUI.h create mode 100644 Source/Core/VideoCommon/Present.cpp create mode 100644 Source/Core/VideoCommon/Present.h diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 428870d02f..d305878f99 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -59,6 +59,7 @@ #include "UICommon/UICommon.h" #include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -456,8 +457,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang if (s_surf == nullptr) __android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null."); - if (g_renderer) - g_renderer->ChangeSurface(s_surf); + if (g_presenter) + g_presenter->ChangeSurface(s_surf); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*, @@ -483,8 +484,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr std::lock_guard surface_guard(s_surface_lock); - if (g_renderer) - g_renderer->ChangeSurface(nullptr); + if (g_presenter) + g_presenter->ChangeSurface(nullptr); if (s_surf) { @@ -503,7 +504,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurfa JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAspectRatio(JNIEnv*, jclass) { - return g_renderer->CalculateDrawAspectRatio(); + return g_presenter->CalculateDrawAspectRatio(); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv*, jclass) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 7757414834..7d7264bd3c 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -84,9 +84,11 @@ #include "VideoCommon/AsyncRequests.h" #include "VideoCommon/Fifo.h" +#include "VideoCommon/FrameDumper.h" #include "VideoCommon/HiresTextures.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PerformanceMetrics.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -540,8 +542,8 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // Render a single frame without anything on it to clear the screen. // This avoids the game list being displayed while the core is finishing initializing. - g_renderer->BeginUIFrame(); - g_renderer->EndUIFrame(); + g_presenter->BeginUIFrame(); + g_presenter->EndUIFrame(); if (cpu_info.HTT) Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4); @@ -731,13 +733,13 @@ static std::string GenerateScreenshotName() void SaveScreenShot() { - Core::RunAsCPUThread([] { g_renderer->SaveScreenshot(GenerateScreenshotName()); }); + Core::RunAsCPUThread([] { g_frame_dumper->SaveScreenshot(GenerateScreenshotName()); }); } void SaveScreenShot(std::string_view name) { Core::RunAsCPUThread([&name] { - g_renderer->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name)); + g_frame_dumper->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name)); }); } diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 613e5a5e18..69e8f2873e 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -639,6 +639,7 @@ + @@ -668,6 +669,7 @@ + @@ -676,6 +678,7 @@ + @@ -1232,6 +1235,7 @@ + @@ -1254,6 +1258,7 @@ + @@ -1262,6 +1267,7 @@ + diff --git a/Source/Core/DolphinNoGUI/PlatformWin32.cpp b/Source/Core/DolphinNoGUI/PlatformWin32.cpp index d4a940bce5..a34ace70ce 100644 --- a/Source/Core/DolphinNoGUI/PlatformWin32.cpp +++ b/Source/Core/DolphinNoGUI/PlatformWin32.cpp @@ -13,7 +13,7 @@ #include #include -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #include "resource.h" namespace @@ -181,8 +181,8 @@ LRESULT PlatformWin32::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam case WM_SIZE: { - if (g_renderer) - g_renderer->ResizeSurface(); + if (g_presenter) + g_presenter->ResizeSurface(); } break; diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 8dcd93bf52..3f3ea57bc6 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -25,7 +25,7 @@ static constexpr auto X_None = None; #include #include #include "UICommon/X11Utils.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #ifndef HOST_NAME_MAX #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX @@ -263,8 +263,8 @@ void PlatformX11::ProcessEvents() break; case ConfigureNotify: { - if (g_renderer) - g_renderer->ResizeSurface(); + if (g_presenter) + g_presenter->ResizeSurface(); } break; } diff --git a/Source/Core/DolphinQt/Config/Graphics/PostProcessingConfigWindow.cpp b/Source/Core/DolphinQt/Config/Graphics/PostProcessingConfigWindow.cpp index 59907bd39b..c6842c7351 100644 --- a/Source/Core/DolphinQt/Config/Graphics/PostProcessingConfigWindow.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/PostProcessingConfigWindow.cpp @@ -21,7 +21,7 @@ #include "DolphinQt/Config/Graphics/EnhancementsWidget.h" #include "VideoCommon/PostProcessing.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoConfig.h" using ConfigurationOption = VideoCommon::PostProcessingConfiguration::ConfigurationOption; @@ -31,9 +31,9 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren const std::string& shader) : QDialog(parent), m_shader(shader) { - if (g_renderer && g_renderer->GetPostProcessor()) + if (g_presenter && g_presenter->GetPostProcessor()) { - m_post_processor = g_renderer->GetPostProcessor()->GetConfig(); + m_post_processor = g_presenter->GetPostProcessor()->GetConfig(); } else { @@ -52,7 +52,7 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren PostProcessingConfigWindow::~PostProcessingConfigWindow() { m_post_processor->SaveOptionsConfiguration(); - if (!(g_renderer && g_renderer->GetPostProcessor())) + if (!(g_presenter && g_presenter->GetPostProcessor())) { delete m_post_processor; } diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index 01dcc46dc1..5eb2055486 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -37,6 +37,7 @@ #include "UICommon/DiscordPresence.h" #include "VideoCommon/Fifo.cpp" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" @@ -76,9 +77,9 @@ void Host::SetRenderHandle(void* handle) return; m_render_handle = handle; - if (g_renderer) + if (g_presenter) { - g_renderer->ChangeSurface(handle); + g_presenter->ChangeSurface(handle); g_controller_interface.ChangeWindow(handle); } } @@ -190,8 +191,8 @@ void Host::SetRenderFullscreen(bool fullscreen) void Host::ResizeSurface(int new_width, int new_height) { - if (g_renderer) - g_renderer->ResizeSurface(); + if (g_presenter) + g_presenter->ResizeSurface(); } std::vector Host_GetPreferredLocales() diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index 891d3614c8..cebbd490c3 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -32,7 +32,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoConfig.h" #ifdef _WIN32 @@ -487,38 +487,40 @@ void RenderWidget::PassEventToImGui(const QEvent* event) const QKeyEvent* key_event = static_cast(event); const bool is_down = event->type() == QEvent::KeyPress; const u32 key = static_cast(key_event->key() & 0x1FF); - auto lock = g_renderer->GetImGuiLock(); - if (key < std::size(ImGui::GetIO().KeysDown)) - ImGui::GetIO().KeysDown[key] = is_down; + + const char* chars = nullptr; if (is_down) { auto utf8 = key_event->text().toUtf8(); - ImGui::GetIO().AddInputCharactersUTF8(utf8.constData()); + + if (utf8.size()) + chars = utf8.constData(); } + + // Pass the key onto ImGui + g_presenter->SetKey(key, is_down, chars); } break; case QEvent::MouseMove: { - auto lock = g_renderer->GetImGuiLock(); - // Qt multiplies all coordinates by the scaling factor in highdpi mode, giving us "scaled" mouse // coordinates (as if the screen was standard dpi). We need to update the mouse position in // native coordinates, as the UI (and game) is rendered at native resolution. const float scale = devicePixelRatio(); - ImGui::GetIO().MousePos.x = static_cast(event)->pos().x() * scale; - ImGui::GetIO().MousePos.y = static_cast(event)->pos().y() * scale; + float x = static_cast(event)->pos().x() * scale; + float y = static_cast(event)->pos().y() * scale; + + g_presenter->SetMousePos(x, y); } break; case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { - auto lock = g_renderer->GetImGuiLock(); const u32 button_mask = static_cast(static_cast(event)->buttons()); - for (size_t i = 0; i < std::size(ImGui::GetIO().MouseDown); i++) - ImGui::GetIO().MouseDown[i] = (button_mask & (1u << i)) != 0; + g_presenter->SetMousePress(button_mask); } break; @@ -529,7 +531,7 @@ void RenderWidget::PassEventToImGui(const QEvent* event) void RenderWidget::SetImGuiKeyMap() { - static constexpr std::array, 21> key_map{{ + static std::array, 21> key_map{{ {ImGuiKey_Tab, Qt::Key_Tab}, {ImGuiKey_LeftArrow, Qt::Key_Left}, {ImGuiKey_RightArrow, Qt::Key_Right}, @@ -552,11 +554,6 @@ void RenderWidget::SetImGuiKeyMap() {ImGuiKey_Y, Qt::Key_Y}, {ImGuiKey_Z, Qt::Key_Z}, }}; - auto lock = g_renderer->GetImGuiLock(); - if (!ImGui::GetCurrentContext()) - return; - - for (auto [imgui_key, qt_key] : key_map) - ImGui::GetIO().KeyMap[imgui_key] = (qt_key & 0x1FF); + g_presenter->SetKeyMap(key_map); } diff --git a/Source/Core/VideoBackends/D3D/D3DRender.cpp b/Source/Core/VideoBackends/D3D/D3DRender.cpp index 988f9fb6ad..6e045979cb 100644 --- a/Source/Core/VideoBackends/D3D/D3DRender.cpp +++ b/Source/Core/VideoBackends/D3D/D3DRender.cpp @@ -30,6 +30,7 @@ #include "VideoCommon/BPFunctions.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PostProcessing.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" @@ -178,16 +179,15 @@ void Renderer::OnConfigChanged(u32 bits) void Renderer::CheckForSwapChainChanges() { - const bool surface_changed = m_surface_changed.TestAndClear(); + const bool surface_changed = g_presenter->SurfaceChangedTestAndClear(); const bool surface_resized = - m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange(); + g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange(); if (!surface_changed && !surface_resized) return; if (surface_changed) { - m_swap_chain->ChangeSurface(m_new_surface_handle); - m_new_surface_handle = nullptr; + m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle()); } else { diff --git a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp b/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp index f4ef717c07..ab6b1a464b 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp @@ -15,6 +15,7 @@ #include "VideoBackends/D3D12/DX12Texture.h" #include "VideoBackends/D3D12/DX12VertexFormat.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoConfig.h" namespace DX12 @@ -419,9 +420,9 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color) void Renderer::CheckForSwapChainChanges() { - const bool surface_changed = m_surface_changed.TestAndClear(); + const bool surface_changed = g_presenter->SurfaceChangedTestAndClear(); const bool surface_resized = - m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange(); + g_presenter->SurfaceResizedTestAndClear() || m_swap_chain->CheckForFullscreenChange(); if (!surface_changed && !surface_resized) return; @@ -429,8 +430,7 @@ void Renderer::CheckForSwapChainChanges() WaitForGPUIdle(); if (surface_changed) { - m_swap_chain->ChangeSurface(m_new_surface_handle); - m_new_surface_handle = nullptr; + m_swap_chain->ChangeSurface(g_presenter->GetNewSurfaceHandle()); } else { diff --git a/Source/Core/VideoBackends/Metal/MTLRenderer.mm b/Source/Core/VideoBackends/Metal/MTLRenderer.mm index 7e915f045a..9d8ae1d532 100644 --- a/Source/Core/VideoBackends/Metal/MTLRenderer.mm +++ b/Source/Core/VideoBackends/Metal/MTLRenderer.mm @@ -13,6 +13,7 @@ #include "VideoBackends/Metal/MTLVertexManager.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" Metal::Renderer::Renderer(MRCOwned layer, int width, int height, float layer_scale) @@ -480,16 +481,15 @@ std::unique_ptr<::BoundingBox> Metal::Renderer::CreateBoundingBox() const void Metal::Renderer::CheckForSurfaceChange() { - if (!m_surface_changed.TestAndClear()) + if (!g_presenter->SurfaceChangedTestAndClear()) return; - m_layer = MRCRetain(static_cast(m_new_surface_handle)); - m_new_surface_handle = nullptr; + m_layer = MRCRetain(static_cast(g_presenter->GetNewSurfaceHandle())); SetupSurface(); } void Metal::Renderer::CheckForSurfaceResize() { - if (!m_surface_resized.TestAndClear()) + if (!g_presenter->SurfaceResizedTestAndClear()) return; SetupSurface(); } diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLRender.cpp index b9864ac7a0..fba72ba11b 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLRender.cpp @@ -32,6 +32,7 @@ #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PostProcessing.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -973,7 +974,7 @@ void Renderer::SelectRightBuffer() glDrawBuffer(GL_BACK_RIGHT); } - void Renderer::SelectMainBuffer() +void Renderer::SelectMainBuffer() { glDrawBuffer(GL_BACK); } @@ -1074,27 +1075,29 @@ void Renderer::WaitForGPUIdle() void Renderer::CheckForSurfaceChange() { - if (!m_surface_changed.TestAndClear()) + if (!g_presenter->SurfaceChangedTestAndClear()) return; - m_main_gl_context->UpdateSurface(m_new_surface_handle); - m_new_surface_handle = nullptr; + m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle()); + + u32 width = m_main_gl_context->GetBackBufferWidth(); + u32 height = m_main_gl_context->GetBackBufferHeight(); // With a surface change, the window likely has new dimensions. - m_backbuffer_width = m_main_gl_context->GetBackBufferWidth(); - m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); - m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height); + g_presenter->SetBackbuffer(width, height); + m_system_framebuffer->UpdateDimensions(width, height); } void Renderer::CheckForSurfaceResize() { - if (!m_surface_resized.TestAndClear()) + if (!g_presenter->SurfaceResizedTestAndClear()) return; m_main_gl_context->Update(); - m_backbuffer_width = m_main_gl_context->GetBackBufferWidth(); - m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); - m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height); + u32 width = m_main_gl_context->GetBackBufferWidth(); + u32 height = m_main_gl_context->GetBackBufferHeight(); + g_presenter->SetBackbuffer(width, height); + m_system_framebuffer->UpdateDimensions(width, height); } void Renderer::BeginUtilityDrawing() diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 5d233a9b87..887bce177f 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -23,6 +23,7 @@ #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/PixelEngine.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" @@ -63,13 +64,12 @@ SWRenderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture void SWRenderer::BindBackbuffer(const ClearColor& clear_color) { // Look for framebuffer resizes - if (!m_surface_resized.TestAndClear()) + if (!g_presenter->SurfaceResizedTestAndClear()) return; GLContext* context = m_window->GetContext(); context->Update(); - m_backbuffer_width = context->GetBackBufferWidth(); - m_backbuffer_height = context->GetBackBufferHeight(); + g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight()); } class SWShader final : public AbstractShader diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp index 5256d6ada3..19c2b19778 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp @@ -33,6 +33,7 @@ #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -379,7 +380,7 @@ void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_comple void Renderer::CheckForSurfaceChange() { - if (!m_surface_changed.TestAndClear() || !m_swap_chain) + if (!g_presenter->SurfaceChangedTestAndClear() || !m_swap_chain) return; // Submit the current draws up until rendering the XFB. @@ -389,9 +390,8 @@ void Renderer::CheckForSurfaceChange() g_command_buffer_mgr->CheckLastPresentFail(); // Recreate the surface. If this fails we're in trouble. - if (!m_swap_chain->RecreateSurface(m_new_surface_handle)) + if (!m_swap_chain->RecreateSurface(g_presenter->GetNewSurfaceHandle())) PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue."); - m_new_surface_handle = nullptr; // Handle case where the dimensions are now different. OnSwapChainResized(); @@ -399,7 +399,7 @@ void Renderer::CheckForSurfaceChange() void Renderer::CheckForSurfaceResize() { - if (!m_surface_resized.TestAndClear()) + if (!g_presenter->SurfaceResizedTestAndClear()) return; // If we don't have a surface, how can we resize the swap chain? @@ -450,8 +450,7 @@ void Renderer::OnConfigChanged(u32 bits) void Renderer::OnSwapChainResized() { - m_backbuffer_width = m_swap_chain->GetWidth(); - m_backbuffer_height = m_swap_chain->GetHeight(); + g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); } void Renderer::BindFramebuffer(VKFramebuffer* fb) diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp index a7f5bb929d..71fb6bf9ba 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp @@ -15,7 +15,7 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #if defined(VK_USE_PLATFORM_XLIB_KHR) #include @@ -265,8 +265,8 @@ bool SwapChain::CreateSwapChain() VkExtent2D size = surface_capabilities.currentExtent; if (size.width == UINT32_MAX) { - size.width = std::max(g_renderer->GetBackbufferWidth(), 1); - size.height = std::max(g_renderer->GetBackbufferHeight(), 1); + size.width = std::max(g_presenter->GetBackbufferWidth(), 1); + size.height = std::max(g_presenter->GetBackbufferHeight(), 1); } size.width = std::clamp(size.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 4e8bff36fc..1cc23ba7aa 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -34,6 +34,8 @@ add_library(videocommon FramebufferManager.h FramebufferShaderGen.cpp FramebufferShaderGen.h + FrameDumper.cpp + FrameDumper.h FreeLookCamera.cpp FreeLookCamera.h GeometryShaderGen.cpp @@ -82,6 +84,8 @@ add_library(videocommon NetPlayGolfUI.h OnScreenDisplay.cpp OnScreenDisplay.h + OnScreenUI.cpp + OnScreenUI.h OpcodeDecoding.cpp OpcodeDecoding.h PerfQueryBase.cpp @@ -98,6 +102,8 @@ add_library(videocommon PixelShaderManager.h PostProcessing.cpp PostProcessing.h + Present.cpp + Present.h RenderBase.cpp RenderBase.h RenderState.cpp diff --git a/Source/Core/VideoCommon/FrameDump.cpp b/Source/Core/VideoCommon/FrameDump.cpp index 55e0e3daa6..c5edffc04e 100644 --- a/Source/Core/VideoCommon/FrameDump.cpp +++ b/Source/Core/VideoCommon/FrameDump.cpp @@ -37,6 +37,7 @@ extern "C" { #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" +#include "VideoCommon/FrameDumper.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/VideoConfig.h" @@ -157,7 +158,7 @@ std::string AVErrorString(int error) } // namespace -bool FrameDump::Start(int w, int h, u64 start_ticks) +bool FFMpegFrameDump::Start(int w, int h, u64 start_ticks) { if (IsStarted()) return true; @@ -169,7 +170,7 @@ bool FrameDump::Start(int w, int h, u64 start_ticks) return PrepareEncoding(w, h, start_ticks, m_savestate_index); } -bool FrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index) +bool FFMpegFrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_index) { m_context = std::make_unique(); @@ -189,7 +190,7 @@ bool FrameDump::PrepareEncoding(int w, int h, u64 start_ticks, u32 savestate_ind return success; } -bool FrameDump::CreateVideoFile() +bool FFMpegFrameDump::CreateVideoFile() { const std::string& format = g_Config.sDumpFormat; @@ -335,12 +336,12 @@ bool FrameDump::CreateVideoFile() return true; } -bool FrameDump::IsFirstFrameInCurrentFile() const +bool FFMpegFrameDump::IsFirstFrameInCurrentFile() const { return m_context->last_pts == AV_NOPTS_VALUE; } -void FrameDump::AddFrame(const FrameData& frame) +void FFMpegFrameDump::AddFrame(const FrameData& frame) { // Are we even dumping? if (!IsStarted()) @@ -402,7 +403,7 @@ void FrameDump::AddFrame(const FrameData& frame) ProcessPackets(); } -void FrameDump::ProcessPackets() +void FFMpegFrameDump::ProcessPackets() { auto pkt = std::unique_ptr>( av_packet_alloc(), [](AVPacket* packet) { av_packet_free(&packet); }); @@ -440,7 +441,7 @@ void FrameDump::ProcessPackets() } } -void FrameDump::Stop() +void FFMpegFrameDump::Stop() { if (!IsStarted()) return; @@ -457,12 +458,12 @@ void FrameDump::Stop() OSD::AddMessage("Stopped dumping frames"); } -bool FrameDump::IsStarted() const +bool FFMpegFrameDump::IsStarted() const { return m_context != nullptr; } -void FrameDump::CloseVideoFile() +void FFMpegFrameDump::CloseVideoFile() { av_frame_free(&m_context->src_frame); av_frame_free(&m_context->scaled_frame); @@ -480,13 +481,13 @@ void FrameDump::CloseVideoFile() m_context.reset(); } -void FrameDump::DoState(PointerWrap& p) +void FFMpegFrameDump::DoState(PointerWrap& p) { if (p.IsReadMode()) ++m_savestate_index; } -void FrameDump::CheckForConfigChange(const FrameData& frame) +void FFMpegFrameDump::CheckForConfigChange(const FrameData& frame) { bool restart_dump = false; @@ -524,7 +525,7 @@ void FrameDump::CheckForConfigChange(const FrameData& frame) } } -FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const +FrameState FFMpegFrameDump::FetchState(u64 ticks, int frame_number) const { FrameState state; state.ticks = ticks; @@ -537,9 +538,9 @@ FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const return state; } -FrameDump::FrameDump() = default; +FFMpegFrameDump::FFMpegFrameDump() = default; -FrameDump::~FrameDump() +FFMpegFrameDump::~FFMpegFrameDump() { Stop(); } diff --git a/Source/Core/VideoCommon/FrameDump.h b/Source/Core/VideoCommon/FrameDump.h index 23e3bead20..18c98c7720 100644 --- a/Source/Core/VideoCommon/FrameDump.h +++ b/Source/Core/VideoCommon/FrameDump.h @@ -10,32 +10,14 @@ struct FrameDumpContext; class PointerWrap; +struct FrameData; +struct FrameState; -class FrameDump +class FFMpegFrameDump { public: - FrameDump(); - ~FrameDump(); - - // Holds relevant emulation state during a rendered frame for - // when it is later asynchronously written. - struct FrameState - { - u64 ticks = 0; - int frame_number = 0; - u32 savestate_index = 0; - int refresh_rate_num = 0; - int refresh_rate_den = 0; - }; - - struct FrameData - { - const u8* data = nullptr; - int width = 0; - int height = 0; - int stride = 0; - FrameState state; - }; + FFMpegFrameDump(); + ~FFMpegFrameDump(); bool Start(int w, int h, u64 start_ticks); void AddFrame(const FrameData&); @@ -68,7 +50,7 @@ private: inline FrameDump::FrameDump() = default; inline FrameDump::~FrameDump() = default; -inline FrameDump::FrameState FrameDump::FetchState(u64 ticks, int frame_number) const +inline FrameState FrameDump::FetchState(u64 ticks, int frame_number) const { return {}; } diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp new file mode 100644 index 0000000000..db0d099376 --- /dev/null +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -0,0 +1,354 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/FrameDumper.h" + +#include "Common/Assert.h" +#include "Common/FileUtil.h" +#include "Common/Image.h" + +#include "Core/Config/GraphicsSettings.h" +#include "Core/Config/MainSettings.h" + +#include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractStagingTexture.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/Present.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/VideoConfig.h" + +static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) +{ + return Common::ConvertRGBAToRGBAndSavePNG(file_name, frame.data, frame.width, frame.height, + frame.stride, + Config::Get(Config::GFX_PNG_COMPRESSION_LEVEL)); +} + +FrameDumper::FrameDumper() +{ +} + +FrameDumper::~FrameDumper() +{ + ShutdownFrameDumping(); +} + +void FrameDumper::DumpCurrentFrame(const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& target_rect, u64 ticks, + int frame_number) +{ + int source_width = src_rect.GetWidth(); + int source_height = src_rect.GetHeight(); + int target_width = target_rect.GetWidth(); + int target_height = target_rect.GetHeight(); + + // We only need to render a copy if we need to stretch/scale the XFB copy. + MathUtil::Rectangle copy_rect = src_rect; + if (source_width != target_width || source_height != target_height) + { + if (!CheckFrameDumpRenderTexture(target_width, target_height)) + return; + + g_renderer->ScaleTexture(m_frame_dump_render_framebuffer.get(), + m_frame_dump_render_framebuffer->GetRect(), src_texture, src_rect); + src_texture = m_frame_dump_render_texture.get(); + copy_rect = src_texture->GetRect(); + } + + if (!CheckFrameDumpReadbackTexture(target_width, target_height)) + return; + + m_frame_dump_readback_texture->CopyFromTexture(src_texture, copy_rect, 0, 0, + m_frame_dump_readback_texture->GetRect()); + m_last_frame_state = m_ffmpeg_dump.FetchState(ticks, frame_number); + m_frame_dump_needs_flush = true; +} + +bool FrameDumper::CheckFrameDumpRenderTexture(u32 target_width, u32 target_height) +{ + // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used). + // Or, resize texture if it isn't large enough to accommodate the current frame. + if (m_frame_dump_render_texture && m_frame_dump_render_texture->GetWidth() == target_width && + m_frame_dump_render_texture->GetHeight() == target_height) + { + return true; + } + + // Recreate texture, but release before creating so we don't temporarily use twice the RAM. + m_frame_dump_render_framebuffer.reset(); + m_frame_dump_render_texture.reset(); + m_frame_dump_render_texture = g_renderer->CreateTexture( + TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, + AbstractTextureFlag_RenderTarget), + "Frame dump render texture"); + if (!m_frame_dump_render_texture) + { + PanicAlertFmt("Failed to allocate frame dump render texture"); + return false; + } + m_frame_dump_render_framebuffer = + g_renderer->CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr); + ASSERT(m_frame_dump_render_framebuffer); + return true; +} + +bool FrameDumper::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height) +{ + std::unique_ptr& rbtex = m_frame_dump_readback_texture; + if (rbtex && rbtex->GetWidth() == target_width && rbtex->GetHeight() == target_height) + return true; + + rbtex.reset(); + rbtex = g_renderer->CreateStagingTexture( + StagingTextureType::Readback, + TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0)); + if (!rbtex) + return false; + + return true; +} + +void FrameDumper::FlushFrameDump() +{ + if (!m_frame_dump_needs_flush) + return; + + // Ensure dumping thread is done with output texture before swapping. + FinishFrameData(); + + std::swap(m_frame_dump_output_texture, m_frame_dump_readback_texture); + + // Queue encoding of the last frame dumped. + auto& output = m_frame_dump_output_texture; + output->Flush(); + if (output->Map()) + { + DumpFrameData(reinterpret_cast(output->GetMappedPointer()), output->GetConfig().width, + output->GetConfig().height, static_cast(output->GetMappedStride())); + } + else + { + ERROR_LOG_FMT(VIDEO, "Failed to map texture for dumping."); + } + + m_frame_dump_needs_flush = false; + + // Shutdown frame dumping if it is no longer active. + if (!IsFrameDumping()) + ShutdownFrameDumping(); +} + +void FrameDumper::ShutdownFrameDumping() +{ + // Ensure the last queued readback has been sent to the encoder. + FlushFrameDump(); + + if (!m_frame_dump_thread_running.IsSet()) + return; + + // Ensure previous frame has been encoded. + FinishFrameData(); + + // Wake thread up, and wait for it to exit. + m_frame_dump_thread_running.Clear(); + m_frame_dump_start.Set(); + if (m_frame_dump_thread.joinable()) + m_frame_dump_thread.join(); + m_frame_dump_render_framebuffer.reset(); + m_frame_dump_render_texture.reset(); + + m_frame_dump_readback_texture.reset(); + m_frame_dump_output_texture.reset(); +} + +void FrameDumper::DumpFrameData(const u8* data, int w, int h, int stride) +{ + m_frame_dump_data = FrameData{data, w, h, stride, m_last_frame_state}; + + if (!m_frame_dump_thread_running.IsSet()) + { + if (m_frame_dump_thread.joinable()) + m_frame_dump_thread.join(); + m_frame_dump_thread_running.Set(); + m_frame_dump_thread = std::thread(&FrameDumper::FrameDumpThreadFunc, this); + } + + // Wake worker thread up. + m_frame_dump_start.Set(); + m_frame_dump_frame_running = true; +} + +void FrameDumper::FinishFrameData() +{ + if (!m_frame_dump_frame_running) + return; + + m_frame_dump_done.Wait(); + m_frame_dump_frame_running = false; + + m_frame_dump_output_texture->Unmap(); +} + +void FrameDumper::FrameDumpThreadFunc() +{ + Common::SetCurrentThreadName("FrameDumping"); + + bool dump_to_ffmpeg = !g_ActiveConfig.bDumpFramesAsImages; + bool frame_dump_started = false; + +// If Dolphin was compiled without ffmpeg, we only support dumping to images. +#if !defined(HAVE_FFMPEG) + if (dump_to_ffmpeg) + { + WARN_LOG_FMT(VIDEO, "FrameDump: Dolphin was not compiled with FFmpeg, using fallback option. " + "Frames will be saved as PNG images instead."); + dump_to_ffmpeg = false; + } +#endif + + while (true) + { + m_frame_dump_start.Wait(); + if (!m_frame_dump_thread_running.IsSet()) + break; + + auto frame = m_frame_dump_data; + + // Save screenshot + if (m_screenshot_request.TestAndClear()) + { + std::lock_guard lk(m_screenshot_lock); + + if (DumpFrameToPNG(frame, m_screenshot_name)) + OSD::AddMessage("Screenshot saved to " + m_screenshot_name); + + // Reset settings + m_screenshot_name.clear(); + m_screenshot_completed.Set(); + } + + if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) + { + if (!frame_dump_started) + { + if (dump_to_ffmpeg) + frame_dump_started = StartFrameDumpToFFMPEG(frame); + else + frame_dump_started = StartFrameDumpToImage(frame); + + // Stop frame dumping if we fail to start. + if (!frame_dump_started) + Config::SetCurrent(Config::MAIN_MOVIE_DUMP_FRAMES, false); + } + + // If we failed to start frame dumping, don't write a frame. + if (frame_dump_started) + { + if (dump_to_ffmpeg) + DumpFrameToFFMPEG(frame); + else + DumpFrameToImage(frame); + } + } + + m_frame_dump_done.Set(); + } + + if (frame_dump_started) + { + // No additional cleanup is needed when dumping to images. + if (dump_to_ffmpeg) + StopFrameDumpToFFMPEG(); + } +} + +#if defined(HAVE_FFMPEG) + +bool FrameDumper::StartFrameDumpToFFMPEG(const FrameData& frame) +{ + // If dumping started at boot, the start time must be set to the boot time to maintain audio sync. + // TODO: Perhaps we should care about this when starting dumping in the middle of emulation too, + // but it's less important there since the first frame to dump usually gets delivered quickly. + const u64 start_ticks = frame.state.frame_number == 0 ? 0 : frame.state.ticks; + return m_ffmpeg_dump.Start(frame.width, frame.height, start_ticks); +} + +void FrameDumper::DumpFrameToFFMPEG(const FrameData& frame) +{ + m_ffmpeg_dump.AddFrame(frame); +} + +void FrameDumper::StopFrameDumpToFFMPEG() +{ + m_ffmpeg_dump.Stop(); +} + +#else + +bool FrameDump::StartFrameDumpToFFMPEG(const FrameData&) +{ + return false; +} + +void FrameDump::DumpFrameToFFMPEG(const FrameData&) +{ +} + +void FrameDump::StopFrameDumpToFFMPEG() +{ +} + +#endif // defined(HAVE_FFMPEG) + +std::string FrameDumper::GetFrameDumpNextImageFileName() const +{ + return fmt::format("{}framedump_{}.png", File::GetUserPath(D_DUMPFRAMES_IDX), + m_frame_dump_image_counter); +} + +bool FrameDumper::StartFrameDumpToImage(const FrameData&) +{ + m_frame_dump_image_counter = 1; + if (!Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES_SILENT)) + { + // Only check for the presence of the first image to confirm overwriting. + // A previous run will always have at least one image, and it's safe to assume that if the user + // has allowed the first image to be overwritten, this will apply any remaining images as well. + std::string filename = GetFrameDumpNextImageFileName(); + if (File::Exists(filename)) + { + if (!AskYesNoFmtT("Frame dump image(s) '{0}' already exists. Overwrite?", filename)) + return false; + } + } + + return true; +} + +void FrameDumper::DumpFrameToImage(const FrameData& frame) +{ + DumpFrameToPNG(frame, GetFrameDumpNextImageFileName()); + m_frame_dump_image_counter++; +} + +void FrameDumper::SaveScreenshot(std::string filename) +{ + std::lock_guard lk(m_screenshot_lock); + m_screenshot_name = std::move(filename); + m_screenshot_request.Set(); +} + +bool FrameDumper::IsFrameDumping() const +{ + if (m_screenshot_request.IsSet()) + return true; + + if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) + return true; + + return false; +} + +std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h new file mode 100644 index 0000000000..2051346ef9 --- /dev/null +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -0,0 +1,121 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/Event.h" +#include "Common/Flag.h" +#include "Common/MathUtil.h" +#include "Common/Thread.h" + +#include "VideoCommon/FrameDump.h" + +class AbstractStagingTexture; +class AbstractTexture; +class AbstractFramebuffer; + +// Holds relevant emulation state during a rendered frame for +// when it is later asynchronously written. +struct FrameState +{ + u64 ticks = 0; + int frame_number = 0; + u32 savestate_index = 0; + int refresh_rate_num = 0; + int refresh_rate_den = 0; +}; + +struct FrameData +{ + const u8* data = nullptr; + int width = 0; + int height = 0; + int stride = 0; + FrameState state; +}; + +class FrameDumper +{ +public: + FrameDumper(); + ~FrameDumper(); + + // Ensures all rendered frames are queued for encoding. + void FlushFrameDump(); + + // Fills the frame dump staging texture with the current XFB texture. + void DumpCurrentFrame(const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect, + const MathUtil::Rectangle& target_rect, u64 ticks, int frame_number); + + void SaveScreenshot(std::string filename); + + bool IsFrameDumping() const; + + void DoState(PointerWrap& p) { m_ffmpeg_dump.DoState(p); } + +private: + // NOTE: The methods below are called on the framedumping thread. + void FrameDumpThreadFunc(); + bool StartFrameDumpToFFMPEG(const FrameData&); + void DumpFrameToFFMPEG(const FrameData&); + void StopFrameDumpToFFMPEG(); + std::string GetFrameDumpNextImageFileName() const; + bool StartFrameDumpToImage(const FrameData&); + void DumpFrameToImage(const FrameData&); + + void ShutdownFrameDumping(); + + // Checks that the frame dump render texture exists and is the correct size. + bool CheckFrameDumpRenderTexture(u32 target_width, u32 target_height); + + // Checks that the frame dump readback texture exists and is the correct size. + bool CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height); + + // Asynchronously encodes the specified pointer of frame data to the frame dump. + void DumpFrameData(const u8* data, int w, int h, int stride); + + // Ensures all encoded frames have been written to the output file. + void FinishFrameData(); + + std::thread m_frame_dump_thread; + Common::Flag m_frame_dump_thread_running; + + // Used to kick frame dump thread. + Common::Event m_frame_dump_start; + + // Set by frame dump thread on frame completion. + Common::Event m_frame_dump_done; + + // Holds emulation state during the last swap when dumping. + FrameState m_last_frame_state; + + // Communication of frame between video and dump threads. + FrameData m_frame_dump_data; + + // Texture used for screenshot/frame dumping + std::unique_ptr m_frame_dump_render_texture; + std::unique_ptr m_frame_dump_render_framebuffer; + + // Double buffer: + std::unique_ptr m_frame_dump_readback_texture; + std::unique_ptr m_frame_dump_output_texture; + // Set when readback texture holds a frame that needs to be dumped. + bool m_frame_dump_needs_flush = false; + // Set when thread is processing output texture. + bool m_frame_dump_frame_running = false; + + // Used to generate screenshot names. + u32 m_frame_dump_image_counter = 0; + + FFMpegFrameDump m_ffmpeg_dump; + + // Screenshots + Common::Flag m_screenshot_request; + Common::Event m_screenshot_completed; + std::mutex m_screenshot_lock; + std::string m_screenshot_name; +}; + +extern std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp new file mode 100644 index 0000000000..72f0c8f08a --- /dev/null +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -0,0 +1,386 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/OnScreenUI.h" + +#include "Common/Profiler.h" +#include "Common/Timer.h" + +#include "Core/Config/MainSettings.h" +#include "Core/Config/NetplaySettings.h" +#include "Core/Movie.h" + +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/FramebufferShaderGen.h" +#include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/NetPlayGolfUI.h" +#include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/PerformanceMetrics.h" +#include "VideoCommon/Present.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Statistics.h" +#include "VideoCommon/VertexManagerBase.h" +#include "VideoCommon/VideoConfig.h" + +#include +#include + +#include +#include + +namespace VideoCommon +{ +bool OnScreenUI::Initialize(u32 width, u32 height, float scale) +{ + std::unique_lock imgui_lock(m_imgui_mutex); + + if (!IMGUI_CHECKVERSION()) + { + PanicAlertFmt("ImGui version check failed"); + return false; + } + if (!ImGui::CreateContext()) + { + PanicAlertFmt("Creating ImGui context failed"); + return false; + } + if (!ImPlot::CreateContext()) + { + PanicAlertFmt("Creating ImPlot context failed"); + return false; + } + + // Don't create an ini file. TODO: Do we want this in the future? + ImGui::GetIO().IniFilename = nullptr; + SetScale(scale); + ImGui::GetStyle().WindowRounding = 7.0f; + + PortableVertexDeclaration vdecl = {}; + vdecl.position = {ComponentFormat::Float, 2, offsetof(ImDrawVert, pos), true, false}; + vdecl.texcoords[0] = {ComponentFormat::Float, 2, offsetof(ImDrawVert, uv), true, false}; + vdecl.colors[0] = {ComponentFormat::UByte, 4, offsetof(ImDrawVert, col), true, false}; + vdecl.stride = sizeof(ImDrawVert); + m_imgui_vertex_format = g_renderer->CreateNativeVertexFormat(vdecl); + if (!m_imgui_vertex_format) + { + PanicAlertFmt("Failed to create ImGui vertex format"); + return false; + } + + // Font texture(s). + { + ImGuiIO& io = ImGui::GetIO(); + u8* font_tex_pixels; + int font_tex_width, font_tex_height; + io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height); + + TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1, + AbstractTextureFormat::RGBA8, 0); + std::unique_ptr font_tex = + g_renderer->CreateTexture(font_tex_config, "ImGui font texture"); + if (!font_tex) + { + PanicAlertFmt("Failed to create ImGui texture"); + return false; + } + font_tex->Load(0, font_tex_width, font_tex_height, font_tex_width, font_tex_pixels, + sizeof(u32) * font_tex_width * font_tex_height); + + io.Fonts->TexID = font_tex.get(); + + m_imgui_textures.push_back(std::move(font_tex)); + } + + if (!RecompileImGuiPipeline()) + return false; + + m_imgui_last_frame_time = Common::Timer::NowUs(); + m_ready = true; + BeginImGuiFrameUnlocked(width, height); // lock is already held + + return true; +} + +OnScreenUI::~OnScreenUI() +{ + std::unique_lock imgui_lock(m_imgui_mutex); + + ImGui::EndFrame(); + ImPlot::DestroyContext(); + ImGui::DestroyContext(); +} + +bool OnScreenUI::RecompileImGuiPipeline() +{ + if (g_presenter->GetBackbufferFormat() == AbstractTextureFormat::Undefined) + { + // No backbuffer (nogui) means no imgui rendering will happen + // Some backends don't like making pipelines with no render targets + return true; + } + + std::unique_ptr vertex_shader = g_renderer->CreateShaderFromSource( + ShaderStage::Vertex, FramebufferShaderGen::GenerateImGuiVertexShader(), + "ImGui vertex shader"); + std::unique_ptr pixel_shader = g_renderer->CreateShaderFromSource( + ShaderStage::Pixel, FramebufferShaderGen::GenerateImGuiPixelShader(), "ImGui pixel shader"); + if (!vertex_shader || !pixel_shader) + { + PanicAlertFmt("Failed to compile ImGui shaders"); + return false; + } + + // GS is used to render the UI to both eyes in stereo modes. + std::unique_ptr geometry_shader; + if (g_renderer->UseGeometryShaderForUI()) + { + geometry_shader = g_renderer->CreateShaderFromSource( + ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 1), + "ImGui passthrough geometry shader"); + if (!geometry_shader) + { + PanicAlertFmt("Failed to compile ImGui geometry shader"); + return false; + } + } + + AbstractPipelineConfig pconfig = {}; + pconfig.vertex_format = m_imgui_vertex_format.get(); + pconfig.vertex_shader = vertex_shader.get(); + pconfig.geometry_shader = geometry_shader.get(); + pconfig.pixel_shader = pixel_shader.get(); + pconfig.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); + pconfig.depth_state = RenderState::GetNoDepthTestingDepthState(); + pconfig.blending_state = RenderState::GetNoBlendingBlendState(); + pconfig.blending_state.blendenable = true; + pconfig.blending_state.srcfactor = SrcBlendFactor::SrcAlpha; + pconfig.blending_state.dstfactor = DstBlendFactor::InvSrcAlpha; + pconfig.blending_state.srcfactoralpha = SrcBlendFactor::Zero; + pconfig.blending_state.dstfactoralpha = DstBlendFactor::One; + pconfig.framebuffer_state.color_texture_format = g_presenter->GetBackbufferFormat(); + pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined; + pconfig.framebuffer_state.samples = 1; + pconfig.framebuffer_state.per_sample_shading = false; + pconfig.usage = AbstractPipelineUsage::Utility; + m_imgui_pipeline = g_renderer->CreatePipeline(pconfig); + if (!m_imgui_pipeline) + { + PanicAlertFmt("Failed to create imgui pipeline"); + return false; + } + + return true; +} + +void OnScreenUI::BeginImGuiFrame(u32 width, u32 height) +{ + std::unique_lock imgui_lock(m_imgui_mutex); + BeginImGuiFrameUnlocked(width, height); +} + +void OnScreenUI::BeginImGuiFrameUnlocked(u32 width, u32 height) +{ + m_backbuffer_width = width; + m_backbuffer_height = height; + + const u64 current_time_us = Common::Timer::NowUs(); + const u64 time_diff_us = current_time_us - m_imgui_last_frame_time; + const float time_diff_secs = static_cast(time_diff_us / 1000000.0); + m_imgui_last_frame_time = current_time_us; + + // Update I/O with window dimensions. + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize = + ImVec2(static_cast(m_backbuffer_width), static_cast(m_backbuffer_height)); + io.DeltaTime = time_diff_secs; + + ImGui::NewFrame(); +} + +void OnScreenUI::DrawImGui() +{ + ImDrawData* draw_data = ImGui::GetDrawData(); + if (!draw_data) + return; + + g_renderer->SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), + static_cast(m_backbuffer_height), 0.0f, 1.0f); + + // Uniform buffer for draws. + struct ImGuiUbo + { + float u_rcp_viewport_size_mul2[2]; + float padding[2]; + }; + ImGuiUbo ubo = {{1.0f / m_backbuffer_width * 2.0f, 1.0f / m_backbuffer_height * 2.0f}}; + + // Set up common state for drawing. + g_renderer->SetPipeline(m_imgui_pipeline.get()); + g_renderer->SetSamplerState(0, RenderState::GetPointSamplerState()); + g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); + + for (int i = 0; i < draw_data->CmdListsCount; i++) + { + const ImDrawList* cmdlist = draw_data->CmdLists[i]; + if (cmdlist->VtxBuffer.empty() || cmdlist->IdxBuffer.empty()) + return; + + u32 base_vertex, base_index; + g_vertex_manager->UploadUtilityVertices(cmdlist->VtxBuffer.Data, sizeof(ImDrawVert), + cmdlist->VtxBuffer.Size, cmdlist->IdxBuffer.Data, + cmdlist->IdxBuffer.Size, &base_vertex, &base_index); + + for (const ImDrawCmd& cmd : cmdlist->CmdBuffer) + { + if (cmd.UserCallback) + { + cmd.UserCallback(cmdlist, &cmd); + continue; + } + + g_renderer->SetScissorRect(g_renderer->ConvertFramebufferRectangle( + MathUtil::Rectangle( + static_cast(cmd.ClipRect.x), static_cast(cmd.ClipRect.y), + static_cast(cmd.ClipRect.z), static_cast(cmd.ClipRect.w)), + g_renderer->GetCurrentFramebuffer())); + g_renderer->SetTexture(0, reinterpret_cast(cmd.TextureId)); + g_renderer->DrawIndexed(base_index, cmd.ElemCount, base_vertex); + base_index += cmd.ElemCount; + } + } + + // Some capture software (such as OBS) hooks SwapBuffers and uses glBlitFramebuffer to copy our + // back buffer just before swap. Because glBlitFramebuffer honors the scissor test, the capture + // itself will be clipped to whatever bounds were last set by ImGui, resulting in a rather useless + // capture whenever any ImGui windows are open. We'll reset the scissor rectangle to the entire + // viewport here to avoid this problem. + g_renderer->SetScissorRect(g_renderer->ConvertFramebufferRectangle( + MathUtil::Rectangle(0, 0, m_backbuffer_width, m_backbuffer_height), + g_renderer->GetCurrentFramebuffer())); +} + +// Create On-Screen-Messages +void OnScreenUI::DrawDebugText() +{ + const bool show_movie_window = + Config::Get(Config::MAIN_SHOW_FRAME_COUNT) || Config::Get(Config::MAIN_SHOW_LAG) || + Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY) || + Config::Get(Config::MAIN_MOVIE_SHOW_RTC) || Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD); + if (show_movie_window) + { + // Position under the FPS display. + ImGui::SetNextWindowPos( + ImVec2(ImGui::GetIO().DisplaySize.x - 10.f * m_backbuffer_scale, 80.f * m_backbuffer_scale), + ImGuiCond_FirstUseEver, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSizeConstraints( + ImVec2(150.0f * m_backbuffer_scale, 20.0f * m_backbuffer_scale), + ImGui::GetIO().DisplaySize); + if (ImGui::Begin("Movie", nullptr, ImGuiWindowFlags_NoFocusOnAppearing)) + { + if (Movie::IsPlayingInput()) + { + ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, Movie::GetCurrentFrame(), + Movie::GetTotalFrames()); + ImGui::Text("Input: %" PRIu64 " / %" PRIu64, Movie::GetCurrentInputCount(), + Movie::GetTotalInputCount()); + } + else if (Config::Get(Config::MAIN_SHOW_FRAME_COUNT)) + { + ImGui::Text("Frame: %" PRIu64, Movie::GetCurrentFrame()); + ImGui::Text("Input: %" PRIu64, Movie::GetCurrentInputCount()); + } + if (Config::Get(Config::MAIN_SHOW_LAG)) + ImGui::Text("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount()); + if (Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY)) + ImGui::TextUnformatted(Movie::GetInputDisplay().c_str()); + if (Config::Get(Config::MAIN_MOVIE_SHOW_RTC)) + ImGui::TextUnformatted(Movie::GetRTCDisplay().c_str()); + if (Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD)) + ImGui::TextUnformatted(Movie::GetRerecords().c_str()); + } + ImGui::End(); + } + + if (g_ActiveConfig.bOverlayStats) + g_stats.Display(); + + if (g_ActiveConfig.bShowNetPlayMessages && g_netplay_chat_ui) + g_netplay_chat_ui->Display(); + + if (Config::Get(Config::NETPLAY_GOLF_MODE_OVERLAY) && g_netplay_golf_ui) + g_netplay_golf_ui->Display(); + + if (g_ActiveConfig.bOverlayProjStats) + g_stats.DisplayProj(); + + if (g_ActiveConfig.bOverlayScissorStats) + g_stats.DisplayScissor(); + + const std::string profile_output = Common::Profiler::ToString(); + if (!profile_output.empty()) + ImGui::TextUnformatted(profile_output.c_str()); +} + +void OnScreenUI::Finalize() +{ + auto lock = GetImGuiLock(); + + g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); + DrawDebugText(); + OSD::DrawMessages(); + ImGui::Render(); +} + +std::unique_lock OnScreenUI::GetImGuiLock() +{ + return std::unique_lock(m_imgui_mutex); +} + +void OnScreenUI::SetScale(float backbuffer_scale) +{ + ImGui::GetIO().DisplayFramebufferScale.x = backbuffer_scale; + ImGui::GetIO().DisplayFramebufferScale.y = backbuffer_scale; + ImGui::GetIO().FontGlobalScale = backbuffer_scale; + ImGui::GetStyle().ScaleAllSizes(backbuffer_scale); + + m_backbuffer_scale = backbuffer_scale; +} +void OnScreenUI::SetKeyMap(std::span> key_map) +{ + auto lock = GetImGuiLock(); + + if (!ImGui::GetCurrentContext()) + return; + + for (auto [imgui_key, qt_key] : key_map) + ImGui::GetIO().KeyMap[imgui_key] = (qt_key & 0x1FF); +} + +void OnScreenUI::SetKey(u32 key, bool is_down, const char* chars) +{ + auto lock = GetImGuiLock(); + if (key < std::size(ImGui::GetIO().KeysDown)) + ImGui::GetIO().KeysDown[key] = is_down; + + if (chars) + ImGui::GetIO().AddInputCharactersUTF8(chars); +} + +void OnScreenUI::SetMousePos(float x, float y) +{ + auto lock = GetImGuiLock(); + + ImGui::GetIO().MousePos.x = x; + ImGui::GetIO().MousePos.y = y; +} + +void OnScreenUI::SetMousePress(u32 button_mask) +{ + auto lock = GetImGuiLock(); + + for (size_t i = 0; i < std::size(ImGui::GetIO().MouseDown); i++) + ImGui::GetIO().MouseDown[i] = (button_mask & (1u << i)) != 0; +} + +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/OnScreenUI.h b/Source/Core/VideoCommon/OnScreenUI.h new file mode 100644 index 0000000000..ea8eeb6630 --- /dev/null +++ b/Source/Core/VideoCommon/OnScreenUI.h @@ -0,0 +1,78 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +class NativeVertexFormat; +class AbstractTexture; +class AbstractPipeline; + +namespace VideoCommon +{ +class OnScreenUI +{ +public: + OnScreenUI() = default; + ~OnScreenUI(); + + // ImGui initialization depends on being able to create textures and pipelines, so do it last. + bool Initialize(u32 width, u32 height, float scale); + + // Returns a lock for the ImGui mutex, enabling data structures to be modified from outside. + // Use with care, only non-drawing functions should be called from outside the video thread, + // as the drawing is tied to a "frame". + std::unique_lock GetImGuiLock(); + + bool IsReady() { return m_ready; } + + // Sets up ImGui state for the next frame. + // This function itself acquires the ImGui lock, so it should not be held. + void BeginImGuiFrame(u32 width, u32 height); + + // Same as above but without locking the ImGui lock. + void BeginImGuiFrameUnlocked(u32 width, u32 height); + + // Renders ImGui windows to the currently-bound framebuffer. + // Should be called with the ImGui lock held. + void DrawImGui(); + + // Recompiles ImGui pipeline - call when stereo mode changes. + bool RecompileImGuiPipeline(); + + void SetScale(float backbuffer_scale); + + void Finalize(); + + // Receive keyboard and mouse from QT + void SetKeyMap(std::span> key_map); + void SetKey(u32 key, bool is_down, const char* chars); + void SetMousePos(float x, float y); + void SetMousePress(u32 button_mask); + +private: + // Destroys all ImGui GPU resources, must do before shutdown. + void ShutdownImGui(); + + void DrawDebugText(); + + // ImGui resources. + std::unique_ptr m_imgui_vertex_format; + std::vector> m_imgui_textures; + std::unique_ptr m_imgui_pipeline; + std::mutex m_imgui_mutex; + u64 m_imgui_last_frame_time; + + u32 m_backbuffer_width = 1; + u32 m_backbuffer_height = 1; + float m_backbuffer_scale = 1.0; + + bool m_ready = false; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 0a1e995c15..f14b8f892b 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -24,6 +24,7 @@ #include "VideoCommon/AbstractShader.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/VertexManagerBase.h" @@ -627,7 +628,7 @@ size_t PostProcessing::CalculateUniformsSize() const void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle& src, const AbstractTexture* src_tex, int src_layer) { - const auto& window_rect = g_renderer->GetTargetRectangle(); + const auto& window_rect = g_presenter->GetTargetRectangle(); const float rcp_src_width = 1.0f / src_tex->GetWidth(); const float rcp_src_height = 1.0f / src_tex->GetHeight(); BuiltinUniforms builtin_uniforms = { diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp new file mode 100644 index 0000000000..5cddf38aa3 --- /dev/null +++ b/Source/Core/VideoCommon/Present.cpp @@ -0,0 +1,524 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Present.h" + +#include "Core/HW/VideoInterface.h" +#include "Core/Host.h" + +#include "InputCommon/ControllerInterface/ControllerInterface.h" + +#include "VideoCommon/FrameDumper.h" +#include "VideoCommon/OnScreenUI.h" +#include "VideoCommon/PostProcessing.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Statistics.h" +#include "VideoCommon/VertexManagerBase.h" +#include "VideoCommon/VideoConfig.h" + +std::unique_ptr g_presenter; + +namespace VideoCommon +{ +static float AspectToWidescreen(float aspect) +{ + return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); +} + +Presenter::Presenter() +{ +} + +Presenter::~Presenter() +{ + // Disable ControllerInterface's aspect ratio adjustments so mapping dialog behaves normally. + g_controller_interface.SetAspectRatioAdjustment(1); +} + +bool Presenter::Initialize() +{ + UpdateDrawRectangle(); + + m_post_processor = std::make_unique(); + if (!m_post_processor->Initialize(m_backbuffer_format)) + return false; + + m_onscreen_ui = std::make_unique(); + if (!m_onscreen_ui->Initialize(m_backbuffer_width, m_backbuffer_height, m_backbuffer_scale)) + return false; + + return true; +} + +void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height) +{ + m_backbuffer_width = backbuffer_width; + m_backbuffer_height = backbuffer_height; + UpdateDrawRectangle(); +} + +void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, + AbstractTextureFormat backbuffer_format) +{ + m_backbuffer_width = backbuffer_width; + m_backbuffer_height = backbuffer_height; + m_backbuffer_scale = backbuffer_scale; + m_backbuffer_format = backbuffer_format; + UpdateDrawRectangle(); +} + +void Presenter::CheckForConfigChanges(u32 changed_bits) +{ + // Check for post-processing shader changes. Done up here as it doesn't affect anything outside + // the post-processor. Note that options are applied every frame, so no need to check those. + if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) + { + // The existing shader must not be in use when it's destroyed + g_renderer->WaitForGPUIdle(); + + m_post_processor->RecompileShader(); + } + + // Stereo mode change requires recompiling our post processing pipeline and imgui pipelines for + // rendering the UI. + if (changed_bits & Renderer::ConfigChangeBits::CONFIG_CHANGE_BIT_STEREO_MODE) + { + m_onscreen_ui->RecompileImGuiPipeline(); + m_post_processor->RecompilePipeline(); + } +} + +void Presenter::BeginUIFrame() +{ + if (g_renderer->IsHeadless()) + return; + + g_renderer->BeginUtilityDrawing(); + g_renderer->BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); +} + +void Presenter::EndUIFrame() +{ + m_onscreen_ui->Finalize(); + + if (g_renderer->IsHeadless()) + { + m_onscreen_ui->DrawImGui(); + + std::lock_guard guard(m_swap_mutex); + g_renderer->PresentBackbuffer(); + g_renderer->EndUtilityDrawing(); + } + + m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); +} + +std::tuple, MathUtil::Rectangle> +Presenter::ConvertStereoRectangle(const MathUtil::Rectangle& rc) const +{ + // Resize target to half its original size + auto draw_rc = rc; + if (g_ActiveConfig.stereo_mode == StereoMode::TAB) + { + // The height may be negative due to flipped rectangles + int height = rc.bottom - rc.top; + draw_rc.top += height / 4; + draw_rc.bottom -= height / 4; + } + else + { + int width = rc.right - rc.left; + draw_rc.left += width / 4; + draw_rc.right -= width / 4; + } + + // Create two target rectangle offset to the sides of the backbuffer + auto left_rc = draw_rc; + auto right_rc = draw_rc; + if (g_ActiveConfig.stereo_mode == StereoMode::TAB) + { + left_rc.top -= m_backbuffer_height / 4; + left_rc.bottom -= m_backbuffer_height / 4; + right_rc.top += m_backbuffer_height / 4; + right_rc.bottom += m_backbuffer_height / 4; + } + else + { + left_rc.left -= m_backbuffer_width / 4; + left_rc.right -= m_backbuffer_width / 4; + right_rc.left += m_backbuffer_width / 4; + right_rc.right += m_backbuffer_width / 4; + } + + return std::make_tuple(left_rc, right_rc); +} + +float Presenter::CalculateDrawAspectRatio() const +{ + const auto aspect_mode = g_ActiveConfig.aspect_mode; + + // If stretch is enabled, we prefer the aspect ratio of the window. + if (aspect_mode == AspectMode::Stretch) + return (static_cast(m_backbuffer_width) / static_cast(m_backbuffer_height)); + + const float aspect_ratio = VideoInterface::GetAspectRatio(); + + if (aspect_mode == AspectMode::AnalogWide || + (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) + { + return AspectToWidescreen(aspect_ratio); + } + + return aspect_ratio; +} + +void Presenter::AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, + MathUtil::Rectangle* source_rect, int fb_width, + int fb_height) +{ + const int orig_target_width = target_rect->GetWidth(); + const int orig_target_height = target_rect->GetHeight(); + const int orig_source_width = source_rect->GetWidth(); + const int orig_source_height = source_rect->GetHeight(); + if (target_rect->left < 0) + { + const int offset = -target_rect->left; + target_rect->left = 0; + source_rect->left += offset * orig_source_width / orig_target_width; + } + if (target_rect->right > fb_width) + { + const int offset = target_rect->right - fb_width; + target_rect->right -= offset; + source_rect->right -= offset * orig_source_width / orig_target_width; + } + if (target_rect->top < 0) + { + const int offset = -target_rect->top; + target_rect->top = 0; + source_rect->top += offset * orig_source_height / orig_target_height; + } + if (target_rect->bottom > fb_height) + { + const int offset = target_rect->bottom - fb_height; + target_rect->bottom -= offset; + source_rect->bottom -= offset * orig_source_height / orig_target_height; + } +} + +void Presenter::ReleaseXFBContentLock() +{ + if (m_xfb_entry) + m_xfb_entry->ReleaseContentLock(); +} + +void Presenter::ChangeSurface(void* new_surface_handle) +{ + std::lock_guard lock(m_swap_mutex); + m_new_surface_handle = new_surface_handle; + m_surface_changed.Set(); +} + +void Presenter::ResizeSurface() +{ + std::lock_guard lock(m_swap_mutex); + m_surface_resized.Set(); +} + +void* Presenter::GetNewSurfaceHandle() +{ + return m_new_surface_handle; + m_new_surface_handle = nullptr; +} + +void Presenter::SetWindowSize(int width, int height) +{ + const auto [out_width, out_height] = g_presenter->CalculateOutputDimensions(width, height); + + // Track the last values of width/height to avoid sending a window resize event every frame. + if (out_width == m_last_window_request_width && out_height == m_last_window_request_height) + return; + + m_last_window_request_width = out_width; + m_last_window_request_height = out_height; + Host_RequestRenderWindowSize(out_width, out_height); +} + +// Crop to exactly 16:9 or 4:3 if enabled and not AspectMode::Stretch. +std::tuple Presenter::ApplyStandardAspectCrop(float width, float height) const +{ + const auto aspect_mode = g_ActiveConfig.aspect_mode; + + if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch) + return {width, height}; + + // Force 4:3 or 16:9 by cropping the image. + const float current_aspect = width / height; + const float expected_aspect = + (aspect_mode == AspectMode::AnalogWide || + (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) ? + (16.0f / 9.0f) : + (4.0f / 3.0f); + if (current_aspect > expected_aspect) + { + // keep height, crop width + width = height * expected_aspect; + } + else + { + // keep width, crop height + height = width / expected_aspect; + } + + return {width, height}; +} + +void Presenter::UpdateDrawRectangle() +{ + const float draw_aspect_ratio = CalculateDrawAspectRatio(); + + // Update aspect ratio hack values + // Won't take effect until next frame + // Don't know if there is a better place for this code so there isn't a 1 frame delay + if (g_ActiveConfig.bWidescreenHack) + { + float source_aspect = VideoInterface::GetAspectRatio(); + if (g_renderer && g_renderer->IsGameWidescreen()) + source_aspect = AspectToWidescreen(source_aspect); + + const float adjust = source_aspect / draw_aspect_ratio; + if (adjust > 1) + { + // Vert+ + g_Config.fAspectRatioHackW = 1; + g_Config.fAspectRatioHackH = 1 / adjust; + } + else + { + // Hor+ + g_Config.fAspectRatioHackW = adjust; + g_Config.fAspectRatioHackH = 1; + } + } + else + { + // Hack is disabled. + g_Config.fAspectRatioHackW = 1; + g_Config.fAspectRatioHackH = 1; + } + + // The rendering window size + const float win_width = static_cast(m_backbuffer_width); + const float win_height = static_cast(m_backbuffer_height); + + // FIXME: this breaks at very low widget sizes + // Make ControllerInterface aware of the render window region actually being used + // to adjust mouse cursor inputs. + g_controller_interface.SetAspectRatioAdjustment(draw_aspect_ratio / (win_width / win_height)); + + float draw_width = draw_aspect_ratio; + float draw_height = 1; + + // Crop the picture to a standard aspect ratio. (if enabled) + auto [crop_width, crop_height] = ApplyStandardAspectCrop(draw_width, draw_height); + + // scale the picture to fit the rendering window + if (win_width / win_height >= crop_width / crop_height) + { + // the window is flatter than the picture + draw_width *= win_height / crop_height; + crop_width *= win_height / crop_height; + draw_height *= win_height / crop_height; + crop_height = win_height; + } + else + { + // the window is skinnier than the picture + draw_width *= win_width / crop_width; + draw_height *= win_width / crop_width; + crop_height *= win_width / crop_width; + crop_width = win_width; + } + + // ensure divisibility by 4 to make it compatible with all the video encoders + draw_width = std::ceil(draw_width) - static_cast(std::ceil(draw_width)) % 4; + draw_height = std::ceil(draw_height) - static_cast(std::ceil(draw_height)) % 4; + + m_target_rectangle.left = static_cast(std::round(win_width / 2.0 - draw_width / 2.0)); + m_target_rectangle.top = static_cast(std::round(win_height / 2.0 - draw_height / 2.0)); + m_target_rectangle.right = m_target_rectangle.left + static_cast(draw_width); + m_target_rectangle.bottom = m_target_rectangle.top + static_cast(draw_height); +} + +std::tuple Presenter::ScaleToDisplayAspectRatio(const int width, + const int height) const +{ + // Scale either the width or height depending the content aspect ratio. + // This way we preserve as much resolution as possible when scaling. + float scaled_width = static_cast(width); + float scaled_height = static_cast(height); + const float draw_aspect = CalculateDrawAspectRatio(); + if (scaled_width / scaled_height >= draw_aspect) + scaled_height = scaled_width / draw_aspect; + else + scaled_width = scaled_height * draw_aspect; + return std::make_tuple(scaled_width, scaled_height); +} + +std::tuple Presenter::CalculateOutputDimensions(int width, int height) const +{ + width = std::max(width, 1); + height = std::max(height, 1); + + auto [scaled_width, scaled_height] = ScaleToDisplayAspectRatio(width, height); + + // Apply crop if enabled. + std::tie(scaled_width, scaled_height) = ApplyStandardAspectCrop(scaled_width, scaled_height); + + width = static_cast(std::ceil(scaled_width)); + height = static_cast(std::ceil(scaled_height)); + + // UpdateDrawRectangle() makes sure that the rendered image is divisible by four for video + // encoders, so do that here too to match it + width -= width % 4; + height -= height % 4; + + return std::make_tuple(width, height); +} + +void Presenter::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) +{ + if (!g_ActiveConfig.backend_info.bSupportsPostProcessing) + { + g_renderer->ShowImage(source_texture, source_rc); + return; + } + + if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && + g_ActiveConfig.backend_info.bUsesExplictQuadBuffering) + { + // Quad-buffered stereo is annoying on GL. + g_renderer->SelectLeftBuffer(); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); + + g_renderer->SelectRightBuffer(); + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); + + g_renderer->SelectMainBuffer(); + } + else if (g_ActiveConfig.stereo_mode == StereoMode::SBS || + g_ActiveConfig.stereo_mode == StereoMode::TAB) + { + const auto [left_rc, right_rc] = ConvertStereoRectangle(target_rc); + + m_post_processor->BlitFromTexture(left_rc, source_rc, source_texture, 0); + m_post_processor->BlitFromTexture(right_rc, source_rc, source_texture, 1); + } + else + { + m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); + } +} + +bool Presenter::SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb_rect, u64 ticks, + int frame_count) +{ + m_xfb_entry = std::move(xfb_entry); + m_xfb_rect = xfb_rect; + bool is_duplicate_frame = m_last_xfb_id == m_xfb_entry->id; + + if (!is_duplicate_frame || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) + { + Present(); + + if (g_frame_dumper->IsFrameDumping()) + { + MathUtil::Rectangle target_rect; + if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_renderer->IsHeadless()) + { + target_rect = GetTargetRectangle(); + } + else + { + int width, height; + std::tie(width, height) = + CalculateOutputDimensions(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); + target_rect = MathUtil::Rectangle(0, 0, width, height); + } + + g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, + frame_count); + } + } + + return is_duplicate_frame; +} + +void Presenter::Present() +{ + m_last_xfb_id = m_xfb_entry->id; + + // Since we use the common pipelines here and draw vertices if a batch is currently being + // built by the vertex loader, we end up trampling over its pointer, as we share the buffer + // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. + g_vertex_manager->Flush(); + + // Render any UI elements to the draw list. + m_onscreen_ui->Finalize(); + + // Render the XFB to the screen. + g_renderer->BeginUtilityDrawing(); + if (!g_renderer->IsHeadless()) + { + g_renderer->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); + + UpdateDrawRectangle(); + + // Adjust the source rectangle instead of using an oversized viewport to render the XFB. + auto render_target_rc = GetTargetRectangle(); + auto render_source_rc = m_xfb_rect; + AdjustRectanglesToFitBounds(&render_target_rc, &render_source_rc, m_backbuffer_width, + m_backbuffer_height); + RenderXFBToScreen(render_target_rc, m_xfb_entry->texture.get(), render_source_rc); + + m_onscreen_ui->DrawImGui(); + + // Present to the window system. + { + std::lock_guard guard(m_swap_mutex); + g_renderer->PresentBackbuffer(); + } + + // Update the window size based on the frame that was just rendered. + // Due to depending on guest state, we need to call this every frame. + SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); + } + + m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); + + g_renderer->EndUtilityDrawing(); +} + +void Presenter::SetKeyMap(std::span> key_map) +{ + m_onscreen_ui->SetKeyMap(key_map); +} + +void Presenter::SetKey(u32 key, bool is_down, const char* chars) +{ + m_onscreen_ui->SetKey(key, is_down, chars); +} + +void Presenter::SetMousePos(float x, float y) +{ + m_onscreen_ui->SetMousePos(x, y); +} + +void Presenter::SetMousePress(u32 button_mask) +{ + m_onscreen_ui->SetMousePress(button_mask); +} + +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h new file mode 100644 index 0000000000..5f535f7208 --- /dev/null +++ b/Source/Core/VideoCommon/Present.h @@ -0,0 +1,132 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/Flag.h" +#include "Common/MathUtil.h" + +#include "VideoCommon/TextureCacheBase.h" +#include "VideoCommon/TextureConfig.h" + +#include +#include +#include +#include +#include + +class AbstractTexture; + +namespace VideoCommon +{ +class OnScreenUI; +class PostProcessing; + +class Presenter +{ +public: + using ClearColor = std::array; + + Presenter(); + virtual ~Presenter(); + + bool SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb_rect, u64 ticks, + int frame_count); + void Present(); + void ClearLastXfbId() { m_last_xfb_id = std::numeric_limits::max(); } + + bool Initialize(); + + void CheckForConfigChanges(u32 changed_bits); + + // Begins/presents a "UI frame". UI frames do not draw any of the console XFB, but this could + // change in the future. + void BeginUIFrame(); + void EndUIFrame(); + + // Display resolution + int GetBackbufferWidth() const { return m_backbuffer_width; } + int GetBackbufferHeight() const { return m_backbuffer_height; } + float GetBackbufferScale() const { return m_backbuffer_scale; } + AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; } + void SetWindowSize(int width, int height); + void SetBackbuffer(int backbuffer_width, int backbuffer_height); + void SetBackbuffer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, + AbstractTextureFormat backbuffer_format); + + void UpdateDrawRectangle(); + + float CalculateDrawAspectRatio() const; + + // Crops the target rectangle to the framebuffer dimensions, reducing the size of the source + // rectangle if it is greater. Works even if the source and target rectangles don't have a + // 1:1 pixel mapping, scaling as appropriate. + void AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, + MathUtil::Rectangle* source_rect, int fb_width, + int fb_height); + + void ReleaseXFBContentLock(); + + // Draws the specified XFB buffer to the screen, performing any post-processing. + // Assumes that the backbuffer has already been bound and cleared. + virtual void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, + const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc); + + VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); } + // Final surface changing + // This is called when the surface is resized (WX) or the window changes (Android). + void ChangeSurface(void* new_surface_handle); + void ResizeSurface(); + bool SurfaceResizedTestAndClear() { return m_surface_resized.TestAndClear(); } + bool SurfaceChangedTestAndClear() { return m_surface_changed.TestAndClear(); } + void* GetNewSurfaceHandle(); + + void SetKeyMap(std::span> key_map); + + void SetKey(u32 key, bool is_down, const char* chars); + void SetMousePos(float x, float y); + void SetMousePress(u32 button_mask); + + const MathUtil::Rectangle& GetTargetRectangle() const { return m_target_rectangle; } + +private: + std::tuple CalculateOutputDimensions(int width, int height) const; + std::tuple ApplyStandardAspectCrop(float width, float height) const; + std::tuple ScaleToDisplayAspectRatio(int width, int height) const; + + // Use this to convert a single target rectangle to two stereo rectangles + std::tuple, MathUtil::Rectangle> + ConvertStereoRectangle(const MathUtil::Rectangle& rc) const; + + std::mutex m_swap_mutex; + + // Backbuffer (window) size and render area + int m_backbuffer_width = 0; + int m_backbuffer_height = 0; + float m_backbuffer_scale = 1.0f; + AbstractTextureFormat m_backbuffer_format = AbstractTextureFormat::Undefined; + + void* m_new_surface_handle = nullptr; + Common::Flag m_surface_changed; + Common::Flag m_surface_resized; + + MathUtil::Rectangle m_target_rectangle = {}; + + RcTcacheEntry m_xfb_entry; + MathUtil::Rectangle m_xfb_rect; + + // Tracking of XFB textures so we don't render duplicate frames. + u64 m_last_xfb_id = std::numeric_limits::max(); + + // These will be set on the first call to SetWindowSize. + int m_last_window_request_width = 0; + int m_last_window_request_height = 0; + + std::unique_ptr m_post_processor; + std::unique_ptr m_onscreen_ui; +}; + +} // namespace VideoCommon + +extern std::unique_ptr g_presenter; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index fcfde79bda..86725f534e 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -14,34 +14,20 @@ #include "VideoCommon/RenderBase.h" #include -#include #include #include -#include -#include #include #include -#include -#include #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" -#include "Common/FileUtil.h" -#include "Common/Flag.h" -#include "Common/Image.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Common/Profiler.h" -#include "Common/StringUtil.h" -#include "Common/Thread.h" -#include "Common/Timer.h" #include "Core/Config/GraphicsSettings.h" -#include "Core/Config/MainSettings.h" -#include "Core/Config/NetplaySettings.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -49,72 +35,41 @@ #include "Core/FifoPlayer/FifoRecorder.h" #include "Core/FreeLookConfig.h" #include "Core/HW/SystemTimers.h" -#include "Core/HW/VideoInterface.h" -#include "Core/Host.h" -#include "Core/Movie.h" #include "Core/System.h" -#include "InputCommon/ControllerInterface/ControllerInterface.h" - #include "VideoCommon/AbstractFramebuffer.h" -#include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" -#include "VideoCommon/BPFunctions.h" -#include "VideoCommon/BPMemory.h" #include "VideoCommon/BoundingBox.h" -#include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" -#include "VideoCommon/FrameDump.h" +#include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" -#include "VideoCommon/FramebufferShaderGen.h" #include "VideoCommon/FreeLookCamera.h" #include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" -#include "VideoCommon/NetPlayChatUI.h" -#include "VideoCommon/NetPlayGolfUI.h" #include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/OpcodeDecoding.h" +#include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" -#include "VideoCommon/PostProcessing.h" +#include "VideoCommon/Present.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/Statistics.h" -#include "VideoCommon/TextureCacheBase.h" -#include "VideoCommon/TextureDecoder.h" -#include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" -#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoBackendBase.h" -#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" -#include "VideoCommon/XFMemory.h" std::unique_ptr g_renderer; -static float AspectToWidescreen(float aspect) -{ - return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); -} - -static bool DumpFrameToPNG(const FrameDump::FrameData& frame, const std::string& file_name) -{ - return Common::ConvertRGBAToRGBAndSavePNG(file_name, frame.data, frame.width, frame.height, - frame.stride, - Config::Get(Config::GFX_PNG_COMPRESSION_LEVEL)); -} - Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, AbstractTextureFormat backbuffer_format) - : m_backbuffer_width(backbuffer_width), m_backbuffer_height(backbuffer_height), - m_backbuffer_scale(backbuffer_scale), - m_backbuffer_format(backbuffer_format), m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{ - MAX_XFB_HEIGHT} + : m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} { UpdateActiveConfig(); FreeLook::UpdateActiveConfig(); - UpdateDrawRectangle(); CalculateTargetSize(); + g_presenter->SetBackbuffer(backbuffer_width, backbuffer_height, backbuffer_scale, + backbuffer_format); + m_is_game_widescreen = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN); g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type); } @@ -123,13 +78,6 @@ Renderer::~Renderer() = default; bool Renderer::Initialize() { - if (!InitializeImGui()) - return false; - - m_post_processor = std::make_unique(); - if (!m_post_processor->Initialize(m_backbuffer_format)) - return false; - m_bounding_box = CreateBoundingBox(); if (g_ActiveConfig.backend_info.bSupportsBBox && !m_bounding_box->Initialize()) { @@ -153,19 +101,11 @@ bool Renderer::Initialize() m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config); } - return true; + return g_presenter->Initialize(); } void Renderer::Shutdown() { - // Disable ControllerInterface's aspect ratio adjustments so mapping dialog behaves normally. - g_controller_interface.SetAspectRatioAdjustment(1); - - // First stop any framedumping, which might need to dump the last xfb frame. This process - // can require additional graphics sub-systems so it needs to be done first - ShutdownFrameDumping(); - ShutdownImGui(); - m_post_processor.reset(); m_bounding_box.reset(); } @@ -399,9 +339,10 @@ bool Renderer::CalculateTargetSize() { if (g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL) { + auto target_rectangle = g_presenter->GetTargetRectangle(); // Set a scale based on the window size - int width = EFB_WIDTH * m_target_rectangle.GetWidth() / m_last_xfb_width; - int height = EFB_HEIGHT * m_target_rectangle.GetHeight() / m_last_xfb_height; + int width = EFB_WIDTH * target_rectangle.GetWidth() / m_last_xfb_width; + int height = EFB_HEIGHT * target_rectangle.GetHeight() / m_last_xfb_height; m_efb_scale = std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1); } else @@ -429,53 +370,6 @@ bool Renderer::CalculateTargetSize() return false; } -std::tuple, MathUtil::Rectangle> -Renderer::ConvertStereoRectangle(const MathUtil::Rectangle& rc) const -{ - // Resize target to half its original size - auto draw_rc = rc; - if (g_ActiveConfig.stereo_mode == StereoMode::TAB) - { - // The height may be negative due to flipped rectangles - int height = rc.bottom - rc.top; - draw_rc.top += height / 4; - draw_rc.bottom -= height / 4; - } - else - { - int width = rc.right - rc.left; - draw_rc.left += width / 4; - draw_rc.right -= width / 4; - } - - // Create two target rectangle offset to the sides of the backbuffer - auto left_rc = draw_rc; - auto right_rc = draw_rc; - if (g_ActiveConfig.stereo_mode == StereoMode::TAB) - { - left_rc.top -= m_backbuffer_height / 4; - left_rc.bottom -= m_backbuffer_height / 4; - right_rc.top += m_backbuffer_height / 4; - right_rc.bottom += m_backbuffer_height / 4; - } - else - { - left_rc.left -= m_backbuffer_width / 4; - left_rc.right -= m_backbuffer_width / 4; - right_rc.left += m_backbuffer_width / 4; - right_rc.right += m_backbuffer_width / 4; - } - - return std::make_tuple(left_rc, right_rc); -} - -void Renderer::SaveScreenshot(std::string filename) -{ - std::lock_guard lk(m_screenshot_lock); - m_screenshot_name = std::move(filename); - m_screenshot_request.Set(); -} - void Renderer::CheckForConfigChanges() { const ShaderHostConfig old_shader_host_config = ShaderHostConfig::GetCurrent(); @@ -515,16 +409,6 @@ void Renderer::CheckForConfigChanges() if (old_efb_access_tile_size != g_ActiveConfig.iEFBAccessTileSize) g_framebuffer_manager->SetEFBCacheTileSize(std::max(g_ActiveConfig.iEFBAccessTileSize, 0)); - // Check for post-processing shader changes. Done up here as it doesn't affect anything outside - // the post-processor. Note that options are applied every frame, so no need to check those. - if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) - { - // The existing shader must not be in use when it's destroyed - WaitForGPUIdle(); - - m_post_processor->RecompileShader(); - } - // Determine which (if any) settings have changed. ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); u32 changed_bits = 0; @@ -545,6 +429,8 @@ void Renderer::CheckForConfigChanges() if (CalculateTargetSize()) changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; + g_presenter->CheckForConfigChanges(changed_bits); + // No changes? if (changed_bits == 0) return; @@ -581,129 +467,6 @@ void Renderer::CheckForConfigChanges() { BPFunctions::SetScissorAndViewport(); } - - // Stereo mode change requires recompiling our post processing pipeline and imgui pipelines for - // rendering the UI. - if (changed_bits & CONFIG_CHANGE_BIT_STEREO_MODE) - { - RecompileImGuiPipeline(); - m_post_processor->RecompilePipeline(); - } -} - -// Create On-Screen-Messages -void Renderer::DrawDebugText() -{ - const bool show_movie_window = - Config::Get(Config::MAIN_SHOW_FRAME_COUNT) || Config::Get(Config::MAIN_SHOW_LAG) || - Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY) || - Config::Get(Config::MAIN_MOVIE_SHOW_RTC) || Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD); - if (show_movie_window) - { - // Position under the FPS display. - ImGui::SetNextWindowPos( - ImVec2(ImGui::GetIO().DisplaySize.x - 10.f * m_backbuffer_scale, 80.f * m_backbuffer_scale), - ImGuiCond_FirstUseEver, ImVec2(1.0f, 0.0f)); - ImGui::SetNextWindowSizeConstraints( - ImVec2(150.0f * m_backbuffer_scale, 20.0f * m_backbuffer_scale), - ImGui::GetIO().DisplaySize); - if (ImGui::Begin("Movie", nullptr, ImGuiWindowFlags_NoFocusOnAppearing)) - { - if (Movie::IsPlayingInput()) - { - ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, Movie::GetCurrentFrame(), - Movie::GetTotalFrames()); - ImGui::Text("Input: %" PRIu64 " / %" PRIu64, Movie::GetCurrentInputCount(), - Movie::GetTotalInputCount()); - } - else if (Config::Get(Config::MAIN_SHOW_FRAME_COUNT)) - { - ImGui::Text("Frame: %" PRIu64, Movie::GetCurrentFrame()); - ImGui::Text("Input: %" PRIu64, Movie::GetCurrentInputCount()); - } - if (Config::Get(Config::MAIN_SHOW_LAG)) - ImGui::Text("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount()); - if (Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY)) - ImGui::TextUnformatted(Movie::GetInputDisplay().c_str()); - if (Config::Get(Config::MAIN_MOVIE_SHOW_RTC)) - ImGui::TextUnformatted(Movie::GetRTCDisplay().c_str()); - if (Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD)) - ImGui::TextUnformatted(Movie::GetRerecords().c_str()); - } - ImGui::End(); - } - - if (g_ActiveConfig.bOverlayStats) - g_stats.Display(); - - if (g_ActiveConfig.bShowNetPlayMessages && g_netplay_chat_ui) - g_netplay_chat_ui->Display(); - - if (Config::Get(Config::NETPLAY_GOLF_MODE_OVERLAY) && g_netplay_golf_ui) - g_netplay_golf_ui->Display(); - - if (g_ActiveConfig.bOverlayProjStats) - g_stats.DisplayProj(); - - if (g_ActiveConfig.bOverlayScissorStats) - g_stats.DisplayScissor(); - - const std::string profile_output = Common::Profiler::ToString(); - if (!profile_output.empty()) - ImGui::TextUnformatted(profile_output.c_str()); -} - -float Renderer::CalculateDrawAspectRatio() const -{ - const auto aspect_mode = g_ActiveConfig.aspect_mode; - - // If stretch is enabled, we prefer the aspect ratio of the window. - if (aspect_mode == AspectMode::Stretch) - return (static_cast(m_backbuffer_width) / static_cast(m_backbuffer_height)); - - const float aspect_ratio = VideoInterface::GetAspectRatio(); - - if (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && m_is_game_widescreen)) - { - return AspectToWidescreen(aspect_ratio); - } - - return aspect_ratio; -} - -void Renderer::AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, - MathUtil::Rectangle* source_rect, int fb_width, - int fb_height) -{ - const int orig_target_width = target_rect->GetWidth(); - const int orig_target_height = target_rect->GetHeight(); - const int orig_source_width = source_rect->GetWidth(); - const int orig_source_height = source_rect->GetHeight(); - if (target_rect->left < 0) - { - const int offset = -target_rect->left; - target_rect->left = 0; - source_rect->left += offset * orig_source_width / orig_target_width; - } - if (target_rect->right > fb_width) - { - const int offset = target_rect->right - fb_width; - target_rect->right -= offset; - source_rect->right -= offset * orig_source_width / orig_target_width; - } - if (target_rect->top < 0) - { - const int offset = -target_rect->top; - target_rect->top = 0; - source_rect->top += offset * orig_source_height / orig_target_height; - } - if (target_rect->bottom > fb_height) - { - const int offset = target_rect->bottom - fb_height; - target_rect->bottom -= offset; - source_rect->bottom -= offset * orig_source_height / orig_target_height; - } } bool Renderer::IsHeadless() const @@ -711,19 +474,6 @@ bool Renderer::IsHeadless() const return true; } -void Renderer::ChangeSurface(void* new_surface_handle) -{ - std::lock_guard lock(m_swap_mutex); - m_new_surface_handle = new_surface_handle; - m_surface_changed.Set(); -} - -void Renderer::ResizeSurface() -{ - std::lock_guard lock(m_swap_mutex); - m_surface_resized.Set(); -} - void Renderer::SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth, float max_depth) { @@ -804,160 +554,6 @@ MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle return result; } -std::tuple Renderer::ScaleToDisplayAspectRatio(const int width, - const int height) const -{ - // Scale either the width or height depending the content aspect ratio. - // This way we preserve as much resolution as possible when scaling. - float scaled_width = static_cast(width); - float scaled_height = static_cast(height); - const float draw_aspect = CalculateDrawAspectRatio(); - if (scaled_width / scaled_height >= draw_aspect) - scaled_height = scaled_width / draw_aspect; - else - scaled_width = scaled_height * draw_aspect; - return std::make_tuple(scaled_width, scaled_height); -} - -void Renderer::UpdateDrawRectangle() -{ - const float draw_aspect_ratio = CalculateDrawAspectRatio(); - - // Update aspect ratio hack values - // Won't take effect until next frame - // Don't know if there is a better place for this code so there isn't a 1 frame delay - if (g_ActiveConfig.bWidescreenHack) - { - float source_aspect = VideoInterface::GetAspectRatio(); - if (m_is_game_widescreen) - source_aspect = AspectToWidescreen(source_aspect); - - const float adjust = source_aspect / draw_aspect_ratio; - if (adjust > 1) - { - // Vert+ - g_Config.fAspectRatioHackW = 1; - g_Config.fAspectRatioHackH = 1 / adjust; - } - else - { - // Hor+ - g_Config.fAspectRatioHackW = adjust; - g_Config.fAspectRatioHackH = 1; - } - } - else - { - // Hack is disabled. - g_Config.fAspectRatioHackW = 1; - g_Config.fAspectRatioHackH = 1; - } - - // The rendering window size - const float win_width = static_cast(m_backbuffer_width); - const float win_height = static_cast(m_backbuffer_height); - - // FIXME: this breaks at very low widget sizes - // Make ControllerInterface aware of the render window region actually being used - // to adjust mouse cursor inputs. - g_controller_interface.SetAspectRatioAdjustment(draw_aspect_ratio / (win_width / win_height)); - - float draw_width = draw_aspect_ratio; - float draw_height = 1; - - // Crop the picture to a standard aspect ratio. (if enabled) - auto [crop_width, crop_height] = ApplyStandardAspectCrop(draw_width, draw_height); - - // scale the picture to fit the rendering window - if (win_width / win_height >= crop_width / crop_height) - { - // the window is flatter than the picture - draw_width *= win_height / crop_height; - crop_width *= win_height / crop_height; - draw_height *= win_height / crop_height; - crop_height = win_height; - } - else - { - // the window is skinnier than the picture - draw_width *= win_width / crop_width; - draw_height *= win_width / crop_width; - crop_height *= win_width / crop_width; - crop_width = win_width; - } - - // ensure divisibility by 4 to make it compatible with all the video encoders - draw_width = std::ceil(draw_width) - static_cast(std::ceil(draw_width)) % 4; - draw_height = std::ceil(draw_height) - static_cast(std::ceil(draw_height)) % 4; - - m_target_rectangle.left = static_cast(std::round(win_width / 2.0 - draw_width / 2.0)); - m_target_rectangle.top = static_cast(std::round(win_height / 2.0 - draw_height / 2.0)); - m_target_rectangle.right = m_target_rectangle.left + static_cast(draw_width); - m_target_rectangle.bottom = m_target_rectangle.top + static_cast(draw_height); -} - -void Renderer::SetWindowSize(int width, int height) -{ - const auto [out_width, out_height] = CalculateOutputDimensions(width, height); - - // Track the last values of width/height to avoid sending a window resize event every frame. - if (out_width == m_last_window_request_width && out_height == m_last_window_request_height) - return; - - m_last_window_request_width = out_width; - m_last_window_request_height = out_height; - Host_RequestRenderWindowSize(out_width, out_height); -} - -// Crop to exactly 16:9 or 4:3 if enabled and not AspectMode::Stretch. -std::tuple Renderer::ApplyStandardAspectCrop(float width, float height) const -{ - const auto aspect_mode = g_ActiveConfig.aspect_mode; - - if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch) - return {width, height}; - - // Force 4:3 or 16:9 by cropping the image. - const float current_aspect = width / height; - const float expected_aspect = (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && m_is_game_widescreen)) ? - (16.0f / 9.0f) : - (4.0f / 3.0f); - if (current_aspect > expected_aspect) - { - // keep height, crop width - width = height * expected_aspect; - } - else - { - // keep width, crop height - height = width / expected_aspect; - } - - return {width, height}; -} - -std::tuple Renderer::CalculateOutputDimensions(int width, int height) const -{ - width = std::max(width, 1); - height = std::max(height, 1); - - auto [scaled_width, scaled_height] = ScaleToDisplayAspectRatio(width, height); - - // Apply crop if enabled. - std::tie(scaled_width, scaled_height) = ApplyStandardAspectCrop(scaled_width, scaled_height); - - width = static_cast(std::ceil(scaled_width)); - height = static_cast(std::ceil(scaled_height)); - - // UpdateDrawRectangle() makes sure that the rendered image is divisible by four for video - // encoders, so do that here too to match it - width -= width % 4; - height -= height % 4; - - return std::make_tuple(width, height); -} - void Renderer::CheckFifoRecording() { const bool was_recording = OpcodeDecoder::g_record_fifo_data; @@ -994,278 +590,14 @@ void Renderer::RecordVideoMemory() texMem); } -bool Renderer::InitializeImGui() -{ - std::unique_lock imgui_lock(m_imgui_mutex); - - if (!IMGUI_CHECKVERSION()) - { - PanicAlertFmt("ImGui version check failed"); - return false; - } - if (!ImGui::CreateContext()) - { - PanicAlertFmt("Creating ImGui context failed"); - return false; - } - if (!ImPlot::CreateContext()) - { - PanicAlertFmt("Creating ImPlot context failed"); - return false; - } - - // Don't create an ini file. TODO: Do we want this in the future? - ImGui::GetIO().IniFilename = nullptr; - ImGui::GetIO().DisplayFramebufferScale.x = m_backbuffer_scale; - ImGui::GetIO().DisplayFramebufferScale.y = m_backbuffer_scale; - ImGui::GetIO().FontGlobalScale = m_backbuffer_scale; - ImGui::GetStyle().ScaleAllSizes(m_backbuffer_scale); - ImGui::GetStyle().WindowRounding = 7.0f; - - PortableVertexDeclaration vdecl = {}; - vdecl.position = {ComponentFormat::Float, 2, offsetof(ImDrawVert, pos), true, false}; - vdecl.texcoords[0] = {ComponentFormat::Float, 2, offsetof(ImDrawVert, uv), true, false}; - vdecl.colors[0] = {ComponentFormat::UByte, 4, offsetof(ImDrawVert, col), true, false}; - vdecl.stride = sizeof(ImDrawVert); - m_imgui_vertex_format = CreateNativeVertexFormat(vdecl); - if (!m_imgui_vertex_format) - { - PanicAlertFmt("Failed to create ImGui vertex format"); - return false; - } - - // Font texture(s). - { - ImGuiIO& io = ImGui::GetIO(); - u8* font_tex_pixels; - int font_tex_width, font_tex_height; - io.Fonts->GetTexDataAsRGBA32(&font_tex_pixels, &font_tex_width, &font_tex_height); - - TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1, - AbstractTextureFormat::RGBA8, 0); - std::unique_ptr font_tex = - CreateTexture(font_tex_config, "ImGui font texture"); - if (!font_tex) - { - PanicAlertFmt("Failed to create ImGui texture"); - return false; - } - font_tex->Load(0, font_tex_width, font_tex_height, font_tex_width, font_tex_pixels, - sizeof(u32) * font_tex_width * font_tex_height); - - io.Fonts->TexID = font_tex.get(); - - m_imgui_textures.push_back(std::move(font_tex)); - } - - if (!RecompileImGuiPipeline()) - return false; - - m_imgui_last_frame_time = Common::Timer::NowUs(); - BeginImGuiFrameUnlocked(); // lock is already held - return true; -} - -bool Renderer::RecompileImGuiPipeline() -{ - if (m_backbuffer_format == AbstractTextureFormat::Undefined) - { - // No backbuffer (nogui) means no imgui rendering will happen - // Some backends don't like making pipelines with no render targets - return true; - } - - std::unique_ptr vertex_shader = - CreateShaderFromSource(ShaderStage::Vertex, FramebufferShaderGen::GenerateImGuiVertexShader(), - "ImGui vertex shader"); - std::unique_ptr pixel_shader = CreateShaderFromSource( - ShaderStage::Pixel, FramebufferShaderGen::GenerateImGuiPixelShader(), "ImGui pixel shader"); - if (!vertex_shader || !pixel_shader) - { - PanicAlertFmt("Failed to compile ImGui shaders"); - return false; - } - - // GS is used to render the UI to both eyes in stereo modes. - std::unique_ptr geometry_shader; - if (UseGeometryShaderForUI()) - { - geometry_shader = CreateShaderFromSource( - ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 1), - "ImGui passthrough geometry shader"); - if (!geometry_shader) - { - PanicAlertFmt("Failed to compile ImGui geometry shader"); - return false; - } - } - - AbstractPipelineConfig pconfig = {}; - pconfig.vertex_format = m_imgui_vertex_format.get(); - pconfig.vertex_shader = vertex_shader.get(); - pconfig.geometry_shader = geometry_shader.get(); - pconfig.pixel_shader = pixel_shader.get(); - pconfig.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); - pconfig.depth_state = RenderState::GetNoDepthTestingDepthState(); - pconfig.blending_state = RenderState::GetNoBlendingBlendState(); - pconfig.blending_state.blendenable = true; - pconfig.blending_state.srcfactor = SrcBlendFactor::SrcAlpha; - pconfig.blending_state.dstfactor = DstBlendFactor::InvSrcAlpha; - pconfig.blending_state.srcfactoralpha = SrcBlendFactor::Zero; - pconfig.blending_state.dstfactoralpha = DstBlendFactor::One; - pconfig.framebuffer_state.color_texture_format = m_backbuffer_format; - pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined; - pconfig.framebuffer_state.samples = 1; - pconfig.framebuffer_state.per_sample_shading = false; - pconfig.usage = AbstractPipelineUsage::Utility; - m_imgui_pipeline = CreatePipeline(pconfig); - if (!m_imgui_pipeline) - { - PanicAlertFmt("Failed to create imgui pipeline"); - return false; - } - - return true; -} - -void Renderer::ShutdownImGui() -{ - std::unique_lock imgui_lock(m_imgui_mutex); - - ImGui::EndFrame(); - ImPlot::DestroyContext(); - ImGui::DestroyContext(); - m_imgui_pipeline.reset(); - m_imgui_vertex_format.reset(); - m_imgui_textures.clear(); -} - -void Renderer::BeginImGuiFrame() -{ - std::unique_lock imgui_lock(m_imgui_mutex); - BeginImGuiFrameUnlocked(); -} - -void Renderer::BeginImGuiFrameUnlocked() -{ - const u64 current_time_us = Common::Timer::NowUs(); - const u64 time_diff_us = current_time_us - m_imgui_last_frame_time; - const float time_diff_secs = static_cast(time_diff_us / 1000000.0); - m_imgui_last_frame_time = current_time_us; - - // Update I/O with window dimensions. - ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize = - ImVec2(static_cast(m_backbuffer_width), static_cast(m_backbuffer_height)); - io.DeltaTime = time_diff_secs; - - ImGui::NewFrame(); -} - -void Renderer::DrawImGui() -{ - ImDrawData* draw_data = ImGui::GetDrawData(); - if (!draw_data) - return; - - SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), - static_cast(m_backbuffer_height), 0.0f, 1.0f); - - // Uniform buffer for draws. - struct ImGuiUbo - { - float u_rcp_viewport_size_mul2[2]; - float padding[2]; - }; - ImGuiUbo ubo = {{1.0f / m_backbuffer_width * 2.0f, 1.0f / m_backbuffer_height * 2.0f}}; - - // Set up common state for drawing. - SetPipeline(m_imgui_pipeline.get()); - SetSamplerState(0, RenderState::GetPointSamplerState()); - g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); - - for (int i = 0; i < draw_data->CmdListsCount; i++) - { - const ImDrawList* cmdlist = draw_data->CmdLists[i]; - if (cmdlist->VtxBuffer.empty() || cmdlist->IdxBuffer.empty()) - return; - - u32 base_vertex, base_index; - g_vertex_manager->UploadUtilityVertices(cmdlist->VtxBuffer.Data, sizeof(ImDrawVert), - cmdlist->VtxBuffer.Size, cmdlist->IdxBuffer.Data, - cmdlist->IdxBuffer.Size, &base_vertex, &base_index); - - for (const ImDrawCmd& cmd : cmdlist->CmdBuffer) - { - if (cmd.UserCallback) - { - cmd.UserCallback(cmdlist, &cmd); - continue; - } - - SetScissorRect(ConvertFramebufferRectangle( - MathUtil::Rectangle( - static_cast(cmd.ClipRect.x), static_cast(cmd.ClipRect.y), - static_cast(cmd.ClipRect.z), static_cast(cmd.ClipRect.w)), - m_current_framebuffer)); - SetTexture(0, reinterpret_cast(cmd.TextureId)); - DrawIndexed(base_index, cmd.ElemCount, base_vertex); - base_index += cmd.ElemCount; - } - } - - // Some capture software (such as OBS) hooks SwapBuffers and uses glBlitFramebuffer to copy our - // back buffer just before swap. Because glBlitFramebuffer honors the scissor test, the capture - // itself will be clipped to whatever bounds were last set by ImGui, resulting in a rather useless - // capture whenever any ImGui windows are open. We'll reset the scissor rectangle to the entire - // viewport here to avoid this problem. - SetScissorRect(ConvertFramebufferRectangle( - MathUtil::Rectangle(0, 0, m_backbuffer_width, m_backbuffer_height), - m_current_framebuffer)); -} - bool Renderer::UseGeometryShaderForUI() const { // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, - // instead drawing twice and the eye selected by glDrawBuffer() (see - // OGL::Renderer::RenderXFBToScreen). + // instead drawing twice and the eye selected by glDrawBuffer() (see Presenter::RenderXFBToScreen) return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && g_ActiveConfig.backend_info.api_type != APIType::OpenGL; } -std::unique_lock Renderer::GetImGuiLock() -{ - return std::unique_lock(m_imgui_mutex); -} - -void Renderer::BeginUIFrame() -{ - if (IsHeadless()) - return; - - BeginUtilityDrawing(); - BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); -} - -void Renderer::EndUIFrame() -{ - { - auto lock = GetImGuiLock(); - ImGui::Render(); - } - - if (!IsHeadless()) - { - DrawImGui(); - - std::lock_guard guard(m_swap_mutex); - PresentBackbuffer(); - EndUtilityDrawing(); - } - - BeginImGuiFrame(); -} - void Renderer::ForceReloadTextures() { m_force_reload_textures.Set(); @@ -1344,11 +676,12 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 else if (aspect_mode == AspectMode::AnalogWide) m_is_game_widescreen = true; } + UpdateWidescreenHeuristic(); // Ensure the last frame was written to the dump. // This is required even if frame dumping has stopped, since the frame dump is one frame // behind the renderer. - FlushFrameDump(); + g_frame_dumper->FlushFrameDump(); if (g_ActiveConfig.bGraphicMods) { @@ -1360,61 +693,18 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 if (xfb_addr && fb_width && fb_stride && fb_height) { // Get the current XFB from texture cache + + g_presenter->ReleaseXFBContentLock(); + MathUtil::Rectangle xfb_rect; - const auto xfb_entry = + RcTcacheEntry xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); - const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id; - if (xfb_entry && (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame)) + bool is_duplicate_frame = + g_presenter->SubmitXFB(std::move(xfb_entry), xfb_rect, ticks, m_frame_count); + + if (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame) { - m_last_xfb_id = xfb_entry->id; - - // Since we use the common pipelines here and draw vertices if a batch is currently being - // built by the vertex loader, we end up trampling over its pointer, as we share the buffer - // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. - g_vertex_manager->Flush(); - - // Render any UI elements to the draw list. - { - auto lock = GetImGuiLock(); - - g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); - DrawDebugText(); - OSD::DrawMessages(); - ImGui::Render(); - } - - // Render the XFB to the screen. - BeginUtilityDrawing(); - if (!IsHeadless()) - { - BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); - - if (!is_duplicate_frame) - UpdateWidescreenHeuristic(); - - UpdateDrawRectangle(); - - // Adjust the source rectangle instead of using an oversized viewport to render the XFB. - auto render_target_rc = GetTargetRectangle(); - auto render_source_rc = xfb_rect; - AdjustRectanglesToFitBounds(&render_target_rc, &render_source_rc, m_backbuffer_width, - m_backbuffer_height); - RenderXFBToScreen(render_target_rc, xfb_entry->texture.get(), render_source_rc); - - DrawImGui(); - - // Present to the window system. - { - std::lock_guard guard(m_swap_mutex); - PresentBackbuffer(); - } - - // Update the window size based on the frame that was just rendered. - // Due to depending on guest state, we need to call this every frame. - SetWindowSize(xfb_rect.GetWidth(), xfb_rect.GetHeight()); - } - if (!is_duplicate_frame) { DolphinAnalytics::PerformanceSample perf_sample; @@ -1423,9 +713,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); - if (IsFrameDumping()) - DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks, m_frame_count); - // Begin new frame m_frame_count++; g_stats.ResetFrame(); @@ -1433,7 +720,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 g_shader_cache->RetrieveAsyncShaders(); g_vertex_manager->OnEndFrame(); - BeginImGuiFrame(); // We invalidate the pipeline object at the start of the frame. // This is for the rare case where only a single pipeline configuration is used, @@ -1467,8 +753,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 // Handle any config changes, this gets propagated to the backend. CheckForConfigChanges(); g_Config.iSaveTargetId = 0; - - EndUtilityDrawing(); } else { @@ -1488,359 +772,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 } } -void Renderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) -{ - if (!g_ActiveConfig.backend_info.bSupportsPostProcessing) - { - ShowImage(source_texture, source_rc); - return - } - - if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && - g_ActiveConfig.backend_info.bUsesExplictQuadBuffering) - { - // Quad-buffered stereo is annoying on GL. - SelectLeftBuffer(); - m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); - - SelectRightBuffer(); - m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); - - SelectMainBuffer(); - } - else if (g_ActiveConfig.stereo_mode == StereoMode::SBS || - g_ActiveConfig.stereo_mode == StereoMode::TAB) - { - const auto [left_rc, right_rc] = ConvertStereoRectangle(target_rc); - - m_post_processor->BlitFromTexture(left_rc, source_rc, source_texture, 0); - m_post_processor->BlitFromTexture(right_rc, source_rc, source_texture, 1); - } - else - { - m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); - } -} - -bool Renderer::IsFrameDumping() const -{ - if (m_screenshot_request.IsSet()) - return true; - - if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) - return true; - - return false; -} - -void Renderer::DumpCurrentFrame(const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect, u64 ticks, - int frame_number) -{ - int source_width = src_rect.GetWidth(); - int source_height = src_rect.GetHeight(); - int target_width, target_height; - if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless()) - { - auto target_rect = GetTargetRectangle(); - target_width = target_rect.GetWidth(); - target_height = target_rect.GetHeight(); - } - else - { - std::tie(target_width, target_height) = CalculateOutputDimensions(source_width, source_height); - } - - // We only need to render a copy if we need to stretch/scale the XFB copy. - MathUtil::Rectangle copy_rect = src_rect; - if (source_width != target_width || source_height != target_height) - { - if (!CheckFrameDumpRenderTexture(target_width, target_height)) - return; - - ScaleTexture(m_frame_dump_render_framebuffer.get(), m_frame_dump_render_framebuffer->GetRect(), - src_texture, src_rect); - src_texture = m_frame_dump_render_texture.get(); - copy_rect = src_texture->GetRect(); - } - - if (!CheckFrameDumpReadbackTexture(target_width, target_height)) - return; - - m_frame_dump_readback_texture->CopyFromTexture(src_texture, copy_rect, 0, 0, - m_frame_dump_readback_texture->GetRect()); - m_last_frame_state = m_frame_dump.FetchState(ticks, frame_number); - m_frame_dump_needs_flush = true; -} - -bool Renderer::CheckFrameDumpRenderTexture(u32 target_width, u32 target_height) -{ - // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used). - // Or, resize texture if it isn't large enough to accommodate the current frame. - if (m_frame_dump_render_texture && m_frame_dump_render_texture->GetWidth() == target_width && - m_frame_dump_render_texture->GetHeight() == target_height) - { - return true; - } - - // Recreate texture, but release before creating so we don't temporarily use twice the RAM. - m_frame_dump_render_framebuffer.reset(); - m_frame_dump_render_texture.reset(); - m_frame_dump_render_texture = - CreateTexture(TextureConfig(target_width, target_height, 1, 1, 1, - AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget), - "Frame dump render texture"); - if (!m_frame_dump_render_texture) - { - PanicAlertFmt("Failed to allocate frame dump render texture"); - return false; - } - m_frame_dump_render_framebuffer = CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr); - ASSERT(m_frame_dump_render_framebuffer); - return true; -} - -bool Renderer::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height) -{ - std::unique_ptr& rbtex = m_frame_dump_readback_texture; - if (rbtex && rbtex->GetWidth() == target_width && rbtex->GetHeight() == target_height) - return true; - - rbtex.reset(); - rbtex = CreateStagingTexture( - StagingTextureType::Readback, - TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0)); - if (!rbtex) - return false; - - return true; -} - -void Renderer::FlushFrameDump() -{ - if (!m_frame_dump_needs_flush) - return; - - // Ensure dumping thread is done with output texture before swapping. - FinishFrameData(); - - std::swap(m_frame_dump_output_texture, m_frame_dump_readback_texture); - - // Queue encoding of the last frame dumped. - auto& output = m_frame_dump_output_texture; - output->Flush(); - if (output->Map()) - { - DumpFrameData(reinterpret_cast(output->GetMappedPointer()), output->GetConfig().width, - output->GetConfig().height, static_cast(output->GetMappedStride())); - } - else - { - ERROR_LOG_FMT(VIDEO, "Failed to map texture for dumping."); - } - - m_frame_dump_needs_flush = false; - - // Shutdown frame dumping if it is no longer active. - if (!IsFrameDumping()) - ShutdownFrameDumping(); -} - -void Renderer::ShutdownFrameDumping() -{ - // Ensure the last queued readback has been sent to the encoder. - FlushFrameDump(); - - if (!m_frame_dump_thread_running.IsSet()) - return; - - // Ensure previous frame has been encoded. - FinishFrameData(); - - // Wake thread up, and wait for it to exit. - m_frame_dump_thread_running.Clear(); - m_frame_dump_start.Set(); - if (m_frame_dump_thread.joinable()) - m_frame_dump_thread.join(); - m_frame_dump_render_framebuffer.reset(); - m_frame_dump_render_texture.reset(); - - m_frame_dump_readback_texture.reset(); - m_frame_dump_output_texture.reset(); -} - -void Renderer::DumpFrameData(const u8* data, int w, int h, int stride) -{ - m_frame_dump_data = FrameDump::FrameData{data, w, h, stride, m_last_frame_state}; - - if (!m_frame_dump_thread_running.IsSet()) - { - if (m_frame_dump_thread.joinable()) - m_frame_dump_thread.join(); - m_frame_dump_thread_running.Set(); - m_frame_dump_thread = std::thread(&Renderer::FrameDumpThreadFunc, this); - } - - // Wake worker thread up. - m_frame_dump_start.Set(); - m_frame_dump_frame_running = true; -} - -void Renderer::FinishFrameData() -{ - if (!m_frame_dump_frame_running) - return; - - m_frame_dump_done.Wait(); - m_frame_dump_frame_running = false; - - m_frame_dump_output_texture->Unmap(); -} - -void Renderer::FrameDumpThreadFunc() -{ - Common::SetCurrentThreadName("FrameDumping"); - - bool dump_to_ffmpeg = !g_ActiveConfig.bDumpFramesAsImages; - bool frame_dump_started = false; - -// If Dolphin was compiled without ffmpeg, we only support dumping to images. -#if !defined(HAVE_FFMPEG) - if (dump_to_ffmpeg) - { - WARN_LOG_FMT(VIDEO, "FrameDump: Dolphin was not compiled with FFmpeg, using fallback option. " - "Frames will be saved as PNG images instead."); - dump_to_ffmpeg = false; - } -#endif - - while (true) - { - m_frame_dump_start.Wait(); - if (!m_frame_dump_thread_running.IsSet()) - break; - - auto frame = m_frame_dump_data; - - // Save screenshot - if (m_screenshot_request.TestAndClear()) - { - std::lock_guard lk(m_screenshot_lock); - - if (DumpFrameToPNG(frame, m_screenshot_name)) - OSD::AddMessage("Screenshot saved to " + m_screenshot_name); - - // Reset settings - m_screenshot_name.clear(); - m_screenshot_completed.Set(); - } - - if (Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES)) - { - if (!frame_dump_started) - { - if (dump_to_ffmpeg) - frame_dump_started = StartFrameDumpToFFMPEG(frame); - else - frame_dump_started = StartFrameDumpToImage(frame); - - // Stop frame dumping if we fail to start. - if (!frame_dump_started) - Config::SetCurrent(Config::MAIN_MOVIE_DUMP_FRAMES, false); - } - - // If we failed to start frame dumping, don't write a frame. - if (frame_dump_started) - { - if (dump_to_ffmpeg) - DumpFrameToFFMPEG(frame); - else - DumpFrameToImage(frame); - } - } - - m_frame_dump_done.Set(); - } - - if (frame_dump_started) - { - // No additional cleanup is needed when dumping to images. - if (dump_to_ffmpeg) - StopFrameDumpToFFMPEG(); - } -} - -#if defined(HAVE_FFMPEG) - -bool Renderer::StartFrameDumpToFFMPEG(const FrameDump::FrameData& frame) -{ - // If dumping started at boot, the start time must be set to the boot time to maintain audio sync. - // TODO: Perhaps we should care about this when starting dumping in the middle of emulation too, - // but it's less important there since the first frame to dump usually gets delivered quickly. - const u64 start_ticks = frame.state.frame_number == 0 ? 0 : frame.state.ticks; - return m_frame_dump.Start(frame.width, frame.height, start_ticks); -} - -void Renderer::DumpFrameToFFMPEG(const FrameDump::FrameData& frame) -{ - m_frame_dump.AddFrame(frame); -} - -void Renderer::StopFrameDumpToFFMPEG() -{ - m_frame_dump.Stop(); -} - -#else - -bool Renderer::StartFrameDumpToFFMPEG(const FrameDump::FrameData&) -{ - return false; -} - -void Renderer::DumpFrameToFFMPEG(const FrameDump::FrameData&) -{ -} - -void Renderer::StopFrameDumpToFFMPEG() -{ -} - -#endif // defined(HAVE_FFMPEG) - -std::string Renderer::GetFrameDumpNextImageFileName() const -{ - return fmt::format("{}framedump_{}.png", File::GetUserPath(D_DUMPFRAMES_IDX), - m_frame_dump_image_counter); -} - -bool Renderer::StartFrameDumpToImage(const FrameDump::FrameData&) -{ - m_frame_dump_image_counter = 1; - if (!Config::Get(Config::MAIN_MOVIE_DUMP_FRAMES_SILENT)) - { - // Only check for the presence of the first image to confirm overwriting. - // A previous run will always have at least one image, and it's safe to assume that if the user - // has allowed the first image to be overwritten, this will apply any remaining images as well. - std::string filename = GetFrameDumpNextImageFileName(); - if (File::Exists(filename)) - { - if (!AskYesNoFmtT("Frame dump image(s) '{0}' already exists. Overwrite?", filename)) - return false; - } - } - - return true; -} - -void Renderer::DumpFrameToImage(const FrameDump::FrameData& frame) -{ - DumpFrameToPNG(frame, GetFrameDumpNextImageFileName()); - m_frame_dump_image_counter++; -} - bool Renderer::UseVertexDepthRange() const { // We can't compute the depth range in the vertex shader if we don't support depth clamp. @@ -1877,7 +808,7 @@ void Renderer::DoState(PointerWrap& p) if (p.IsReadMode()) { // Force the next xfb to be displayed. - m_last_xfb_id = std::numeric_limits::max(); + g_presenter->ClearLastXfbId(); m_was_orthographically_anamorphic = false; @@ -1886,7 +817,7 @@ void Renderer::DoState(PointerWrap& p) } #if defined(HAVE_FFMPEG) - m_frame_dump.DoState(p); + g_frame_dumper->DoState(p); #endif } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 853a840f39..17fe407618 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -23,16 +23,10 @@ #include #include "Common/CommonTypes.h" -#include "Common/Event.h" #include "Common/Flag.h" #include "Common/MathUtil.h" -#include "VideoCommon/AsyncShaderCompiler.h" -#include "VideoCommon/BPMemory.h" -#include "VideoCommon/FrameDump.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" -#include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/RenderState.h" -#include "VideoCommon/TextureConfig.h" class AbstractFramebuffer; class AbstractPipeline; @@ -41,23 +35,22 @@ class AbstractTexture; class AbstractStagingTexture; class BoundingBox; class NativeVertexFormat; -class NetPlayChatUI; class PixelShaderManager; class PointerWrap; -struct TextureConfig; struct ComputePipelineConfig; struct AbstractPipelineConfig; struct PortableVertexDeclaration; +struct TextureConfig; +enum class AbstractTextureFormat : u32; enum class ShaderStage; enum class EFBAccessType; enum class EFBReinterpretType; enum class StagingTextureType; -enum class AspectMode; namespace VideoCommon { -class PostProcessing; -} // namespace VideoCommon +class AsyncShaderCompiler; +} struct EfbPokeData { @@ -145,11 +138,6 @@ public: // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } - // Display resolution - int GetBackbufferWidth() const { return m_backbuffer_width; } - int GetBackbufferHeight() const { return m_backbuffer_height; } - float GetBackbufferScale() const { return m_backbuffer_scale; } - void SetWindowSize(int width, int height); // Sets viewport and scissor to the specified rectangle. rect is assumed to be in framebuffer // coordinates, i.e. lower-left origin in OpenGL. @@ -174,25 +162,6 @@ public: // Use this to convert a whole native EFB rect to backbuffer coordinates MathUtil::Rectangle ConvertEFBRectangle(const MathUtil::Rectangle& rc) const; - const MathUtil::Rectangle& GetTargetRectangle() const { return m_target_rectangle; } - float CalculateDrawAspectRatio() const; - - // Crops the target rectangle to the framebuffer dimensions, reducing the size of the source - // rectangle if it is greater. Works even if the source and target rectangles don't have a - // 1:1 pixel mapping, scaling as appropriate. - void AdjustRectanglesToFitBounds(MathUtil::Rectangle* target_rect, - MathUtil::Rectangle* source_rect, int fb_width, - int fb_height); - - std::tuple ScaleToDisplayAspectRatio(int width, int height) const; - void UpdateDrawRectangle(); - - std::tuple ApplyStandardAspectCrop(float width, float height) const; - - // Use this to convert a single target rectangle to two stereo rectangles - std::tuple, MathUtil::Rectangle> - ConvertStereoRectangle(const MathUtil::Rectangle& rc) const; - unsigned int GetEFBScale() const; // Use this to upscale native EFB coordinates to IDEAL internal resolution @@ -203,10 +172,6 @@ public: float EFBToScaledXf(float x) const; float EFBToScaledYf(float y) const; - // Random utilities - void SaveScreenshot(std::string filename); - void DrawDebugText(); - virtual void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); @@ -230,12 +195,18 @@ public: void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); void UpdateWidescreenHeuristic(); + bool IsGameWidescreen() const { return m_is_game_widescreen; } - // Draws the specified XFB buffer to the screen, performing any post-processing. - // Assumes that the backbuffer has already been bound and cleared. - virtual void RenderXFBToScreen(const MathUtil::Rectangle& target_rc, - const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc); + // A simple presentation fallback, only used by video software + virtual void ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) + { + } + + // For opengl's glDrawBuffer + virtual void SelectLeftBuffer() {} + virtual void SelectRightBuffer() {} + virtual void SelectMainBuffer() {} // Called when the configuration changes, and backend structures need to be updated. virtual void OnConfigChanged(u32 bits) {} @@ -243,11 +214,7 @@ public: PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } bool EFBHasAlphaChannel() const; - VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); } - // Final surface changing - // This is called when the surface is resized (WX) or the window changes (Android). - void ChangeSurface(void* new_surface_handle); - void ResizeSurface(); + bool UseVertexDepthRange() const; void DoState(PointerWrap& p); @@ -257,22 +224,11 @@ public: // interface and final XFB. bool UseGeometryShaderForUI() const; - // Returns a lock for the ImGui mutex, enabling data structures to be modified from outside. - // Use with care, only non-drawing functions should be called from outside the video thread, - // as the drawing is tied to a "frame". - std::unique_lock GetImGuiLock(); - - // Begins/presents a "UI frame". UI frames do not draw any of the console XFB, but this could - // change in the future. - void BeginUIFrame(); - void EndUIFrame(); - // Will forcibly reload all textures on the next swap void ForceReloadTextures(); const GraphicsModManager& GetGraphicsModManager() const; -protected: // Bitmask containing information about which configuration has changed for the backend. enum ConfigChangeBits : u32 { @@ -286,6 +242,7 @@ protected: CONFIG_CHANGE_BIT_BBOX = (1 << 7) }; +protected: std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); @@ -294,36 +251,11 @@ protected: void CheckFifoRecording(); void RecordVideoMemory(); - // ImGui initialization depends on being able to create textures and pipelines, so do it last. - bool InitializeImGui(); - - // Recompiles ImGui pipeline - call when stereo mode changes. - bool RecompileImGuiPipeline(); - - // Sets up ImGui state for the next frame. - // This function itself acquires the ImGui lock, so it should not be held. - void BeginImGuiFrame(); - - // Same as above but without locking the ImGui lock. - void BeginImGuiFrameUnlocked(); - - // Destroys all ImGui GPU resources, must do before shutdown. - void ShutdownImGui(); - - // Renders ImGui windows to the currently-bound framebuffer. - // Should be called with the ImGui lock held. - void DrawImGui(); - virtual std::unique_ptr CreateBoundingBox() const = 0; AbstractFramebuffer* m_current_framebuffer = nullptr; const AbstractPipeline* m_current_pipeline = nullptr; - Common::Flag m_screenshot_request; - Common::Event m_screenshot_completed; - std::mutex m_screenshot_lock; - std::string m_screenshot_name; - bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; @@ -331,72 +263,12 @@ protected: int m_target_width = 1; int m_target_height = 1; - // Backbuffer (window) size and render area - int m_backbuffer_width = 0; - int m_backbuffer_height = 0; - float m_backbuffer_scale = 1.0f; - AbstractTextureFormat m_backbuffer_format = AbstractTextureFormat::Undefined; - MathUtil::Rectangle m_target_rectangle = {}; int m_frame_count = 0; - std::unique_ptr m_post_processor; - - void* m_new_surface_handle = nullptr; - Common::Flag m_surface_changed; - Common::Flag m_surface_resized; - std::mutex m_swap_mutex; - - // ImGui resources. - std::unique_ptr m_imgui_vertex_format; - std::vector> m_imgui_textures; - std::unique_ptr m_imgui_pipeline; - std::mutex m_imgui_mutex; - u64 m_imgui_last_frame_time; - private: - std::tuple CalculateOutputDimensions(int width, int height) const; - PixelFormat m_prev_efb_format = PixelFormat::INVALID_FMT; unsigned int m_efb_scale = 1; - // These will be set on the first call to SetWindowSize. - int m_last_window_request_width = 0; - int m_last_window_request_height = 0; - - // frame dumping: - FrameDump m_frame_dump; - std::thread m_frame_dump_thread; - Common::Flag m_frame_dump_thread_running; - - // Used to kick frame dump thread. - Common::Event m_frame_dump_start; - - // Set by frame dump thread on frame completion. - Common::Event m_frame_dump_done; - - // Holds emulation state during the last swap when dumping. - FrameDump::FrameState m_last_frame_state; - - // Communication of frame between video and dump threads. - FrameDump::FrameData m_frame_dump_data; - - // Texture used for screenshot/frame dumping - std::unique_ptr m_frame_dump_render_texture; - std::unique_ptr m_frame_dump_render_framebuffer; - - // Double buffer: - std::unique_ptr m_frame_dump_readback_texture; - std::unique_ptr m_frame_dump_output_texture; - // Set when readback texture holds a frame that needs to be dumped. - bool m_frame_dump_needs_flush = false; - // Set when thread is processing output texture. - bool m_frame_dump_frame_running = false; - - // Used to generate screenshot names. - u32 m_frame_dump_image_counter = 0; - - // Tracking of XFB textures so we don't render duplicate frames. - u64 m_last_xfb_id = std::numeric_limits::max(); u64 m_last_xfb_ticks = 0; u32 m_last_xfb_addr = 0; u32 m_last_xfb_width = 0; @@ -417,40 +289,6 @@ private: // Ultimate Spider-Man to crash std::array m_bounding_box_fallback = {}; - // NOTE: The methods below are called on the framedumping thread. - void FrameDumpThreadFunc(); - bool StartFrameDumpToFFMPEG(const FrameDump::FrameData&); - void DumpFrameToFFMPEG(const FrameDump::FrameData&); - void StopFrameDumpToFFMPEG(); - std::string GetFrameDumpNextImageFileName() const; - bool StartFrameDumpToImage(const FrameDump::FrameData&); - void DumpFrameToImage(const FrameDump::FrameData&); - - void ShutdownFrameDumping(); - - bool IsFrameDumping() const; - - // Checks that the frame dump render texture exists and is the correct size. - bool CheckFrameDumpRenderTexture(u32 target_width, u32 target_height); - - // Checks that the frame dump readback texture exists and is the correct size. - bool CheckFrameDumpReadbackTexture(u32 target_width, u32 target_height); - - // Fills the frame dump staging texture with the current XFB texture. - void DumpCurrentFrame(const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect, u64 ticks, int frame_number); - - // Asynchronously encodes the specified pointer of frame data to the frame dump. - void DumpFrameData(const u8* data, int w, int h, int stride); - - // Ensures all rendered frames are queued for encoding. - void FlushFrameDump(); - - // Ensures all encoded frames have been written to the output file. - void FinishFrameData(); - - std::unique_ptr m_netplay_chat_ui; - Common::Flag m_force_reload_textures; GraphicsModManager m_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 59ae917686..1be1ad1167 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -14,6 +14,7 @@ #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FramebufferShaderGen.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" @@ -162,7 +163,7 @@ void ShaderCache::WaitForAsyncCompiler() bool running = true; constexpr auto update_ui_progress = [](size_t completed, size_t total) { - g_renderer->BeginUIFrame(); + g_presenter->BeginUIFrame(); const float center_x = ImGui::GetIO().DisplaySize.x * 0.5f; const float center_y = ImGui::GetIO().DisplaySize.y * 0.5f; @@ -183,7 +184,7 @@ void ShaderCache::WaitForAsyncCompiler() } ImGui::End(); - g_renderer->EndUIFrame(); + g_presenter->EndUIFrame(); }; while (running && @@ -195,8 +196,8 @@ void ShaderCache::WaitForAsyncCompiler() } // Just render nothing to clear the screen - g_renderer->BeginUIFrame(); - g_renderer->EndUIFrame(); + g_presenter->BeginUIFrame(); + g_presenter->EndUIFrame(); } template diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 8ba3e14d58..167848b425 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -44,12 +44,14 @@ #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" +#include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/TMEM.h" #include "VideoCommon/TextureCacheBase.h" @@ -322,6 +324,8 @@ void VideoBackendBase::InitializeShared() // do not initialize again for the config window m_initialized = true; + g_presenter = std::make_unique(); + auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); command_processor.Init(system); @@ -333,6 +337,7 @@ void VideoBackendBase::InitializeShared() system.GetGeometryShaderManager().Init(); system.GetPixelShaderManager().Init(); TMEM::Init(); + g_frame_dumper = std::make_unique(); g_Config.VerifyValidity(); UpdateActiveConfig(); @@ -340,6 +345,9 @@ void VideoBackendBase::InitializeShared() void VideoBackendBase::ShutdownShared() { + g_frame_dumper.reset(); + g_presenter.reset(); + if (g_shader_cache) g_shader_cache->Shutdown(); if (g_renderer) From e57eb04ed3b40e806fa081fc1bdaeaa0c03e4e66 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 13:50:57 +1300 Subject: [PATCH 06/60] Rename FrameDump.{cpp,h} to FrameDumpFFMpeg.{cpp,h} The name kind of conflicts with a new FrameDumper class --- Source/Core/Core/State.cpp | 2 +- Source/Core/DolphinLib.props | 4 ++-- Source/Core/VideoCommon/CMakeLists.txt | 4 ++-- .../Core/VideoCommon/{FrameDump.cpp => FrameDumpFFMpeg.cpp} | 2 +- Source/Core/VideoCommon/{FrameDump.h => FrameDumpFFMpeg.h} | 0 Source/Core/VideoCommon/FrameDumper.h | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename Source/Core/VideoCommon/{FrameDump.cpp => FrameDumpFFMpeg.cpp} (99%) rename Source/Core/VideoCommon/{FrameDump.h => FrameDumpFFMpeg.h} (100%) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 405af72f4a..4392182a22 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -42,7 +42,7 @@ #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" -#include "VideoCommon/FrameDump.h" +#include "VideoCommon/FrameDumpFFMpeg.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/VideoBackendBase.h" diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 69e8f2873e..80daf63a32 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -638,7 +638,7 @@ - + @@ -1234,7 +1234,7 @@ - + diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 1cc23ba7aa..20b3257082 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -36,6 +36,7 @@ add_library(videocommon FramebufferShaderGen.h FrameDumper.cpp FrameDumper.h + FrameDumpFFMpeg.h FreeLookCamera.cpp FreeLookCamera.h GeometryShaderGen.cpp @@ -203,8 +204,7 @@ endif() if(FFmpeg_FOUND) target_sources(videocommon PRIVATE - FrameDump.cpp - FrameDump.h + FrameDumpFFMpeg.cpp ) target_link_libraries(videocommon PRIVATE FFmpeg::avcodec diff --git a/Source/Core/VideoCommon/FrameDump.cpp b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp similarity index 99% rename from Source/Core/VideoCommon/FrameDump.cpp rename to Source/Core/VideoCommon/FrameDumpFFMpeg.cpp index c5edffc04e..09fdf4dbac 100644 --- a/Source/Core/VideoCommon/FrameDump.cpp +++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp @@ -1,7 +1,7 @@ // Copyright 2009 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoCommon/FrameDump.h" +#include "VideoCommon/FrameDumpFFMpeg.h" #if defined(__FreeBSD__) #define __STDC_CONSTANT_MACROS 1 diff --git a/Source/Core/VideoCommon/FrameDump.h b/Source/Core/VideoCommon/FrameDumpFFMpeg.h similarity index 100% rename from Source/Core/VideoCommon/FrameDump.h rename to Source/Core/VideoCommon/FrameDumpFFMpeg.h diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index 2051346ef9..f2c53a28b9 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -9,7 +9,7 @@ #include "Common/MathUtil.h" #include "Common/Thread.h" -#include "VideoCommon/FrameDump.h" +#include "VideoCommon/FrameDumpFFMpeg.h" class AbstractStagingTexture; class AbstractTexture; From 8a236293455e9a71aa6fb400a6253c27540e403b Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 27 Jan 2023 11:34:59 +1300 Subject: [PATCH 07/60] Split AbstractGfx out of Renderer Almost all the virtual functions in Renderer are part of dolphin's "graphics api abstraction layer", which has slowly formed over the last decade or two. Most of the work was done previously with the introduction of the various "AbstractX" classes, associated with texture cache cleanups and implementation of newer graphics APIs (Direct3D 12, Vulkan, Metal). We are simply taking the last step and yeeting these functions out of Renderer. This "AbstractGfx" class is now completely agnostic of any details from the flipper/hollywood GPU we are emulating, though somewhat specialized. (Will not build, this commit only contains changes outside VideoBackends) --- Source/Core/DolphinLib.props | 2 + Source/Core/DolphinQt/Host.cpp | 11 +- Source/Core/VideoCommon/AbstractGfx.cpp | 138 +++++++++++++ Source/Core/VideoCommon/AbstractGfx.h | 174 ++++++++++++++++ Source/Core/VideoCommon/AbstractTexture.cpp | 4 +- Source/Core/VideoCommon/BPFunctions.cpp | 10 +- Source/Core/VideoCommon/BoundingBox.h | 3 + Source/Core/VideoCommon/CMakeLists.txt | 2 + Source/Core/VideoCommon/FrameDumper.cpp | 11 +- .../Core/VideoCommon/FramebufferManager.cpp | 192 +++++++++--------- Source/Core/VideoCommon/OnScreenUI.cpp | 36 ++-- Source/Core/VideoCommon/PostProcessing.cpp | 30 +-- Source/Core/VideoCommon/Present.cpp | 51 ++--- Source/Core/VideoCommon/Present.h | 4 +- Source/Core/VideoCommon/RenderBase.cpp | 176 ++++------------ Source/Core/VideoCommon/RenderBase.h | 129 +----------- Source/Core/VideoCommon/ShaderCache.cpp | 68 +++---- Source/Core/VideoCommon/TextureCacheBase.cpp | 110 +++++----- .../Core/VideoCommon/VertexLoaderManager.cpp | 4 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 11 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 10 +- 21 files changed, 645 insertions(+), 531 deletions(-) create mode 100644 Source/Core/VideoCommon/AbstractGfx.cpp create mode 100644 Source/Core/VideoCommon/AbstractGfx.h diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 80daf63a32..bc0f486082 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -618,6 +618,7 @@ + @@ -1219,6 +1220,7 @@ + diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index 5eb2055486..036dc2cffa 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -36,9 +36,9 @@ #include "UICommon/DiscordPresence.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/Fifo.cpp" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" static thread_local bool tls_is_host_thread = false; @@ -150,11 +150,11 @@ bool Host::GetRenderFullFocus() void Host::SetRenderFocus(bool focus) { m_render_focus = focus; - if (g_renderer && m_render_fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) + if (g_gfx && m_render_fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) { RunWithGPUThreadInactive([focus] { if (!Config::Get(Config::MAIN_RENDER_TO_MAIN)) - g_renderer->SetFullscreen(focus); + g_gfx->SetFullscreen(focus); }); } } @@ -182,10 +182,9 @@ void Host::SetRenderFullscreen(bool fullscreen) { m_render_fullscreen = fullscreen; - if (g_renderer && g_renderer->IsFullscreen() != fullscreen && - g_ActiveConfig.ExclusiveFullscreenEnabled()) + if (g_gfx && g_gfx->IsFullscreen() != fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled()) { - RunWithGPUThreadInactive([fullscreen] { g_renderer->SetFullscreen(fullscreen); }); + RunWithGPUThreadInactive([fullscreen] { g_gfx->SetFullscreen(fullscreen); }); } } diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp new file mode 100644 index 0000000000..4406cd46b6 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -0,0 +1,138 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/AbstractGfx.h" + +#include "Common/Assert.h" + +#include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/ShaderCache.h" +#include "VideoCommon/VertexManagerBase.h" +#include "VideoCommon/VideoConfig.h" + +bool AbstractGfx::IsHeadless() const +{ + return true; +} + +void AbstractGfx::BeginUtilityDrawing() +{ + if (g_renderer) + g_renderer->BeginUtilityDrawing(); +} + +void AbstractGfx::EndUtilityDrawing() +{ + if (g_renderer) + g_renderer->EndUtilityDrawing(); +} + +void AbstractGfx::SetFramebuffer(AbstractFramebuffer* framebuffer) +{ + m_current_framebuffer = framebuffer; +} + +void AbstractGfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +{ + m_current_framebuffer = framebuffer; +} + +void AbstractGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, + const ClearColor& color_value, float depth_value) +{ + m_current_framebuffer = framebuffer; +} + +void AbstractGfx::ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, bool colorEnable, + bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z); +} + +void AbstractGfx::SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth, + float max_depth) +{ + SetViewport(static_cast(rect.left), static_cast(rect.top), + static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), min_depth, + max_depth); + SetScissorRect(rect); +} + +void AbstractGfx::ScaleTexture(AbstractFramebuffer* dst_framebuffer, + const MathUtil::Rectangle& dst_rect, + const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect) +{ + ASSERT(dst_framebuffer->GetColorFormat() == AbstractTextureFormat::RGBA8); + + BeginUtilityDrawing(); + + // The shader needs to know the source rectangle. + const auto converted_src_rect = + ConvertFramebufferRectangle(src_rect, src_texture->GetWidth(), src_texture->GetHeight()); + const float rcp_src_width = 1.0f / src_texture->GetWidth(); + const float rcp_src_height = 1.0f / src_texture->GetHeight(); + const std::array uniforms = {{converted_src_rect.left * rcp_src_width, + converted_src_rect.top * rcp_src_height, + converted_src_rect.GetWidth() * rcp_src_width, + converted_src_rect.GetHeight() * rcp_src_height}}; + g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); + + // Discard if we're overwriting the whole thing. + if (static_cast(dst_rect.GetWidth()) == dst_framebuffer->GetWidth() && + static_cast(dst_rect.GetHeight()) == dst_framebuffer->GetHeight()) + { + SetAndDiscardFramebuffer(dst_framebuffer); + } + else + { + SetFramebuffer(dst_framebuffer); + } + + SetViewportAndScissor(ConvertFramebufferRectangle(dst_rect, dst_framebuffer)); + SetPipeline(dst_framebuffer->GetLayers() > 1 ? g_shader_cache->GetRGBA8StereoCopyPipeline() : + g_shader_cache->GetRGBA8CopyPipeline()); + SetTexture(0, src_texture); + SetSamplerState(0, RenderState::GetLinearSamplerState()); + Draw(0, 3); + EndUtilityDrawing(); + if (dst_framebuffer->GetColorAttachment()) + dst_framebuffer->GetColorAttachment()->FinishedRendering(); +} + +MathUtil::Rectangle +AbstractGfx::ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, + const AbstractFramebuffer* framebuffer) const +{ + return ConvertFramebufferRectangle(rect, framebuffer->GetWidth(), framebuffer->GetHeight()); +} + +MathUtil::Rectangle +AbstractGfx::ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, u32 fb_width, + u32 fb_height) const +{ + MathUtil::Rectangle ret = rect; + if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) + { + ret.top = fb_height - rect.bottom; + ret.bottom = fb_height - rect.top; + } + return ret; +} + +std::unique_ptr AbstractGfx::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} + +bool AbstractGfx::UseGeometryShaderForUI() const +{ + // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, + // instead drawing twice and the eye selected by glDrawBuffer() (see Presenter::RenderXFBToScreen) + return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && + g_ActiveConfig.backend_info.api_type != APIType::OpenGL; +} diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h new file mode 100644 index 0000000000..2db077c98c --- /dev/null +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -0,0 +1,174 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/MathUtil.h" + +#include "VideoCommon/RenderState.h" + +#include +#include + +class AbstractFramebuffer; +class AbstractPipeline; +class AbstractShader; +class AbstractTexture; +class AbstractStagingTexture; +class NativeVertexFormat; +struct ComputePipelineConfig; +struct AbstractPipelineConfig; +struct PortableVertexDeclaration; +struct TextureConfig; +enum class AbstractTextureFormat : u32; +enum class ShaderStage; +enum class StagingTextureType; + +struct SurfaceInfo +{ + u32 width = 0; + u32 height = 0; + float scale = 0.0f; + AbstractTextureFormat format = {}; +}; + +namespace VideoCommon +{ +class AsyncShaderCompiler; +} + +// Bitmask containing information about which configuration has changed for the backend. +enum ConfigChangeBits : u32 +{ + CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), + CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), + CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), + CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), + CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), + CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), + CONFIG_CHANGE_BIT_VSYNC = (1 << 6), + CONFIG_CHANGE_BIT_BBOX = (1 << 7) +}; + +using ClearColor = std::array; + +class AbstractGfx +{ +public: + virtual bool IsHeadless() const = 0; + + virtual void SetPipeline(const AbstractPipeline* pipeline) {} + virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} + virtual void SetTexture(u32 index, const AbstractTexture* texture) {} + virtual void SetSamplerState(u32 index, const SamplerState& state) {} + virtual void SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) {} + virtual void UnbindTexture(const AbstractTexture* texture) {} + virtual void SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) + { + } + virtual void SetFullscreen(bool enable_fullscreen) {} + virtual bool IsFullscreen() const { return false; } + virtual void BeginUtilityDrawing(); + virtual void EndUtilityDrawing(); + virtual std::unique_ptr CreateTexture(const TextureConfig& config, + std::string_view name = "") = 0; + virtual std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0; + virtual std::unique_ptr + CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) = 0; + + // Framebuffer operations. + virtual void SetFramebuffer(AbstractFramebuffer* framebuffer); + virtual void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer); + virtual void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, + const ClearColor& color_value = {}, float depth_value = 0.0f); + + virtual void ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, bool colorEnable, + bool alphaEnable, bool zEnable, u32 color, u32 z); + + // Drawing with currently-bound pipeline state. + virtual void Draw(u32 base_vertex, u32 num_vertices) {} + virtual void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) {} + + // Dispatching compute shaders with currently-bound state. + virtual void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, + u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) + { + } + + // Binds the backbuffer for rendering. The buffer will be cleared immediately after binding. + // This is where any window size changes are detected, therefore m_backbuffer_width and/or + // m_backbuffer_height may change after this function returns. + virtual void BindBackbuffer(const ClearColor& clear_color = {}) {} + + // Presents the backbuffer to the window system, or "swaps buffers". + virtual void PresentBackbuffer() {} + + // Shader modules/objects. + virtual std::unique_ptr CreateShaderFromSource(ShaderStage stage, + std::string_view source, + std::string_view name = "") = 0; + virtual std::unique_ptr CreateShaderFromBinary(ShaderStage stage, + const void* data, size_t length, + std::string_view name = "") = 0; + virtual std::unique_ptr + CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) = 0; + virtual std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data = nullptr, + size_t cache_data_length = 0) = 0; + + AbstractFramebuffer* GetCurrentFramebuffer() const { return m_current_framebuffer; } + + // Sets viewport and scissor to the specified rectangle. rect is assumed to be in framebuffer + // coordinates, i.e. lower-left origin in OpenGL. + void SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth = 0.0f, + float max_depth = 1.0f); + + // Scales a GPU texture using a copy shader. + virtual void ScaleTexture(AbstractFramebuffer* dst_framebuffer, + const MathUtil::Rectangle& dst_rect, + const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect); + + // Converts an upper-left to lower-left if required by the backend, optionally + // clamping to the framebuffer size. + MathUtil::Rectangle ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, + u32 fb_width, u32 fb_height) const; + MathUtil::Rectangle + ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, + const AbstractFramebuffer* framebuffer) const; + + virtual void Flush() {} + virtual void WaitForGPUIdle() {} + + // For opengl's glDrawBuffer + virtual void SelectLeftBuffer() {} + virtual void SelectRightBuffer() {} + virtual void SelectMainBuffer() {} + + // A simple presentation fallback, only used by video software + virtual void ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) + { + } + + virtual std::unique_ptr CreateAsyncShaderCompiler(); + + // Called when the configuration changes, and backend structures need to be updated. + virtual void OnConfigChanged(u32 bits) {} + + // Returns true if a layer-expanding geometry shader should be used when rendering the user + // interface and final XFB. + bool UseGeometryShaderForUI() const; + + // Returns info about the main surface (aka backbuffer) + virtual SurfaceInfo GetSurfaceInfo() const { return {}; } + +protected: + AbstractFramebuffer* m_current_framebuffer = nullptr; + const AbstractPipeline* m_current_pipeline = nullptr; +}; + +extern std::unique_ptr g_gfx; diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp index fb34e2413e..03b83b21cc 100644 --- a/Source/Core/VideoCommon/AbstractTexture.cpp +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -8,8 +8,8 @@ #include "Common/Assert.h" #include "Common/Image.h" #include "Common/MsgHandler.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractStagingTexture.h" -#include "VideoCommon/RenderBase.h" AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c) { @@ -36,7 +36,7 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level) TextureConfig readback_texture_config(level_width, level_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0); auto readback_texture = - g_renderer->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config); + g_gfx->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config); if (!readback_texture) return false; diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index c065f1cc5f..a37e3720ff 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -12,6 +12,7 @@ #include "Common/Logging/Log.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/RenderBase.h" @@ -198,9 +199,8 @@ void SetScissorAndViewport() auto native_rc = ComputeScissorRects().Best(); auto target_rc = g_renderer->ConvertEFBRectangle(native_rc.rect); - auto converted_rc = - g_renderer->ConvertFramebufferRectangle(target_rc, g_renderer->GetCurrentFramebuffer()); - g_renderer->SetScissorRect(converted_rc); + auto converted_rc = g_gfx->ConvertFramebufferRectangle(target_rc, g_gfx->GetCurrentFramebuffer()); + g_gfx->SetScissorRect(converted_rc); float raw_x = (xfmem.viewport.xOrig - native_rc.x_off) - xfmem.viewport.wd; float raw_y = (xfmem.viewport.yOrig - native_rc.y_off) + xfmem.viewport.ht; @@ -280,9 +280,9 @@ void SetScissorAndViewport() // Lower-left flip. if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - y = static_cast(g_renderer->GetCurrentFramebuffer()->GetHeight()) - y - height; + y = static_cast(g_gfx->GetCurrentFramebuffer()->GetHeight()) - y - height; - g_renderer->SetViewport(x, y, width, height, near_depth, far_depth); + g_gfx->SetViewport(x, y, width, height, near_depth, far_depth); } void SetDepthMode() diff --git a/Source/Core/VideoCommon/BoundingBox.h b/Source/Core/VideoCommon/BoundingBox.h index bf8ceeb818..d8d2508716 100644 --- a/Source/Core/VideoCommon/BoundingBox.h +++ b/Source/Core/VideoCommon/BoundingBox.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "Common/CommonTypes.h" @@ -49,3 +50,5 @@ private: std::array m_dirty = {}; bool m_is_valid = true; }; + +extern std::unique_ptr g_bounding_box; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 20b3257082..4507904afd 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(videocommon AbstractFramebuffer.cpp AbstractFramebuffer.h + AbstractGfx.cpp + AbstractGfx.h AbstractShader.h AbstractStagingTexture.cpp AbstractStagingTexture.h diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index db0d099376..35431a4abf 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -11,6 +11,7 @@ #include "Core/Config/MainSettings.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/OnScreenDisplay.h" @@ -51,8 +52,8 @@ void FrameDumper::DumpCurrentFrame(const AbstractTexture* src_texture, if (!CheckFrameDumpRenderTexture(target_width, target_height)) return; - g_renderer->ScaleTexture(m_frame_dump_render_framebuffer.get(), - m_frame_dump_render_framebuffer->GetRect(), src_texture, src_rect); + g_gfx->ScaleTexture(m_frame_dump_render_framebuffer.get(), + m_frame_dump_render_framebuffer->GetRect(), src_texture, src_rect); src_texture = m_frame_dump_render_texture.get(); copy_rect = src_texture->GetRect(); } @@ -79,7 +80,7 @@ bool FrameDumper::CheckFrameDumpRenderTexture(u32 target_width, u32 target_heigh // Recreate texture, but release before creating so we don't temporarily use twice the RAM. m_frame_dump_render_framebuffer.reset(); m_frame_dump_render_texture.reset(); - m_frame_dump_render_texture = g_renderer->CreateTexture( + m_frame_dump_render_texture = g_gfx->CreateTexture( TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_RenderTarget), "Frame dump render texture"); @@ -89,7 +90,7 @@ bool FrameDumper::CheckFrameDumpRenderTexture(u32 target_width, u32 target_heigh return false; } m_frame_dump_render_framebuffer = - g_renderer->CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr); + g_gfx->CreateFramebuffer(m_frame_dump_render_texture.get(), nullptr); ASSERT(m_frame_dump_render_framebuffer); return true; } @@ -101,7 +102,7 @@ bool FrameDumper::CheckFrameDumpReadbackTexture(u32 target_width, u32 target_hei return true; rbtex.reset(); - rbtex = g_renderer->CreateStagingTexture( + rbtex = g_gfx->CreateStagingTexture( StagingTextureType::Readback, TextureConfig(target_width, target_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0)); if (!rbtex) diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index f025b2dfde..861536813e 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -11,6 +11,7 @@ #include "Common/MsgHandler.h" #include "Core/Config/GraphicsSettings.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractPipeline.h" #include "VideoCommon/AbstractShader.h" #include "VideoCommon/AbstractStagingTexture.h" @@ -171,17 +172,17 @@ bool FramebufferManager::CreateEFBFramebuffer() const TextureConfig efb_depth_texture_config = GetEFBDepthTextureConfig(); // We need a second texture to swap with for changing pixel formats - m_efb_color_texture = g_renderer->CreateTexture(efb_color_texture_config, "EFB color texture"); - m_efb_depth_texture = g_renderer->CreateTexture(efb_depth_texture_config, "EFB depth texture"); + m_efb_color_texture = g_gfx->CreateTexture(efb_color_texture_config, "EFB color texture"); + m_efb_depth_texture = g_gfx->CreateTexture(efb_depth_texture_config, "EFB depth texture"); m_efb_convert_color_texture = - g_renderer->CreateTexture(efb_color_texture_config, "EFB convert color texture"); + g_gfx->CreateTexture(efb_color_texture_config, "EFB convert color texture"); if (!m_efb_color_texture || !m_efb_depth_texture || !m_efb_convert_color_texture) return false; m_efb_framebuffer = - g_renderer->CreateFramebuffer(m_efb_color_texture.get(), m_efb_depth_texture.get()); + g_gfx->CreateFramebuffer(m_efb_color_texture.get(), m_efb_depth_texture.get()); m_efb_convert_framebuffer = - g_renderer->CreateFramebuffer(m_efb_convert_color_texture.get(), m_efb_depth_texture.get()); + g_gfx->CreateFramebuffer(m_efb_convert_color_texture.get(), m_efb_depth_texture.get()); if (!m_efb_framebuffer || !m_efb_convert_framebuffer) return false; @@ -191,7 +192,7 @@ bool FramebufferManager::CreateEFBFramebuffer() u32 flags = 0; if (!g_ActiveConfig.backend_info.bSupportsPartialMultisampleResolve) flags |= AbstractTextureFlag_RenderTarget; - m_efb_resolve_color_texture = g_renderer->CreateTexture( + m_efb_resolve_color_texture = g_gfx->CreateTexture( TextureConfig(efb_color_texture_config.width, efb_color_texture_config.height, 1, efb_color_texture_config.layers, 1, efb_color_texture_config.format, flags), "EFB color resolve texture"); @@ -201,7 +202,7 @@ bool FramebufferManager::CreateEFBFramebuffer() if (!g_ActiveConfig.backend_info.bSupportsPartialMultisampleResolve) { m_efb_color_resolve_framebuffer = - g_renderer->CreateFramebuffer(m_efb_resolve_color_texture.get(), nullptr); + g_gfx->CreateFramebuffer(m_efb_resolve_color_texture.get(), nullptr); if (!m_efb_color_resolve_framebuffer) return false; } @@ -210,7 +211,7 @@ bool FramebufferManager::CreateEFBFramebuffer() // We also need one to convert the D24S8 to R32F if that is being used (Adreno). if (g_ActiveConfig.MultisamplingEnabled() || GetEFBDepthFormat() != AbstractTextureFormat::R32F) { - m_efb_depth_resolve_texture = g_renderer->CreateTexture( + m_efb_depth_resolve_texture = g_gfx->CreateTexture( TextureConfig(efb_depth_texture_config.width, efb_depth_texture_config.height, 1, efb_depth_texture_config.layers, 1, GetEFBDepthCopyFormat(), AbstractTextureFlag_RenderTarget), @@ -219,15 +220,15 @@ bool FramebufferManager::CreateEFBFramebuffer() return false; m_efb_depth_resolve_framebuffer = - g_renderer->CreateFramebuffer(m_efb_depth_resolve_texture.get(), nullptr); + g_gfx->CreateFramebuffer(m_efb_depth_resolve_texture.get(), nullptr); if (!m_efb_depth_resolve_framebuffer) return false; } // Clear the renderable textures out. - g_renderer->SetAndClearFramebuffer( - m_efb_framebuffer.get(), {{0.0f, 0.0f, 0.0f, 0.0f}}, - g_ActiveConfig.backend_info.bSupportsReversedDepthRange ? 1.0f : 0.0f); + g_gfx->SetAndClearFramebuffer(m_efb_framebuffer.get(), {{0.0f, 0.0f, 0.0f, 0.0f}}, + g_ActiveConfig.backend_info.bSupportsReversedDepthRange ? 1.0f : + 0.0f); return true; } @@ -245,7 +246,7 @@ void FramebufferManager::DestroyEFBFramebuffer() void FramebufferManager::BindEFBFramebuffer() { - g_renderer->SetFramebuffer(m_efb_framebuffer.get()); + g_gfx->SetFramebuffer(m_efb_framebuffer.get()); } AbstractTexture* FramebufferManager::ResolveEFBColorTexture(const MathUtil::Rectangle& region) @@ -270,15 +271,15 @@ AbstractTexture* FramebufferManager::ResolveEFBColorTexture(const MathUtil::Rect else { m_efb_color_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(m_efb_color_resolve_framebuffer.get()); - g_renderer->SetPipeline(m_efb_color_resolve_pipeline.get()); - g_renderer->SetTexture(0, m_efb_color_texture.get()); - g_renderer->SetSamplerState(0, RenderState::GetPointSamplerState()); - g_renderer->SetViewportAndScissor(clamped_region); - g_renderer->Draw(0, 3); + g_gfx->BeginUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(m_efb_color_resolve_framebuffer.get()); + g_gfx->SetPipeline(m_efb_color_resolve_pipeline.get()); + g_gfx->SetTexture(0, m_efb_color_texture.get()); + g_gfx->SetSamplerState(0, RenderState::GetPointSamplerState()); + g_gfx->SetViewportAndScissor(clamped_region); + g_gfx->Draw(0, 3); m_efb_resolve_color_texture->FinishedRendering(); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); } m_efb_resolve_color_texture->FinishedRendering(); return m_efb_resolve_color_texture.get(); @@ -298,16 +299,16 @@ AbstractTexture* FramebufferManager::ResolveEFBDepthTexture(const MathUtil::Rect clamped_region.ClampUL(0, 0, GetEFBWidth(), GetEFBHeight()); m_efb_depth_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(m_efb_depth_resolve_framebuffer.get()); - g_renderer->SetPipeline(IsEFBMultisampled() ? m_efb_depth_resolve_pipeline.get() : - m_efb_depth_cache.copy_pipeline.get()); - g_renderer->SetTexture(0, m_efb_depth_texture.get()); - g_renderer->SetSamplerState(0, RenderState::GetPointSamplerState()); - g_renderer->SetViewportAndScissor(clamped_region); - g_renderer->Draw(0, 3); + g_gfx->BeginUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(m_efb_depth_resolve_framebuffer.get()); + g_gfx->SetPipeline(IsEFBMultisampled() ? m_efb_depth_resolve_pipeline.get() : + m_efb_depth_cache.copy_pipeline.get()); + g_gfx->SetTexture(0, m_efb_depth_texture.get()); + g_gfx->SetSamplerState(0, RenderState::GetPointSamplerState()); + g_gfx->SetViewportAndScissor(clamped_region); + g_gfx->Draw(0, 3); m_efb_depth_resolve_texture->FinishedRendering(); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); return m_efb_depth_resolve_texture.get(); } @@ -322,17 +323,17 @@ bool FramebufferManager::ReinterpretPixelData(EFBReinterpretType convtype) // buffer, which we want to preserve. If we find this to be hindering performance in the // future (e.g. on mobile/tilers), it may be worth discarding only the color buffer. m_efb_color_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); - g_renderer->SetFramebuffer(m_efb_convert_framebuffer.get()); - g_renderer->SetViewportAndScissor(m_efb_framebuffer->GetRect()); - g_renderer->SetPipeline(m_format_conversion_pipelines[static_cast(convtype)].get()); - g_renderer->SetTexture(0, m_efb_color_texture.get()); - g_renderer->Draw(0, 3); + g_gfx->BeginUtilityDrawing(); + g_gfx->SetFramebuffer(m_efb_convert_framebuffer.get()); + g_gfx->SetViewportAndScissor(m_efb_framebuffer->GetRect()); + g_gfx->SetPipeline(m_format_conversion_pipelines[static_cast(convtype)].get()); + g_gfx->SetTexture(0, m_efb_color_texture.get()); + g_gfx->Draw(0, 3); // And swap the framebuffers around, so we do new drawing to the converted framebuffer. std::swap(m_efb_color_texture, m_efb_convert_color_texture); std::swap(m_efb_framebuffer, m_efb_convert_framebuffer); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); InvalidatePeekCache(true); return true; } @@ -342,7 +343,7 @@ bool FramebufferManager::CompileConversionPipelines() for (u32 i = 0; i < NUM_EFB_REINTERPRET_TYPES; i++) { EFBReinterpretType convtype = static_cast(i); - std::unique_ptr pixel_shader = g_renderer->CreateShaderFromSource( + std::unique_ptr pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateFormatConversionShader(convtype, GetEFBSamples()), fmt::format("Framebuffer conversion pixel shader {}", convtype)); @@ -358,7 +359,7 @@ bool FramebufferManager::CompileConversionPipelines() config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = GetEFBFramebufferState(); config.usage = AbstractPipelineUsage::Utility; - m_format_conversion_pipelines[i] = g_renderer->CreatePipeline(config); + m_format_conversion_pipelines[i] = g_gfx->CreatePipeline(config); if (!m_format_conversion_pipelines[i]) return false; } @@ -493,7 +494,7 @@ void FramebufferManager::RefreshPeekCache() if (flush_command_buffer) { - g_renderer->Flush(); + g_gfx->Flush(); } } @@ -562,33 +563,33 @@ bool FramebufferManager::CompileReadbackPipelines() config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetColorFramebufferState(GetEFBColorFormat()); config.usage = AbstractPipelineUsage::Utility; - m_efb_color_cache.copy_pipeline = g_renderer->CreatePipeline(config); + m_efb_color_cache.copy_pipeline = g_gfx->CreatePipeline(config); if (!m_efb_color_cache.copy_pipeline) return false; // same for depth, except different format config.framebuffer_state.color_texture_format = GetEFBDepthCopyFormat(); - m_efb_depth_cache.copy_pipeline = g_renderer->CreatePipeline(config); + m_efb_depth_cache.copy_pipeline = g_gfx->CreatePipeline(config); if (!m_efb_depth_cache.copy_pipeline) return false; if (IsEFBMultisampled()) { - auto depth_resolve_shader = g_renderer->CreateShaderFromSource( + auto depth_resolve_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateResolveDepthPixelShader(GetEFBSamples()), "Depth resolve pixel shader"); if (!depth_resolve_shader) return false; config.pixel_shader = depth_resolve_shader.get(); - m_efb_depth_resolve_pipeline = g_renderer->CreatePipeline(config); + m_efb_depth_resolve_pipeline = g_gfx->CreatePipeline(config); if (!m_efb_depth_resolve_pipeline) return false; if (!g_ActiveConfig.backend_info.bSupportsPartialMultisampleResolve) { config.framebuffer_state.color_texture_format = GetEFBColorFormat(); - auto color_resolve_shader = g_renderer->CreateShaderFromSource( + auto color_resolve_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateResolveColorPixelShader(GetEFBSamples()), "Color resolve pixel shader"); @@ -596,14 +597,14 @@ bool FramebufferManager::CompileReadbackPipelines() return false; config.pixel_shader = color_resolve_shader.get(); - m_efb_color_resolve_pipeline = g_renderer->CreatePipeline(config); + m_efb_color_resolve_pipeline = g_gfx->CreatePipeline(config); if (!m_efb_color_resolve_pipeline) return false; } } // EFB restore pipeline - auto restore_shader = g_renderer->CreateShaderFromSource( + auto restore_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateEFBRestorePixelShader(), "EFB restore pixel shader"); if (!restore_shader) @@ -614,7 +615,7 @@ bool FramebufferManager::CompileReadbackPipelines() config.framebuffer_state.per_sample_shading = false; config.vertex_shader = g_shader_cache->GetScreenQuadVertexShader(); config.pixel_shader = restore_shader.get(); - m_efb_restore_pipeline = g_renderer->CreatePipeline(config); + m_efb_restore_pipeline = g_gfx->CreatePipeline(config); if (!m_efb_restore_pipeline) return false; @@ -635,12 +636,12 @@ bool FramebufferManager::CreateReadbackFramebuffer() const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH, IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget); - m_efb_color_cache.texture = g_renderer->CreateTexture(color_config, "EFB color cache"); + m_efb_color_cache.texture = g_gfx->CreateTexture(color_config, "EFB color cache"); if (!m_efb_color_cache.texture) return false; m_efb_color_cache.framebuffer = - g_renderer->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr); + g_gfx->CreateFramebuffer(m_efb_color_cache.texture.get(), nullptr); if (!m_efb_color_cache.framebuffer) return false; } @@ -657,21 +658,21 @@ bool FramebufferManager::CreateReadbackFramebuffer() IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, 1, 1, GetEFBDepthCopyFormat(), AbstractTextureFlag_RenderTarget); - m_efb_depth_cache.texture = g_renderer->CreateTexture(depth_config, "EFB depth cache"); + m_efb_depth_cache.texture = g_gfx->CreateTexture(depth_config, "EFB depth cache"); if (!m_efb_depth_cache.texture) return false; m_efb_depth_cache.framebuffer = - g_renderer->CreateFramebuffer(m_efb_depth_cache.texture.get(), nullptr); + g_gfx->CreateFramebuffer(m_efb_depth_cache.texture.get(), nullptr); if (!m_efb_depth_cache.framebuffer) return false; } // Staging texture use the full EFB dimensions, as this is the buffer for the whole cache. - m_efb_color_cache.readback_texture = g_renderer->CreateStagingTexture( + m_efb_color_cache.readback_texture = g_gfx->CreateStagingTexture( StagingTextureType::Mutable, TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBColorFormat(), 0)); - m_efb_depth_cache.readback_texture = g_renderer->CreateStagingTexture( + m_efb_depth_cache.readback_texture = g_gfx->CreateStagingTexture( StagingTextureType::Mutable, TextureConfig(EFB_WIDTH, EFB_HEIGHT, 1, 1, 1, GetEFBDepthCopyFormat(), 0)); if (!m_efb_color_cache.readback_texture || !m_efb_depth_cache.readback_texture) @@ -737,7 +738,7 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index, bool async // TODO: This won't produce correct results at IRs above 2x. More samples are required. // This is the same issue as with EFB copies. src_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); const float rcp_src_width = 1.0f / m_efb_framebuffer->GetWidth(); const float rcp_src_height = 1.0f / m_efb_framebuffer->GetHeight(); @@ -748,14 +749,13 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index, bool async // Viewport will not be TILE_SIZExTILE_SIZE for the last row of tiles, assuming a tile size of // 64, because 528 is not evenly divisible by 64. - g_renderer->SetAndDiscardFramebuffer(data.framebuffer.get()); - g_renderer->SetViewportAndScissor( - MathUtil::Rectangle(0, 0, rect.GetWidth(), rect.GetHeight())); - g_renderer->SetPipeline(data.copy_pipeline.get()); - g_renderer->SetTexture(0, src_texture); - g_renderer->SetSamplerState(0, depth ? RenderState::GetPointSamplerState() : - RenderState::GetLinearSamplerState()); - g_renderer->Draw(0, 3); + g_gfx->SetAndDiscardFramebuffer(data.framebuffer.get()); + g_gfx->SetViewportAndScissor(MathUtil::Rectangle(0, 0, rect.GetWidth(), rect.GetHeight())); + g_gfx->SetPipeline(data.copy_pipeline.get()); + g_gfx->SetTexture(0, src_texture); + g_gfx->SetSamplerState(0, depth ? RenderState::GetPointSamplerState() : + RenderState::GetLinearSamplerState()); + g_gfx->Draw(0, 3); // Copy from EFB or copy texture to staging texture. // No need to call FinishedRendering() here because CopyFromTexture() transitions. @@ -763,7 +763,7 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index, bool async data.texture.get(), MathUtil::Rectangle(0, 0, rect.GetWidth(), rect.GetHeight()), 0, 0, rect); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); } else { @@ -790,7 +790,7 @@ void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool clear { FlushEFBPokes(); FlagPeekCacheAsOutOfDate(); - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); // Set up uniforms. struct Uniforms @@ -809,17 +809,17 @@ void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool clear uniforms.clear_depth = 1.0f - uniforms.clear_depth; g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); - const auto target_rc = g_renderer->ConvertFramebufferRectangle( - g_renderer->ConvertEFBRectangle(rc), m_efb_framebuffer.get()); - g_renderer->SetPipeline(m_efb_clear_pipelines[clear_color][clear_alpha][clear_z].get()); - g_renderer->SetViewportAndScissor(target_rc); - g_renderer->Draw(0, 3); - g_renderer->EndUtilityDrawing(); + const auto target_rc = g_gfx->ConvertFramebufferRectangle(g_renderer->ConvertEFBRectangle(rc), + m_efb_framebuffer.get()); + g_gfx->SetPipeline(m_efb_clear_pipelines[clear_color][clear_alpha][clear_z].get()); + g_gfx->SetViewportAndScissor(target_rc); + g_gfx->Draw(0, 3); + g_gfx->EndUtilityDrawing(); } bool FramebufferManager::CompileClearPipelines() { - auto vertex_shader = g_renderer->CreateShaderFromSource( + auto vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, FramebufferShaderGen::GenerateClearVertexShader(), "Clear vertex shader"); if (!vertex_shader) @@ -848,7 +848,7 @@ bool FramebufferManager::CompileClearPipelines() config.depth_state.updateenable = depth_enable != 0; m_efb_clear_pipelines[color_enable][alpha_enable][depth_enable] = - g_renderer->CreatePipeline(config); + g_gfx->CreatePipeline(config); if (!m_efb_clear_pipelines[color_enable][alpha_enable][depth_enable]) return false; } @@ -957,17 +957,17 @@ void FramebufferManager::DrawPokeVertices(const EFBPokeVertex* vertices, u32 ver const AbstractPipeline* pipeline) { // Copy to vertex buffer. - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); u32 base_vertex, base_index; g_vertex_manager->UploadUtilityVertices(vertices, sizeof(EFBPokeVertex), static_cast(vertex_count), nullptr, 0, &base_vertex, &base_index); // Now we can draw. - g_renderer->SetViewportAndScissor(m_efb_framebuffer->GetRect()); - g_renderer->SetPipeline(pipeline); - g_renderer->Draw(base_vertex, vertex_count); - g_renderer->EndUtilityDrawing(); + g_gfx->SetViewportAndScissor(m_efb_framebuffer->GetRect()); + g_gfx->SetPipeline(pipeline); + g_gfx->Draw(base_vertex, vertex_count); + g_gfx->EndUtilityDrawing(); } bool FramebufferManager::CompilePokePipelines() @@ -985,11 +985,11 @@ bool FramebufferManager::CompilePokePipelines() vtx_decl.colors[0].offset = offsetof(EFBPokeVertex, color); vtx_decl.stride = sizeof(EFBPokeVertex); - m_poke_vertex_format = g_renderer->CreateNativeVertexFormat(vtx_decl); + m_poke_vertex_format = g_gfx->CreateNativeVertexFormat(vtx_decl); if (!m_poke_vertex_format) return false; - auto poke_vertex_shader = g_renderer->CreateShaderFromSource( + auto poke_vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, FramebufferShaderGen::GenerateEFBPokeVertexShader(), "EFB poke vertex shader"); if (!poke_vertex_shader) @@ -1007,14 +1007,14 @@ bool FramebufferManager::CompilePokePipelines() config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = GetEFBFramebufferState(); config.usage = AbstractPipelineUsage::Utility; - m_color_poke_pipeline = g_renderer->CreatePipeline(config); + m_color_poke_pipeline = g_gfx->CreatePipeline(config); if (!m_color_poke_pipeline) return false; // Turn off color writes, depth writes on for depth pokes. config.depth_state = RenderState::GetAlwaysWriteDepthState(); config.blending_state = RenderState::GetNoColorWriteBlendState(); - m_depth_poke_pipeline = g_renderer->CreatePipeline(config); + m_depth_poke_pipeline = g_gfx->CreatePipeline(config); if (!m_depth_poke_pipeline) return false; @@ -1077,9 +1077,9 @@ void FramebufferManager::DoLoadState(PointerWrap& p) color_tex->texture->GetLayers() != m_efb_color_texture->GetLayers()) { WARN_LOG_FMT(VIDEO, "Failed to deserialize EFB contents. Clearing instead."); - g_renderer->SetAndClearFramebuffer( - m_efb_framebuffer.get(), {{0.0f, 0.0f, 0.0f, 0.0f}}, - g_ActiveConfig.backend_info.bSupportsReversedDepthRange ? 1.0f : 0.0f); + g_gfx->SetAndClearFramebuffer(m_efb_framebuffer.get(), {{0.0f, 0.0f, 0.0f, 0.0f}}, + g_ActiveConfig.backend_info.bSupportsReversedDepthRange ? 1.0f : + 0.0f); return; } @@ -1089,15 +1089,15 @@ void FramebufferManager::DoLoadState(PointerWrap& p) color_tex->texture->GetHeight() != m_efb_color_texture->GetHeight(); // Draw the deserialized textures over the EFB. - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(m_efb_framebuffer.get()); - g_renderer->SetViewportAndScissor(m_efb_framebuffer->GetRect()); - g_renderer->SetPipeline(m_efb_restore_pipeline.get()); - g_renderer->SetTexture(0, color_tex->texture.get()); - g_renderer->SetTexture(1, depth_tex->texture.get()); - g_renderer->SetSamplerState(0, rescale ? RenderState::GetLinearSamplerState() : - RenderState::GetPointSamplerState()); - g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState()); - g_renderer->Draw(0, 3); - g_renderer->EndUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(m_efb_framebuffer.get()); + g_gfx->SetViewportAndScissor(m_efb_framebuffer->GetRect()); + g_gfx->SetPipeline(m_efb_restore_pipeline.get()); + g_gfx->SetTexture(0, color_tex->texture.get()); + g_gfx->SetTexture(1, depth_tex->texture.get()); + g_gfx->SetSamplerState(0, rescale ? RenderState::GetLinearSamplerState() : + RenderState::GetPointSamplerState()); + g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState()); + g_gfx->Draw(0, 3); + g_gfx->EndUtilityDrawing(); } diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 72f0c8f08a..291f0c23e2 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -10,6 +10,7 @@ #include "Core/Config/NetplaySettings.h" #include "Core/Movie.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractPipeline.h" #include "VideoCommon/AbstractShader.h" #include "VideoCommon/FramebufferShaderGen.h" @@ -18,7 +19,6 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" @@ -61,7 +61,7 @@ bool OnScreenUI::Initialize(u32 width, u32 height, float scale) vdecl.texcoords[0] = {ComponentFormat::Float, 2, offsetof(ImDrawVert, uv), true, false}; vdecl.colors[0] = {ComponentFormat::UByte, 4, offsetof(ImDrawVert, col), true, false}; vdecl.stride = sizeof(ImDrawVert); - m_imgui_vertex_format = g_renderer->CreateNativeVertexFormat(vdecl); + m_imgui_vertex_format = g_gfx->CreateNativeVertexFormat(vdecl); if (!m_imgui_vertex_format) { PanicAlertFmt("Failed to create ImGui vertex format"); @@ -78,7 +78,7 @@ bool OnScreenUI::Initialize(u32 width, u32 height, float scale) TextureConfig font_tex_config(font_tex_width, font_tex_height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0); std::unique_ptr font_tex = - g_renderer->CreateTexture(font_tex_config, "ImGui font texture"); + g_gfx->CreateTexture(font_tex_config, "ImGui font texture"); if (!font_tex) { PanicAlertFmt("Failed to create ImGui texture"); @@ -120,10 +120,10 @@ bool OnScreenUI::RecompileImGuiPipeline() return true; } - std::unique_ptr vertex_shader = g_renderer->CreateShaderFromSource( + std::unique_ptr vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, FramebufferShaderGen::GenerateImGuiVertexShader(), "ImGui vertex shader"); - std::unique_ptr pixel_shader = g_renderer->CreateShaderFromSource( + std::unique_ptr pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateImGuiPixelShader(), "ImGui pixel shader"); if (!vertex_shader || !pixel_shader) { @@ -133,9 +133,9 @@ bool OnScreenUI::RecompileImGuiPipeline() // GS is used to render the UI to both eyes in stereo modes. std::unique_ptr geometry_shader; - if (g_renderer->UseGeometryShaderForUI()) + if (g_gfx->UseGeometryShaderForUI()) { - geometry_shader = g_renderer->CreateShaderFromSource( + geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 1), "ImGui passthrough geometry shader"); if (!geometry_shader) @@ -163,7 +163,7 @@ bool OnScreenUI::RecompileImGuiPipeline() pconfig.framebuffer_state.samples = 1; pconfig.framebuffer_state.per_sample_shading = false; pconfig.usage = AbstractPipelineUsage::Utility; - m_imgui_pipeline = g_renderer->CreatePipeline(pconfig); + m_imgui_pipeline = g_gfx->CreatePipeline(pconfig); if (!m_imgui_pipeline) { PanicAlertFmt("Failed to create imgui pipeline"); @@ -204,8 +204,8 @@ void OnScreenUI::DrawImGui() if (!draw_data) return; - g_renderer->SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), - static_cast(m_backbuffer_height), 0.0f, 1.0f); + g_gfx->SetViewport(0.0f, 0.0f, static_cast(m_backbuffer_width), + static_cast(m_backbuffer_height), 0.0f, 1.0f); // Uniform buffer for draws. struct ImGuiUbo @@ -216,8 +216,8 @@ void OnScreenUI::DrawImGui() ImGuiUbo ubo = {{1.0f / m_backbuffer_width * 2.0f, 1.0f / m_backbuffer_height * 2.0f}}; // Set up common state for drawing. - g_renderer->SetPipeline(m_imgui_pipeline.get()); - g_renderer->SetSamplerState(0, RenderState::GetPointSamplerState()); + g_gfx->SetPipeline(m_imgui_pipeline.get()); + g_gfx->SetSamplerState(0, RenderState::GetPointSamplerState()); g_vertex_manager->UploadUtilityUniforms(&ubo, sizeof(ubo)); for (int i = 0; i < draw_data->CmdListsCount; i++) @@ -239,13 +239,13 @@ void OnScreenUI::DrawImGui() continue; } - g_renderer->SetScissorRect(g_renderer->ConvertFramebufferRectangle( + g_gfx->SetScissorRect(g_gfx->ConvertFramebufferRectangle( MathUtil::Rectangle( static_cast(cmd.ClipRect.x), static_cast(cmd.ClipRect.y), static_cast(cmd.ClipRect.z), static_cast(cmd.ClipRect.w)), - g_renderer->GetCurrentFramebuffer())); - g_renderer->SetTexture(0, reinterpret_cast(cmd.TextureId)); - g_renderer->DrawIndexed(base_index, cmd.ElemCount, base_vertex); + g_gfx->GetCurrentFramebuffer())); + g_gfx->SetTexture(0, reinterpret_cast(cmd.TextureId)); + g_gfx->DrawIndexed(base_index, cmd.ElemCount, base_vertex); base_index += cmd.ElemCount; } } @@ -255,9 +255,9 @@ void OnScreenUI::DrawImGui() // itself will be clipped to whatever bounds were last set by ImGui, resulting in a rather useless // capture whenever any ImGui windows are open. We'll reset the scissor rectangle to the entire // viewport here to avoid this problem. - g_renderer->SetScissorRect(g_renderer->ConvertFramebufferRectangle( + g_gfx->SetScissorRect(g_gfx->ConvertFramebufferRectangle( MathUtil::Rectangle(0, 0, m_backbuffer_width, m_backbuffer_height), - g_renderer->GetCurrentFramebuffer())); + g_gfx->GetCurrentFramebuffer())); } // Create On-Screen-Messages diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index f14b8f892b..7f4faed582 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -20,12 +20,12 @@ #include "Common/StringUtil.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractPipeline.h" #include "VideoCommon/AbstractShader.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoCommon.h" @@ -417,9 +417,9 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst, const MathUtil::Rectangle& src, const AbstractTexture* src_tex, int src_layer) { - if (g_renderer->GetCurrentFramebuffer()->GetColorFormat() != m_framebuffer_format) + if (g_gfx->GetCurrentFramebuffer()->GetColorFormat() != m_framebuffer_format) { - m_framebuffer_format = g_renderer->GetCurrentFramebuffer()->GetColorFormat(); + m_framebuffer_format = g_gfx->GetCurrentFramebuffer()->GetColorFormat(); RecompilePipeline(); } @@ -430,12 +430,12 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst, g_vertex_manager->UploadUtilityUniforms(m_uniform_staging_buffer.data(), static_cast(m_uniform_staging_buffer.size())); - g_renderer->SetViewportAndScissor( - g_renderer->ConvertFramebufferRectangle(dst, g_renderer->GetCurrentFramebuffer())); - g_renderer->SetPipeline(m_pipeline.get()); - g_renderer->SetTexture(0, src_tex); - g_renderer->SetSamplerState(0, RenderState::GetLinearSamplerState()); - g_renderer->Draw(0, 3); + g_gfx->SetViewportAndScissor( + g_gfx->ConvertFramebufferRectangle(dst, g_gfx->GetCurrentFramebuffer())); + g_gfx->SetPipeline(m_pipeline.get()); + g_gfx->SetTexture(0, src_tex); + g_gfx->SetSamplerState(0, RenderState::GetLinearSamplerState()); + g_gfx->Draw(0, 3); } std::string PostProcessing::GetUniformBufferHeader() const @@ -598,8 +598,8 @@ bool PostProcessing::CompileVertexShader() ss << "}\n"; - m_vertex_shader = g_renderer->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), - "Post-processing vertex shader"); + m_vertex_shader = + g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), "Post-processing vertex shader"); if (!m_vertex_shader) { PanicAlertFmt("Failed to compile post-processing vertex shader"); @@ -688,7 +688,7 @@ bool PostProcessing::CompilePixelShader() // Generate GLSL and compile the new shader. m_config.LoadShader(g_ActiveConfig.sPostProcessingShader); - m_pixel_shader = g_renderer->CreateShaderFromSource( + m_pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, GetHeader() + m_config.GetShaderCode() + GetFooter(), fmt::format("Post-processing pixel shader: {}", m_config.GetShader())); if (!m_pixel_shader) @@ -697,7 +697,7 @@ bool PostProcessing::CompilePixelShader() // Use default shader. m_config.LoadDefaultShader(); - m_pixel_shader = g_renderer->CreateShaderFromSource( + m_pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, GetHeader() + m_config.GetShaderCode() + GetFooter(), "Default post-processing pixel shader"); if (!m_pixel_shader) @@ -716,14 +716,14 @@ bool PostProcessing::CompilePipeline() AbstractPipelineConfig config = {}; config.vertex_shader = m_vertex_shader.get(); config.geometry_shader = - g_renderer->UseGeometryShaderForUI() ? g_shader_cache->GetTexcoordGeometryShader() : nullptr; + g_gfx->UseGeometryShaderForUI() ? g_shader_cache->GetTexcoordGeometryShader() : nullptr; config.pixel_shader = m_pixel_shader.get(); config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); config.depth_state = RenderState::GetNoDepthTestingDepthState(); config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetColorFramebufferState(m_framebuffer_format); config.usage = AbstractPipelineUsage::Utility; - m_pipeline = g_renderer->CreatePipeline(config); + m_pipeline = g_gfx->CreatePipeline(config); if (!m_pipeline) return false; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 5cddf38aa3..8f10707d0d 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -8,6 +8,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/FrameDumper.h" #include "VideoCommon/OnScreenUI.h" #include "VideoCommon/PostProcessing.h" @@ -47,6 +48,9 @@ bool Presenter::Initialize() if (!m_onscreen_ui->Initialize(m_backbuffer_width, m_backbuffer_height, m_backbuffer_scale)) return false; + if (!g_gfx->IsHeadless()) + SetBackbuffer(g_gfx->GetSurfaceInfo()); + return true; } @@ -57,13 +61,12 @@ void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height) UpdateDrawRectangle(); } -void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, - AbstractTextureFormat backbuffer_format) +void Presenter::SetBackbuffer(SurfaceInfo info) { - m_backbuffer_width = backbuffer_width; - m_backbuffer_height = backbuffer_height; - m_backbuffer_scale = backbuffer_scale; - m_backbuffer_format = backbuffer_format; + m_backbuffer_width = info.width; + m_backbuffer_height = info.height; + m_backbuffer_scale = info.scale; + m_backbuffer_format = info.format; UpdateDrawRectangle(); } @@ -74,14 +77,14 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) { // The existing shader must not be in use when it's destroyed - g_renderer->WaitForGPUIdle(); + g_gfx->WaitForGPUIdle(); m_post_processor->RecompileShader(); } // Stereo mode change requires recompiling our post processing pipeline and imgui pipelines for // rendering the UI. - if (changed_bits & Renderer::ConfigChangeBits::CONFIG_CHANGE_BIT_STEREO_MODE) + if (changed_bits & ConfigChangeBits::CONFIG_CHANGE_BIT_STEREO_MODE) { m_onscreen_ui->RecompileImGuiPipeline(); m_post_processor->RecompilePipeline(); @@ -90,24 +93,24 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) void Presenter::BeginUIFrame() { - if (g_renderer->IsHeadless()) + if (g_gfx->IsHeadless()) return; - g_renderer->BeginUtilityDrawing(); - g_renderer->BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); + g_gfx->BeginUtilityDrawing(); + g_gfx->BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); } void Presenter::EndUIFrame() { m_onscreen_ui->Finalize(); - if (g_renderer->IsHeadless()) + if (g_gfx->IsHeadless()) { m_onscreen_ui->DrawImGui(); std::lock_guard guard(m_swap_mutex); - g_renderer->PresentBackbuffer(); - g_renderer->EndUtilityDrawing(); + g_gfx->PresentBackbuffer(); + g_gfx->EndUtilityDrawing(); } m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); @@ -392,7 +395,7 @@ void Presenter::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, { if (!g_ActiveConfig.backend_info.bSupportsPostProcessing) { - g_renderer->ShowImage(source_texture, source_rc); + g_gfx->ShowImage(source_texture, source_rc); return; } @@ -400,13 +403,13 @@ void Presenter::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, g_ActiveConfig.backend_info.bUsesExplictQuadBuffering) { // Quad-buffered stereo is annoying on GL. - g_renderer->SelectLeftBuffer(); + g_gfx->SelectLeftBuffer(); m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 0); - g_renderer->SelectRightBuffer(); + g_gfx->SelectRightBuffer(); m_post_processor->BlitFromTexture(target_rc, source_rc, source_texture, 1); - g_renderer->SelectMainBuffer(); + g_gfx->SelectMainBuffer(); } else if (g_ActiveConfig.stereo_mode == StereoMode::SBS || g_ActiveConfig.stereo_mode == StereoMode::TAB) @@ -436,7 +439,7 @@ bool Presenter::SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb if (g_frame_dumper->IsFrameDumping()) { MathUtil::Rectangle target_rect; - if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_renderer->IsHeadless()) + if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) { target_rect = GetTargetRectangle(); } @@ -469,10 +472,10 @@ void Presenter::Present() m_onscreen_ui->Finalize(); // Render the XFB to the screen. - g_renderer->BeginUtilityDrawing(); - if (!g_renderer->IsHeadless()) + g_gfx->BeginUtilityDrawing(); + if (!g_gfx->IsHeadless()) { - g_renderer->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); + g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); UpdateDrawRectangle(); @@ -488,7 +491,7 @@ void Presenter::Present() // Present to the window system. { std::lock_guard guard(m_swap_mutex); - g_renderer->PresentBackbuffer(); + g_gfx->PresentBackbuffer(); } // Update the window size based on the frame that was just rendered. @@ -498,7 +501,7 @@ void Presenter::Present() m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); } void Presenter::SetKeyMap(std::span> key_map) diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 5f535f7208..3b99c3207d 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -16,6 +16,7 @@ #include class AbstractTexture; +struct SurfaceInfo; namespace VideoCommon { @@ -51,8 +52,7 @@ public: AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; } void SetWindowSize(int width, int height); void SetBackbuffer(int backbuffer_width, int backbuffer_height); - void SetBackbuffer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, - AbstractTextureFormat backbuffer_format); + void SetBackbuffer(SurfaceInfo info); void UpdateDrawRectangle(); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 86725f534e..ec5f25267f 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -38,6 +38,7 @@ #include "Core/System.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BoundingBox.h" #include "VideoCommon/CommandProcessor.h" @@ -59,17 +60,12 @@ std::unique_ptr g_renderer; -Renderer::Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, - AbstractTextureFormat backbuffer_format) - : m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} +Renderer::Renderer() : m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} { UpdateActiveConfig(); FreeLook::UpdateActiveConfig(); CalculateTargetSize(); - g_presenter->SetBackbuffer(backbuffer_width, backbuffer_height, backbuffer_scale, - backbuffer_format); - m_is_game_widescreen = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN); g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type); } @@ -78,13 +74,6 @@ Renderer::~Renderer() = default; bool Renderer::Initialize() { - m_bounding_box = CreateBoundingBox(); - if (g_ActiveConfig.backend_info.bSupportsBBox && !m_bounding_box->Initialize()) - { - PanicAlertFmt("Failed to initialize bounding box."); - return false; - } - if (g_ActiveConfig.bGraphicMods) { // If a config change occurred in a previous session, @@ -101,12 +90,12 @@ bool Renderer::Initialize() m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config); } - return g_presenter->Initialize(); + return true; } void Renderer::Shutdown() { - m_bounding_box.reset(); + g_bounding_box.reset(); } void Renderer::BeginUtilityDrawing() @@ -121,31 +110,38 @@ void Renderer::EndUtilityDrawing() BPFunctions::SetScissorAndViewport(); } -void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) -{ - m_current_framebuffer = framebuffer; -} - -void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) -{ - m_current_framebuffer = framebuffer; -} - -void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) -{ - m_current_framebuffer = framebuffer; -} - bool Renderer::EFBHasAlphaChannel() const { return m_prev_efb_format == PixelFormat::RGBA6_Z24; } -void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) +void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, + bool z_enable, u32 color, u32 z) { - g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z); + g_framebuffer_manager->FlushEFBPokes(); + g_framebuffer_manager->FlagPeekCacheAsOutOfDate(); + + // Native -> EFB coordinates + MathUtil::Rectangle target_rc = Renderer::ConvertEFBRectangle(rc); + target_rc.ClampUL(0, 0, m_target_width, m_target_height); + + // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha + // channel to 0xFF. + // On backends that don't allow masking Alpha clears, this allows us to use the fast path + // almost all the time + if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 || + bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 || + bpmem.zcontrol.pixel_format == PixelFormat::Z24) + { + // Force alpha writes, and clear the alpha channel. + alpha_enable = true; + color &= 0x00FFFFFF; + } + + g_gfx->ClearRegion(rc, target_rc, color_enable, alpha_enable, z_enable, color, z); + + // Scissor rect must be restored. + BPFunctions::SetScissorAndViewport(); } void Renderer::ReinterpretPixelData(EFBReinterpretType convtype) @@ -155,17 +151,17 @@ void Renderer::ReinterpretPixelData(EFBReinterpretType convtype) bool Renderer::IsBBoxEnabled() const { - return m_bounding_box->IsEnabled(); + return g_bounding_box->IsEnabled(); } void Renderer::BBoxEnable(PixelShaderManager& pixel_shader_manager) { - m_bounding_box->Enable(pixel_shader_manager); + g_bounding_box->Enable(pixel_shader_manager); } void Renderer::BBoxDisable(PixelShaderManager& pixel_shader_manager) { - m_bounding_box->Disable(pixel_shader_manager); + g_bounding_box->Disable(pixel_shader_manager); } u16 Renderer::BBoxRead(u32 index) @@ -173,7 +169,7 @@ u16 Renderer::BBoxRead(u32 index) if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) return m_bounding_box_fallback[index]; - return m_bounding_box->Get(index); + return g_bounding_box->Get(index); } void Renderer::BBoxWrite(u32 index, u16 value) @@ -184,7 +180,7 @@ void Renderer::BBoxWrite(u32 index, u16 value) return; } - m_bounding_box->Set(index, value); + g_bounding_box->Set(index, value); } void Renderer::BBoxFlush() @@ -192,7 +188,7 @@ void Renderer::BBoxFlush() if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) return; - m_bounding_box->Flush(); + g_bounding_box->Flush(); } u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) @@ -436,13 +432,13 @@ void Renderer::CheckForConfigChanges() return; // Notify the backend of the changes, if any. - OnConfigChanged(changed_bits); + g_gfx->OnConfigChanged(changed_bits); // If there's any shader changes, wait for the GPU to finish before destroying anything. if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { - WaitForGPUIdle(); - SetPipeline(nullptr); + g_gfx->WaitForGPUIdle(); + g_gfx->SetPipeline(nullptr); } // Framebuffer changed? @@ -469,81 +465,6 @@ void Renderer::CheckForConfigChanges() } } -bool Renderer::IsHeadless() const -{ - return true; -} - -void Renderer::SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth, - float max_depth) -{ - SetViewport(static_cast(rect.left), static_cast(rect.top), - static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), min_depth, - max_depth); - SetScissorRect(rect); -} - -void Renderer::ScaleTexture(AbstractFramebuffer* dst_framebuffer, - const MathUtil::Rectangle& dst_rect, - const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect) -{ - ASSERT(dst_framebuffer->GetColorFormat() == AbstractTextureFormat::RGBA8); - - BeginUtilityDrawing(); - - // The shader needs to know the source rectangle. - const auto converted_src_rect = - ConvertFramebufferRectangle(src_rect, src_texture->GetWidth(), src_texture->GetHeight()); - const float rcp_src_width = 1.0f / src_texture->GetWidth(); - const float rcp_src_height = 1.0f / src_texture->GetHeight(); - const std::array uniforms = {{converted_src_rect.left * rcp_src_width, - converted_src_rect.top * rcp_src_height, - converted_src_rect.GetWidth() * rcp_src_width, - converted_src_rect.GetHeight() * rcp_src_height}}; - g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); - - // Discard if we're overwriting the whole thing. - if (static_cast(dst_rect.GetWidth()) == dst_framebuffer->GetWidth() && - static_cast(dst_rect.GetHeight()) == dst_framebuffer->GetHeight()) - { - SetAndDiscardFramebuffer(dst_framebuffer); - } - else - { - SetFramebuffer(dst_framebuffer); - } - - SetViewportAndScissor(ConvertFramebufferRectangle(dst_rect, dst_framebuffer)); - SetPipeline(dst_framebuffer->GetLayers() > 1 ? g_shader_cache->GetRGBA8StereoCopyPipeline() : - g_shader_cache->GetRGBA8CopyPipeline()); - SetTexture(0, src_texture); - SetSamplerState(0, RenderState::GetLinearSamplerState()); - Draw(0, 3); - EndUtilityDrawing(); - if (dst_framebuffer->GetColorAttachment()) - dst_framebuffer->GetColorAttachment()->FinishedRendering(); -} - -MathUtil::Rectangle -Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, - const AbstractFramebuffer* framebuffer) const -{ - return ConvertFramebufferRectangle(rect, framebuffer->GetWidth(), framebuffer->GetHeight()); -} - -MathUtil::Rectangle Renderer::ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, - u32 fb_width, u32 fb_height) const -{ - MathUtil::Rectangle ret = rect; - if (g_ActiveConfig.backend_info.bUsesLowerLeftOrigin) - { - ret.top = fb_height - rect.bottom; - ret.bottom = fb_height - rect.top; - } - return ret; -} - MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle& rc) const { MathUtil::Rectangle result; @@ -590,14 +511,6 @@ void Renderer::RecordVideoMemory() texMem); } -bool Renderer::UseGeometryShaderForUI() const -{ - // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, - // instead drawing twice and the eye selected by glDrawBuffer() (see Presenter::RenderXFBToScreen) - return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && - g_ActiveConfig.backend_info.api_type != APIType::OpenGL; -} - void Renderer::ForceReloadTextures() { m_force_reload_textures.Set(); @@ -756,7 +669,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 } else { - Flush(); + g_gfx->Flush(); } // Update our last xfb values @@ -768,7 +681,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 } else { - Flush(); + g_gfx->Flush(); } } @@ -803,7 +716,7 @@ void Renderer::DoState(PointerWrap& p) p.Do(m_last_xfb_height); p.DoArray(m_bounding_box_fallback); - m_bounding_box->DoState(p); + g_bounding_box->DoState(p); if (p.IsReadMode()) { @@ -821,11 +734,6 @@ void Renderer::DoState(PointerWrap& p) #endif } -std::unique_ptr Renderer::CreateAsyncShaderCompiler() -{ - return std::make_unique(); -} - const GraphicsModManager& Renderer::GetGraphicsModManager() const { return m_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 17fe407618..fb88dfd1eb 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -64,100 +64,18 @@ struct EfbPokeData class Renderer { public: - Renderer(int backbuffer_width, int backbuffer_height, float backbuffer_scale, - AbstractTextureFormat backbuffer_format); + Renderer(); virtual ~Renderer(); - using ClearColor = std::array; - - virtual bool IsHeadless() const = 0; - virtual bool Initialize(); virtual void Shutdown(); - virtual void SetPipeline(const AbstractPipeline* pipeline) {} - virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} - virtual void SetTexture(u32 index, const AbstractTexture* texture) {} - virtual void SetSamplerState(u32 index, const SamplerState& state) {} - virtual void SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) {} - virtual void UnbindTexture(const AbstractTexture* texture) {} - virtual void SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) - { - } - virtual void SetFullscreen(bool enable_fullscreen) {} - virtual bool IsFullscreen() const { return false; } - virtual void BeginUtilityDrawing(); - virtual void EndUtilityDrawing(); - virtual std::unique_ptr CreateTexture(const TextureConfig& config, - std::string_view name = "") = 0; - virtual std::unique_ptr - CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0; - virtual std::unique_ptr - CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) = 0; - - // Framebuffer operations. - virtual void SetFramebuffer(AbstractFramebuffer* framebuffer); - virtual void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer); - virtual void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value = {}, float depth_value = 0.0f); - - // Drawing with currently-bound pipeline state. - virtual void Draw(u32 base_vertex, u32 num_vertices) {} - virtual void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) {} - - // Dispatching compute shaders with currently-bound state. - virtual void DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, - u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) - { - } - - // Binds the backbuffer for rendering. The buffer will be cleared immediately after binding. - // This is where any window size changes are detected, therefore m_backbuffer_width and/or - // m_backbuffer_height may change after this function returns. - virtual void BindBackbuffer(const ClearColor& clear_color = {}) {} - - // Presents the backbuffer to the window system, or "swaps buffers". - virtual void PresentBackbuffer() {} - - // Shader modules/objects. - virtual std::unique_ptr CreateShaderFromSource(ShaderStage stage, - std::string_view source, - std::string_view name = "") = 0; - virtual std::unique_ptr CreateShaderFromBinary(ShaderStage stage, - const void* data, size_t length, - std::string_view name = "") = 0; - virtual std::unique_ptr - CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) = 0; - virtual std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data = nullptr, - size_t cache_data_length = 0) = 0; - - AbstractFramebuffer* GetCurrentFramebuffer() const { return m_current_framebuffer; } - + void BeginUtilityDrawing(); + void EndUtilityDrawing(); // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } - // Sets viewport and scissor to the specified rectangle. rect is assumed to be in framebuffer - // coordinates, i.e. lower-left origin in OpenGL. - void SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth = 0.0f, - float max_depth = 1.0f); - - // Scales a GPU texture using a copy shader. - virtual void ScaleTexture(AbstractFramebuffer* dst_framebuffer, - const MathUtil::Rectangle& dst_rect, - const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect); - - // Converts an upper-left to lower-left if required by the backend, optionally - // clamping to the framebuffer size. - MathUtil::Rectangle ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, - u32 fb_width, u32 fb_height) const; - MathUtil::Rectangle - ConvertFramebufferRectangle(const MathUtil::Rectangle& rect, - const AbstractFramebuffer* framebuffer) const; - // EFB coordinate conversion functions // Use this to convert a whole native EFB rect to backbuffer coordinates MathUtil::Rectangle ConvertEFBRectangle(const MathUtil::Rectangle& rc) const; @@ -188,29 +106,12 @@ public: void BBoxWrite(u32 index, u16 value); void BBoxFlush(); - virtual void Flush() {} - virtual void WaitForGPUIdle() {} - // Finish up the current frame, print some stats void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); void UpdateWidescreenHeuristic(); bool IsGameWidescreen() const { return m_is_game_widescreen; } - // A simple presentation fallback, only used by video software - virtual void ShowImage(const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) - { - } - - // For opengl's glDrawBuffer - virtual void SelectLeftBuffer() {} - virtual void SelectRightBuffer() {} - virtual void SelectMainBuffer() {} - - // Called when the configuration changes, and backend structures need to be updated. - virtual void OnConfigChanged(u32 bits) {} - PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } bool EFBHasAlphaChannel() const; @@ -218,30 +119,11 @@ public: bool UseVertexDepthRange() const; void DoState(PointerWrap& p); - virtual std::unique_ptr CreateAsyncShaderCompiler(); - - // Returns true if a layer-expanding geometry shader should be used when rendering the user - // interface and final XFB. - bool UseGeometryShaderForUI() const; - // Will forcibly reload all textures on the next swap void ForceReloadTextures(); const GraphicsModManager& GetGraphicsModManager() const; - // Bitmask containing information about which configuration has changed for the backend. - enum ConfigChangeBits : u32 - { - CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), - CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), - CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), - CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), - CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), - CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), - CONFIG_CHANGE_BIT_VSYNC = (1 << 6), - CONFIG_CHANGE_BIT_BBOX = (1 << 7) - }; - protected: std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); @@ -251,11 +133,6 @@ protected: void CheckFifoRecording(); void RecordVideoMemory(); - virtual std::unique_ptr CreateBoundingBox() const = 0; - - AbstractFramebuffer* m_current_framebuffer = nullptr; - const AbstractPipeline* m_current_pipeline = nullptr; - bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 1be1ad1167..b9876dc533 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -10,12 +10,12 @@ #include "Common/MsgHandler.h" #include "Core/ConfigManager.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/ConstantManager.h" #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FramebufferShaderGen.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" @@ -45,7 +45,7 @@ bool ShaderCache::Initialize() if (!CompileSharedPipelines()) return false; - m_async_shader_compiler = g_renderer->CreateAsyncShaderCompiler(); + m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); return true; } @@ -122,7 +122,7 @@ const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineUid& uid) std::unique_ptr pipeline; std::optional pipeline_config = GetGXPipelineConfig(uid); if (pipeline_config) - pipeline = g_renderer->CreatePipeline(*pipeline_config); + pipeline = g_gfx->CreatePipeline(*pipeline_config); if (g_ActiveConfig.bShaderCache && !exists_in_cache) AppendGXPipelineUID(uid); return InsertGXPipeline(uid, std::move(pipeline)); @@ -154,7 +154,7 @@ const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineU std::unique_ptr pipeline; std::optional pipeline_config = GetGXPipelineConfig(uid); if (pipeline_config) - pipeline = g_renderer->CreatePipeline(*pipeline_config); + pipeline = g_gfx->CreatePipeline(*pipeline_config); return InsertGXUberPipeline(uid, std::move(pipeline)); } @@ -235,7 +235,7 @@ void ShaderCache::LoadShaderCache(T& cache, APIType api_type, const char* type, CacheReader(T& cache_) : cache(cache_) {} void Read(const K& key, const u8* value, u32 value_size) { - auto shader = g_renderer->CreateShaderFromBinary(stage, value, value_size); + auto shader = g_gfx->CreateShaderFromBinary(stage, value, value_size); if (shader) { auto& entry = cache.shader_map[key]; @@ -298,7 +298,7 @@ void ShaderCache::LoadPipelineCache(T& cache, LinearDiskCache& if (!config) return; - auto pipeline = g_renderer->CreatePipeline(*config, value, value_size); + auto pipeline = g_gfx->CreatePipeline(*config, value, value_size); if (!pipeline) { // If any of the pipelines fail to create, consider the cache stale. @@ -435,7 +435,7 @@ std::unique_ptr ShaderCache::CompileVertexShader(const VertexSha { const ShaderCode source_code = GenerateVertexShaderCode(m_api_type, m_host_config, uid.GetUidData()); - return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer()); + return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer()); } std::unique_ptr @@ -443,15 +443,15 @@ ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) con { const ShaderCode source_code = UberShader::GenVertexShader(m_api_type, m_host_config, uid.GetUidData()); - return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), - fmt::to_string(*uid.GetUidData())); + return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), + fmt::to_string(*uid.GetUidData())); } std::unique_ptr ShaderCache::CompilePixelShader(const PixelShaderUid& uid) const { const ShaderCode source_code = GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData()); - return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer()); + return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer()); } std::unique_ptr @@ -459,8 +459,8 @@ ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const { const ShaderCode source_code = UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData()); - return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), - fmt::to_string(*uid.GetUidData())); + return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), + fmt::to_string(*uid.GetUidData())); } const AbstractShader* ShaderCache::InsertVertexShader(const VertexShaderUid& uid, @@ -556,8 +556,8 @@ const AbstractShader* ShaderCache::CreateGeometryShader(const GeometryShaderUid& const ShaderCode source_code = GenerateGeometryShaderCode(m_api_type, m_host_config, uid.GetUidData()); std::unique_ptr shader = - g_renderer->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), - fmt::format("Geometry shader: {}", *uid.GetUidData())); + g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), + fmt::format("Geometry shader: {}", *uid.GetUidData())); auto& entry = m_gs_cache.shader_map[uid]; entry.pending = false; @@ -1159,7 +1159,7 @@ void ShaderCache::QueuePipelineCompile(const GXPipelineUid& uid, u32 priority) bool Compile() override { if (config) - pipeline = g_renderer->CreatePipeline(*config); + pipeline = g_gfx->CreatePipeline(*config); return true; } @@ -1234,7 +1234,7 @@ void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineUid& uid, u32 pri bool Compile() override { if (config) - UberPipeline = g_renderer->CreatePipeline(*config); + UberPipeline = g_gfx->CreatePipeline(*config); return true; } @@ -1372,7 +1372,7 @@ ShaderCache::GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShader return iter->second.get(); auto shader_code = TextureConversionShaderGen::GeneratePixelShader(m_api_type, uid.GetUidData()); - auto shader = g_renderer->CreateShaderFromSource( + auto shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, shader_code.GetBuffer(), fmt::format("EFB copy to VRAM pixel shader: {}", *uid.GetUidData())); if (!shader) @@ -1392,7 +1392,7 @@ ShaderCache::GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShader config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetRGBA8FramebufferState(); config.usage = AbstractPipelineUsage::Utility; - auto iiter = m_efb_copy_to_vram_pipelines.emplace(uid, g_renderer->CreatePipeline(config)); + auto iiter = m_efb_copy_to_vram_pipelines.emplace(uid, g_gfx->CreatePipeline(config)); return iiter.first->second.get(); } @@ -1404,7 +1404,7 @@ const AbstractPipeline* ShaderCache::GetEFBCopyToRAMPipeline(const EFBCopyParams const std::string shader_code = TextureConversionShaderTiled::GenerateEncodingShader(uid, m_api_type); - const auto shader = g_renderer->CreateShaderFromSource( + const auto shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, shader_code, fmt::format("EFB copy to RAM pixel shader: {}", uid)); if (!shader) { @@ -1420,19 +1420,19 @@ const AbstractPipeline* ShaderCache::GetEFBCopyToRAMPipeline(const EFBCopyParams config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetColorFramebufferState(AbstractTextureFormat::BGRA8); config.usage = AbstractPipelineUsage::Utility; - auto iiter = m_efb_copy_to_ram_pipelines.emplace(uid, g_renderer->CreatePipeline(config)); + auto iiter = m_efb_copy_to_ram_pipelines.emplace(uid, g_gfx->CreatePipeline(config)); return iiter.first->second.get(); } bool ShaderCache::CompileSharedPipelines() { - m_screen_quad_vertex_shader = g_renderer->CreateShaderFromSource( + m_screen_quad_vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, FramebufferShaderGen::GenerateScreenQuadVertexShader(), "Screen quad vertex shader"); - m_texture_copy_vertex_shader = g_renderer->CreateShaderFromSource( + m_texture_copy_vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, FramebufferShaderGen::GenerateTextureCopyVertexShader(), "Texture copy vertex shader"); - m_efb_copy_vertex_shader = g_renderer->CreateShaderFromSource( + m_efb_copy_vertex_shader = g_gfx->CreateShaderFromSource( ShaderStage::Vertex, TextureConversionShaderGen::GenerateVertexShader(m_api_type).GetBuffer(), "EFB copy vertex shader"); if (!m_screen_quad_vertex_shader || !m_texture_copy_vertex_shader || !m_efb_copy_vertex_shader) @@ -1440,20 +1440,20 @@ bool ShaderCache::CompileSharedPipelines() if (UseGeometryShaderForEFBCopies()) { - m_texcoord_geometry_shader = g_renderer->CreateShaderFromSource( + m_texcoord_geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 0), "Texcoord passthrough geometry shader"); - m_color_geometry_shader = g_renderer->CreateShaderFromSource( + m_color_geometry_shader = g_gfx->CreateShaderFromSource( ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(0, 1), "Color passthrough geometry shader"); if (!m_texcoord_geometry_shader || !m_color_geometry_shader) return false; } - m_texture_copy_pixel_shader = g_renderer->CreateShaderFromSource( + m_texture_copy_pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateTextureCopyPixelShader(), "Texture copy pixel shader"); - m_color_pixel_shader = g_renderer->CreateShaderFromSource( + m_color_pixel_shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, FramebufferShaderGen::GenerateColorPixelShader(), "Color pixel shader"); if (!m_texture_copy_pixel_shader || !m_color_pixel_shader) return false; @@ -1468,14 +1468,14 @@ bool ShaderCache::CompileSharedPipelines() config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetRGBA8FramebufferState(); config.usage = AbstractPipelineUsage::Utility; - m_copy_rgba8_pipeline = g_renderer->CreatePipeline(config); + m_copy_rgba8_pipeline = g_gfx->CreatePipeline(config); if (!m_copy_rgba8_pipeline) return false; if (UseGeometryShaderForEFBCopies()) { config.geometry_shader = m_texcoord_geometry_shader.get(); - m_rgba8_stereo_copy_pipeline = g_renderer->CreatePipeline(config); + m_rgba8_stereo_copy_pipeline = g_gfx->CreatePipeline(config); if (!m_rgba8_stereo_copy_pipeline) return false; } @@ -1488,7 +1488,7 @@ bool ShaderCache::CompileSharedPipelines() for (size_t i = 0; i < NUM_PALETTE_CONVERSION_SHADERS; i++) { TLUTFormat format = static_cast(i); - auto shader = g_renderer->CreateShaderFromSource( + auto shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, TextureConversionShaderTiled::GeneratePaletteConversionShader(format, m_api_type), fmt::format("Palette conversion pixel shader: {}", format)); @@ -1496,7 +1496,7 @@ bool ShaderCache::CompileSharedPipelines() return false; config.pixel_shader = shader.get(); - m_palette_conversion_pipelines[i] = g_renderer->CreatePipeline(config); + m_palette_conversion_pipelines[i] = g_gfx->CreatePipeline(config); if (!m_palette_conversion_pipelines[i]) return false; } @@ -1527,7 +1527,7 @@ const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat return nullptr; } - std::unique_ptr shader = g_renderer->CreateShaderFromSource( + std::unique_ptr shader = g_gfx->CreateShaderFromSource( ShaderStage::Pixel, shader_source, fmt::format("Texture reinterpret pixel shader: {} to {}", from_format, to_format)); if (!shader) @@ -1546,7 +1546,7 @@ const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat config.blending_state = RenderState::GetNoBlendingBlendState(); config.framebuffer_state = RenderState::GetRGBA8FramebufferState(); config.usage = AbstractPipelineUsage::Utility; - auto iiter = m_texture_reinterpret_pipelines.emplace(key, g_renderer->CreatePipeline(config)); + auto iiter = m_texture_reinterpret_pipelines.emplace(key, g_gfx->CreatePipeline(config)); return iiter.first->second.get(); } @@ -1574,7 +1574,7 @@ ShaderCache::GetTextureDecodingShader(TextureFormat format, fmt::format("Texture decoding compute shader: {}", format); std::unique_ptr shader = - g_renderer->CreateShaderFromSource(ShaderStage::Compute, shader_source, name); + g_gfx->CreateShaderFromSource(ShaderStage::Compute, shader_source, name); if (!shader) { m_texture_decoding_shaders.emplace(key, nullptr); diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 6102767e70..e991669950 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -34,6 +34,7 @@ #include "Core/System.h" #include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/FramebufferManager.h" @@ -298,7 +299,7 @@ RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const decoded_entry->SetNotCopy(); decoded_entry->may_have_overlapping_textures = entry->may_have_overlapping_textures; - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); const u32 palette_size = entry->format == TextureFormat::I4 ? 32 : 512; u32 texel_buffer_offset; @@ -318,19 +319,19 @@ RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const uniforms.texel_buffer_offset = texel_buffer_offset; g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); - g_renderer->SetAndDiscardFramebuffer(decoded_entry->framebuffer.get()); - g_renderer->SetViewportAndScissor(decoded_entry->texture->GetRect()); - g_renderer->SetPipeline(pipeline); - g_renderer->SetTexture(1, entry->texture.get()); - g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState()); - g_renderer->Draw(0, 3); - g_renderer->EndUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(decoded_entry->framebuffer.get()); + g_gfx->SetViewportAndScissor(decoded_entry->texture->GetRect()); + g_gfx->SetPipeline(pipeline); + g_gfx->SetTexture(1, entry->texture.get()); + g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState()); + g_gfx->Draw(0, 3); + g_gfx->EndUtilityDrawing(); decoded_entry->texture->FinishedRendering(); } else { ERROR_LOG_FMT(VIDEO, "Texel buffer upload of {} bytes failed", palette_size); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); } textures_by_address.emplace(decoded_entry->addr, decoded_entry); @@ -369,14 +370,14 @@ RcTcacheEntry TextureCacheBase::ReinterpretEntry(const RcTcacheEntry& existing_e reinterpreted_entry->may_have_overlapping_textures = existing_entry->may_have_overlapping_textures; - g_renderer->BeginUtilityDrawing(); - g_renderer->SetAndDiscardFramebuffer(reinterpreted_entry->framebuffer.get()); - g_renderer->SetViewportAndScissor(reinterpreted_entry->texture->GetRect()); - g_renderer->SetPipeline(pipeline); - g_renderer->SetTexture(0, existing_entry->texture.get()); - g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState()); - g_renderer->Draw(0, 3); - g_renderer->EndUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(reinterpreted_entry->framebuffer.get()); + g_gfx->SetViewportAndScissor(reinterpreted_entry->texture->GetRect()); + g_gfx->SetPipeline(pipeline); + g_gfx->SetTexture(0, existing_entry->texture.get()); + g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState()); + g_gfx->Draw(0, 3); + g_gfx->EndUtilityDrawing(); reinterpreted_entry->texture->FinishedRendering(); textures_by_address.emplace(reinterpreted_entry->addr, reinterpreted_entry); @@ -408,9 +409,8 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(RcTcacheEntry& entry, u32 new_wi } // No need to convert the coordinates here since they'll be the same. - g_renderer->ScaleTexture(new_texture->framebuffer.get(), - new_texture->texture->GetConfig().GetRect(), entry->texture.get(), - entry->texture->GetConfig().GetRect()); + g_gfx->ScaleTexture(new_texture->framebuffer.get(), new_texture->texture->GetConfig().GetRect(), + entry->texture.get(), entry->texture->GetConfig().GetRect()); entry->texture.swap(new_texture->texture); entry->framebuffer.swap(new_texture->framebuffer); @@ -432,8 +432,7 @@ bool TextureCacheBase::CheckReadbackTexture(u32 width, u32 height, AbstractTextu TextureConfig staging_config(std::max(width, 128u), std::max(height, 128u), 1, 1, 1, format, 0); m_readback_texture.reset(); - m_readback_texture = - g_renderer->CreateStagingTexture(StagingTextureType::Readback, staging_config); + m_readback_texture = g_gfx->CreateStagingTexture(StagingTextureType::Readback, staging_config); return m_readback_texture != nullptr; } @@ -1081,7 +1080,7 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex, state.tm0.anisotropic_filtering = false; } - g_renderer->SetSamplerState(index, state); + g_gfx->SetSamplerState(index, state); auto& system = Core::System::GetInstance(); auto& pixel_shader_manager = system.GetPixelShaderManager(); pixel_shader_manager.SetSamplerState(index, state.tm0.hex, state.tm1.hex); @@ -1096,7 +1095,7 @@ void TextureCacheBase::BindTextures(BitSet32 used_textures) const RcTcacheEntry& tentry = bound_textures[i]; if (used_textures[i] && tentry) { - g_renderer->SetTexture(i, tentry->texture.get()); + g_gfx->SetTexture(i, tentry->texture.get()); pixel_shader_manager.SetTexDims(i, tentry->native_width, tentry->native_height); const float custom_tex_scale = tentry->GetWidth() / float(tentry->native_width); @@ -2013,8 +2012,8 @@ void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry) // We may have to scale if one of the copies is not internal resolution. if (srcrect.GetWidth() != dstrect.GetWidth() || srcrect.GetHeight() != dstrect.GetHeight()) { - g_renderer->ScaleTexture(stitched_entry->framebuffer.get(), dstrect, entry->texture.get(), - srcrect); + g_gfx->ScaleTexture(stitched_entry->framebuffer.get(), dstrect, entry->texture.get(), + srcrect); } else { @@ -2521,7 +2520,7 @@ std::unique_ptr TextureCacheBase::GetEFBCopyStagingTextu return ptr; } - std::unique_ptr tex = g_renderer->CreateStagingTexture( + std::unique_ptr tex = g_gfx->CreateStagingTexture( StagingTextureType::Readback, m_efb_encoding_texture->GetConfig()); if (!tex) WARN_LOG_FMT(VIDEO, "Failed to create EFB copy staging texture"); @@ -2614,7 +2613,7 @@ TextureCacheBase::AllocateTexture(const TextureConfig& config) return std::move(entry); } - std::unique_ptr texture = g_renderer->CreateTexture(config); + std::unique_ptr texture = g_gfx->CreateTexture(config); if (!texture) { WARN_LOG_FMT(VIDEO, "Failed to allocate a {}x{}x{} texture", config.width, config.height, @@ -2625,7 +2624,7 @@ TextureCacheBase::AllocateTexture(const TextureConfig& config) std::unique_ptr framebuffer; if (config.IsRenderTarget()) { - framebuffer = g_renderer->CreateFramebuffer(texture.get(), nullptr); + framebuffer = g_gfx->CreateFramebuffer(texture.get(), nullptr); if (!framebuffer) { WARN_LOG_FMT(VIDEO, "Failed to allocate a {}x{}x{} framebuffer", config.width, config.height, @@ -2745,12 +2744,11 @@ bool TextureCacheBase::CreateUtilityTextures() { constexpr TextureConfig encoding_texture_config( EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8, AbstractTextureFlag_RenderTarget); - m_efb_encoding_texture = - g_renderer->CreateTexture(encoding_texture_config, "EFB encoding texture"); + m_efb_encoding_texture = g_gfx->CreateTexture(encoding_texture_config, "EFB encoding texture"); if (!m_efb_encoding_texture) return false; - m_efb_encoding_framebuffer = g_renderer->CreateFramebuffer(m_efb_encoding_texture.get(), nullptr); + m_efb_encoding_framebuffer = g_gfx->CreateFramebuffer(m_efb_encoding_texture.get(), nullptr); if (!m_efb_encoding_framebuffer) return false; @@ -2759,7 +2757,7 @@ bool TextureCacheBase::CreateUtilityTextures() constexpr TextureConfig decoding_texture_config( 1024, 1024, 1, 1, 1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_ComputeImage); m_decoding_texture = - g_renderer->CreateTexture(decoding_texture_config, "GPU texture decoding texture"); + g_gfx->CreateTexture(decoding_texture_config, "GPU texture decoding texture"); if (!m_decoding_texture) return false; } @@ -2788,14 +2786,14 @@ void TextureCacheBase::CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_c } const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); - const auto framebuffer_rect = g_renderer->ConvertFramebufferRectangle( + const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle( scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer()); AbstractTexture* src_texture = is_depth_copy ? g_framebuffer_manager->ResolveEFBDepthTexture(framebuffer_rect) : g_framebuffer_manager->ResolveEFBColorTexture(framebuffer_rect); src_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); // Fill uniform buffer. struct Uniforms @@ -2832,14 +2830,14 @@ void TextureCacheBase::CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_c g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); // Use the copy pipeline to render the VRAM copy. - g_renderer->SetAndDiscardFramebuffer(entry->framebuffer.get()); - g_renderer->SetViewportAndScissor(entry->framebuffer->GetRect()); - g_renderer->SetPipeline(copy_pipeline); - g_renderer->SetTexture(0, src_texture); - g_renderer->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() : - RenderState::GetPointSamplerState()); - g_renderer->Draw(0, 3); - g_renderer->EndUtilityDrawing(); + g_gfx->SetAndDiscardFramebuffer(entry->framebuffer.get()); + g_gfx->SetViewportAndScissor(entry->framebuffer->GetRect()); + g_gfx->SetPipeline(copy_pipeline); + g_gfx->SetTexture(0, src_texture); + g_gfx->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() : + RenderState::GetPointSamplerState()); + g_gfx->Draw(0, 3); + g_gfx->EndUtilityDrawing(); entry->texture->FinishedRendering(); } @@ -2862,14 +2860,14 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& } const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); - const auto framebuffer_rect = g_renderer->ConvertFramebufferRectangle( + const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle( scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer()); AbstractTexture* src_texture = params.depth ? g_framebuffer_manager->ResolveEFBDepthTexture(framebuffer_rect) : g_framebuffer_manager->ResolveEFBColorTexture(framebuffer_rect); src_texture->FinishedRendering(); - g_renderer->BeginUtilityDrawing(); + g_gfx->BeginUtilityDrawing(); // Fill uniform buffer. struct Uniforms @@ -2909,15 +2907,15 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& const auto encode_rect = MathUtil::Rectangle(0, 0, render_width, render_height); // Render to GPU texture, and then copy to CPU-accessible texture. - g_renderer->SetAndDiscardFramebuffer(m_efb_encoding_framebuffer.get()); - g_renderer->SetViewportAndScissor(encode_rect); - g_renderer->SetPipeline(copy_pipeline); - g_renderer->SetTexture(0, src_texture); - g_renderer->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() : - RenderState::GetPointSamplerState()); - g_renderer->Draw(0, 3); + g_gfx->SetAndDiscardFramebuffer(m_efb_encoding_framebuffer.get()); + g_gfx->SetViewportAndScissor(encode_rect); + g_gfx->SetPipeline(copy_pipeline); + g_gfx->SetTexture(0, src_texture); + g_gfx->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() : + RenderState::GetPointSamplerState()); + g_gfx->Draw(0, 3); dst->CopyFromTexture(m_efb_encoding_texture.get(), encode_rect, 0, 0, encode_rect); - g_renderer->EndUtilityDrawing(); + g_gfx->EndUtilityDrawing(); // Flush if there's sufficient draws between this copy and the last. g_vertex_manager->OnEFBCopyToRAM(); @@ -2970,12 +2968,12 @@ bool TextureCacheBase::DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, c aligned_height, src_offset, row_stride / bytes_per_buffer_elem, palette_offset}; g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); - g_renderer->SetComputeImageTexture(m_decoding_texture.get(), false, true); + g_gfx->SetComputeImageTexture(m_decoding_texture.get(), false, true); auto dispatch_groups = TextureConversionShaderTiled::GetDispatchCount(info, aligned_width, aligned_height); - g_renderer->DispatchComputeShader(shader, info->group_size_x, info->group_size_y, 1, - dispatch_groups.first, dispatch_groups.second, 1); + g_gfx->DispatchComputeShader(shader, info->group_size_x, info->group_size_y, 1, + dispatch_groups.first, dispatch_groups.second, 1); // Copy from decoding texture -> final texture // This is because we don't want to have to create compute view for every layer diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp index 9bee673db4..f3d580b801 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.cpp +++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp @@ -20,12 +20,12 @@ #include "Core/HW/Memmap.h" #include "Core/System.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/DataReader.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/NativeVertexFormat.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderBase.h" #include "VideoCommon/VertexManagerBase.h" @@ -140,7 +140,7 @@ NativeVertexFormat* GetOrCreateMatchingFormat(const PortableVertexDeclaration& d auto iter = s_native_vertex_map.find(decl); if (iter == s_native_vertex_map.end()) { - std::unique_ptr fmt = g_renderer->CreateNativeVertexFormat(decl); + std::unique_ptr fmt = g_gfx->CreateNativeVertexFormat(decl); auto ipair = s_native_vertex_map.emplace(decl, std::move(fmt)); iter = ipair.first; } diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 1ccfebad0c..5f6b8a310e 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -17,6 +17,7 @@ #include "Core/DolphinAnalytics.h" #include "Core/System.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/BoundingBox.h" #include "VideoCommon/DataReader.h" @@ -329,7 +330,7 @@ void VertexManagerBase::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 ba g_renderer->BBoxFlush(); } - g_renderer->DrawIndexed(base_index, num_indices, base_vertex); + g_gfx->DrawIndexed(base_index, num_indices, base_vertex); } void VertexManagerBase::UploadUniforms() @@ -599,7 +600,7 @@ void VertexManagerBase::Flush() UpdatePipelineObject(); if (m_current_pipeline_object) { - g_renderer->SetPipeline(m_current_pipeline_object); + g_gfx->SetPipeline(m_current_pipeline_object); if (PerfQueryBase::ShouldEmulate()) g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP); @@ -877,7 +878,7 @@ void VertexManagerBase::OnDraw() u32 diff = m_draw_counter - m_last_efb_copy_draw_counter; if (m_unflushed_efb_copy && diff > MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK) { - g_renderer->Flush(); + g_gfx->Flush(); m_unflushed_efb_copy = false; m_last_efb_copy_draw_counter = m_draw_counter; } @@ -892,7 +893,7 @@ void VertexManagerBase::OnDraw() m_scheduled_command_buffer_kicks.end(), m_draw_counter)) { // Kick a command buffer on the background thread. - g_renderer->Flush(); + g_gfx->Flush(); m_unflushed_efb_copy = false; m_last_efb_copy_draw_counter = m_draw_counter; } @@ -927,7 +928,7 @@ void VertexManagerBase::OnEFBCopyToRAM() } m_unflushed_efb_copy = false; - g_renderer->Flush(); + g_gfx->Flush(); } void VertexManagerBase::OnEndFrame() diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 167848b425..40fcd8b0f4 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -324,7 +324,9 @@ void VideoBackendBase::InitializeShared() // do not initialize again for the config window m_initialized = true; + g_renderer = std::make_unique(); g_presenter = std::make_unique(); + g_frame_dumper = std::make_unique(); auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); @@ -337,7 +339,13 @@ void VideoBackendBase::InitializeShared() system.GetGeometryShaderManager().Init(); system.GetPixelShaderManager().Init(); TMEM::Init(); - g_frame_dumper = std::make_unique(); + + if (!g_renderer->Initialize() || !g_presenter->Initialize()) + { + PanicAlertFmtT("Failed to initialize renderer classes"); + Shutdown(); + return; + } g_Config.VerifyValidity(); UpdateActiveConfig(); From f0336a3129ea431862e6e3bf1ccf51a384cf3f57 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 27 Jan 2023 13:21:09 +1300 Subject: [PATCH 08/60] Implement AbstractGfx for OpenGL Mostly involves moving contents of OGLRender to OGLGfx and OGLConfig --- Source/Core/DolphinLib.props | 6 +- Source/Core/VideoBackends/OGL/CMakeLists.txt | 6 +- .../Core/VideoBackends/OGL/OGLBoundingBox.cpp | 4 +- .../OGL/{OGLRender.cpp => OGLConfig.cpp} | 744 +----------------- Source/Core/VideoBackends/OGL/OGLConfig.h | 78 ++ Source/Core/VideoBackends/OGL/OGLGfx.cpp | 730 +++++++++++++++++ .../OGL/{OGLRender.h => OGLGfx.h} | 117 +-- Source/Core/VideoBackends/OGL/OGLMain.cpp | 22 +- .../OGL/OGLNativeVertexFormat.cpp | 4 +- .../Core/VideoBackends/OGL/OGLPerfQuery.cpp | 5 +- Source/Core/VideoBackends/OGL/OGLPipeline.cpp | 1 - .../VideoBackends/OGL/OGLStreamBuffer.cpp | 2 +- Source/Core/VideoBackends/OGL/OGLTexture.cpp | 16 +- .../VideoBackends/OGL/OGLVertexManager.cpp | 8 +- .../VideoBackends/OGL/ProgramShaderCache.cpp | 6 +- .../Core/VideoBackends/OGL/SamplerCache.cpp | 2 +- Source/Core/VideoBackends/OGL/SamplerCache.h | 2 +- Source/Core/VideoCommon/AbstractGfx.cpp | 2 + Source/Core/VideoCommon/BoundingBox.cpp | 2 + Source/Core/VideoCommon/VideoBackendBase.cpp | 20 +- 20 files changed, 905 insertions(+), 872 deletions(-) rename Source/Core/VideoBackends/OGL/{OGLRender.cpp => OGLConfig.cpp} (53%) create mode 100644 Source/Core/VideoBackends/OGL/OGLConfig.h create mode 100644 Source/Core/VideoBackends/OGL/OGLGfx.cpp rename Source/Core/VideoBackends/OGL/{OGLRender.h => OGLGfx.h} (65%) diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index bc0f486082..a3d1bbacb7 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -569,9 +569,10 @@ + + - @@ -1175,11 +1176,12 @@ + + - diff --git a/Source/Core/VideoBackends/OGL/CMakeLists.txt b/Source/Core/VideoBackends/OGL/CMakeLists.txt index 9a9d9caf2d..8fb8524054 100644 --- a/Source/Core/VideoBackends/OGL/CMakeLists.txt +++ b/Source/Core/VideoBackends/OGL/CMakeLists.txt @@ -2,14 +2,16 @@ add_library(videoogl GPUTimer.h OGLBoundingBox.cpp OGLBoundingBox.h + OGLConfig.cpp + OGLConfig.h + OGLGfx.cpp + OGLGfx.h OGLMain.cpp OGLNativeVertexFormat.cpp OGLPerfQuery.cpp OGLPerfQuery.h OGLPipeline.cpp OGLPipeline.h - OGLRender.cpp - OGLRender.h OGLShader.cpp OGLShader.h OGLStreamBuffer.cpp diff --git a/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp b/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp index b0f7139782..d21326c1f5 100644 --- a/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp +++ b/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp @@ -3,7 +3,7 @@ #include "VideoBackends/OGL/OGLBoundingBox.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoCommon/DriverDetails.h" namespace OGL @@ -35,7 +35,7 @@ std::vector OGLBoundingBox::Read(u32 index, u32 length) // on nVidia drivers. This is more noticeable at higher internal resolutions. // Using glGetBufferSubData instead does not seem to exhibit this slowdown. if (!DriverDetails::HasBug(DriverDetails::BUG_SLOW_GETBUFFERSUBDATA) && - !static_cast(g_renderer.get())->IsGLES()) + !static_cast(g_gfx.get())->IsGLES()) { // We also need to ensure the the CPU does not receive stale values which have been updated by // the GPU. Apparently the buffer here is not coherent on NVIDIA drivers. Not sure if this is a diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLConfig.cpp similarity index 53% rename from Source/Core/VideoBackends/OGL/OGLRender.cpp rename to Source/Core/VideoBackends/OGL/OGLConfig.cpp index fba72ba11b..bafc28841d 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLConfig.cpp @@ -1,136 +1,26 @@ -// Copyright 2008 Dolphin Emulator Project +// Copyright 2023 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLConfig.h" -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" #include "Common/GL/GLContext.h" -#include "Common/GL/GLUtil.h" +#include "Common/GL/GLExtensions/GLExtensions.h" #include "Common/Logging/LogManager.h" -#include "Common/MathUtil.h" #include "Common/MsgHandler.h" -#include "Common/StringUtil.h" #include "Core/Config/GraphicsSettings.h" -#include "VideoBackends/OGL/OGLBoundingBox.h" -#include "VideoBackends/OGL/OGLPipeline.h" -#include "VideoBackends/OGL/OGLShader.h" -#include "VideoBackends/OGL/OGLTexture.h" -#include "VideoBackends/OGL/OGLVertexManager.h" -#include "VideoBackends/OGL/ProgramShaderCache.h" -#include "VideoBackends/OGL/SamplerCache.h" - -#include "VideoCommon/BPFunctions.h" #include "VideoCommon/DriverDetails.h" -#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/PostProcessing.h" -#include "VideoCommon/Present.h" -#include "VideoCommon/RenderState.h" -#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" +#include +#include +#include + namespace OGL { -VideoConfig g_ogl_config; - -static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const char* message, const void* userParam) -{ - const char* s_source; - const char* s_type; - - // Performance - DualCore driver performance warning: - // DualCore application thread syncing with server thread - if (id == 0x200b0) - return; - - switch (source) - { - case GL_DEBUG_SOURCE_API_ARB: - s_source = "API"; - break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - s_source = "Window System"; - break; - case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: - s_source = "Shader Compiler"; - break; - case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: - s_source = "Third Party"; - break; - case GL_DEBUG_SOURCE_APPLICATION_ARB: - s_source = "Application"; - break; - case GL_DEBUG_SOURCE_OTHER_ARB: - s_source = "Other"; - break; - default: - s_source = "Unknown"; - break; - } - switch (type) - { - case GL_DEBUG_TYPE_ERROR_ARB: - s_type = "Error"; - break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: - s_type = "Deprecated"; - break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: - s_type = "Undefined"; - break; - case GL_DEBUG_TYPE_PORTABILITY_ARB: - s_type = "Portability"; - break; - case GL_DEBUG_TYPE_PERFORMANCE_ARB: - s_type = "Performance"; - break; - case GL_DEBUG_TYPE_OTHER_ARB: - s_type = "Other"; - break; - default: - s_type = "Unknown"; - break; - } - switch (severity) - { - case GL_DEBUG_SEVERITY_HIGH_ARB: - ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); - break; - case GL_DEBUG_SEVERITY_MEDIUM_ARB: - WARN_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); - break; - case GL_DEBUG_SEVERITY_LOW_ARB: - DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); - break; - default: - ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); - break; - } -} - -// Two small Fallbacks to avoid GL_ARB_ES2_compatibility -static void APIENTRY DepthRangef(GLfloat neardepth, GLfloat fardepth) -{ - glDepthRange(neardepth, fardepth); -} -static void APIENTRY ClearDepthf(GLfloat depthval) -{ - glClearDepth(depthval); -} - -static void InitDriverInfo() +void InitDriverInfo() { const std::string_view svendor(g_ogl_config.gl_vendor); const std::string_view srenderer(g_ogl_config.gl_renderer); @@ -320,26 +210,8 @@ static void InitDriverInfo() DriverDetails::Init(DriverDetails::API_OPENGL, vendor, driver, version, family); } -// Init functions -Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_scale) - : ::Renderer(static_cast(std::max(main_gl_context->GetBackBufferWidth(), 1u)), - static_cast(std::max(main_gl_context->GetBackBufferHeight(), 1u)), - backbuffer_scale, AbstractTextureFormat::RGBA8), - m_main_gl_context(std::move(main_gl_context)), - m_current_rasterization_state(RenderState::GetInvalidRasterizationState()), - m_current_depth_state(RenderState::GetInvalidDepthState()), - m_current_blend_state(RenderState::GetInvalidBlendingState()) +bool PopulateConfig(GLContext* m_main_gl_context) { - // Create the window framebuffer. - if (!m_main_gl_context->IsHeadless()) - { - m_system_framebuffer = std::make_unique( - nullptr, nullptr, AbstractTextureFormat::RGBA8, AbstractTextureFormat::Undefined, - std::max(m_main_gl_context->GetBackBufferWidth(), 1u), - std::max(m_main_gl_context->GetBackBufferHeight(), 1u), 1, 1, 0); - m_current_framebuffer = m_system_framebuffer.get(); - } - bool bSuccess = true; bool supports_glsl_cache = false; @@ -347,8 +219,6 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ g_ogl_config.gl_renderer = (const char*)glGetString(GL_RENDERER); g_ogl_config.gl_version = (const char*)glGetString(GL_VERSION); - InitDriverInfo(); - if (!m_main_gl_context->IsGLES()) { if (!GLExtensions::Supports("GL_ARB_framebuffer_object")) @@ -402,15 +272,6 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ "GPU: Does your video card support OpenGL 3.3?"); bSuccess = false; } - - // OpenGL 3 doesn't provide GLES like float functions for depth. - // They are in core in OpenGL 4.1, so almost every driver should support them. - // But for the oldest ones, we provide fallbacks to the old double functions. - if (!GLExtensions::Supports("GL_ARB_ES2_compatibility")) - { - glDepthRangef = DepthRangef; - glClearDepthf = ClearDepthf; - } } // Copy the GPU name to g_Config, so Analytics can see it. @@ -694,29 +555,6 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ } g_Config.backend_info.bSupportsPipelineCacheData = supports_glsl_cache; - if (g_ogl_config.bSupportsDebug) - { - if (GLExtensions::Supports("GL_KHR_debug")) - { - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true); - glDebugMessageCallback(ErrorCallback, nullptr); - } - else - { - glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true); - glDebugMessageCallbackARB(ErrorCallback, nullptr); - } - if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU, - Common::Log::LogLevel::LERROR)) - { - glEnable(GL_DEBUG_OUTPUT); - } - else - { - glDisable(GL_DEBUG_OUTPUT); - } - } - int samples; glGetIntegerv(GL_SAMPLES, &samples); if (samples > 1) @@ -733,11 +571,7 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ } if (!bSuccess) - { - // Not all needed extensions are supported, so we have to stop here. - // Else some of the next calls might crash. - return; - } + return false; g_Config.VerifyValidity(); UpdateActiveConfig(); @@ -772,565 +606,7 @@ Renderer::Renderer(std::unique_ptr main_gl_context, float backbuffer_ g_ogl_config.bSupportsCopySubImage ? "" : "CopyImageSubData ", g_ActiveConfig.backend_info.bSupportsDepthClamp ? "" : "DepthClamp "); - // Handle VSync on/off - if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive); - - if (g_ActiveConfig.backend_info.bSupportsClipControl) - glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); - - if (g_ActiveConfig.backend_info.bSupportsDepthClamp) - { - glEnable(GL_CLIP_DISTANCE0); - glEnable(GL_CLIP_DISTANCE1); - glEnable(GL_DEPTH_CLAMP); - } - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment - - glGenFramebuffers(1, &m_shared_read_framebuffer); - glGenFramebuffers(1, &m_shared_draw_framebuffer); - - if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) - GLUtil::EnablePrimitiveRestart(m_main_gl_context.get()); - - UpdateActiveConfig(); -} - -Renderer::~Renderer() = default; - -bool Renderer::IsHeadless() const -{ - return m_main_gl_context->IsHeadless(); -} - -bool Renderer::Initialize() -{ - if (!::Renderer::Initialize()) - return false; - return true; } -void Renderer::Shutdown() -{ - ::Renderer::Shutdown(); - - glDeleteFramebuffers(1, &m_shared_draw_framebuffer); - glDeleteFramebuffers(1, &m_shared_read_framebuffer); -} - -std::unique_ptr Renderer::CreateTexture(const TextureConfig& config, - std::string_view name) -{ - return std::make_unique(config, name); -} - -std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, - const TextureConfig& config) -{ - return OGLStagingTexture::Create(type, config); -} - -std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) -{ - return OGLFramebuffer::Create(static_cast(color_attachment), - static_cast(depth_attachment)); -} - -std::unique_ptr -Renderer::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) -{ - return OGLShader::CreateFromSource(stage, source, name); -} - -std::unique_ptr -Renderer::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, - [[maybe_unused]] std::string_view name) -{ - return nullptr; -} - -std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) -{ - return OGLPipeline::Create(config, cache_data, cache_data_length); -} - -void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) -{ - glScissor(rc.left, rc.top, rc.GetWidth(), rc.GetHeight()); -} - -std::unique_ptr<::BoundingBox> Renderer::CreateBoundingBox() const -{ - return std::make_unique(); -} - -void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) -{ - if (g_ogl_config.bSupportViewportFloat) - { - glViewportIndexedf(0, x, y, width, height); - } - else - { - auto iceilf = [](float f) { return static_cast(std::ceil(f)); }; - glViewport(iceilf(x), iceilf(y), iceilf(width), iceilf(height)); - } - - glDepthRangef(near_depth, far_depth); -} - -void Renderer::Draw(u32 base_vertex, u32 num_vertices) -{ - glDrawArrays(static_cast(m_current_pipeline)->GetGLPrimitive(), base_vertex, - num_vertices); -} - -void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) -{ - if (g_ogl_config.bSupportsGLBaseVertex) - { - glDrawElementsBaseVertex(static_cast(m_current_pipeline)->GetGLPrimitive(), - num_indices, GL_UNSIGNED_SHORT, - static_cast(nullptr) + base_index, base_vertex); - } - else - { - glDrawElements(static_cast(m_current_pipeline)->GetGLPrimitive(), - num_indices, GL_UNSIGNED_SHORT, static_cast(nullptr) + base_index); - } -} - -void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, - u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) -{ - glUseProgram(static_cast(shader)->GetGLComputeProgramID()); - glDispatchCompute(groups_x, groups_y, groups_z); - - // We messed up the program binding, so restore it. - ProgramShaderCache::InvalidateLastProgram(); - if (m_current_pipeline) - static_cast(m_current_pipeline)->GetProgram()->shader.Bind(); - - // Barrier to texture can be used for reads. - if (m_bound_image_texture) - glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); -} - -void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) -{ - g_framebuffer_manager->FlushEFBPokes(); - g_framebuffer_manager->FlagPeekCacheAsOutOfDate(); - - u32 clear_mask = 0; - if (colorEnable || alphaEnable) - { - glColorMask(colorEnable, colorEnable, colorEnable, alphaEnable); - glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f, - float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f); - clear_mask = GL_COLOR_BUFFER_BIT; - } - if (zEnable) - { - glDepthMask(zEnable ? GL_TRUE : GL_FALSE); - glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f); - clear_mask |= GL_DEPTH_BUFFER_BIT; - } - - // Update rect for clearing the picture - // glColorMask/glDepthMask/glScissor affect glClear (glViewport does not) - const auto converted_target_rc = - ConvertFramebufferRectangle(ConvertEFBRectangle(rc), m_current_framebuffer); - SetScissorRect(converted_target_rc); - - glClear(clear_mask); - - // Restore color/depth mask. - if (colorEnable || alphaEnable) - { - glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate, - m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate); - } - if (zEnable) - glDepthMask(m_current_depth_state.updateenable); - - // Scissor rect must be restored. - BPFunctions::SetScissorAndViewport(); -} - -void Renderer::SelectLeftBuffer() -{ - glDrawBuffer(GL_BACK_LEFT); -} - -void Renderer::SelectRightBuffer() -{ - glDrawBuffer(GL_BACK_RIGHT); -} - -void Renderer::SelectMainBuffer() -{ - glDrawBuffer(GL_BACK); -} - -void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) -{ - if (m_current_framebuffer == framebuffer) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, static_cast(framebuffer)->GetFBO()); - m_current_framebuffer = framebuffer; -} - -void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) -{ - // EXT_discard_framebuffer could be used here to save bandwidth on tilers. - SetFramebuffer(framebuffer); -} - -void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) -{ - SetFramebuffer(framebuffer); - - 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); - glClearDepthf(depth_value); - clear_mask |= GL_DEPTH_BUFFER_BIT; - } - glClear(clear_mask); - glEnable(GL_SCISSOR_TEST); - - // Restore color/depth mask. - if (framebuffer->HasColorBuffer()) - { - glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate, - m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate); - } - if (framebuffer->HasDepthBuffer()) - glDepthMask(m_current_depth_state.updateenable); -} - -void Renderer::BindBackbuffer(const ClearColor& clear_color) -{ - CheckForSurfaceChange(); - CheckForSurfaceResize(); - SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color); -} - -void Renderer::PresentBackbuffer() -{ - if (g_ogl_config.bSupportsDebug) - { - if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU, - Common::Log::LogLevel::LERROR)) - { - glEnable(GL_DEBUG_OUTPUT); - } - else - { - glDisable(GL_DEBUG_OUTPUT); - } - } - - // Swap the back and front buffers, presenting the image. - m_main_gl_context->Swap(); -} - -void Renderer::OnConfigChanged(u32 bits) -{ - if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) - m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive); - - if (bits & CONFIG_CHANGE_BIT_ANISOTROPY) - g_sampler_cache->Clear(); -} - -void Renderer::Flush() -{ - // ensure all commands are sent to the GPU. - // Otherwise the driver could batch several frames together. - glFlush(); -} - -void Renderer::WaitForGPUIdle() -{ - glFinish(); -} - -void Renderer::CheckForSurfaceChange() -{ - if (!g_presenter->SurfaceChangedTestAndClear()) - return; - - m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle()); - - u32 width = m_main_gl_context->GetBackBufferWidth(); - u32 height = m_main_gl_context->GetBackBufferHeight(); - - // With a surface change, the window likely has new dimensions. - g_presenter->SetBackbuffer(width, height); - m_system_framebuffer->UpdateDimensions(width, height); -} - -void Renderer::CheckForSurfaceResize() -{ - if (!g_presenter->SurfaceResizedTestAndClear()) - return; - - m_main_gl_context->Update(); - u32 width = m_main_gl_context->GetBackBufferWidth(); - u32 height = m_main_gl_context->GetBackBufferHeight(); - g_presenter->SetBackbuffer(width, height); - m_system_framebuffer->UpdateDimensions(width, height); -} - -void Renderer::BeginUtilityDrawing() -{ - ::Renderer::BeginUtilityDrawing(); - if (g_ActiveConfig.backend_info.bSupportsDepthClamp) - { - glDisable(GL_CLIP_DISTANCE0); - glDisable(GL_CLIP_DISTANCE1); - } -} - -void Renderer::EndUtilityDrawing() -{ - ::Renderer::EndUtilityDrawing(); - if (g_ActiveConfig.backend_info.bSupportsDepthClamp) - { - glEnable(GL_CLIP_DISTANCE0); - glEnable(GL_CLIP_DISTANCE1); - } -} - -void Renderer::ApplyRasterizationState(const RasterizationState state) -{ - if (m_current_rasterization_state == state) - return; - - // none, ccw, cw, ccw - if (state.cullmode != CullMode::None) - { - // TODO: GX_CULL_ALL not supported, yet! - glEnable(GL_CULL_FACE); - glFrontFace(state.cullmode == CullMode::Front ? GL_CCW : GL_CW); - } - else - { - glDisable(GL_CULL_FACE); - } - - m_current_rasterization_state = state; -} - -void Renderer::ApplyDepthState(const DepthState state) -{ - if (m_current_depth_state == state) - return; - - const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, - GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS}; - - if (state.testenable) - { - glEnable(GL_DEPTH_TEST); - glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE); - glDepthFunc(glCmpFuncs[u32(state.func.Value())]); - } - else - { - // if the test is disabled write is disabled too - // TODO: When PE performance metrics are being emulated via occlusion queries, we should - // (probably?) enable depth test with depth function ALWAYS here - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - } - - m_current_depth_state = state; -} - -void Renderer::ApplyBlendingState(const BlendingState state) -{ - if (m_current_blend_state == state) - return; - - bool useDualSource = state.usedualsrc; - - const GLenum src_factors[8] = {GL_ZERO, - GL_ONE, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, - useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : - (GLenum)GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA}; - const GLenum dst_factors[8] = {GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, - useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : - (GLenum)GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA}; - - if (state.blendenable) - glEnable(GL_BLEND); - else - glDisable(GL_BLEND); - - // Always call glBlendEquationSeparate and glBlendFuncSeparate, even when - // GL_BLEND is disabled, as a workaround for some bugs (possibly graphics - // driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic - // Adventure 2 Battle: graphics crash when loading first Dark level" - GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; - GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; - glBlendEquationSeparate(equation, equationAlpha); - glBlendFuncSeparate(src_factors[u32(state.srcfactor.Value())], - dst_factors[u32(state.dstfactor.Value())], - src_factors[u32(state.srcfactoralpha.Value())], - dst_factors[u32(state.dstfactoralpha.Value())]); - - const GLenum logic_op_codes[16] = { - GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, - GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, - GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET}; - - // Logic ops aren't available in GLES3 - if (!IsGLES()) - { - if (state.logicopenable) - { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(logic_op_codes[u32(state.logicmode.Value())]); - } - else - { - glDisable(GL_COLOR_LOGIC_OP); - } - } - - glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate); - m_current_blend_state = state; -} - -void Renderer::SetPipeline(const AbstractPipeline* pipeline) -{ - if (m_current_pipeline == pipeline) - return; - - if (pipeline) - { - ApplyRasterizationState(static_cast(pipeline)->GetRasterizationState()); - ApplyDepthState(static_cast(pipeline)->GetDepthState()); - ApplyBlendingState(static_cast(pipeline)->GetBlendingState()); - ProgramShaderCache::BindVertexFormat( - static_cast(pipeline)->GetVertexFormat()); - static_cast(pipeline)->GetProgram()->shader.Bind(); - } - else - { - ProgramShaderCache::InvalidateLastProgram(); - glUseProgram(0); - } - m_current_pipeline = pipeline; -} - -void Renderer::SetTexture(u32 index, const AbstractTexture* texture) -{ - const OGLTexture* gl_texture = static_cast(texture); - if (m_bound_textures[index] == gl_texture) - return; - - glActiveTexture(GL_TEXTURE0 + index); - if (gl_texture) - glBindTexture(gl_texture->GetGLTarget(), gl_texture->GetGLTextureId()); - else - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - m_bound_textures[index] = gl_texture; -} - -void Renderer::SetSamplerState(u32 index, const SamplerState& state) -{ - g_sampler_cache->SetSamplerState(index, state); -} - -void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) -{ - if (m_bound_image_texture == texture) - return; - - if (texture) - { - const GLenum access = read ? (write ? GL_READ_WRITE : GL_READ_ONLY) : GL_WRITE_ONLY; - glBindImageTexture(0, static_cast(texture)->GetGLTextureId(), 0, GL_TRUE, 0, - access, static_cast(texture)->GetGLFormatForImageTexture()); - } - else - { - glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); - } - - m_bound_image_texture = texture; -} - -void Renderer::UnbindTexture(const AbstractTexture* texture) -{ - for (size_t i = 0; i < m_bound_textures.size(); i++) - { - if (m_bound_textures[i] != texture) - continue; - - glActiveTexture(static_cast(GL_TEXTURE0 + i)); - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - m_bound_textures[i] = nullptr; - } - - if (m_bound_image_texture == texture) - { - glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); - m_bound_image_texture = nullptr; - } -} - -std::unique_ptr Renderer::CreateAsyncShaderCompiler() -{ - return std::make_unique(); -} - -void Renderer::BindSharedReadFramebuffer() -{ - glBindFramebuffer(GL_READ_FRAMEBUFFER, m_shared_read_framebuffer); -} - -void Renderer::BindSharedDrawFramebuffer() -{ - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared_draw_framebuffer); -} - -void Renderer::RestoreFramebufferBinding() -{ - glBindFramebuffer( - GL_FRAMEBUFFER, - m_current_framebuffer ? static_cast(m_current_framebuffer)->GetFBO() : 0); -} - } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLConfig.h b/Source/Core/VideoBackends/OGL/OGLConfig.h new file mode 100644 index 0000000000..eb3d20e70c --- /dev/null +++ b/Source/Core/VideoBackends/OGL/OGLConfig.h @@ -0,0 +1,78 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/CommonTypes.h" + +class GLContext; + +namespace OGL +{ +enum GlslVersion +{ + Glsl130, + Glsl140, + Glsl150, + Glsl330, + Glsl400, // and above + Glsl430, + GlslEs300, // GLES 3.0 + GlslEs310, // GLES 3.1 + GlslEs320, // GLES 3.2 +}; +enum class EsTexbufType +{ + TexbufNone, + TexbufCore, + TexbufOes, + TexbufExt +}; + +enum class EsFbFetchType +{ + FbFetchNone, + FbFetchExt, + FbFetchArm, +}; + +// ogl-only config, so not in VideoConfig.h +struct VideoConfig +{ + bool bIsES; + bool bSupportsGLPinnedMemory; + bool bSupportsGLSync; + bool bSupportsGLBaseVertex; + bool bSupportsGLBufferStorage; + bool bSupportsMSAA; + GlslVersion eSupportedGLSLVersion; + bool bSupportViewportFloat; + bool bSupportsAEP; + bool bSupportsDebug; + bool bSupportsCopySubImage; + u8 SupportedESPointSize; + EsTexbufType SupportedESTextureBuffer; + bool bSupportsTextureStorage; + bool bSupports2DTextureStorageMultisample; + bool bSupports3DTextureStorageMultisample; + bool bSupportsConservativeDepth; + bool bSupportsImageLoadStore; + bool bSupportsAniso; + bool bSupportsBitfield; + bool bSupportsTextureSubImage; + EsFbFetchType SupportedFramebufferFetch; + bool bSupportsShaderThreadShuffleNV; + + const char* gl_vendor; + const char* gl_renderer; + const char* gl_version; + + s32 max_samples; +}; + +void InitDriverInfo(); +bool PopulateConfig(GLContext* main_gl_context); + +extern VideoConfig g_ogl_config; + +} // namespace OGL \ No newline at end of file diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.cpp b/Source/Core/VideoBackends/OGL/OGLGfx.cpp new file mode 100644 index 0000000000..88e544c235 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/OGLGfx.cpp @@ -0,0 +1,730 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoBackends/OGL/OGLGfx.h" + +#include "Common/GL/GLContext.h" +#include "Common/GL/GLExtensions/GLExtensions.h" +#include "Common/Logging/LogManager.h" + +#include "Core/Config/GraphicsSettings.h" + +#include "VideoBackends/OGL/OGLConfig.h" +#include "VideoBackends/OGL/OGLPipeline.h" +#include "VideoBackends/OGL/OGLShader.h" +#include "VideoBackends/OGL/OGLTexture.h" +#include "VideoBackends/OGL/ProgramShaderCache.h" +#include "VideoBackends/OGL/SamplerCache.h" + +#include "VideoCommon/AsyncShaderCompiler.h" +#include "VideoCommon/DriverDetails.h" +#include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/Present.h" +#include "VideoCommon/VideoConfig.h" + +#include + +namespace OGL +{ +VideoConfig g_ogl_config; + +static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, + GLsizei length, const char* message, const void* userParam) +{ + const char* s_source; + const char* s_type; + + // Performance - DualCore driver performance warning: + // DualCore application thread syncing with server thread + if (id == 0x200b0) + return; + + switch (source) + { + case GL_DEBUG_SOURCE_API_ARB: + s_source = "API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + s_source = "Window System"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + s_source = "Shader Compiler"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: + s_source = "Third Party"; + break; + case GL_DEBUG_SOURCE_APPLICATION_ARB: + s_source = "Application"; + break; + case GL_DEBUG_SOURCE_OTHER_ARB: + s_source = "Other"; + break; + default: + s_source = "Unknown"; + break; + } + switch (type) + { + case GL_DEBUG_TYPE_ERROR_ARB: + s_type = "Error"; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + s_type = "Deprecated"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + s_type = "Undefined"; + break; + case GL_DEBUG_TYPE_PORTABILITY_ARB: + s_type = "Portability"; + break; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + s_type = "Performance"; + break; + case GL_DEBUG_TYPE_OTHER_ARB: + s_type = "Other"; + break; + default: + s_type = "Unknown"; + break; + } + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH_ARB: + ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); + break; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: + WARN_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); + break; + case GL_DEBUG_SEVERITY_LOW_ARB: + DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); + break; + default: + ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message); + break; + } +} + +// Two small Fallbacks to avoid GL_ARB_ES2_compatibility +static void APIENTRY DepthRangef(GLfloat neardepth, GLfloat fardepth) +{ + glDepthRange(neardepth, fardepth); +} +static void APIENTRY ClearDepthf(GLfloat depthval) +{ + glClearDepth(depthval); +} + +OGLGfx::OGLGfx(std::unique_ptr main_gl_context, float backbuffer_scale) + : m_main_gl_context(std::move(main_gl_context)), + m_current_rasterization_state(RenderState::GetInvalidRasterizationState()), + m_current_depth_state(RenderState::GetInvalidDepthState()), + m_current_blend_state(RenderState::GetInvalidBlendingState()), + m_backbuffer_scale(backbuffer_scale) +{ + // Create the window framebuffer. + if (!m_main_gl_context->IsHeadless()) + { + m_system_framebuffer = std::make_unique( + nullptr, nullptr, AbstractTextureFormat::RGBA8, AbstractTextureFormat::Undefined, + std::max(m_main_gl_context->GetBackBufferWidth(), 1u), + std::max(m_main_gl_context->GetBackBufferHeight(), 1u), 1, 1, 0); + m_current_framebuffer = m_system_framebuffer.get(); + } + + if (m_main_gl_context->IsGLES()) + { + // OpenGL 3 doesn't provide GLES like float functions for depth. + // They are in core in OpenGL 4.1, so almost every driver should support them. + // But for the oldest ones, we provide fallbacks to the old double functions. + if (!GLExtensions::Supports("GL_ARB_ES2_compatibility")) + { + glDepthRangef = DepthRangef; + glClearDepthf = ClearDepthf; + } + } + + if (!PopulateConfig(m_main_gl_context.get())) + { + // Not all needed extensions are supported, so we have to stop here. + // Else some of the next calls might crash. + return; + } + InitDriverInfo(); + + // Setup Debug logging + if (g_ogl_config.bSupportsDebug) + { + if (GLExtensions::Supports("GL_KHR_debug")) + { + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true); + glDebugMessageCallback(ErrorCallback, nullptr); + } + else + { + glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true); + glDebugMessageCallbackARB(ErrorCallback, nullptr); + } + if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU, + Common::Log::LogLevel::LERROR)) + { + glEnable(GL_DEBUG_OUTPUT); + } + else + { + glDisable(GL_DEBUG_OUTPUT); + } + } + + // Handle VSync on/off + if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) + m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive); + + if (g_ActiveConfig.backend_info.bSupportsClipControl) + glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + + if (g_ActiveConfig.backend_info.bSupportsDepthClamp) + { + glEnable(GL_CLIP_DISTANCE0); + glEnable(GL_CLIP_DISTANCE1); + glEnable(GL_DEPTH_CLAMP); + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment + + glGenFramebuffers(1, &m_shared_read_framebuffer); + glGenFramebuffers(1, &m_shared_draw_framebuffer); + + if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) + GLUtil::EnablePrimitiveRestart(m_main_gl_context.get()); + + UpdateActiveConfig(); +} + +OGLGfx::~OGLGfx() +{ + glDeleteFramebuffers(1, &m_shared_draw_framebuffer); + glDeleteFramebuffers(1, &m_shared_read_framebuffer); +} + +bool OGLGfx::IsHeadless() const +{ + return m_main_gl_context->IsHeadless(); +} + +std::unique_ptr OGLGfx::CreateTexture(const TextureConfig& config, + std::string_view name) +{ + return std::make_unique(config, name); +} + +std::unique_ptr OGLGfx::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return OGLStagingTexture::Create(type, config); +} + +std::unique_ptr OGLGfx::CreateFramebuffer(AbstractTexture* color_attachment, + AbstractTexture* depth_attachment) +{ + return OGLFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + +std::unique_ptr +OGLGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +{ + return OGLShader::CreateFromSource(stage, source, name); +} + +std::unique_ptr +OGLGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, + [[maybe_unused]] std::string_view name) +{ + return nullptr; +} + +std::unique_ptr OGLGfx::CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data, + size_t cache_data_length) +{ + return OGLPipeline::Create(config, cache_data, cache_data_length); +} + +void OGLGfx::SetScissorRect(const MathUtil::Rectangle& rc) +{ + glScissor(rc.left, rc.top, rc.GetWidth(), rc.GetHeight()); +} + +void OGLGfx::SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) +{ + if (g_ogl_config.bSupportViewportFloat) + { + glViewportIndexedf(0, x, y, width, height); + } + else + { + auto iceilf = [](float f) { return static_cast(std::ceil(f)); }; + glViewport(iceilf(x), iceilf(y), iceilf(width), iceilf(height)); + } + + glDepthRangef(near_depth, far_depth); +} + +void OGLGfx::Draw(u32 base_vertex, u32 num_vertices) +{ + glDrawArrays(static_cast(m_current_pipeline)->GetGLPrimitive(), base_vertex, + num_vertices); +} + +void OGLGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) +{ + if (g_ogl_config.bSupportsGLBaseVertex) + { + glDrawElementsBaseVertex(static_cast(m_current_pipeline)->GetGLPrimitive(), + num_indices, GL_UNSIGNED_SHORT, + static_cast(nullptr) + base_index, base_vertex); + } + else + { + glDrawElements(static_cast(m_current_pipeline)->GetGLPrimitive(), + num_indices, GL_UNSIGNED_SHORT, static_cast(nullptr) + base_index); + } +} + +void OGLGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, + u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) +{ + glUseProgram(static_cast(shader)->GetGLComputeProgramID()); + glDispatchCompute(groups_x, groups_y, groups_z); + + // We messed up the program binding, so restore it. + ProgramShaderCache::InvalidateLastProgram(); + if (m_current_pipeline) + static_cast(m_current_pipeline)->GetProgram()->shader.Bind(); + + // Barrier to texture can be used for reads. + if (m_bound_image_texture) + glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); +} + +void OGLGfx::SelectLeftBuffer() +{ + glDrawBuffer(GL_BACK_LEFT); +} + +void OGLGfx::SelectRightBuffer() +{ + glDrawBuffer(GL_BACK_RIGHT); +} + +void OGLGfx::SelectMainBuffer() +{ + glDrawBuffer(GL_BACK); +} + +void OGLGfx::SetFramebuffer(AbstractFramebuffer* framebuffer) +{ + if (m_current_framebuffer == framebuffer) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, static_cast(framebuffer)->GetFBO()); + m_current_framebuffer = framebuffer; +} + +void OGLGfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +{ + // EXT_discard_framebuffer could be used here to save bandwidth on tilers. + SetFramebuffer(framebuffer); +} + +void OGLGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, + float depth_value) +{ + SetFramebuffer(framebuffer); + + 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); + glClearDepthf(depth_value); + clear_mask |= GL_DEPTH_BUFFER_BIT; + } + glClear(clear_mask); + glEnable(GL_SCISSOR_TEST); + + // Restore color/depth mask. + if (framebuffer->HasColorBuffer()) + { + glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate, + m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate); + } + if (framebuffer->HasDepthBuffer()) + glDepthMask(m_current_depth_state.updateenable); +} + +void OGLGfx::ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, bool colorEnable, + bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + u32 clear_mask = 0; + if (colorEnable || alphaEnable) + { + glColorMask(colorEnable, colorEnable, colorEnable, alphaEnable); + glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f, + float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f); + clear_mask = GL_COLOR_BUFFER_BIT; + } + if (zEnable) + { + glDepthMask(zEnable ? GL_TRUE : GL_FALSE); + glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f); + clear_mask |= GL_DEPTH_BUFFER_BIT; + } + + // Update rect for clearing the picture + // glColorMask/glDepthMask/glScissor affect glClear (glViewport does not) + g_gfx->SetScissorRect(target_rc); + + glClear(clear_mask); + + // Restore color/depth mask. + if (colorEnable || alphaEnable) + { + glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate, + m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate); + } + if (zEnable) + glDepthMask(m_current_depth_state.updateenable); +} + +void OGLGfx::BindBackbuffer(const ClearColor& clear_color) +{ + CheckForSurfaceChange(); + CheckForSurfaceResize(); + SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color); +} + +void OGLGfx::PresentBackbuffer() +{ + if (g_ogl_config.bSupportsDebug) + { + if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU, + Common::Log::LogLevel::LERROR)) + { + glEnable(GL_DEBUG_OUTPUT); + } + else + { + glDisable(GL_DEBUG_OUTPUT); + } + } + + // Swap the back and front buffers, presenting the image. + m_main_gl_context->Swap(); +} + +void OGLGfx::OnConfigChanged(u32 bits) +{ + if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) + m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive); + + if (bits & CONFIG_CHANGE_BIT_ANISOTROPY) + g_sampler_cache->Clear(); +} + +void OGLGfx::Flush() +{ + // ensure all commands are sent to the GPU. + // Otherwise the driver could batch several frames together. + glFlush(); +} + +void OGLGfx::WaitForGPUIdle() +{ + glFinish(); +} + +void OGLGfx::CheckForSurfaceChange() +{ + if (!g_presenter->SurfaceChangedTestAndClear()) + return; + + m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle()); + + u32 width = m_main_gl_context->GetBackBufferWidth(); + u32 height = m_main_gl_context->GetBackBufferHeight(); + + // With a surface change, the window likely has new dimensions. + g_presenter->SetBackbuffer(width, height); + m_system_framebuffer->UpdateDimensions(width, height); +} + +void OGLGfx::CheckForSurfaceResize() +{ + if (!g_presenter->SurfaceResizedTestAndClear()) + return; + + m_main_gl_context->Update(); + u32 width = m_main_gl_context->GetBackBufferWidth(); + u32 height = m_main_gl_context->GetBackBufferHeight(); + g_presenter->SetBackbuffer(width, height); + m_system_framebuffer->UpdateDimensions(width, height); +} + +void OGLGfx::BeginUtilityDrawing() +{ + AbstractGfx::BeginUtilityDrawing(); + if (g_ActiveConfig.backend_info.bSupportsDepthClamp) + { + glDisable(GL_CLIP_DISTANCE0); + glDisable(GL_CLIP_DISTANCE1); + } +} + +void OGLGfx::EndUtilityDrawing() +{ + AbstractGfx::EndUtilityDrawing(); + if (g_ActiveConfig.backend_info.bSupportsDepthClamp) + { + glEnable(GL_CLIP_DISTANCE0); + glEnable(GL_CLIP_DISTANCE1); + } +} + +void OGLGfx::ApplyRasterizationState(const RasterizationState state) +{ + if (m_current_rasterization_state == state) + return; + + // none, ccw, cw, ccw + if (state.cullmode != CullMode::None) + { + // TODO: GX_CULL_ALL not supported, yet! + glEnable(GL_CULL_FACE); + glFrontFace(state.cullmode == CullMode::Front ? GL_CCW : GL_CW); + } + else + { + glDisable(GL_CULL_FACE); + } + + m_current_rasterization_state = state; +} + +void OGLGfx::ApplyDepthState(const DepthState state) +{ + if (m_current_depth_state == state) + return; + + const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, + GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS}; + + if (state.testenable) + { + glEnable(GL_DEPTH_TEST); + glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE); + glDepthFunc(glCmpFuncs[u32(state.func.Value())]); + } + else + { + // if the test is disabled write is disabled too + // TODO: When PE performance metrics are being emulated via occlusion queries, we should + // (probably?) enable depth test with depth function ALWAYS here + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + } + + m_current_depth_state = state; +} + +void OGLGfx::ApplyBlendingState(const BlendingState state) +{ + if (m_current_blend_state == state) + return; + + bool useDualSource = state.usedualsrc; + + const GLenum src_factors[8] = {GL_ZERO, + GL_ONE, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, + useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : + (GLenum)GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA}; + const GLenum dst_factors[8] = {GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, + useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : + (GLenum)GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA}; + + if (state.blendenable) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + // Always call glBlendEquationSeparate and glBlendFuncSeparate, even when + // GL_BLEND is disabled, as a workaround for some bugs (possibly graphics + // driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic + // Adventure 2 Battle: graphics crash when loading first Dark level" + GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; + GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; + glBlendEquationSeparate(equation, equationAlpha); + glBlendFuncSeparate(src_factors[u32(state.srcfactor.Value())], + dst_factors[u32(state.dstfactor.Value())], + src_factors[u32(state.srcfactoralpha.Value())], + dst_factors[u32(state.dstfactoralpha.Value())]); + + const GLenum logic_op_codes[16] = { + GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, + GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, + GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET}; + + // Logic ops aren't available in GLES3 + if (!IsGLES()) + { + if (state.logicopenable) + { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(logic_op_codes[u32(state.logicmode.Value())]); + } + else + { + glDisable(GL_COLOR_LOGIC_OP); + } + } + + glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate); + m_current_blend_state = state; +} + +void OGLGfx::SetPipeline(const AbstractPipeline* pipeline) +{ + if (m_current_pipeline == pipeline) + return; + + if (pipeline) + { + ApplyRasterizationState(static_cast(pipeline)->GetRasterizationState()); + ApplyDepthState(static_cast(pipeline)->GetDepthState()); + ApplyBlendingState(static_cast(pipeline)->GetBlendingState()); + ProgramShaderCache::BindVertexFormat( + static_cast(pipeline)->GetVertexFormat()); + static_cast(pipeline)->GetProgram()->shader.Bind(); + } + else + { + ProgramShaderCache::InvalidateLastProgram(); + glUseProgram(0); + } + m_current_pipeline = pipeline; +} + +void OGLGfx::SetTexture(u32 index, const AbstractTexture* texture) +{ + const OGLTexture* gl_texture = static_cast(texture); + if (m_bound_textures[index] == gl_texture) + return; + + glActiveTexture(GL_TEXTURE0 + index); + if (gl_texture) + glBindTexture(gl_texture->GetGLTarget(), gl_texture->GetGLTextureId()); + else + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + m_bound_textures[index] = gl_texture; +} + +void OGLGfx::SetSamplerState(u32 index, const SamplerState& state) +{ + g_sampler_cache->SetSamplerState(index, state); +} + +void OGLGfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) +{ + if (m_bound_image_texture == texture) + return; + + if (texture) + { + const GLenum access = read ? (write ? GL_READ_WRITE : GL_READ_ONLY) : GL_WRITE_ONLY; + glBindImageTexture(0, static_cast(texture)->GetGLTextureId(), 0, GL_TRUE, 0, + access, static_cast(texture)->GetGLFormatForImageTexture()); + } + else + { + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); + } + + m_bound_image_texture = texture; +} + +void OGLGfx::UnbindTexture(const AbstractTexture* texture) +{ + for (size_t i = 0; i < m_bound_textures.size(); i++) + { + if (m_bound_textures[i] != texture) + continue; + + glActiveTexture(static_cast(GL_TEXTURE0 + i)); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + m_bound_textures[i] = nullptr; + } + + if (m_bound_image_texture == texture) + { + glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); + m_bound_image_texture = nullptr; + } +} + +std::unique_ptr OGLGfx::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} + +bool OGLGfx::IsGLES() const +{ + return m_main_gl_context->IsGLES(); +} + +void OGLGfx::BindSharedReadFramebuffer() +{ + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_shared_read_framebuffer); +} + +void OGLGfx::BindSharedDrawFramebuffer() +{ + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared_draw_framebuffer); +} + +void OGLGfx::RestoreFramebufferBinding() +{ + glBindFramebuffer( + GL_FRAMEBUFFER, + m_current_framebuffer ? static_cast(m_current_framebuffer)->GetFBO() : 0); +} + +SurfaceInfo OGLGfx::GetSurfaceInfo() const +{ + return {std::max(m_main_gl_context->GetBackBufferWidth(), 1u), + std::max(m_main_gl_context->GetBackBufferHeight(), 1u), m_backbuffer_scale, + AbstractTextureFormat::RGBA8}; +} + +} // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLRender.h b/Source/Core/VideoBackends/OGL/OGLGfx.h similarity index 65% rename from Source/Core/VideoBackends/OGL/OGLRender.h rename to Source/Core/VideoBackends/OGL/OGLGfx.h index c42543a32b..8d92766387 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.h +++ b/Source/Core/VideoBackends/OGL/OGLGfx.h @@ -1,99 +1,25 @@ -// Copyright 2008 Dolphin Emulator Project +// Copyright 2023 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include -#include +#include "VideoCommon/AbstractGfx.h" -#include "Common/GL/GLContext.h" -#include "Common/GL/GLExtensions/GLExtensions.h" -#include "VideoCommon/RenderBase.h" - -class BoundingBox; +class GLContext; namespace OGL { class OGLFramebuffer; -class OGLPipeline; class OGLTexture; -enum GlslVersion -{ - Glsl130, - Glsl140, - Glsl150, - Glsl330, - Glsl400, // and above - Glsl430, - GlslEs300, // GLES 3.0 - GlslEs310, // GLES 3.1 - GlslEs320, // GLES 3.2 -}; -enum class EsTexbufType -{ - TexbufNone, - TexbufCore, - TexbufOes, - TexbufExt -}; - -enum class EsFbFetchType -{ - FbFetchNone, - FbFetchExt, - FbFetchArm, -}; - -// ogl-only config, so not in VideoConfig.h -struct VideoConfig -{ - bool bIsES; - bool bSupportsGLPinnedMemory; - bool bSupportsGLSync; - bool bSupportsGLBaseVertex; - bool bSupportsGLBufferStorage; - bool bSupportsMSAA; - GlslVersion eSupportedGLSLVersion; - bool bSupportViewportFloat; - bool bSupportsAEP; - bool bSupportsDebug; - bool bSupportsCopySubImage; - u8 SupportedESPointSize; - EsTexbufType SupportedESTextureBuffer; - bool bSupportsTextureStorage; - bool bSupports2DTextureStorageMultisample; - bool bSupports3DTextureStorageMultisample; - bool bSupportsConservativeDepth; - bool bSupportsImageLoadStore; - bool bSupportsAniso; - bool bSupportsBitfield; - bool bSupportsTextureSubImage; - EsFbFetchType SupportedFramebufferFetch; - bool bSupportsShaderThreadShuffleNV; - - const char* gl_vendor; - const char* gl_renderer; - const char* gl_version; - - s32 max_samples; -}; -extern VideoConfig g_ogl_config; - -class Renderer : public ::Renderer +class OGLGfx final : public AbstractGfx { public: - Renderer(std::unique_ptr main_gl_context, float backbuffer_scale); - ~Renderer() override; - - static Renderer* GetInstance() { return static_cast(g_renderer.get()); } + OGLGfx(std::unique_ptr main_gl_context, float backbuffer_scale); + ~OGLGfx(); bool IsHeadless() const override; - bool Initialize() override; - void Shutdown() override; - std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; std::unique_ptr @@ -116,6 +42,8 @@ public: void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) override; void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; + void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; @@ -137,34 +65,30 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) override; + virtual void SelectLeftBuffer() override; + virtual void SelectRightBuffer() override; + virtual void SelectMainBuffer() override; std::unique_ptr CreateAsyncShaderCompiler() override; // Only call methods from this on the GPU thread. GLContext* GetMainGLContext() const { return m_main_gl_context.get(); } - bool IsGLES() const { return m_main_gl_context->IsGLES(); } + bool IsGLES() const; // Invalidates a cached texture binding. Required for texel buffers when they borrow the units. void InvalidateTextureBinding(u32 index) { m_bound_textures[index] = nullptr; } // The shared framebuffer exists for copying textures when extensions are not available. It is // slower, but the only way to do these things otherwise. - GLuint GetSharedReadFramebuffer() const { return m_shared_read_framebuffer; } - GLuint GetSharedDrawFramebuffer() const { return m_shared_draw_framebuffer; } + u32 GetSharedReadFramebuffer() const { return m_shared_read_framebuffer; } + u32 GetSharedDrawFramebuffer() const { return m_shared_draw_framebuffer; } void BindSharedReadFramebuffer(); void BindSharedDrawFramebuffer(); // Restores FBO binding after it's been changed. void RestoreFramebufferBinding(); -protected: - std::unique_ptr CreateBoundingBox() const override; - - virtual void SelectLeftBuffer() override; - virtual void SelectRightBuffer() override; - virtual void SelectMainBuffer() override; + SurfaceInfo GetSurfaceInfo() const override; private: void CheckForSurfaceChange(); @@ -181,7 +105,14 @@ private: RasterizationState m_current_rasterization_state; DepthState m_current_depth_state; BlendingState m_current_blend_state; - GLuint m_shared_read_framebuffer = 0; - GLuint m_shared_draw_framebuffer = 0; + u32 m_shared_read_framebuffer = 0; + u32 m_shared_draw_framebuffer = 0; + float m_backbuffer_scale; }; + +inline OGLGfx* GetOGLGfx() +{ + return static_cast(g_gfx.get()); +} + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLMain.cpp b/Source/Core/VideoBackends/OGL/OGLMain.cpp index f8ed07dc94..a36d9f99a4 100644 --- a/Source/Core/VideoBackends/OGL/OGLMain.cpp +++ b/Source/Core/VideoBackends/OGL/OGLMain.cpp @@ -46,8 +46,10 @@ Make AA apply instantly during gameplay if possible #include "Core/Config/GraphicsSettings.h" +#include "VideoBackends/OGL/OGLBoundingBox.h" +#include "VideoBackends/OGL/OGLConfig.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoBackends/OGL/OGLPerfQuery.h" -#include "VideoBackends/OGL/OGLRender.h" #include "VideoBackends/OGL/OGLVertexManager.h" #include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoBackends/OGL/SamplerCache.h" @@ -185,26 +187,16 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) if (!InitializeGLExtensions(main_gl_context.get()) || !FillBackendInfo()) return false; - InitializeShared(); - g_renderer = std::make_unique(std::move(main_gl_context), wsi.render_surface_scale); + g_gfx = std::make_unique(std::move(main_gl_context), wsi.render_surface_scale); ProgramShaderCache::Init(); + g_vertex_manager = std::make_unique(); - g_shader_cache = std::make_unique(); - g_framebuffer_manager = std::make_unique(); g_perf_query = GetPerfQuery(); - g_texture_cache = std::make_unique(); g_sampler_cache = std::make_unique(); + g_bounding_box = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize()) - { - PanicAlertFmtT("Failed to initialize renderer classes"); - Shutdown(); - return false; - } + InitializeShared(); - g_shader_cache->InitializeShaderCache(); return true; } diff --git a/Source/Core/VideoBackends/OGL/OGLNativeVertexFormat.cpp b/Source/Core/VideoBackends/OGL/OGLNativeVertexFormat.cpp index b77755bb12..f1213fcde5 100644 --- a/Source/Core/VideoBackends/OGL/OGLNativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/OGL/OGLNativeVertexFormat.cpp @@ -6,7 +6,7 @@ #include "Common/GL/GLUtil.h" #include "Common/MsgHandler.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoBackends/OGL/OGLVertexManager.h" #include "VideoBackends/OGL/ProgramShaderCache.h" @@ -19,7 +19,7 @@ namespace OGL { std::unique_ptr -Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +OGLGfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { return std::make_unique(vtx_decl); } diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp index 550b837de8..00816b679d 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp @@ -8,7 +8,8 @@ #include "Common/CommonTypes.h" #include "Common/GL/GLExtensions/GLExtensions.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLGfx.h" +#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -16,7 +17,7 @@ namespace OGL { std::unique_ptr GetPerfQuery() { - const bool is_gles = static_cast(g_renderer.get())->IsGLES(); + const bool is_gles = static_cast(g_gfx.get())->IsGLES(); if (is_gles && GLExtensions::Supports("GL_NV_occlusion_query_samples")) return std::make_unique(); else if (is_gles) diff --git a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp index b4962cf270..3f9dea623f 100644 --- a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp @@ -5,7 +5,6 @@ #include "Common/Assert.h" -#include "VideoBackends/OGL/OGLRender.h" #include "VideoBackends/OGL/OGLShader.h" #include "VideoBackends/OGL/OGLVertexManager.h" #include "VideoBackends/OGL/ProgramShaderCache.h" diff --git a/Source/Core/VideoBackends/OGL/OGLStreamBuffer.cpp b/Source/Core/VideoBackends/OGL/OGLStreamBuffer.cpp index 762eaec1b0..b4f26db453 100644 --- a/Source/Core/VideoBackends/OGL/OGLStreamBuffer.cpp +++ b/Source/Core/VideoBackends/OGL/OGLStreamBuffer.cpp @@ -8,7 +8,7 @@ #include "Common/MathUtil.h" #include "Common/MemoryUtil.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLConfig.h" #include "VideoCommon/DriverDetails.h" #include "VideoCommon/OnScreenDisplay.h" diff --git a/Source/Core/VideoBackends/OGL/OGLTexture.cpp b/Source/Core/VideoBackends/OGL/OGLTexture.cpp index 8c1b14cd08..3f4c47cbeb 100644 --- a/Source/Core/VideoBackends/OGL/OGLTexture.cpp +++ b/Source/Core/VideoBackends/OGL/OGLTexture.cpp @@ -7,6 +7,8 @@ #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" +#include "VideoBackends/OGL/OGLConfig.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoBackends/OGL/SamplerCache.h" #include "VideoCommon/VideoConfig.h" @@ -160,7 +162,7 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config, std::string_view name) OGLTexture::~OGLTexture() { - Renderer::GetInstance()->UnbindTexture(this); + GetOGLGfx()->UnbindTexture(this); glDeleteTextures(1, &m_texId); } @@ -190,10 +192,10 @@ void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle const MathUtil::Rectangle& dst_rect, u32 dst_layer, u32 dst_level) { - Renderer::GetInstance()->BindSharedReadFramebuffer(); + GetOGLGfx()->BindSharedReadFramebuffer(); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, src_level, src_layer); - Renderer::GetInstance()->BindSharedDrawFramebuffer(); + GetOGLGfx()->BindSharedDrawFramebuffer(); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level, dst_layer); @@ -206,7 +208,7 @@ void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle // The default state for the scissor test is enabled. We don't need to do a full state // restore, as the framebuffer and scissor test are the only things we changed. glEnable(GL_SCISSOR_TEST); - Renderer::GetInstance()->RestoreFramebufferBinding(); + GetOGLGfx()->RestoreFramebufferBinding(); } void OGLTexture::ResolveFromTexture(const AbstractTexture* src, @@ -388,7 +390,7 @@ void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src, else { // Mutate the shared framebuffer. - Renderer::GetInstance()->BindSharedReadFramebuffer(); + GetOGLGfx()->BindSharedReadFramebuffer(); if (AbstractTexture::IsDepthFormat(gltex->GetFormat())) { glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0, 0); @@ -404,7 +406,7 @@ void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src, glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), GetGLFormatForTextureFormat(src->GetFormat()), GetGLTypeForTextureFormat(src->GetFormat()), reinterpret_cast(dst_offset)); - Renderer::GetInstance()->RestoreFramebufferBinding(); + GetOGLGfx()->RestoreFramebufferBinding(); } glPixelStorei(GL_PACK_ROW_LENGTH, 0); @@ -597,7 +599,7 @@ std::unique_ptr OGLFramebuffer::Create(OGLTexture* color_attachm } DEBUG_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - Renderer::GetInstance()->RestoreFramebufferBinding(); + GetOGLGfx()->RestoreFramebufferBinding(); return std::make_unique(color_attachment, depth_attachment, color_format, depth_format, width, height, layers, samples, fbo); diff --git a/Source/Core/VideoBackends/OGL/OGLVertexManager.cpp b/Source/Core/VideoBackends/OGL/OGLVertexManager.cpp index e47e425a3b..03b2f71ac8 100644 --- a/Source/Core/VideoBackends/OGL/OGLVertexManager.cpp +++ b/Source/Core/VideoBackends/OGL/OGLVertexManager.cpp @@ -12,8 +12,8 @@ #include "Common/CommonTypes.h" #include "Common/GL/GLExtensions/GLExtensions.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoBackends/OGL/OGLPipeline.h" -#include "VideoBackends/OGL/OGLRender.h" #include "VideoBackends/OGL/OGLStreamBuffer.h" #include "VideoBackends/OGL/ProgramShaderCache.h" @@ -116,7 +116,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff // Bind the correct view to the texel buffer slot. glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast(format)]); - Renderer::GetInstance()->InvalidateTextureBinding(0); + GetOGLGfx()->InvalidateTextureBinding(0); return true; } @@ -141,11 +141,11 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast(format)]); - Renderer::GetInstance()->InvalidateTextureBinding(0); + GetOGLGfx()->InvalidateTextureBinding(0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast(palette_format)]); - Renderer::GetInstance()->InvalidateTextureBinding(1); + GetOGLGfx()->InvalidateTextureBinding(1); return true; } diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index b4635c36ec..289e99cae0 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -22,7 +22,8 @@ #include "Core/ConfigManager.h" #include "Core/System.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLConfig.h" +#include "VideoBackends/OGL/OGLGfx.h" #include "VideoBackends/OGL/OGLShader.h" #include "VideoBackends/OGL/OGLStreamBuffer.h" #include "VideoBackends/OGL/OGLVertexManager.h" @@ -863,8 +864,7 @@ u64 ProgramShaderCache::GenerateShaderID() bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param) { - std::unique_ptr context = - static_cast(g_renderer.get())->GetMainGLContext()->CreateSharedContext(); + std::unique_ptr context = GetOGLGfx()->GetMainGLContext()->CreateSharedContext(); if (!context) { PanicAlertFmt("Failed to create shared context for shader compiling."); diff --git a/Source/Core/VideoBackends/OGL/SamplerCache.cpp b/Source/Core/VideoBackends/OGL/SamplerCache.cpp index c111caa546..2673fd2ddd 100644 --- a/Source/Core/VideoBackends/OGL/SamplerCache.cpp +++ b/Source/Core/VideoBackends/OGL/SamplerCache.cpp @@ -6,7 +6,7 @@ #include #include "Common/CommonTypes.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoBackends/OGL/OGLConfig.h" #include "VideoCommon/VideoConfig.h" namespace OGL diff --git a/Source/Core/VideoBackends/OGL/SamplerCache.h b/Source/Core/VideoBackends/OGL/SamplerCache.h index ad0a9bf3db..be8860c9f8 100644 --- a/Source/Core/VideoBackends/OGL/SamplerCache.h +++ b/Source/Core/VideoBackends/OGL/SamplerCache.h @@ -9,7 +9,7 @@ #include "Common/CommonTypes.h" #include "Common/GL/GLUtil.h" -#include "VideoBackends/OGL/OGLRender.h" +#include "VideoCommon/RenderState.h" namespace OGL { diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp index 4406cd46b6..17c95b863a 100644 --- a/Source/Core/VideoCommon/AbstractGfx.cpp +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -13,6 +13,8 @@ #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" +std::unique_ptr g_gfx; + bool AbstractGfx::IsHeadless() const { return true; diff --git a/Source/Core/VideoCommon/BoundingBox.cpp b/Source/Core/VideoCommon/BoundingBox.cpp index af0961aab6..0429559846 100644 --- a/Source/Core/VideoCommon/BoundingBox.cpp +++ b/Source/Core/VideoCommon/BoundingBox.cpp @@ -13,6 +13,8 @@ #include +std::unique_ptr g_bounding_box; + void BoundingBox::Enable(PixelShaderManager& pixel_shader_manager) { m_is_active = true; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 40fcd8b0f4..1ced44e3ae 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -39,8 +39,10 @@ #include "VideoBackends/Metal/VideoBackend.h" #endif +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AsyncRequests.h" #include "VideoCommon/BPStructs.h" +#include "VideoCommon/BoundingBox.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" @@ -324,9 +326,16 @@ void VideoBackendBase::InitializeShared() // do not initialize again for the config window m_initialized = true; - g_renderer = std::make_unique(); + if (!g_renderer) + { + // Null and Software Backends supply their own Renderer + g_renderer = std::make_unique(); + } g_presenter = std::make_unique(); g_frame_dumper = std::make_unique(); + g_texture_cache = std::make_unique(); + g_framebuffer_manager = std::make_unique(); + g_shader_cache = std::make_unique(); auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); @@ -340,7 +349,9 @@ void VideoBackendBase::InitializeShared() system.GetPixelShaderManager().Init(); TMEM::Init(); - if (!g_renderer->Initialize() || !g_presenter->Initialize()) + if (!g_vertex_manager->Initialize() || !g_renderer->Initialize() || !g_presenter->Initialize() || + !g_shader_cache->Initialize() || !g_framebuffer_manager->Initialize() || + !g_texture_cache->Initialize() || !g_bounding_box->Initialize()) { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); @@ -349,6 +360,8 @@ void VideoBackendBase::InitializeShared() g_Config.VerifyValidity(); UpdateActiveConfig(); + + g_shader_cache->InitializeShaderCache(); } void VideoBackendBase::ShutdownShared() @@ -363,12 +376,15 @@ void VideoBackendBase::ShutdownShared() if (g_texture_cache) g_texture_cache->Shutdown(); + g_bounding_box.reset(); g_perf_query.reset(); g_texture_cache.reset(); g_framebuffer_manager.reset(); g_shader_cache.reset(); g_vertex_manager.reset(); g_renderer.reset(); + g_presenter.reset(); + g_gfx.reset(); m_initialized = false; From d37f83ffeba218c6ef94b78532aed50b1ebbc4a4 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 27 Jan 2023 15:07:05 +1300 Subject: [PATCH 09/60] Implement AbstractGfx for Software & Null --- Source/Core/DolphinLib.props | 6 +- Source/Core/VideoBackends/Null/CMakeLists.txt | 4 +- .../Core/VideoBackends/Null/NullBackend.cpp | 24 +--- Source/Core/VideoBackends/Null/NullGfx.cpp | 90 +++++++++++++ .../Null/{NullRender.h => NullGfx.h} | 25 ++-- Source/Core/VideoBackends/Null/NullRender.cpp | 92 ------------- .../VideoBackends/Software/CMakeLists.txt | 2 + Source/Core/VideoBackends/Software/SWGfx.cpp | 127 ++++++++++++++++++ Source/Core/VideoBackends/Software/SWGfx.h | 55 ++++++++ .../VideoBackends/Software/SWRenderer.cpp | 125 ----------------- .../Core/VideoBackends/Software/SWRenderer.h | 45 ------- .../Core/VideoBackends/Software/SWTexture.cpp | 10 +- Source/Core/VideoBackends/Software/SWmain.cpp | 21 +-- Source/Core/VideoCommon/AbstractGfx.h | 2 + Source/Core/VideoCommon/RenderBase.h | 4 +- 15 files changed, 314 insertions(+), 318 deletions(-) create mode 100644 Source/Core/VideoBackends/Null/NullGfx.cpp rename Source/Core/VideoBackends/Null/{NullRender.h => NullGfx.h} (83%) delete mode 100644 Source/Core/VideoBackends/Null/NullRender.cpp create mode 100644 Source/Core/VideoBackends/Software/SWGfx.cpp create mode 100644 Source/Core/VideoBackends/Software/SWGfx.h diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index a3d1bbacb7..b491f2f880 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -561,7 +561,7 @@ - + @@ -588,6 +588,7 @@ + @@ -1172,7 +1173,7 @@ - + @@ -1195,6 +1196,7 @@ + diff --git a/Source/Core/VideoBackends/Null/CMakeLists.txt b/Source/Core/VideoBackends/Null/CMakeLists.txt index e95877323f..e8ad21bbe8 100644 --- a/Source/Core/VideoBackends/Null/CMakeLists.txt +++ b/Source/Core/VideoBackends/Null/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(videonull NullBackend.cpp NullBoundingBox.h - NullRender.cpp - NullRender.h + NullGfx.cpp + NullGfx.h NullTexture.cpp NullTexture.h NullVertexManager.cpp diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index 80f8431ee7..b90363818e 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -11,12 +11,14 @@ #include "Common/Common.h" #include "Common/MsgHandler.h" -#include "VideoBackends/Null/NullRender.h" +#include "VideoBackends/Null/NullBoundingBox.h" +#include "VideoBackends/Null/NullGfx.h" #include "VideoBackends/Null/NullVertexManager.h" #include "VideoBackends/Null/PerfQuery.h" #include "VideoBackends/Null/TextureCache.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -69,25 +71,13 @@ void VideoBackend::InitBackendInfo() bool VideoBackend::Initialize(const WindowSystemInfo& wsi) { - InitializeShared(); - - g_renderer = std::make_unique(); + g_gfx = std::make_unique(); + g_renderer = std::make_unique(); + g_bounding_box = std::make_unique(); g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); - g_framebuffer_manager = std::make_unique(); - g_texture_cache = std::make_unique(); - g_shader_cache = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize()) - { - PanicAlertFmt("Failed to initialize renderer classes"); - Shutdown(); - return false; - } - - g_shader_cache->InitializeShaderCache(); + InitializeShared(); return true; } diff --git a/Source/Core/VideoBackends/Null/NullGfx.cpp b/Source/Core/VideoBackends/Null/NullGfx.cpp new file mode 100644 index 0000000000..ce8da5ff06 --- /dev/null +++ b/Source/Core/VideoBackends/Null/NullGfx.cpp @@ -0,0 +1,90 @@ +// Copyright 2015 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoBackends/Null/NullGfx.h" + +#include "VideoBackends/Null/NullBoundingBox.h" +#include "VideoBackends/Null/NullTexture.h" + +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/NativeVertexFormat.h" +#include "VideoCommon/VideoConfig.h" + +namespace Null +{ +// Init functions +NullGfx::NullGfx() +{ + UpdateActiveConfig(); +} + +NullGfx::~NullGfx() +{ + UpdateActiveConfig(); +} + +bool NullGfx::IsHeadless() const +{ + return true; +} + +std::unique_ptr NullGfx::CreateTexture(const TextureConfig& config, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(config); +} + +std::unique_ptr NullGfx::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return std::make_unique(type, config); +} + +class NullShader final : public AbstractShader +{ +public: + explicit NullShader(ShaderStage stage) : AbstractShader(stage) {} +}; + +std::unique_ptr +NullGfx::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(stage); +} + +std::unique_ptr +NullGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(stage); +} + +class NullPipeline final : public AbstractPipeline +{ +}; + +std::unique_ptr NullGfx::CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data, + size_t cache_data_length) +{ + return std::make_unique(); +} + +std::unique_ptr NullGfx::CreateFramebuffer(AbstractTexture* color_attachment, + AbstractTexture* depth_attachment) +{ + return NullFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + +std::unique_ptr +NullGfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +{ + return std::make_unique(vtx_decl); +} + +NullRenderer::~NullRenderer() = default; + +} // namespace Null diff --git a/Source/Core/VideoBackends/Null/NullRender.h b/Source/Core/VideoBackends/Null/NullGfx.h similarity index 83% rename from Source/Core/VideoBackends/Null/NullRender.h rename to Source/Core/VideoBackends/Null/NullGfx.h index 061e78c61a..5afb3995b8 100644 --- a/Source/Core/VideoBackends/Null/NullRender.h +++ b/Source/Core/VideoBackends/Null/NullGfx.h @@ -3,17 +3,16 @@ #pragma once +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/RenderBase.h" -class BoundingBox; - namespace Null { -class Renderer final : public ::Renderer +class NullGfx final : public AbstractGfx { public: - Renderer(); - ~Renderer() override; + NullGfx(); + ~NullGfx() override; bool IsHeadless() const override; @@ -34,18 +33,18 @@ public: std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data = nullptr, size_t cache_data_length = 0) override; +}; + +class NullRenderer final : public Renderer +{ +public: + NullRenderer() {} + ~NullRenderer() override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; } void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} - void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) override - { - } - void ReinterpretPixelData(EFBReinterpretType convtype) override {} - -protected: - std::unique_ptr CreateBoundingBox() const override; }; + } // namespace Null diff --git a/Source/Core/VideoBackends/Null/NullRender.cpp b/Source/Core/VideoBackends/Null/NullRender.cpp deleted file mode 100644 index e7b7a7907c..0000000000 --- a/Source/Core/VideoBackends/Null/NullRender.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "VideoBackends/Null/NullRender.h" - -#include "VideoBackends/Null/NullBoundingBox.h" -#include "VideoBackends/Null/NullTexture.h" - -#include "VideoCommon/AbstractPipeline.h" -#include "VideoCommon/AbstractShader.h" -#include "VideoCommon/NativeVertexFormat.h" -#include "VideoCommon/VideoConfig.h" - -namespace Null -{ -// Init functions -Renderer::Renderer() : ::Renderer(1, 1, 1.0f, AbstractTextureFormat::RGBA8) -{ - UpdateActiveConfig(); -} - -Renderer::~Renderer() -{ - UpdateActiveConfig(); -} - -bool Renderer::IsHeadless() const -{ - return true; -} - -std::unique_ptr Renderer::CreateTexture(const TextureConfig& config, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(config); -} - -std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, - const TextureConfig& config) -{ - return std::make_unique(type, config); -} - -class NullShader final : public AbstractShader -{ -public: - explicit NullShader(ShaderStage stage) : AbstractShader(stage) {} -}; - -std::unique_ptr -Renderer::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(stage); -} - -std::unique_ptr -Renderer::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(stage); -} - -class NullPipeline final : public AbstractPipeline -{ -}; - -std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) -{ - return std::make_unique(); -} - -std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) -{ - return NullFramebuffer::Create(static_cast(color_attachment), - static_cast(depth_attachment)); -} - -std::unique_ptr -Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) -{ - return std::make_unique(vtx_decl); -} - -std::unique_ptr Renderer::CreateBoundingBox() const -{ - return std::make_unique(); -} -} // namespace Null diff --git a/Source/Core/VideoBackends/Software/CMakeLists.txt b/Source/Core/VideoBackends/Software/CMakeLists.txt index f71421cb57..7fc904bbf2 100644 --- a/Source/Core/VideoBackends/Software/CMakeLists.txt +++ b/Source/Core/VideoBackends/Software/CMakeLists.txt @@ -14,6 +14,8 @@ add_library(videosoftware SWmain.cpp SWBoundingBox.cpp SWBoundingBox.h + SWGfx.cpp + SWGfx.h SWOGLWindow.cpp SWOGLWindow.h SWRenderer.cpp diff --git a/Source/Core/VideoBackends/Software/SWGfx.cpp b/Source/Core/VideoBackends/Software/SWGfx.cpp new file mode 100644 index 0000000000..b53b7ae89a --- /dev/null +++ b/Source/Core/VideoBackends/Software/SWGfx.cpp @@ -0,0 +1,127 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoBackends/Software/SWGfx.h" + +#include "Common/GL/GLContext.h" + +#include "VideoBackends/Software/EfbCopy.h" +#include "VideoBackends/Software/Rasterizer.h" +#include "VideoBackends/Software/SWOGLWindow.h" +#include "VideoBackends/Software/SWTexture.h" + +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/NativeVertexFormat.h" +#include "VideoCommon/Present.h" + +namespace SW +{ +SWGfx::SWGfx(std::unique_ptr window) : m_window(std::move(window)) +{ +} + +bool SWGfx::IsHeadless() const +{ + return m_window->IsHeadless(); +} + +std::unique_ptr SWGfx::CreateTexture(const TextureConfig& config, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(config); +} + +std::unique_ptr SWGfx::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) +{ + return std::make_unique(type, config); +} + +std::unique_ptr SWGfx::CreateFramebuffer(AbstractTexture* color_attachment, + AbstractTexture* depth_attachment) +{ + return SWFramebuffer::Create(static_cast(color_attachment), + static_cast(depth_attachment)); +} + +void SWGfx::BindBackbuffer(const ClearColor& clear_color) +{ + // Look for framebuffer resizes + if (!g_presenter->SurfaceResizedTestAndClear()) + return; + + GLContext* context = m_window->GetContext(); + context->Update(); + g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight()); +} + +class SWShader final : public AbstractShader +{ +public: + explicit SWShader(ShaderStage stage) : AbstractShader(stage) {} + ~SWShader() = default; + + BinaryData GetBinary() const override { return {}; } +}; + +std::unique_ptr +SWGfx::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(stage); +} + +std::unique_ptr +SWGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, + [[maybe_unused]] std::string_view name) +{ + return std::make_unique(stage); +} + +class SWPipeline final : public AbstractPipeline +{ +public: + SWPipeline() = default; + ~SWPipeline() override = default; +}; + +std::unique_ptr SWGfx::CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data, + size_t cache_data_length) +{ + return std::make_unique(); +} + +// Called on the GPU thread +void SWGfx::ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) +{ + if (!IsHeadless()) + m_window->ShowImage(source_texture, source_rc); +} + +void SWGfx::ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, bool colorEnable, + bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + EfbCopy::ClearEfb(); +} + +std::unique_ptr +SWGfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +{ + return std::make_unique(vtx_decl); +} + +void SWGfx::SetScissorRect(const MathUtil::Rectangle& rc) +{ + // BPFunctions calls SetScissorRect with the "best" scissor rect whenever the viewport or scissor + // changes. However, the software renderer is actually able to use multiple scissor rects (which + // is necessary in a few renderering edge cases, such as with Major Minor's Majestic March). + // Thus, we use this as a signal to update the list of scissor rects, but ignore the parameter. + Rasterizer::ScissorChanged(); +} + +} // namespace SW \ No newline at end of file diff --git a/Source/Core/VideoBackends/Software/SWGfx.h b/Source/Core/VideoBackends/Software/SWGfx.h new file mode 100644 index 0000000000..48015fda25 --- /dev/null +++ b/Source/Core/VideoBackends/Software/SWGfx.h @@ -0,0 +1,55 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "VideoCommon/AbstractGfx.h" + +class SWOGLWindow; + +namespace SW +{ +class SWGfx final : public AbstractGfx +{ +public: + SWGfx(std::unique_ptr window); + + bool IsHeadless() const override; + + std::unique_ptr CreateTexture(const TextureConfig& config, + std::string_view name) override; + std::unique_ptr + CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; + std::unique_ptr + CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override; + + void BindBackbuffer(const ClearColor& clear_color = {}) override; + + std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, + std::string_view name) override; + std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, + size_t length, + std::string_view name) override; + std::unique_ptr + CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override; + std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data = nullptr, + size_t cache_data_length = 0) override; + + void ShowImage(const AbstractTexture* source_texture, + const MathUtil::Rectangle& source_rc) override; + + void ScaleTexture(AbstractFramebuffer* dst_framebuffer, const MathUtil::Rectangle& dst_rect, + const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect) override; + + void SetScissorRect(const MathUtil::Rectangle& rc) override; + + void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; + +private: + std::unique_ptr m_window; +}; + +} // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 887bce177f..7440fba1b3 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -6,117 +6,17 @@ #include #include "Common/CommonTypes.h" -#include "Common/GL/GLContext.h" #include "Core/HW/Memmap.h" #include "Core/System.h" -#include "VideoBackends/Software/EfbCopy.h" #include "VideoBackends/Software/EfbInterface.h" -#include "VideoBackends/Software/Rasterizer.h" -#include "VideoBackends/Software/SWBoundingBox.h" -#include "VideoBackends/Software/SWOGLWindow.h" -#include "VideoBackends/Software/SWTexture.h" -#include "VideoCommon/AbstractPipeline.h" -#include "VideoCommon/AbstractShader.h" -#include "VideoCommon/AbstractTexture.h" -#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/PixelEngine.h" -#include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" -#include "VideoCommon/VideoCommon.h" namespace SW { -SWRenderer::SWRenderer(std::unique_ptr window) - : ::Renderer(static_cast(std::max(window->GetContext()->GetBackBufferWidth(), 1u)), - static_cast(std::max(window->GetContext()->GetBackBufferHeight(), 1u)), 1.0f, - AbstractTextureFormat::RGBA8), - m_window(std::move(window)) -{ -} - -bool SWRenderer::IsHeadless() const -{ - return m_window->IsHeadless(); -} - -std::unique_ptr SWRenderer::CreateTexture(const TextureConfig& config, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(config); -} - -std::unique_ptr -SWRenderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) -{ - return std::make_unique(type, config); -} - -std::unique_ptr -SWRenderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) -{ - return SWFramebuffer::Create(static_cast(color_attachment), - static_cast(depth_attachment)); -} - -void SWRenderer::BindBackbuffer(const ClearColor& clear_color) -{ - // Look for framebuffer resizes - if (!g_presenter->SurfaceResizedTestAndClear()) - return; - - GLContext* context = m_window->GetContext(); - context->Update(); - g_presenter->SetBackbuffer(context->GetBackBufferWidth(), context->GetBackBufferHeight()); -} - -class SWShader final : public AbstractShader -{ -public: - explicit SWShader(ShaderStage stage) : AbstractShader(stage) {} - ~SWShader() = default; - - BinaryData GetBinary() const override { return {}; } -}; - -std::unique_ptr -SWRenderer::CreateShaderFromSource(ShaderStage stage, [[maybe_unused]] std::string_view source, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(stage); -} - -std::unique_ptr -SWRenderer::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, - [[maybe_unused]] std::string_view name) -{ - return std::make_unique(stage); -} - -class SWPipeline final : public AbstractPipeline -{ -public: - SWPipeline() = default; - ~SWPipeline() override = default; -}; - -std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) -{ - return std::make_unique(); -} - -// Called on the GPU thread -void SWRenderer::ShowImage(const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) -{ - if (!IsHeadless()) - m_window->ShowImage(source_texture, source_rc); -} - u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) { u32 value = 0; @@ -165,29 +65,4 @@ u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) return value; } -std::unique_ptr SWRenderer::CreateBoundingBox() const -{ - return std::make_unique(); -} - -void SWRenderer::ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) -{ - EfbCopy::ClearEfb(); -} - -std::unique_ptr -SWRenderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) -{ - return std::make_unique(vtx_decl); -} - -void SWRenderer::SetScissorRect(const MathUtil::Rectangle& rc) -{ - // BPFunctions calls SetScissorRect with the "best" scissor rect whenever the viewport or scissor - // changes. However, the software renderer is actually able to use multiple scissor rects (which - // is necessary in a few renderering edge cases, such as with Major Minor's Majestic March). - // Thus, we use this as a signal to update the list of scissor rects, but ignore the parameter. - Rasterizer::ScissorChanged(); -} } // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 7444834a18..e34a016e15 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -10,59 +10,14 @@ #include "VideoCommon/RenderBase.h" -class BoundingBox; -class SWOGLWindow; - namespace SW { class SWRenderer final : public Renderer { public: - SWRenderer(std::unique_ptr window); - - bool IsHeadless() const override; - - std::unique_ptr CreateTexture(const TextureConfig& config, - std::string_view name) override; - std::unique_ptr - CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override; - std::unique_ptr - CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override; - - void BindBackbuffer(const ClearColor& clear_color = {}) override; - - std::unique_ptr CreateShaderFromSource(ShaderStage stage, std::string_view source, - std::string_view name) override; - std::unique_ptr CreateShaderFromBinary(ShaderStage stage, const void* data, - size_t length, - std::string_view name) override; - std::unique_ptr - CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override; - std::unique_ptr CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data = nullptr, - size_t cache_data_length = 0) override; - u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} - void ShowImage(const AbstractTexture* source_texture, - const MathUtil::Rectangle& source_rc) override; - - void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z) override; - void ReinterpretPixelData(EFBReinterpretType convtype) override {} - - void ScaleTexture(AbstractFramebuffer* dst_framebuffer, const MathUtil::Rectangle& dst_rect, - const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect) override; - - void SetScissorRect(const MathUtil::Rectangle& rc) override; - -protected: - std::unique_ptr CreateBoundingBox() const override; - -private: - std::unique_ptr m_window; }; } // namespace SW diff --git a/Source/Core/VideoBackends/Software/SWTexture.cpp b/Source/Core/VideoBackends/Software/SWTexture.cpp index 6805ff0eb8..2e925c4686 100644 --- a/Source/Core/VideoBackends/Software/SWTexture.cpp +++ b/Source/Core/VideoBackends/Software/SWTexture.cpp @@ -8,7 +8,7 @@ #include "Common/Assert.h" #include "VideoBackends/Software/CopyRegion.h" -#include "VideoBackends/Software/SWRenderer.h" +#include "VideoBackends/Software/SWGfx.h" namespace SW { @@ -48,10 +48,10 @@ void CopyTextureData(const TextureConfig& src_config, const u8* src_ptr, u32 src } } // namespace -void SWRenderer::ScaleTexture(AbstractFramebuffer* dst_framebuffer, - const MathUtil::Rectangle& dst_rect, - const AbstractTexture* src_texture, - const MathUtil::Rectangle& src_rect) +void SWGfx::ScaleTexture(AbstractFramebuffer* dst_framebuffer, + const MathUtil::Rectangle& dst_rect, + const AbstractTexture* src_texture, + const MathUtil::Rectangle& src_rect) { const SWTexture* software_source_texture = static_cast(src_texture); SWTexture* software_dest_texture = static_cast(dst_framebuffer->GetColorAttachment()); diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index b749f447bb..46101afe77 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -16,6 +16,8 @@ #include "VideoBackends/Software/Clipper.h" #include "VideoBackends/Software/EfbInterface.h" #include "VideoBackends/Software/Rasterizer.h" +#include "VideoBackends/Software/SWBoundingBox.h" +#include "VideoBackends/Software/SWGfx.h" #include "VideoBackends/Software/SWOGLWindow.h" #include "VideoBackends/Software/SWRenderer.h" #include "VideoBackends/Software/SWTexture.h" @@ -23,6 +25,7 @@ #include "VideoBackends/Software/TextureCache.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -96,8 +99,6 @@ void VideoSoftware::InitBackendInfo() bool VideoSoftware::Initialize(const WindowSystemInfo& wsi) { - InitializeShared(); - std::unique_ptr window = SWOGLWindow::Create(wsi); if (!window) return false; @@ -105,23 +106,13 @@ bool VideoSoftware::Initialize(const WindowSystemInfo& wsi) Clipper::Init(); Rasterizer::Init(); - g_renderer = std::make_unique(std::move(window)); + g_gfx = std::make_unique(std::move(window)); + g_bounding_box = std::make_unique(); g_vertex_manager = std::make_unique(); - g_shader_cache = std::make_unique(); - g_framebuffer_manager = std::make_unique(); g_perf_query = std::make_unique(); - g_texture_cache = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize()) - { - PanicAlertFmt("Failed to initialize renderer classes"); - Shutdown(); - return false; - } + InitializeShared(); - g_shader_cache->InitializeShaderCache(); return true; } diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index 2db077c98c..eb5944fc84 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -55,6 +55,8 @@ using ClearColor = std::array; class AbstractGfx { public: + virtual ~AbstractGfx() = default; + virtual bool IsHeadless() const = 0; virtual void SetPipeline(const AbstractPipeline* pipeline) {} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index fb88dfd1eb..7bd18e16e7 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -90,8 +90,8 @@ public: float EFBToScaledXf(float x) const; float EFBToScaledYf(float y) const; - virtual void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z); + void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, + bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); void RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle& sourceRc, u32 fbStride, u32 fbHeight, float Gamma = 1.0f); From 58b70b2fb2abac12dddd947a69f7fa5b41b1a8b7 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 14:53:19 +1300 Subject: [PATCH 10/60] Don't set common globals from Video Backends --- .../Core/VideoBackends/Null/NullBackend.cpp | 11 ++---- Source/Core/VideoBackends/OGL/OGLMain.cpp | 13 +++---- .../Core/VideoBackends/OGL/OGLPerfQuery.cpp | 3 +- Source/Core/VideoBackends/OGL/OGLPerfQuery.h | 2 +- Source/Core/VideoBackends/Software/SWmain.cpp | 12 ++---- Source/Core/VideoCommon/VideoBackendBase.cpp | 39 +++++++++++++++---- Source/Core/VideoCommon/VideoBackendBase.h | 20 +++++++++- 7 files changed, 65 insertions(+), 35 deletions(-) diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index b90363818e..900a911699 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -71,14 +71,9 @@ void VideoBackend::InitBackendInfo() bool VideoBackend::Initialize(const WindowSystemInfo& wsi) { - g_gfx = std::make_unique(); - g_renderer = std::make_unique(); - g_bounding_box = std::make_unique(); - g_vertex_manager = std::make_unique(); - g_perf_query = std::make_unique(); - - InitializeShared(); - return true; + return InitializeShared(std::make_unique(), std::make_unique(), + std::make_unique(), std::make_unique(), + std::make_unique(), std::make_unique()); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/OGL/OGLMain.cpp b/Source/Core/VideoBackends/OGL/OGLMain.cpp index a36d9f99a4..c47b94ceb8 100644 --- a/Source/Core/VideoBackends/OGL/OGLMain.cpp +++ b/Source/Core/VideoBackends/OGL/OGLMain.cpp @@ -187,17 +187,16 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) if (!InitializeGLExtensions(main_gl_context.get()) || !FillBackendInfo()) return false; - g_gfx = std::make_unique(std::move(main_gl_context), wsi.render_surface_scale); + auto gfx = std::make_unique(std::move(main_gl_context), wsi.render_surface_scale); ProgramShaderCache::Init(); - - g_vertex_manager = std::make_unique(); - g_perf_query = GetPerfQuery(); g_sampler_cache = std::make_unique(); - g_bounding_box = std::make_unique(); - InitializeShared(); + auto vertex_manager = std::make_unique(); + auto perf_query = GetPerfQuery(gfx->IsGLES()); + auto bounding_box = std::make_unique(); - return true; + return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box)); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp index 00816b679d..cae5f71297 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp @@ -15,9 +15,8 @@ namespace OGL { -std::unique_ptr GetPerfQuery() +std::unique_ptr GetPerfQuery(bool is_gles) { - const bool is_gles = static_cast(g_gfx.get())->IsGLES(); if (is_gles && GLExtensions::Supports("GL_NV_occlusion_query_samples")) return std::make_unique(); else if (is_gles) diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.h b/Source/Core/VideoBackends/OGL/OGLPerfQuery.h index 8ca2bd4334..f593ab5447 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.h +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.h @@ -12,7 +12,7 @@ namespace OGL { -std::unique_ptr GetPerfQuery(); +std::unique_ptr GetPerfQuery(bool is_gles); class PerfQuery : public PerfQueryBase { diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index 46101afe77..8703ea4b4d 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -106,14 +106,10 @@ bool VideoSoftware::Initialize(const WindowSystemInfo& wsi) Clipper::Init(); Rasterizer::Init(); - g_gfx = std::make_unique(std::move(window)); - g_bounding_box = std::make_unique(); - g_vertex_manager = std::make_unique(); - g_perf_query = std::make_unique(); - - InitializeShared(); - - return true; + return InitializeShared(std::make_unique(std::move(window)), + std::make_unique(), std::make_unique(), + std::make_unique(), std::make_unique(), + std::make_unique()); } void VideoSoftware::Shutdown() diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 1ced44e3ae..2f930ceac2 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -317,7 +317,25 @@ void VideoBackendBase::DoState(PointerWrap& p) system.GetFifo().GpuMaySleep(); } -void VideoBackendBase::InitializeShared() +bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, + std::unique_ptr vertex_manager, + std::unique_ptr perf_query, + std::unique_ptr bounding_box) +{ + // All hardware backends use the default RendererBase and TextureCacheBase. + // Only Null and Software backends override them + + return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box), std::make_unique(), + std::make_unique()); +} + +bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, + std::unique_ptr vertex_manager, + std::unique_ptr perf_query, + std::unique_ptr bounding_box, + std::unique_ptr renderer, + std::unique_ptr texture_cache) { memset(reinterpret_cast(&g_main_cp_state), 0, sizeof(g_main_cp_state)); memset(reinterpret_cast(&g_preprocess_cp_state), 0, sizeof(g_preprocess_cp_state)); @@ -326,14 +344,17 @@ void VideoBackendBase::InitializeShared() // do not initialize again for the config window m_initialized = true; - if (!g_renderer) - { - // Null and Software Backends supply their own Renderer - g_renderer = std::make_unique(); - } + g_gfx = std::move(gfx); + g_vertex_manager = std::move(vertex_manager); + g_perf_query = std::move(perf_query); + g_bounding_box = std::move(bounding_box); + + // Null and Software Backends supply their own derived Renderer and Texture Cache + g_texture_cache = std::move(texture_cache); + g_renderer = std::move(renderer); + g_presenter = std::make_unique(); g_frame_dumper = std::make_unique(); - g_texture_cache = std::make_unique(); g_framebuffer_manager = std::make_unique(); g_shader_cache = std::make_unique(); @@ -355,13 +376,15 @@ void VideoBackendBase::InitializeShared() { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); - return; + return false; } g_Config.VerifyValidity(); UpdateActiveConfig(); g_shader_cache->InitializeShaderCache(); + + return true; } void VideoBackendBase::ShutdownShared() diff --git a/Source/Core/VideoCommon/VideoBackendBase.h b/Source/Core/VideoCommon/VideoBackendBase.h index 8238bc2067..2ce235f8f8 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.h +++ b/Source/Core/VideoCommon/VideoBackendBase.h @@ -18,6 +18,12 @@ class Mapping; } class PointerWrap; +class AbstractGfx; +class BoundingBox; +class Renderer; +class TextureCacheBase; +class VertexManagerBase; + enum class FieldType { Odd, @@ -71,7 +77,19 @@ public: void DoState(PointerWrap& p); protected: - void InitializeShared(); + // For hardware backends + bool InitializeShared(std::unique_ptr gfx, + std::unique_ptr vertex_manager, + std::unique_ptr perf_query, + std::unique_ptr bounding_box); + + // For software and null backends. Allows overriding the default Renderer and Texture Cache + bool InitializeShared(std::unique_ptr gfx, + std::unique_ptr vertex_manager, + std::unique_ptr perf_query, + std::unique_ptr bounding_box, + std::unique_ptr renderer, + std::unique_ptr texture_cache); void ShutdownShared(); bool m_initialized = false; From 2a2014af09a6d3a569f6f74f4d12b9fb02c6dffb Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 15:12:28 +1300 Subject: [PATCH 11/60] Implement AbstractGfx for Vulkan --- Source/Core/DolphinLib.props | 4 +- .../Core/VideoBackends/Vulkan/CMakeLists.txt | 4 +- .../VideoBackends/Vulkan/StateTracker.cpp | 2 +- .../VideoBackends/Vulkan/VKBoundingBox.cpp | 4 +- .../Core/VideoBackends/Vulkan/VKBoundingBox.h | 1 + .../Vulkan/{VKRenderer.cpp => VKGfx.cpp} | 167 +++++++----------- .../Vulkan/{VKRenderer.h => VKGfx.h} | 28 ++- Source/Core/VideoBackends/Vulkan/VKMain.cpp | 31 ++-- .../Core/VideoBackends/Vulkan/VKPerfQuery.cpp | 4 +- .../Core/VideoBackends/Vulkan/VKTexture.cpp | 6 +- .../VideoBackends/Vulkan/VKVertexManager.cpp | 12 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 7 +- 12 files changed, 109 insertions(+), 161 deletions(-) rename Source/Core/VideoBackends/Vulkan/{VKRenderer.cpp => VKGfx.cpp} (77%) rename Source/Core/VideoBackends/Vulkan/{VKRenderer.h => VKGfx.h} (86%) diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index b491f2f880..b244c00e98 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -610,7 +610,7 @@ - + @@ -1214,7 +1214,7 @@ - + diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 5fd6aa1ed7..7abf9a038b 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -17,8 +17,8 @@ add_library(videovulkan VKPerfQuery.h VKPipeline.cpp VKPipeline.h - VKRenderer.cpp - VKRenderer.h + VKGfx.cpp + VKGfx.h VKShader.cpp VKShader.h VKStreamBuffer.cpp diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index 8b01c66ca8..8cee69cfa9 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -7,8 +7,8 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VKPipeline.h" -#include "VideoBackends/Vulkan/VKRenderer.h" #include "VideoBackends/Vulkan/VKShader.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VKVertexFormat.h" diff --git a/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp index a182bd8483..7765e97a25 100644 --- a/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.cpp @@ -11,7 +11,7 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/VKRenderer.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VulkanContext.h" namespace Vulkan @@ -65,7 +65,7 @@ std::vector VKBoundingBox::Read(u32 index, u32 length) VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); // Wait until these commands complete. - Renderer::GetInstance()->ExecuteCommandBuffer(false, true); + VKGfx::GetInstance()->ExecuteCommandBuffer(false, true); // Cache is now valid. m_readback_buffer->InvalidateCPUCache(); diff --git a/Source/Core/VideoBackends/Vulkan/VKBoundingBox.h b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.h index 15a5a172d9..107abc7551 100644 --- a/Source/Core/VideoBackends/Vulkan/VKBoundingBox.h +++ b/Source/Core/VideoBackends/Vulkan/VKBoundingBox.h @@ -8,6 +8,7 @@ #include #include "Common/CommonTypes.h" +#include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/VulkanLoader.h" #include "VideoCommon/BoundingBox.h" diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp similarity index 77% rename from Source/Core/VideoBackends/Vulkan/VKRenderer.cpp rename to Source/Core/VideoBackends/Vulkan/VKGfx.cpp index 19c2b19778..1cb029313f 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp @@ -1,7 +1,7 @@ // Copyright 2016 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoBackends/Vulkan/VKRenderer.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include #include @@ -15,153 +15,99 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Core/Core.h" - #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" -#include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/VKBoundingBox.h" -#include "VideoBackends/Vulkan/VKPerfQuery.h" #include "VideoBackends/Vulkan/VKPipeline.h" #include "VideoBackends/Vulkan/VKShader.h" -#include "VideoBackends/Vulkan/VKStreamBuffer.h" #include "VideoBackends/Vulkan/VKSwapChain.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VKVertexFormat.h" -#include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/Present.h" #include "VideoCommon/RenderState.h" -#include "VideoCommon/VertexManagerBase.h" -#include "VideoCommon/VideoBackendBase.h" -#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" -#include "VideoCommon/XFMemory.h" namespace Vulkan { -Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale) - : ::Renderer(swap_chain ? static_cast(swap_chain->GetWidth()) : 1, - swap_chain ? static_cast(swap_chain->GetHeight()) : 0, backbuffer_scale, - swap_chain ? swap_chain->GetTextureFormat() : AbstractTextureFormat::Undefined), - m_swap_chain(std::move(swap_chain)) +VKGfx::VKGfx(std::unique_ptr swap_chain, float backbuffer_scale) + : m_swap_chain(std::move(swap_chain)), m_backbuffer_scale(backbuffer_scale) { UpdateActiveConfig(); for (SamplerState& m_sampler_state : m_sampler_states) m_sampler_state = RenderState::GetPointSamplerState(); -} - -Renderer::~Renderer() = default; - -bool Renderer::IsHeadless() const -{ - return m_swap_chain == nullptr; -} - -bool Renderer::Initialize() -{ - if (!::Renderer::Initialize()) - return false; // Various initialization routines will have executed commands on the command buffer. // Execute what we have done before beginning the first frame. ExecuteCommandBuffer(true, false); - return true; } -void Renderer::Shutdown() +VKGfx::~VKGfx() = default; + +bool VKGfx::IsHeadless() const { - ::Renderer::Shutdown(); - m_swap_chain.reset(); + return m_swap_chain == nullptr; } -std::unique_ptr Renderer::CreateTexture(const TextureConfig& config, - std::string_view name) +std::unique_ptr VKGfx::CreateTexture(const TextureConfig& config, + std::string_view name) { return VKTexture::Create(config, name); } -std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, - const TextureConfig& config) +std::unique_ptr VKGfx::CreateStagingTexture(StagingTextureType type, + const TextureConfig& config) { return VKStagingTexture::Create(type, config); } std::unique_ptr -Renderer::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +VKGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) { return VKShader::CreateFromSource(stage, source, name); } -std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage, - const void* data, size_t length, - std::string_view name) +std::unique_ptr VKGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, + size_t length, std::string_view name) { return VKShader::CreateFromBinary(stage, data, length, name); } std::unique_ptr -Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +VKGfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { return std::make_unique(vtx_decl); } -std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) +std::unique_ptr VKGfx::CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data, + size_t cache_data_length) { return VKPipeline::Create(config); } -std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) +std::unique_ptr VKGfx::CreateFramebuffer(AbstractTexture* color_attachment, + AbstractTexture* depth_attachment) { return VKFramebuffer::Create(static_cast(color_attachment), static_cast(depth_attachment)); } -void Renderer::SetPipeline(const AbstractPipeline* pipeline) +void VKGfx::SetPipeline(const AbstractPipeline* pipeline) { StateTracker::GetInstance()->SetPipeline(static_cast(pipeline)); } -std::unique_ptr Renderer::CreateBoundingBox() const +void VKGfx::ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, bool color_enable, + bool alpha_enable, bool z_enable, u32 color, u32 z) { - return std::make_unique(); -} - -void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) -{ - g_framebuffer_manager->FlushEFBPokes(); - g_framebuffer_manager->FlagPeekCacheAsOutOfDate(); - - // Native -> EFB coordinates - MathUtil::Rectangle target_rc = Renderer::ConvertEFBRectangle(rc); - - // Size we pass this size to vkBeginRenderPass, it has to be clamped to the framebuffer - // dimensions. The other backends just silently ignore this case. - target_rc.ClampUL(0, 0, m_target_width, m_target_height); - VkRect2D target_vk_rc = { {target_rc.left, target_rc.top}, {static_cast(target_rc.GetWidth()), static_cast(target_rc.GetHeight())}}; - // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha - // channel to 0xFF. This hopefully allows us to use the fast path in most cases. - if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 || - bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 || - bpmem.zcontrol.pixel_format == PixelFormat::Z24) - { - // Force alpha writes, and clear the alpha channel. This is different from the other backends, - // where the existing values of the alpha channel are preserved. - alpha_enable = true; - color &= 0x00FFFFFF; - } - // Convert RGBA8 -> floating-point values. VkClearValue clear_color_value = {}; VkClearValue clear_depth_value = {}; @@ -248,17 +194,17 @@ void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable g_framebuffer_manager->ClearEFB(rc, color_enable, alpha_enable, z_enable, color, z); } -void Renderer::Flush() +void VKGfx::Flush() { ExecuteCommandBuffer(true, false); } -void Renderer::WaitForGPUIdle() +void VKGfx::WaitForGPUIdle() { ExecuteCommandBuffer(false, true); } -void Renderer::BindBackbuffer(const ClearColor& clear_color) +void VKGfx::BindBackbuffer(const ClearColor& clear_color) { StateTracker::GetInstance()->EndRenderPass(); @@ -335,7 +281,7 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color) ClearColor{{0.0f, 0.0f, 0.0f, 1.0f}}); } -void Renderer::PresentBackbuffer() +void VKGfx::PresentBackbuffer() { // End drawing to backbuffer StateTracker::GetInstance()->EndRenderPass(); @@ -356,7 +302,7 @@ void Renderer::PresentBackbuffer() StateTracker::GetInstance()->InvalidateCachedState(); } -void Renderer::SetFullscreen(bool enable_fullscreen) +void VKGfx::SetFullscreen(bool enable_fullscreen) { if (!m_swap_chain->IsFullscreenSupported()) return; @@ -364,12 +310,12 @@ void Renderer::SetFullscreen(bool enable_fullscreen) m_swap_chain->SetNextFullscreenState(enable_fullscreen); } -bool Renderer::IsFullscreen() const +bool VKGfx::IsFullscreen() const { return m_swap_chain && m_swap_chain->GetCurrentFullscreenState(); } -void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion) +void VKGfx::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion) { StateTracker::GetInstance()->EndRenderPass(); @@ -378,7 +324,7 @@ void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_comple StateTracker::GetInstance()->InvalidateCachedState(); } -void Renderer::CheckForSurfaceChange() +void VKGfx::CheckForSurfaceChange() { if (!g_presenter->SurfaceChangedTestAndClear() || !m_swap_chain) return; @@ -397,7 +343,7 @@ void Renderer::CheckForSurfaceChange() OnSwapChainResized(); } -void Renderer::CheckForSurfaceResize() +void VKGfx::CheckForSurfaceResize() { if (!g_presenter->SurfaceResizedTestAndClear()) return; @@ -421,7 +367,7 @@ void Renderer::CheckForSurfaceResize() OnSwapChainResized(); } -void Renderer::OnConfigChanged(u32 bits) +void VKGfx::OnConfigChanged(u32 bits) { if (bits & CONFIG_CHANGE_BIT_HOST_CONFIG) g_object_cache->ReloadPipelineCache(); @@ -448,12 +394,12 @@ void Renderer::OnConfigChanged(u32 bits) } } -void Renderer::OnSwapChainResized() +void VKGfx::OnSwapChainResized() { g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); } -void Renderer::BindFramebuffer(VKFramebuffer* fb) +void VKGfx::BindFramebuffer(VKFramebuffer* fb) { StateTracker::GetInstance()->EndRenderPass(); @@ -474,7 +420,7 @@ void Renderer::BindFramebuffer(VKFramebuffer* fb) m_current_framebuffer = fb; } -void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) +void VKGfx::SetFramebuffer(AbstractFramebuffer* framebuffer) { if (m_current_framebuffer == framebuffer) return; @@ -483,7 +429,7 @@ void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) BindFramebuffer(vkfb); } -void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +void VKGfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) { if (m_current_framebuffer == framebuffer) return; @@ -496,8 +442,8 @@ void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) StateTracker::GetInstance()->BeginDiscardRenderPass(); } -void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) +void VKGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, + float depth_value) { VKFramebuffer* vkfb = static_cast(framebuffer); BindFramebuffer(vkfb); @@ -520,7 +466,7 @@ void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, num_clear_values); } -void Renderer::SetTexture(u32 index, const AbstractTexture* texture) +void VKGfx::SetTexture(u32 index, const AbstractTexture* texture) { // Texture should always be in SHADER_READ_ONLY layout prior to use. // This is so we don't need to transition during render passes. @@ -531,7 +477,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) { if (StateTracker::GetInstance()->InRenderPass()) { - WARN_LOG_FMT(VIDEO, "Transitioning image in render pass in Renderer::SetTexture()"); + WARN_LOG_FMT(VIDEO, "Transitioning image in render pass in VKGfx::SetTexture()"); StateTracker::GetInstance()->EndRenderPass(); } @@ -547,7 +493,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) } } -void Renderer::SetSamplerState(u32 index, const SamplerState& state) +void VKGfx::SetSamplerState(u32 index, const SamplerState& state) { // Skip lookup if the state hasn't changed. if (m_sampler_states[index] == state) @@ -565,7 +511,7 @@ void Renderer::SetSamplerState(u32 index, const SamplerState& state) m_sampler_states[index] = state; } -void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) +void VKGfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) { VKTexture* vk_texture = static_cast(texture); if (vk_texture) @@ -583,12 +529,12 @@ void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool } } -void Renderer::UnbindTexture(const AbstractTexture* texture) +void VKGfx::UnbindTexture(const AbstractTexture* texture) { StateTracker::GetInstance()->UnbindTexture(static_cast(texture)->GetView()); } -void Renderer::ResetSamplerStates() +void VKGfx::ResetSamplerStates() { // Invalidate all sampler states, next draw will re-initialize them. for (u32 i = 0; i < m_sampler_states.size(); i++) @@ -601,7 +547,7 @@ void Renderer::ResetSamplerStates() g_object_cache->ClearSamplerCache(); } -void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) +void VKGfx::SetScissorRect(const MathUtil::Rectangle& rc) { VkRect2D scissor = {{rc.left, rc.top}, {static_cast(rc.GetWidth()), static_cast(rc.GetHeight())}}; @@ -621,14 +567,14 @@ void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) StateTracker::GetInstance()->SetScissor(scissor); } -void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) +void VKGfx::SetViewport(float x, float y, float width, float height, float near_depth, + float far_depth) { VkViewport viewport = {x, y, width, height, near_depth, far_depth}; StateTracker::GetInstance()->SetViewport(viewport); } -void Renderer::Draw(u32 base_vertex, u32 num_vertices) +void VKGfx::Draw(u32 base_vertex, u32 num_vertices) { if (!StateTracker::GetInstance()->Bind()) return; @@ -636,7 +582,7 @@ void Renderer::Draw(u32 base_vertex, u32 num_vertices) vkCmdDraw(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_vertices, 1, base_vertex, 0); } -void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) +void VKGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) { if (!StateTracker::GetInstance()->Bind()) return; @@ -645,12 +591,19 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) base_vertex, 0); } -void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, - u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) +void VKGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, + u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { StateTracker::GetInstance()->SetComputeShader(static_cast(shader)); if (StateTracker::GetInstance()->BindCompute()) vkCmdDispatch(g_command_buffer_mgr->GetCurrentCommandBuffer(), groups_x, groups_y, groups_z); } +SurfaceInfo VKGfx::GetSurfaceInfo() const +{ + return {m_swap_chain ? m_swap_chain->GetWidth() : 1u, + m_swap_chain ? m_swap_chain->GetHeight() : 0u, m_backbuffer_scale, + m_swap_chain ? m_swap_chain->GetTextureFormat() : AbstractTextureFormat::Undefined}; +} + } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.h b/Source/Core/VideoBackends/Vulkan/VKGfx.h similarity index 86% rename from Source/Core/VideoBackends/Vulkan/VKRenderer.h rename to Source/Core/VideoBackends/Vulkan/VKGfx.h index 11fc7dd96e..8cab073386 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.h +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.h @@ -4,16 +4,12 @@ #pragma once #include -#include #include #include #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/Constants.h" -#include "VideoCommon/RenderBase.h" - -class BoundingBox; -struct XFBSourceBase; +#include "VideoCommon/AbstractGfx.h" namespace Vulkan { @@ -23,19 +19,16 @@ class VKFramebuffer; class VKPipeline; class VKTexture; -class Renderer : public ::Renderer +class VKGfx : public ::AbstractGfx { public: - Renderer(std::unique_ptr swap_chain, float backbuffer_scale); - ~Renderer() override; + VKGfx(std::unique_ptr swap_chain, float backbuffer_scale); + ~VKGfx() override; - static Renderer* GetInstance() { return static_cast(g_renderer.get()); } + static VKGfx* GetInstance() { return static_cast(g_gfx.get()); } bool IsHeadless() const override; - bool Initialize() override; - void Shutdown() override; - std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; std::unique_ptr @@ -60,8 +53,8 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; @@ -84,13 +77,13 @@ public: void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; + // Returns info about the main surface (aka backbuffer) + virtual SurfaceInfo GetSurfaceInfo() const override; + // Completes the current render pass, executes the command buffer, and restores state ready for // next render. Use when you want to kick the current buffer to make room for new data. void ExecuteCommandBuffer(bool execute_off_thread, bool wait_for_completion = false); -protected: - std::unique_ptr CreateBoundingBox() const override; - private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); @@ -101,6 +94,7 @@ private: void BindFramebuffer(VKFramebuffer* fb); std::unique_ptr m_swap_chain; + float m_backbuffer_scale; // Keep a copy of sampler states to avoid cache lookups every draw std::array m_sampler_states = {}; diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index 13379aebed..a61b9d210a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -12,8 +12,9 @@ #include "VideoBackends/Vulkan/Constants.h" #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/StateTracker.h" +#include "VideoBackends/Vulkan/VKBoundingBox.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VKPerfQuery.h" -#include "VideoBackends/Vulkan/VKRenderer.h" #include "VideoBackends/Vulkan/VKSwapChain.h" #include "VideoBackends/Vulkan/VKVertexManager.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -193,8 +194,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) g_Config.backend_info.bSupportsExclusiveFullscreen = enable_surface && g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface); - // With the backend information populated, we can now initialize videocommon. - InitializeShared(); + UpdateActiveConfig(); // Create command buffers. We do this separately because the other classes depend on it. g_command_buffer_mgr = std::make_unique(g_Config.bBackendMultithreading); @@ -234,24 +234,23 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) return false; } - // Create main wrapper instances. - g_renderer = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); - g_vertex_manager = std::make_unique(); - g_shader_cache = std::make_unique(); - g_framebuffer_manager = std::make_unique(); - g_texture_cache = std::make_unique(); - g_perf_query = std::make_unique(); + auto gfx = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); + auto vertex_manager = std::make_unique(); + auto perf_query = std::make_unique(); + auto bounding_box = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize() || !PerfQuery::GetInstance()->Initialize()) + if (!InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box))) { - PanicAlertFmt("Failed to initialize renderer classes"); - Shutdown(); return false; } - g_shader_cache->InitializeShaderCache(); + if (!PerfQuery::GetInstance()->Initialize()) + { + PanicAlertFmt("Failed to initialize PerfQuery"); + return false; + } + return true; } diff --git a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp index 9571c69677..c03d110eac 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp @@ -13,7 +13,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/VKRenderer.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/VideoCommon.h" @@ -234,7 +234,7 @@ void PerfQuery::PartialFlush(bool blocking) if (blocking || m_query_buffer[m_query_readback_pos].fence_counter == g_command_buffer_mgr->GetCurrentFenceCounter()) { - Renderer::GetInstance()->ExecuteCommandBuffer(true, blocking); + VKGfx::GetInstance()->ExecuteCommandBuffer(true, blocking); } ReadbackQueries(); diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 11c51f4ffc..3175e2f33f 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -17,7 +17,7 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/VKRenderer.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VKStreamBuffer.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -367,7 +367,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* // Execute the command buffer first. WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false); // Try allocating again. This may cause a fence wait. if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) @@ -967,7 +967,7 @@ void VKStagingTexture::Flush() if (g_command_buffer_mgr->GetCurrentFenceCounter() == m_flush_fence_counter) { // Execute the command buffer and wait for it to finish. - Renderer::GetInstance()->ExecuteCommandBuffer(false, true); + VKGfx::GetInstance()->ExecuteCommandBuffer(false, true); } else { diff --git a/Source/Core/VideoBackends/Vulkan/VKVertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VKVertexManager.cpp index 025fa2be29..5069d3e6d0 100644 --- a/Source/Core/VideoBackends/Vulkan/VKVertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKVertexManager.cpp @@ -14,7 +14,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/VKRenderer.h" +#include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VKStreamBuffer.h" #include "VideoBackends/Vulkan/VKVertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -152,7 +152,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride) { // Flush any pending commands first, so that we can wait on the fences WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in vertex/index buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false); // Attempt to allocate again, this may cause a fence wait if (!has_vbuffer_allocation) @@ -266,7 +266,7 @@ bool VertexManager::ReserveConstantStorage() // The only places that call constant updates are safe to have state restored. WARN_LOG_FMT(VIDEO, "Executing command buffer while waiting for space in uniform buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false); // Since we are on a new command buffer, all constants have been invalidated, and we need // to reupload them. We may as well do this now, since we're issuing a draw anyway. @@ -337,7 +337,7 @@ void VertexManager::UploadUtilityUniforms(const void* data, u32 data_size) g_vulkan_context->GetUniformBufferAlignment())) { WARN_LOG_FMT(VIDEO, "Executing command buffer while waiting for ext space in uniform buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false); } StateTracker::GetInstance()->SetUtilityUniformBuffer( @@ -358,7 +358,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff { // Try submitting cmdbuffer. WARN_LOG_FMT(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false, false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false, false); if (!m_texel_stream_buffer->ReserveMemory(data_size, elem_size)) { PanicAlertFmt("Failed to allocate {} bytes from texel buffer", data_size); @@ -388,7 +388,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff { // Try submitting cmdbuffer. WARN_LOG_FMT(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); - Renderer::GetInstance()->ExecuteCommandBuffer(false, false); + VKGfx::GetInstance()->ExecuteCommandBuffer(false, false); if (!m_texel_stream_buffer->ReserveMemory(reserve_size, elem_size)) { PanicAlertFmt("Failed to allocate {} bytes from texel buffer", reserve_size); diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 2f930ceac2..3814cc3690 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -370,9 +370,10 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, system.GetPixelShaderManager().Init(); TMEM::Init(); - if (!g_vertex_manager->Initialize() || !g_renderer->Initialize() || !g_presenter->Initialize() || - !g_shader_cache->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize() || !g_bounding_box->Initialize()) + if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || + !g_renderer->Initialize() || !g_presenter->Initialize() || + !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || + !g_bounding_box->Initialize()) { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); From 8f5b196019f60393ea21990e311c744c20aa840e Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 15:13:49 +1300 Subject: [PATCH 12/60] Minimise include polution from RenderState --- Source/Core/VideoCommon/RenderBase.cpp | 4 +++- Source/Core/VideoCommon/RenderBase.h | 2 +- Source/Core/VideoCommon/RenderState.cpp | 1 + Source/Core/VideoCommon/RenderState.h | 14 ++++++++++++-- Source/Core/VideoCommon/VideoState.cpp | 1 + 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index ec5f25267f..159dd6baaf 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -60,7 +60,9 @@ std::unique_ptr g_renderer; -Renderer::Renderer() : m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} +Renderer::Renderer() + : m_prev_efb_format{PixelFormat::INVALID_FMT}, + m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} { UpdateActiveConfig(); FreeLook::UpdateActiveConfig(); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 7bd18e16e7..6a9d86387c 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -143,7 +143,7 @@ protected: int m_frame_count = 0; private: - PixelFormat m_prev_efb_format = PixelFormat::INVALID_FMT; + PixelFormat m_prev_efb_format; unsigned int m_efb_scale = 1; u64 m_last_xfb_ticks = 0; diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp index 85e9adee15..2f9ac39c5e 100644 --- a/Source/Core/VideoCommon/RenderState.cpp +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -6,6 +6,7 @@ #include #include +#include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureConfig.h" void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_type) diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h index 49590d55b5..b38e9522bb 100644 --- a/Source/Core/VideoCommon/RenderState.h +++ b/Source/Core/VideoCommon/RenderState.h @@ -4,12 +4,22 @@ #pragma once #include "Common/BitField.h" +#include "Common/CommonTypes.h" -#include "VideoCommon/BPMemory.h" -#include "VideoCommon/BPStructs.h" +struct BPMemory; enum class AbstractTextureFormat : u32; +enum class CompareMode : u32; +enum class CullMode : u32; +enum class DstBlendFactor : u32; +enum class FilterMode : u32; +enum class LODType : u32; +enum class LogicOp : u32; +enum class PixelFormat : u32; +enum class SrcBlendFactor : u32; +enum class WrapMode : u32; + enum class PrimitiveType : u32 { Points, diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 34b4d6332d..75e9e4d3bc 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -8,6 +8,7 @@ #include "Common/ChunkFile.h" #include "Core/System.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/BPStructs.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" From 74d3b3443a3eb2bb1697207b549d2d7127f4b98f Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 15:14:10 +1300 Subject: [PATCH 13/60] Fix warning about compare sign mismatch --- Source/Core/VideoCommon/BPFunctions.cpp | 4 ++-- Source/Core/VideoCommon/VideoCommon.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index a37e3720ff..808e7959e5 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -158,11 +158,11 @@ ScissorResult::ScissorResult(const BPMemory& bpmemory, std::pair v for (const auto& x_range : x_ranges) { DEBUG_ASSERT(x_range.start < x_range.end); - DEBUG_ASSERT(x_range.end <= EFB_WIDTH); + DEBUG_ASSERT(static_cast(x_range.end) <= EFB_WIDTH); for (const auto& y_range : y_ranges) { DEBUG_ASSERT(y_range.start < y_range.end); - DEBUG_ASSERT(y_range.end <= EFB_HEIGHT); + DEBUG_ASSERT(static_cast(y_range.end) <= EFB_HEIGHT); m_result.emplace_back(x_range, y_range); } } diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index 2e5aa46936..517e8b3e9b 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -12,18 +12,18 @@ #include "VideoCommon/BPMemory.h" // These are accurate (disregarding AA modes). -constexpr u32 EFB_WIDTH = 640; -constexpr u32 EFB_HEIGHT = 528; +constexpr u32 EFB_WIDTH = 640u; +constexpr u32 EFB_HEIGHT = 528u; // Max XFB width is 720. You can only copy out 640 wide areas of efb to XFB // so you need multiple copies to do the full width. // The VI can do horizontal scaling (TODO: emulate). -constexpr u32 MAX_XFB_WIDTH = 720; +constexpr u32 MAX_XFB_WIDTH = 720u; // Although EFB height is 528, 576-line XFB's can be created either with // vertical scaling by the EFB copy operation or copying to multiple XFB's // that are next to each other in memory (TODO: handle that situation). -constexpr u32 MAX_XFB_HEIGHT = 576; +constexpr u32 MAX_XFB_HEIGHT = 576u; #define PRIM_LOG(t, ...) DEBUG_LOG_FMT(VIDEO, t __VA_OPT__(, ) __VA_ARGS__) From 26e00c3bb4957ce9d26415e415d0b137742844bc Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 15:30:29 +1300 Subject: [PATCH 14/60] Fix warning about using & with bools --- Source/Core/VideoCommon/PixelShaderGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 7db650e431..1d7cc375d6 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -323,7 +323,7 @@ void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& hos // If bounding box is enabled when a UID cache is created, then later disabled, we shouldn't // emit the bounding box portion of the shader. - uid_data->bounding_box &= host_config.bounding_box & host_config.backend_bbox; + uid_data->bounding_box &= host_config.bounding_box && host_config.backend_bbox; } void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type, From f158ff300b72cff5ca1e2e62bc239111987188af Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 16:23:30 +1300 Subject: [PATCH 15/60] Handle VideoSoftware's present fallback better Not a good idea to abuse bSupportsPostProcessing --- Source/Core/VideoBackends/Null/NullGfx.cpp | 5 +++++ Source/Core/VideoBackends/Null/NullGfx.h | 1 + Source/Core/VideoBackends/Software/SWGfx.cpp | 5 +++++ Source/Core/VideoBackends/Software/SWGfx.h | 1 + Source/Core/VideoCommon/AbstractGfx.h | 3 +++ Source/Core/VideoCommon/Present.cpp | 14 ++++++++------ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Source/Core/VideoBackends/Null/NullGfx.cpp b/Source/Core/VideoBackends/Null/NullGfx.cpp index ce8da5ff06..7449aae09c 100644 --- a/Source/Core/VideoBackends/Null/NullGfx.cpp +++ b/Source/Core/VideoBackends/Null/NullGfx.cpp @@ -29,6 +29,11 @@ bool NullGfx::IsHeadless() const return true; } +bool NullGfx::SupportsUtilityDrawing() const +{ + return false; +} + std::unique_ptr NullGfx::CreateTexture(const TextureConfig& config, [[maybe_unused]] std::string_view name) { diff --git a/Source/Core/VideoBackends/Null/NullGfx.h b/Source/Core/VideoBackends/Null/NullGfx.h index 5afb3995b8..b1dff2bc2f 100644 --- a/Source/Core/VideoBackends/Null/NullGfx.h +++ b/Source/Core/VideoBackends/Null/NullGfx.h @@ -15,6 +15,7 @@ public: ~NullGfx() override; bool IsHeadless() const override; + virtual bool SupportsUtilityDrawing() const override; std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; diff --git a/Source/Core/VideoBackends/Software/SWGfx.cpp b/Source/Core/VideoBackends/Software/SWGfx.cpp index b53b7ae89a..7e8c234540 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.cpp +++ b/Source/Core/VideoBackends/Software/SWGfx.cpp @@ -27,6 +27,11 @@ bool SWGfx::IsHeadless() const return m_window->IsHeadless(); } +bool SWGfx::SupportsUtilityDrawing() const +{ + return false; +} + std::unique_ptr SWGfx::CreateTexture(const TextureConfig& config, [[maybe_unused]] std::string_view name) { diff --git a/Source/Core/VideoBackends/Software/SWGfx.h b/Source/Core/VideoBackends/Software/SWGfx.h index 48015fda25..1337d89525 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.h +++ b/Source/Core/VideoBackends/Software/SWGfx.h @@ -15,6 +15,7 @@ public: SWGfx(std::unique_ptr window); bool IsHeadless() const override; + virtual bool SupportsUtilityDrawing() const override; std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index eb5944fc84..d34690ec01 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -59,6 +59,9 @@ public: virtual bool IsHeadless() const = 0; + // Does the backend support drawing a UI or doing post-processing + virtual bool SupportsUtilityDrawing() const { return true; } + virtual void SetPipeline(const AbstractPipeline* pipeline) {} virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} virtual void SetTexture(u32 index, const AbstractTexture* texture) {} diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 8f10707d0d..a89b41b1dd 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -393,12 +393,6 @@ void Presenter::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, const AbstractTexture* source_texture, const MathUtil::Rectangle& source_rc) { - if (!g_ActiveConfig.backend_info.bSupportsPostProcessing) - { - g_gfx->ShowImage(source_texture, source_rc); - return; - } - if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer && g_ActiveConfig.backend_info.bUsesExplictQuadBuffering) { @@ -463,6 +457,14 @@ void Presenter::Present() { m_last_xfb_id = m_xfb_entry->id; + if (!g_gfx->SupportsUtilityDrawing()) + { + // Video Software doesn't support Drawing a UI or doing post-processing + // So just Show the XFB + g_gfx->ShowImage(m_xfb_entry->texture.get(), m_xfb_rect); + return; + } + // Since we use the common pipelines here and draw vertices if a batch is currently being // built by the vertex loader, we end up trampling over its pointer, as we share the buffer // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. From 06ae08ca8b9acd77022585c3a766a8d26e61793a Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 21:08:39 +1300 Subject: [PATCH 16/60] Fix misnamed local variable --- Source/Core/VideoBackends/Vulkan/VKGfx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp index 1cb029313f..f2aeaef0ca 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp @@ -36,8 +36,8 @@ VKGfx::VKGfx(std::unique_ptr swap_chain, float backbuffer_scale) : m_swap_chain(std::move(swap_chain)), m_backbuffer_scale(backbuffer_scale) { UpdateActiveConfig(); - for (SamplerState& m_sampler_state : m_sampler_states) - m_sampler_state = RenderState::GetPointSamplerState(); + for (SamplerState& sampler_state : m_sampler_states) + sampler_state = RenderState::GetPointSamplerState(); // Various initialization routines will have executed commands on the command buffer. // Execute what we have done before beginning the first frame. From 18c799f0b6291df2a994489ebcd34d3dfa290ff3 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 21:30:56 +1300 Subject: [PATCH 17/60] Present: Set surface info before initializing ImGui Otherwise you get subtle bugs in vulkan that take hours to track down. --- Source/Core/VideoCommon/Present.cpp | 76 +++++++++++++++++------------ 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index a89b41b1dd..55df419d0c 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -40,17 +40,20 @@ bool Presenter::Initialize() { UpdateDrawRectangle(); - m_post_processor = std::make_unique(); - if (!m_post_processor->Initialize(m_backbuffer_format)) - return false; - - m_onscreen_ui = std::make_unique(); - if (!m_onscreen_ui->Initialize(m_backbuffer_width, m_backbuffer_height, m_backbuffer_scale)) - return false; - if (!g_gfx->IsHeadless()) + { SetBackbuffer(g_gfx->GetSurfaceInfo()); + m_post_processor = std::make_unique(); + if (!m_post_processor->Initialize(m_backbuffer_format)) + return false; + + m_onscreen_ui = std::make_unique(); + if (!m_onscreen_ui->Initialize(m_backbuffer_width, m_backbuffer_height, m_backbuffer_scale)) + return false; + + } + return true; } @@ -74,7 +77,7 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) { // Check for post-processing shader changes. Done up here as it doesn't affect anything outside // the post-processor. Note that options are applied every frame, so no need to check those. - if (m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) + if (m_post_processor && m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) { // The existing shader must not be in use when it's destroyed g_gfx->WaitForGPUIdle(); @@ -86,8 +89,10 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) // rendering the UI. if (changed_bits & ConfigChangeBits::CONFIG_CHANGE_BIT_STEREO_MODE) { - m_onscreen_ui->RecompileImGuiPipeline(); - m_post_processor->RecompilePipeline(); + if (m_onscreen_ui) + m_onscreen_ui->RecompileImGuiPipeline(); + if (m_post_processor) + m_post_processor->RecompilePipeline(); } } @@ -425,6 +430,7 @@ bool Presenter::SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb m_xfb_entry = std::move(xfb_entry); m_xfb_rect = xfb_rect; bool is_duplicate_frame = m_last_xfb_id == m_xfb_entry->id; + m_last_xfb_id = m_xfb_entry->id; if (!is_duplicate_frame || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) { @@ -455,7 +461,8 @@ bool Presenter::SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb void Presenter::Present() { - m_last_xfb_id = m_xfb_entry->id; + if (g_gfx->IsHeadless() || (!m_onscreen_ui && !m_xfb_entry)) + return; if (!g_gfx->SupportsUtilityDrawing()) { @@ -470,60 +477,69 @@ void Presenter::Present() // with the loader, and it has not been unmapped yet. Force a pipeline flush to avoid this. g_vertex_manager->Flush(); - // Render any UI elements to the draw list. - m_onscreen_ui->Finalize(); + UpdateDrawRectangle(); + + g_gfx->BeginUtilityDrawing(); + g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); // Render the XFB to the screen. - g_gfx->BeginUtilityDrawing(); - if (!g_gfx->IsHeadless()) + if (m_xfb_entry) { - g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); - - UpdateDrawRectangle(); - // Adjust the source rectangle instead of using an oversized viewport to render the XFB. auto render_target_rc = GetTargetRectangle(); auto render_source_rc = m_xfb_rect; AdjustRectanglesToFitBounds(&render_target_rc, &render_source_rc, m_backbuffer_width, m_backbuffer_height); RenderXFBToScreen(render_target_rc, m_xfb_entry->texture.get(), render_source_rc); + } + if (m_onscreen_ui) + { + m_onscreen_ui->Finalize(); m_onscreen_ui->DrawImGui(); + } - // Present to the window system. - { - std::lock_guard guard(m_swap_mutex); - g_gfx->PresentBackbuffer(); - } + // Present to the window system. + { + std::lock_guard guard(m_swap_mutex); + g_gfx->PresentBackbuffer(); + } + if (m_xfb_entry) + { // Update the window size based on the frame that was just rendered. // Due to depending on guest state, we need to call this every frame. SetWindowSize(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); } - m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); + if (m_onscreen_ui) + m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); g_gfx->EndUtilityDrawing(); } void Presenter::SetKeyMap(std::span> key_map) { - m_onscreen_ui->SetKeyMap(key_map); + if (m_onscreen_ui) + m_onscreen_ui->SetKeyMap(key_map); } void Presenter::SetKey(u32 key, bool is_down, const char* chars) { - m_onscreen_ui->SetKey(key, is_down, chars); + if (m_onscreen_ui) + m_onscreen_ui->SetKey(key, is_down, chars); } void Presenter::SetMousePos(float x, float y) { - m_onscreen_ui->SetMousePos(x, y); + if (m_onscreen_ui) + m_onscreen_ui->SetMousePos(x, y); } void Presenter::SetMousePress(u32 button_mask) { - m_onscreen_ui->SetMousePress(button_mask); + if (m_onscreen_ui) + m_onscreen_ui->SetMousePress(button_mask); } } // namespace VideoCommon From b007b8e104fbf57bc5d92bc94ca7eb997edc6332 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sat, 28 Jan 2023 23:33:01 +1300 Subject: [PATCH 18/60] Replace BeginUI/EndUI --- Source/Core/Core/Core.cpp | 5 ---- Source/Core/VideoCommon/Present.cpp | 31 ++++--------------------- Source/Core/VideoCommon/Present.h | 5 ---- Source/Core/VideoCommon/ShaderCache.cpp | 9 +++---- 4 files changed, 8 insertions(+), 42 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 7d7264bd3c..e4d2af12ab 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -540,11 +540,6 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi } Common::ScopeGuard video_guard{[] { g_video_backend->Shutdown(); }}; - // Render a single frame without anything on it to clear the screen. - // This avoids the game list being displayed while the core is finishing initializing. - g_presenter->BeginUIFrame(); - g_presenter->EndUIFrame(); - if (cpu_info.HTT) Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4); else diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 55df419d0c..25d79f47ff 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -52,6 +52,9 @@ bool Presenter::Initialize() if (!m_onscreen_ui->Initialize(m_backbuffer_width, m_backbuffer_height, m_backbuffer_scale)) return false; + // Draw a blank frame (and complete OnScreenUI initialization) + g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}}); + g_gfx->PresentBackbuffer(); } return true; @@ -77,7 +80,8 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) { // Check for post-processing shader changes. Done up here as it doesn't affect anything outside // the post-processor. Note that options are applied every frame, so no need to check those. - if (m_post_processor && m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) + if (m_post_processor && + m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) { // The existing shader must not be in use when it's destroyed g_gfx->WaitForGPUIdle(); @@ -96,31 +100,6 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) } } -void Presenter::BeginUIFrame() -{ - if (g_gfx->IsHeadless()) - return; - - g_gfx->BeginUtilityDrawing(); - g_gfx->BindBackbuffer({0.0f, 0.0f, 0.0f, 1.0f}); -} - -void Presenter::EndUIFrame() -{ - m_onscreen_ui->Finalize(); - - if (g_gfx->IsHeadless()) - { - m_onscreen_ui->DrawImGui(); - - std::lock_guard guard(m_swap_mutex); - g_gfx->PresentBackbuffer(); - g_gfx->EndUtilityDrawing(); - } - - m_onscreen_ui->BeginImGuiFrame(m_backbuffer_width, m_backbuffer_height); -} - std::tuple, MathUtil::Rectangle> Presenter::ConvertStereoRectangle(const MathUtil::Rectangle& rc) const { diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 3b99c3207d..507c5ee424 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -40,11 +40,6 @@ public: void CheckForConfigChanges(u32 changed_bits); - // Begins/presents a "UI frame". UI frames do not draw any of the console XFB, but this could - // change in the future. - void BeginUIFrame(); - void EndUIFrame(); - // Display resolution int GetBackbufferWidth() const { return m_backbuffer_width; } int GetBackbufferHeight() const { return m_backbuffer_height; } diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index b9876dc533..1e46f3527b 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -163,8 +163,6 @@ void ShaderCache::WaitForAsyncCompiler() bool running = true; constexpr auto update_ui_progress = [](size_t completed, size_t total) { - g_presenter->BeginUIFrame(); - const float center_x = ImGui::GetIO().DisplaySize.x * 0.5f; const float center_y = ImGui::GetIO().DisplaySize.y * 0.5f; const float scale = ImGui::GetIO().DisplayFramebufferScale.x; @@ -184,7 +182,7 @@ void ShaderCache::WaitForAsyncCompiler() } ImGui::End(); - g_presenter->EndUIFrame(); + g_presenter->Present(); }; while (running && @@ -195,9 +193,8 @@ void ShaderCache::WaitForAsyncCompiler() m_async_shader_compiler->RetrieveWorkItems(); } - // Just render nothing to clear the screen - g_presenter->BeginUIFrame(); - g_presenter->EndUIFrame(); + // An extra Present to clear the screen + g_presenter->Present(); } template From 89d9ec0a84edf2c9c2e8d7cd4b3cf295814418ff Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 14:54:41 +1300 Subject: [PATCH 19/60] Fix warning --- Source/Core/DolphinQt/MainWindow.cpp | 4 ++-- Source/Core/DolphinQt/MainWindow.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index f4d52c0fa5..59fc19f6a3 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1383,7 +1383,7 @@ void MainWindow::SetStateSlot(int slot) void MainWindow::IncrementSelectedStateSlot() { - int state_slot = m_state_slot + 1; + u32 state_slot = m_state_slot + 1; if (state_slot > State::NUM_STATES) state_slot = 1; m_menu_bar->SetStateSlot(state_slot); @@ -1391,7 +1391,7 @@ void MainWindow::IncrementSelectedStateSlot() void MainWindow::DecrementSelectedStateSlot() { - int state_slot = m_state_slot - 1; + u32 state_slot = m_state_slot - 1; if (state_slot < 1) state_slot = State::NUM_STATES; m_menu_bar->SetStateSlot(state_slot); diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h index 953b6b1811..fde91a8ae2 100644 --- a/Source/Core/DolphinQt/MainWindow.h +++ b/Source/Core/DolphinQt/MainWindow.h @@ -217,7 +217,7 @@ private: bool m_exit_requested = false; bool m_fullscreen_requested = false; bool m_is_screensaver_inhibited = false; - int m_state_slot = 1; + u32 m_state_slot = 1; std::unique_ptr m_pending_boot; ControllersWindow* m_controllers_window = nullptr; From eaae5b4a4541da9e747e79a174ab470c4ead00d6 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 14:55:53 +1300 Subject: [PATCH 20/60] Implement AbstactGfx for Metal --- .../Core/VideoBackends/Metal/CMakeLists.txt | 4 +- .../Metal/{MTLRenderer.h => MTLGfx.h} | 18 ++- .../Metal/{MTLRenderer.mm => MTLGfx.mm} | 136 +++++++++--------- Source/Core/VideoBackends/Metal/MTLMain.mm | 40 ++---- .../Core/VideoBackends/Metal/MTLObjectCache.h | 1 + Source/Core/VideoBackends/Metal/MTLTexture.mm | 2 +- 6 files changed, 95 insertions(+), 106 deletions(-) rename Source/Core/VideoBackends/Metal/{MTLRenderer.h => MTLGfx.h} (88%) rename Source/Core/VideoBackends/Metal/{MTLRenderer.mm => MTLGfx.mm} (80%) diff --git a/Source/Core/VideoBackends/Metal/CMakeLists.txt b/Source/Core/VideoBackends/Metal/CMakeLists.txt index 7be576f52a..db633a0380 100644 --- a/Source/Core/VideoBackends/Metal/CMakeLists.txt +++ b/Source/Core/VideoBackends/Metal/CMakeLists.txt @@ -2,6 +2,8 @@ add_library(videometal MRCHelpers.h MTLBoundingBox.mm MTLBoundingBox.h + MTLGfx.mm + MTLGfx.h MTLMain.mm MTLObjectCache.h MTLObjectCache.mm @@ -9,8 +11,6 @@ add_library(videometal MTLPerfQuery.h MTLPipeline.mm MTLPipeline.h - MTLRenderer.mm - MTLRenderer.h MTLShader.mm MTLShader.h MTLStateTracker.mm diff --git a/Source/Core/VideoBackends/Metal/MTLRenderer.h b/Source/Core/VideoBackends/Metal/MTLGfx.h similarity index 88% rename from Source/Core/VideoBackends/Metal/MTLRenderer.h rename to Source/Core/VideoBackends/Metal/MTLGfx.h index f8b97dc0d3..4e2dca5662 100644 --- a/Source/Core/VideoBackends/Metal/MTLRenderer.h +++ b/Source/Core/VideoBackends/Metal/MTLGfx.h @@ -6,7 +6,7 @@ #include #include -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoBackends/Metal/MRCHelpers.h" @@ -15,16 +15,14 @@ namespace Metal class Framebuffer; class Texture; -class Renderer final : public ::Renderer +class Gfx final : public ::AbstractGfx { public: - Renderer(MRCOwned layer, int width, int height, float layer_scale); - ~Renderer() override; + Gfx(MRCOwned layer); + ~Gfx() override; bool IsHeadless() const override; - bool Initialize() override; - std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; std::unique_ptr @@ -49,8 +47,8 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; @@ -71,8 +69,8 @@ public: void BindBackbuffer(const ClearColor& clear_color = {}) override; void PresentBackbuffer() override; -protected: - std::unique_ptr<::BoundingBox> CreateBoundingBox() const override; + // Returns info about the main surface (aka backbuffer) + SurfaceInfo GetSurfaceInfo() const override; private: MRCOwned m_layer; diff --git a/Source/Core/VideoBackends/Metal/MTLRenderer.mm b/Source/Core/VideoBackends/Metal/MTLGfx.mm similarity index 80% rename from Source/Core/VideoBackends/Metal/MTLRenderer.mm rename to Source/Core/VideoBackends/Metal/MTLGfx.mm index 9d8ae1d532..2d44156102 100644 --- a/Source/Core/VideoBackends/Metal/MTLRenderer.mm +++ b/Source/Core/VideoBackends/Metal/MTLGfx.mm @@ -1,7 +1,7 @@ // Copyright 2022 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoBackends/Metal/MTLRenderer.h" +#include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLBoundingBox.h" #include "VideoBackends/Metal/MTLObjectCache.h" @@ -16,33 +16,28 @@ #include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" -Metal::Renderer::Renderer(MRCOwned layer, int width, int height, float layer_scale) - : ::Renderer(width, height, layer_scale, Util::ToAbstract([layer pixelFormat])), - m_layer(std::move(layer)) +#include + +Metal::Gfx::Gfx(MRCOwned layer) + : m_layer(std::move(layer)) { UpdateActiveConfig(); [m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive]; + + SetupSurface(); + g_state_tracker->FlushEncoders(); } -Metal::Renderer::~Renderer() = default; +Metal::Gfx::~Gfx() = default; -bool Metal::Renderer::IsHeadless() const +bool Metal::Gfx::IsHeadless() const { return m_layer == nullptr; } -bool Metal::Renderer::Initialize() -{ - if (!::Renderer::Initialize()) - return false; - SetupSurface(); - g_state_tracker->FlushEncoders(); - return true; -} - // MARK: Texture Creation -std::unique_ptr Metal::Renderer::CreateTexture(const TextureConfig& config, +std::unique_ptr Metal::Gfx::CreateTexture(const TextureConfig& config, std::string_view name) { @autoreleasepool @@ -78,7 +73,7 @@ std::unique_ptr Metal::Renderer::CreateTexture(const TextureCon } std::unique_ptr -Metal::Renderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) +Metal::Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) { @autoreleasepool { @@ -99,7 +94,7 @@ Metal::Renderer::CreateStagingTexture(StagingTextureType type, const TextureConf } std::unique_ptr -Metal::Renderer::CreateFramebuffer(AbstractTexture* color_attachment, +Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) { AbstractTexture* const either_attachment = color_attachment ? color_attachment : depth_attachment; @@ -111,7 +106,7 @@ Metal::Renderer::CreateFramebuffer(AbstractTexture* color_attachment, // MARK: Pipeline Creation -std::unique_ptr Metal::Renderer::CreateShaderFromSource(ShaderStage stage, +std::unique_ptr Metal::Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) { @@ -125,7 +120,7 @@ std::unique_ptr Metal::Renderer::CreateShaderFromSource(ShaderSt return CreateShaderFromMSL(stage, std::move(*msl), source, name); } -std::unique_ptr Metal::Renderer::CreateShaderFromBinary(ShaderStage stage, +std::unique_ptr Metal::Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) @@ -159,7 +154,7 @@ static NSString* GenericShaderName(ShaderStage stage) // clang-format on -std::unique_ptr Metal::Renderer::CreateShaderFromMSL(ShaderStage stage, +std::unique_ptr Metal::Gfx::CreateShaderFromMSL(ShaderStage stage, std::string msl, std::string_view glsl, std::string_view name) @@ -244,7 +239,7 @@ std::unique_ptr Metal::Renderer::CreateShaderFromMSL(ShaderStage } std::unique_ptr -Metal::Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +Metal::Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { @autoreleasepool { @@ -253,13 +248,13 @@ Metal::Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_d } std::unique_ptr -Metal::Renderer::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data, +Metal::Gfx::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data, size_t cache_data_length) { return g_object_cache->CreatePipeline(config); } -void Metal::Renderer::Flush() +void Metal::Gfx::Flush() { @autoreleasepool { @@ -267,7 +262,7 @@ void Metal::Renderer::Flush() } } -void Metal::Renderer::WaitForGPUIdle() +void Metal::Gfx::WaitForGPUIdle() { @autoreleasepool { @@ -276,7 +271,7 @@ void Metal::Renderer::WaitForGPUIdle() } } -void Metal::Renderer::OnConfigChanged(u32 bits) +void Metal::Gfx::OnConfigChanged(u32 bits) { if (bits & CONFIG_CHANGE_BIT_VSYNC) [m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive]; @@ -288,14 +283,14 @@ void Metal::Renderer::OnConfigChanged(u32 bits) } } -void Metal::Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, - bool alpha_enable, bool z_enable, u32 color, u32 z) +void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& rc, + const MathUtil::Rectangle& target_rc, + bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { - MathUtil::Rectangle target_rc = Renderer::ConvertEFBRectangle(rc); - target_rc.ClampUL(0, 0, m_target_width, m_target_height); - + u32 framebuffer_width = m_current_framebuffer->GetWidth(); + u32 framebuffer_height = m_current_framebuffer->GetHeight(); // All Metal render passes are fullscreen, so we can only run a fast clear if the target is too - if (target_rc == MathUtil::Rectangle(0, 0, m_target_width, m_target_height)) + if (target_rc == MathUtil::Rectangle(0, 0, framebuffer_width, framebuffer_height)) { // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha // channel to 0xFF. This hopefully allows us to use the fast path in most cases. @@ -339,12 +334,12 @@ void Metal::Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color g_state_tracker->EnableEncoderLabel(true); } -void Metal::Renderer::SetPipeline(const AbstractPipeline* pipeline) +void Metal::Gfx::SetPipeline(const AbstractPipeline* pipeline) { g_state_tracker->SetPipeline(static_cast(pipeline)); } -void Metal::Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) +void Metal::Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer) { // Shouldn't be bound as a texture. if (AbstractTexture* color = framebuffer->GetColorAttachment()) @@ -356,7 +351,7 @@ void Metal::Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) g_state_tracker->SetCurrentFramebuffer(static_cast(framebuffer)); } -void Metal::Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +void Metal::Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) { @autoreleasepool { @@ -365,7 +360,7 @@ void Metal::Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) } } -void Metal::Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, +void Metal::Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, float depth_value) { @autoreleasepool @@ -377,39 +372,39 @@ void Metal::Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, } } -void Metal::Renderer::SetScissorRect(const MathUtil::Rectangle& rc) +void Metal::Gfx::SetScissorRect(const MathUtil::Rectangle& rc) { g_state_tracker->SetScissor(rc); } -void Metal::Renderer::SetTexture(u32 index, const AbstractTexture* texture) +void Metal::Gfx::SetTexture(u32 index, const AbstractTexture* texture) { g_state_tracker->SetTexture( index, texture ? static_cast(texture)->GetMTLTexture() : nullptr); } -void Metal::Renderer::SetSamplerState(u32 index, const SamplerState& state) +void Metal::Gfx::SetSamplerState(u32 index, const SamplerState& state) { g_state_tracker->SetSampler(index, state); } -void Metal::Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) +void Metal::Gfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) { g_state_tracker->SetComputeTexture(static_cast(texture)); } -void Metal::Renderer::UnbindTexture(const AbstractTexture* texture) +void Metal::Gfx::UnbindTexture(const AbstractTexture* texture) { g_state_tracker->UnbindTexture(static_cast(texture)->GetMTLTexture()); } -void Metal::Renderer::SetViewport(float x, float y, float width, float height, float near_depth, +void Metal::Gfx::SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) { g_state_tracker->SetViewport(x, y, width, height, near_depth, far_depth); } -void Metal::Renderer::Draw(u32 base_vertex, u32 num_vertices) +void Metal::Gfx::Draw(u32 base_vertex, u32 num_vertices) { @autoreleasepool { @@ -417,7 +412,7 @@ void Metal::Renderer::Draw(u32 base_vertex, u32 num_vertices) } } -void Metal::Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) +void Metal::Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) { @autoreleasepool { @@ -425,7 +420,7 @@ void Metal::Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vert } } -void Metal::Renderer::DispatchComputeShader(const AbstractShader* shader, // +void Metal::Gfx::DispatchComputeShader(const AbstractShader* shader, // u32 groupsize_x, u32 groupsize_y, u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { @@ -437,7 +432,7 @@ void Metal::Renderer::DispatchComputeShader(const AbstractShader* shader, // } } -void Metal::Renderer::BindBackbuffer(const ClearColor& clear_color) +void Metal::Gfx::BindBackbuffer(const ClearColor& clear_color) { @autoreleasepool { @@ -449,7 +444,7 @@ void Metal::Renderer::BindBackbuffer(const ClearColor& clear_color) } } -void Metal::Renderer::PresentBackbuffer() +void Metal::Gfx::PresentBackbuffer() { @autoreleasepool { @@ -474,12 +469,7 @@ void Metal::Renderer::PresentBackbuffer() } } -std::unique_ptr<::BoundingBox> Metal::Renderer::CreateBoundingBox() const -{ - return std::make_unique(); -} - -void Metal::Renderer::CheckForSurfaceChange() +void Metal::Gfx::CheckForSurfaceChange() { if (!g_presenter->SurfaceChangedTestAndClear()) return; @@ -487,26 +477,40 @@ void Metal::Renderer::CheckForSurfaceChange() SetupSurface(); } -void Metal::Renderer::CheckForSurfaceResize() +void Metal::Gfx::CheckForSurfaceResize() { if (!g_presenter->SurfaceResizedTestAndClear()) return; SetupSurface(); } -void Metal::Renderer::SetupSurface() +void Metal::Gfx::SetupSurface() { - CGSize size = [m_layer bounds].size; - // TODO: Update m_backbuffer_scale (need to make doing that not break everything) - const float backbuffer_scale = [m_layer contentsScale]; - size.width *= backbuffer_scale; - size.height *= backbuffer_scale; - [m_layer setDrawableSize:size]; - m_backbuffer_width = size.width; - m_backbuffer_height = size.height; - TextureConfig cfg(m_backbuffer_width, m_backbuffer_height, 1, 1, 1, m_backbuffer_format, - AbstractTextureFlag_RenderTarget); + auto info = GetSurfaceInfo(); + + [m_layer setDrawableSize:{static_cast(info.width), static_cast(info.height)}]; + + TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format, AbstractTextureFlag_RenderTarget); m_bb_texture = std::make_unique(nullptr, cfg); m_backbuffer = std::make_unique(m_bb_texture.get(), nullptr, // - m_backbuffer_width, m_backbuffer_height, 1, 1); + info.width, info.height, 1, 1); + + if (g_presenter) + g_presenter->SetBackbuffer(info); +} + +SurfaceInfo Metal::Gfx::GetSurfaceInfo() const +{ + if (!m_layer) // Headless + return {}; + + CGSize size = [m_layer bounds].size; + const float scale = [m_layer contentsScale]; + + return { + static_cast(size.width * scale), + static_cast(size.height * scale), + scale, + Util::ToAbstract([m_layer pixelFormat]) + }; } diff --git a/Source/Core/VideoBackends/Metal/MTLMain.mm b/Source/Core/VideoBackends/Metal/MTLMain.mm index a5e40bc59f..d7be2cc534 100644 --- a/Source/Core/VideoBackends/Metal/MTLMain.mm +++ b/Source/Core/VideoBackends/Metal/MTLMain.mm @@ -16,13 +16,15 @@ #include "Common/Common.h" #include "Common/MsgHandler.h" +#include "VideoBackends/Metal/MTLBoundingBox.h" #include "VideoBackends/Metal/MTLObjectCache.h" #include "VideoBackends/Metal/MTLPerfQuery.h" -#include "VideoBackends/Metal/MTLRenderer.h" +#include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLStateTracker.h" #include "VideoBackends/Metal/MTLUtil.h" #include "VideoBackends/Metal/MTLVertexManager.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -93,47 +95,31 @@ bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi) MRCOwned> adapter = std::move(devs[selected_adapter_index]); Util::PopulateBackendInfoFeatures(&g_Config, adapter); - // With the backend information populated, we can now initialize videocommon. - InitializeShared(); + UpdateActiveConfig(); MRCOwned layer = MRCRetain(static_cast(wsi.render_surface)); [layer setDevice:adapter]; if (Util::ToAbstract([layer pixelFormat]) == AbstractTextureFormat::Undefined) [layer setPixelFormat:MTLPixelFormatBGRA8Unorm]; - CGSize size = [layer bounds].size; - float scale = [layer contentsScale]; - if (!layer) // headless - scale = 1.0; ObjectCache::Initialize(std::move(adapter)); g_state_tracker = std::make_unique(); - g_renderer = std::make_unique(std::move(layer), size.width * scale, - size.height * scale, scale); - g_vertex_manager = std::make_unique(); - g_perf_query = std::make_unique(); - g_framebuffer_manager = std::make_unique(); - g_texture_cache = std::make_unique(); - g_shader_cache = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize()) - { - PanicAlertFmt("Failed to initialize renderer classes"); - Shutdown(); - return false; - } - - g_shader_cache->InitializeShaderCache(); - - return true; + return InitializeShared( + std::make_unique(std::move(layer)), + std::make_unique(), + std::make_unique(), + std::make_unique() + ); } } void Metal::VideoBackend::Shutdown() { - ObjectCache::Shutdown(); ShutdownShared(); + + g_state_tracker.reset(); + ObjectCache::Shutdown(); } void Metal::VideoBackend::InitBackendInfo() diff --git a/Source/Core/VideoBackends/Metal/MTLObjectCache.h b/Source/Core/VideoBackends/Metal/MTLObjectCache.h index d9f786b51f..9ae28a6352 100644 --- a/Source/Core/VideoBackends/Metal/MTLObjectCache.h +++ b/Source/Core/VideoBackends/Metal/MTLObjectCache.h @@ -9,6 +9,7 @@ #include "VideoBackends/Metal/MRCHelpers.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/BPMemory.h" struct AbstractPipelineConfig; class AbstractPipeline; diff --git a/Source/Core/VideoBackends/Metal/MTLTexture.mm b/Source/Core/VideoBackends/Metal/MTLTexture.mm index 52a857f5d8..0f06e818de 100644 --- a/Source/Core/VideoBackends/Metal/MTLTexture.mm +++ b/Source/Core/VideoBackends/Metal/MTLTexture.mm @@ -6,7 +6,7 @@ #include "Common/Align.h" #include "Common/Assert.h" -#include "VideoBackends/Metal/MTLRenderer.h" +#include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLStateTracker.h" Metal::Texture::Texture(MRCOwned> tex, const TextureConfig& config) From 3c73707dfe8dfb99edab6b8f2b2847160abddaf5 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 23:55:33 +1300 Subject: [PATCH 21/60] Fix dead code. --- Source/Core/VideoCommon/Present.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 25d79f47ff..159fe080af 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -214,8 +214,9 @@ void Presenter::ResizeSurface() void* Presenter::GetNewSurfaceHandle() { - return m_new_surface_handle; + void *handle = m_new_surface_handle; m_new_surface_handle = nullptr; + return handle; } void Presenter::SetWindowSize(int width, int height) From 8bc8e43dd66e80bd061735d4f113e226250fe254 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 23:57:37 +1300 Subject: [PATCH 22/60] Add virtual Initialize() to PerfQueryBase Both DX12 and Vulkan already had one. --- Source/Core/VideoBackends/D3D12/D3D12PerfQuery.h | 2 +- Source/Core/VideoBackends/Vulkan/VKMain.cpp | 15 ++------------- Source/Core/VideoBackends/Vulkan/VKPerfQuery.h | 2 +- Source/Core/VideoCommon/PerfQueryBase.h | 2 ++ Source/Core/VideoCommon/VideoBackendBase.cpp | 6 +++--- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.h b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.h index b7409cedb5..5f3c725eef 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.h +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.h @@ -17,7 +17,7 @@ public: static PerfQuery* GetInstance() { return static_cast(g_perf_query.get()); } - bool Initialize(); + bool Initialize() override; void ResolveQueries(); void EnableQuery(PerfQueryGroup group) override; diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index a61b9d210a..721887af7a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -239,19 +239,8 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) auto perf_query = std::make_unique(); auto bounding_box = std::make_unique(); - if (!InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), - std::move(bounding_box))) - { - return false; - } - - if (!PerfQuery::GetInstance()->Initialize()) - { - PanicAlertFmt("Failed to initialize PerfQuery"); - return false; - } - - return true; + return !InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box)); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.h b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.h index ee4cabd7d6..a8d31f57e8 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.h +++ b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.h @@ -20,7 +20,7 @@ public: static PerfQuery* GetInstance() { return static_cast(g_perf_query.get()); } - bool Initialize(); + bool Initialize() override; void EnableQuery(PerfQueryGroup group) override; void DisableQuery(PerfQueryGroup group) override; diff --git a/Source/Core/VideoCommon/PerfQueryBase.h b/Source/Core/VideoCommon/PerfQueryBase.h index 9671c33ec3..183a27c80c 100644 --- a/Source/Core/VideoCommon/PerfQueryBase.h +++ b/Source/Core/VideoCommon/PerfQueryBase.h @@ -34,6 +34,8 @@ public: PerfQueryBase() : m_query_count(0) {} virtual ~PerfQueryBase() {} + virtual bool Initialize() { return true; } + // Checks if performance queries are enabled in the gameini configuration. // NOTE: Called from CPU+GPU thread static bool ShouldEmulate(); diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 3814cc3690..5119ba5ed1 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -371,9 +371,9 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, TMEM::Init(); if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_presenter->Initialize() || - !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || - !g_bounding_box->Initialize()) + !g_perf_query->Initialize() || !g_renderer->Initialize() || + !g_presenter->Initialize() || !g_framebuffer_manager->Initialize() || + !g_texture_cache->Initialize() || !g_bounding_box->Initialize()) { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); From 5a2d119bda4fda240731dc45286aeff9fb68918f Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 23:58:32 +1300 Subject: [PATCH 23/60] Implement AbstractGfx for Dx11 --- Source/Core/DolphinLib.props | 4 +- Source/Core/VideoBackends/D3D/CMakeLists.txt | 4 +- .../D3D/{D3DRender.cpp => D3DGfx.cpp} | 85 ++++++++++--------- .../D3D/{D3DRender.h => D3DGfx.h} | 12 +-- Source/Core/VideoBackends/D3D/D3DMain.cpp | 26 ++---- .../D3D/D3DNativeVertexFormat.cpp | 4 +- .../VideoBackends/D3D/D3DVertexManager.cpp | 2 +- Source/Core/VideoBackends/D3D/DXPipeline.cpp | 4 +- 8 files changed, 67 insertions(+), 74 deletions(-) rename Source/Core/VideoBackends/D3D/{D3DRender.cpp => D3DGfx.cpp} (73%) rename Source/Core/VideoBackends/D3D/{D3DRender.h => D3DGfx.h} (92%) diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index b244c00e98..4df8e0cd84 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -534,7 +534,7 @@ - + @@ -1148,7 +1148,7 @@ - + diff --git a/Source/Core/VideoBackends/D3D/CMakeLists.txt b/Source/Core/VideoBackends/D3D/CMakeLists.txt index f296040526..ed75b29b77 100644 --- a/Source/Core/VideoBackends/D3D/CMakeLists.txt +++ b/Source/Core/VideoBackends/D3D/CMakeLists.txt @@ -7,8 +7,8 @@ add_library(videod3d D3DNativeVertexFormat.cpp D3DPerfQuery.cpp D3DPerfQuery.h - D3DRender.cpp - D3DRender.h + D3DGfx.cpp + D3DGfx.h D3DState.cpp D3DState.h D3DSwapChain.cpp diff --git a/Source/Core/VideoBackends/D3D/D3DRender.cpp b/Source/Core/VideoBackends/D3D/D3DGfx.cpp similarity index 73% rename from Source/Core/VideoBackends/D3D/D3DRender.cpp rename to Source/Core/VideoBackends/D3D/D3DGfx.cpp index 6e045979cb..dc391f08b3 100644 --- a/Source/Core/VideoBackends/D3D/D3DRender.cpp +++ b/Source/Core/VideoBackends/D3D/D3DGfx.cpp @@ -1,7 +1,7 @@ // Copyright 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoBackends/D3D/D3DRender.h" +#include "VideoBackends/D3D/D3DGfx.h" #include #include @@ -37,34 +37,31 @@ namespace DX11 { -Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale) - : ::Renderer(swap_chain ? swap_chain->GetWidth() : 0, swap_chain ? swap_chain->GetHeight() : 0, - backbuffer_scale, - swap_chain ? swap_chain->GetFormat() : AbstractTextureFormat::Undefined), - m_swap_chain(std::move(swap_chain)) +Gfx::Gfx(std::unique_ptr swap_chain, float backbuffer_scale) + : m_backbuffer_scale(backbuffer_scale), m_swap_chain(std::move(swap_chain)) { } -Renderer::~Renderer() = default; +Gfx::~Gfx() = default; -bool Renderer::IsHeadless() const +bool Gfx::IsHeadless() const { return !m_swap_chain; } -std::unique_ptr Renderer::CreateTexture(const TextureConfig& config, +std::unique_ptr Gfx::CreateTexture(const TextureConfig& config, std::string_view name) { return DXTexture::Create(config, name); } -std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, +std::unique_ptr Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) { return DXStagingTexture::Create(type, config); } -std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, +std::unique_ptr Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) { return DXFramebuffer::Create(static_cast(color_attachment), @@ -72,7 +69,7 @@ std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture } std::unique_ptr -Renderer::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) { auto bytecode = DXShader::CompileShader(D3D::feature_level, stage, source); if (!bytecode) @@ -81,21 +78,21 @@ Renderer::CreateShaderFromSource(ShaderStage stage, std::string_view source, std return DXShader::CreateFromBytecode(stage, std::move(*bytecode), name); } -std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage, +std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) { return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length), name); } -std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config, +std::unique_ptr Gfx::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data, size_t cache_data_length) { return DXPipeline::Create(config); } -void Renderer::SetPipeline(const AbstractPipeline* pipeline) +void Gfx::SetPipeline(const AbstractPipeline* pipeline) { const DXPipeline* dx_pipeline = static_cast(pipeline); if (m_current_pipeline == dx_pipeline) @@ -123,7 +120,7 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) } } -void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) +void Gfx::SetScissorRect(const MathUtil::Rectangle& rc) { // TODO: Move to stateman const CD3D11_RECT rect(rc.left, rc.top, std::max(rc.right, rc.left + 1), @@ -131,7 +128,7 @@ void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) D3D::context->RSSetScissorRects(1, &rect); } -void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, +void Gfx::SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) { // TODO: Move to stateman @@ -139,19 +136,19 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne D3D::context->RSSetViewports(1, &vp); } -void Renderer::Draw(u32 base_vertex, u32 num_vertices) +void Gfx::Draw(u32 base_vertex, u32 num_vertices) { D3D::stateman->Apply(); D3D::context->Draw(num_vertices, base_vertex); } -void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) +void Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) { D3D::stateman->Apply(); D3D::context->DrawIndexed(num_indices, base_index, base_vertex); } -void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, +void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { D3D::stateman->SetComputeShader(static_cast(shader)->GetD3DComputeShader()); @@ -159,25 +156,25 @@ void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize D3D::context->Dispatch(groups_x, groups_y, groups_z); } -void Renderer::BindBackbuffer(const ClearColor& clear_color) +void Gfx::BindBackbuffer(const ClearColor& clear_color) { CheckForSwapChainChanges(); SetAndClearFramebuffer(m_swap_chain->GetFramebuffer(), clear_color); } -void Renderer::PresentBackbuffer() +void Gfx::PresentBackbuffer() { m_swap_chain->Present(); } -void Renderer::OnConfigChanged(u32 bits) +void Gfx::OnConfigChanged(u32 bits) { // Quad-buffer changes require swap chain recreation. if (bits & CONFIG_CHANGE_BIT_STEREO_MODE && m_swap_chain) m_swap_chain->SetStereo(SwapChain::WantsStereo()); } -void Renderer::CheckForSwapChainChanges() +void Gfx::CheckForSwapChainChanges() { const bool surface_changed = g_presenter->SurfaceChangedTestAndClear(); const bool surface_resized = @@ -194,11 +191,10 @@ void Renderer::CheckForSwapChainChanges() m_swap_chain->ResizeSwapChain(); } - m_backbuffer_width = m_swap_chain->GetWidth(); - m_backbuffer_height = m_swap_chain->GetHeight(); + g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); } -void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) +void Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer) { if (m_current_framebuffer == framebuffer) return; @@ -219,12 +215,12 @@ void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) m_current_framebuffer = fb; } -void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +void Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) { SetFramebuffer(framebuffer); } -void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, +void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, float depth_value) { SetFramebuffer(framebuffer); @@ -242,53 +238,58 @@ void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, } } -void Renderer::SetTexture(u32 index, const AbstractTexture* texture) +void Gfx::SetTexture(u32 index, const AbstractTexture* texture) { D3D::stateman->SetTexture(index, texture ? static_cast(texture)->GetD3DSRV() : nullptr); } -void Renderer::SetSamplerState(u32 index, const SamplerState& state) +void Gfx::SetSamplerState(u32 index, const SamplerState& state) { D3D::stateman->SetSampler(index, m_state_cache.Get(state)); } -void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) +void Gfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) { D3D::stateman->SetComputeUAV(texture ? static_cast(texture)->GetD3DUAV() : nullptr); } -void Renderer::UnbindTexture(const AbstractTexture* texture) +void Gfx::UnbindTexture(const AbstractTexture* texture) { if (D3D::stateman->UnsetTexture(static_cast(texture)->GetD3DSRV()) != 0) D3D::stateman->ApplyTextures(); } -std::unique_ptr Renderer::CreateBoundingBox() const -{ - return std::make_unique(); -} - -void Renderer::Flush() +void Gfx::Flush() { D3D::context->Flush(); } -void Renderer::WaitForGPUIdle() +void Gfx::WaitForGPUIdle() { // There is no glFinish() equivalent in D3D. D3D::context->Flush(); } -void Renderer::SetFullscreen(bool enable_fullscreen) +void Gfx::SetFullscreen(bool enable_fullscreen) { if (m_swap_chain) m_swap_chain->SetFullscreen(enable_fullscreen); } -bool Renderer::IsFullscreen() const +bool Gfx::IsFullscreen() const { return m_swap_chain && m_swap_chain->GetFullscreen(); } +SurfaceInfo Gfx::GetSurfaceInfo() const +{ + return { + m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, + m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, + m_backbuffer_scale, + m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined + }; +} + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/D3DRender.h b/Source/Core/VideoBackends/D3D/D3DGfx.h similarity index 92% rename from Source/Core/VideoBackends/D3D/D3DRender.h rename to Source/Core/VideoBackends/D3D/D3DGfx.h index a0d25bc270..8bbf998e36 100644 --- a/Source/Core/VideoBackends/D3D/D3DRender.h +++ b/Source/Core/VideoBackends/D3D/D3DGfx.h @@ -6,7 +6,7 @@ #include #include #include "VideoBackends/D3D/D3DState.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/AbstractGfx.h" class BoundingBox; @@ -16,11 +16,11 @@ class SwapChain; class DXTexture; class DXFramebuffer; -class Renderer : public ::Renderer +class Gfx : public ::AbstractGfx { public: - Renderer(std::unique_ptr swap_chain, float backbuffer_scale); - ~Renderer() override; + Gfx(std::unique_ptr swap_chain, float backbuffer_scale); + ~Gfx() override; StateCache& GetStateCache() { return m_state_cache; } @@ -69,14 +69,14 @@ public: void OnConfigChanged(u32 bits) override; -protected: - std::unique_ptr CreateBoundingBox() const override; + SurfaceInfo GetSurfaceInfo() const override; private: void CheckForSwapChainChanges(); StateCache m_state_cache; + float m_backbuffer_scale; std::unique_ptr m_swap_chain; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/D3DMain.cpp b/Source/Core/VideoBackends/D3D/D3DMain.cpp index 1748a0b3fc..ae2e0cd959 100644 --- a/Source/Core/VideoBackends/D3D/D3DMain.cpp +++ b/Source/Core/VideoBackends/D3D/D3DMain.cpp @@ -14,11 +14,12 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBoundingBox.h" #include "VideoBackends/D3D/D3DPerfQuery.h" -#include "VideoBackends/D3D/D3DRender.h" +#include "VideoBackends/D3D/D3DGfx.h" #include "VideoBackends/D3D/D3DSwapChain.h" #include "VideoBackends/D3D/D3DVertexManager.h" #include "VideoBackends/D3DCommon/D3DCommon.h" +#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/TextureCacheBase.h" @@ -143,7 +144,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) return false; FillBackendInfo(); - InitializeShared(); + UpdateActiveConfig(); std::unique_ptr swap_chain; if (wsi.render_surface && !(swap_chain = SwapChain::Create(wsi))) @@ -154,22 +155,13 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) return false; } - g_renderer = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); - g_vertex_manager = std::make_unique(); - g_shader_cache = std::make_unique(); - g_framebuffer_manager = std::make_unique(); - g_texture_cache = std::make_unique(); - g_perf_query = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize()) - { - Shutdown(); - return false; - } + auto gfx = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); + auto vertex_manager = std::make_unique(); + auto perf_query = std::make_unique(); + auto bounding_box = std::make_unique(); - g_shader_cache->InitializeShaderCache(); - return true; + return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box)); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp index aca5a177fa..54c1ae8220 100644 --- a/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp @@ -7,7 +7,7 @@ #include "Common/EnumMap.h" #include "VideoBackends/D3D/D3DBase.h" -#include "VideoBackends/D3D/D3DRender.h" +#include "VideoBackends/D3D/D3DGfx.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DVertexManager.h" #include "VideoBackends/D3D/DXShader.h" @@ -18,7 +18,7 @@ namespace DX11 std::mutex s_input_layout_lock; std::unique_ptr -Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { return std::make_unique(vtx_decl); } diff --git a/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp b/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp index ce41e90e31..4aa1c40266 100644 --- a/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp +++ b/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp @@ -13,7 +13,7 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBoundingBox.h" -#include "VideoBackends/D3D/D3DRender.h" +#include "VideoBackends/D3D/D3DGfx.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3DCommon/D3DCommon.h" diff --git a/Source/Core/VideoBackends/D3D/DXPipeline.cpp b/Source/Core/VideoBackends/D3D/DXPipeline.cpp index 0bdfeb0012..3553e8d512 100644 --- a/Source/Core/VideoBackends/D3D/DXPipeline.cpp +++ b/Source/Core/VideoBackends/D3D/DXPipeline.cpp @@ -7,7 +7,7 @@ #include "Common/Logging/Log.h" #include "VideoBackends/D3D/D3DBase.h" -#include "VideoBackends/D3D/D3DRender.h" +#include "VideoBackends/D3D/D3DGfx.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DVertexManager.h" #include "VideoBackends/D3D/DXShader.h" @@ -32,7 +32,7 @@ DXPipeline::~DXPipeline() = default; std::unique_ptr DXPipeline::Create(const AbstractPipelineConfig& config) { - StateCache& state_cache = static_cast(g_renderer.get())->GetStateCache(); + StateCache& state_cache = static_cast(g_gfx.get())->GetStateCache(); ID3D11RasterizerState* rasterizer_state = state_cache.Get(config.rasterization_state); ID3D11DepthStencilState* depth_state = state_cache.Get(config.depth_state); ID3D11BlendState* blend_state = state_cache.Get(config.blending_state); From 8ad59f8ccf95e5e6906887d40ed30717a33b84f6 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Sun, 29 Jan 2023 23:58:54 +1300 Subject: [PATCH 24/60] Add AbstractGfx for DX12 --- Source/Core/DolphinLib.props | 4 +- .../Core/VideoBackends/D3D12/CMakeLists.txt | 4 +- .../VideoBackends/D3D12/D3D12BoundingBox.cpp | 8 +- .../D3D12/{D3D12Renderer.cpp => D3D12Gfx.cpp} | 142 ++++++++---------- .../D3D12/{D3D12Renderer.h => D3D12Gfx.h} | 25 ++- .../VideoBackends/D3D12/D3D12PerfQuery.cpp | 7 +- .../D3D12/D3D12VertexManager.cpp | 40 ++--- .../Core/VideoBackends/D3D12/DX12Texture.cpp | 6 +- .../Core/VideoBackends/D3D12/VideoBackend.cpp | 32 ++-- 9 files changed, 119 insertions(+), 149 deletions(-) rename Source/Core/VideoBackends/D3D12/{D3D12Renderer.cpp => D3D12Gfx.cpp} (84%) rename Source/Core/VideoBackends/D3D12/{D3D12Renderer.h => D3D12Gfx.h} (92%) diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 4df8e0cd84..9bf63bbb51 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -545,7 +545,7 @@ - + @@ -1157,7 +1157,7 @@ - + diff --git a/Source/Core/VideoBackends/D3D12/CMakeLists.txt b/Source/Core/VideoBackends/D3D12/CMakeLists.txt index 16f458e2e9..bc89b89df3 100644 --- a/Source/Core/VideoBackends/D3D12/CMakeLists.txt +++ b/Source/Core/VideoBackends/D3D12/CMakeLists.txt @@ -3,8 +3,8 @@ add_library(videod3d12 D3D12BoundingBox.h D3D12PerfQuery.cpp D3D12PerfQuery.h - D3D12Renderer.cpp - D3D12Renderer.h + D3D12Gfx.cpp + D3D12Gfx.h D3D12StreamBuffer.cpp D3D12StreamBuffer.h D3D12SwapChain.cpp diff --git a/Source/Core/VideoBackends/D3D12/D3D12BoundingBox.cpp b/Source/Core/VideoBackends/D3D12/D3D12BoundingBox.cpp index 4eec9f2e22..1627e39343 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12BoundingBox.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12BoundingBox.cpp @@ -6,7 +6,7 @@ #include "Common/Assert.h" #include "Common/Logging/Log.h" -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/DX12Context.h" namespace DX12 @@ -22,7 +22,7 @@ bool D3D12BoundingBox::Initialize() if (!CreateBuffers()) return false; - Renderer::GetInstance()->SetPixelShaderUAV(m_gpu_descriptor.cpu_handle); + Gfx::GetInstance()->SetPixelShaderUAV(m_gpu_descriptor.cpu_handle); return true; } @@ -35,7 +35,7 @@ std::vector D3D12BoundingBox::Read(u32 index, u32 length) 0, BUFFER_SIZE); ResourceBarrier(g_dx_context->GetCommandList(), m_gpu_buffer.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - Renderer::GetInstance()->ExecuteCommandList(true); + Gfx::GetInstance()->ExecuteCommandList(true); // Read back to cached values. std::vector values(length); @@ -62,7 +62,7 @@ void D3D12BoundingBox::Write(u32 index, const std::vector& values) if (!m_upload_buffer.ReserveMemory(copy_size, sizeof(BBoxType))) { WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in bbox stream buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); if (!m_upload_buffer.ReserveMemory(copy_size, sizeof(BBoxType))) { PanicAlertFmt("Failed to allocate bbox stream buffer space"); diff --git a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp similarity index 84% rename from Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp rename to Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp index ab6b1a464b..ea9610ae34 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Renderer.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp @@ -1,7 +1,7 @@ // Copyright 2019 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "Common/Logging/Log.h" @@ -28,11 +28,8 @@ static bool UsesDynamicVertexLoader(const AbstractPipeline* pipeline) (g_ActiveConfig.UseVSForLinePointExpand() && usage != AbstractPipelineUsage::Utility); } -Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale) - : ::Renderer(swap_chain ? swap_chain->GetWidth() : 0, swap_chain ? swap_chain->GetHeight() : 0, - backbuffer_scale, - swap_chain ? swap_chain->GetFormat() : AbstractTextureFormat::Undefined), - m_swap_chain(std::move(swap_chain)) +Gfx::Gfx(std::unique_ptr swap_chain, float backbuffer_scale) + : m_backbuffer_scale(backbuffer_scale), m_swap_chain(std::move(swap_chain)) { m_state.root_signature = g_dx_context->GetGXRootSignature(); @@ -44,41 +41,26 @@ Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale } } -Renderer::~Renderer() = default; +Gfx::~Gfx() = default; -bool Renderer::IsHeadless() const +bool Gfx::IsHeadless() const { return !m_swap_chain; } -bool Renderer::Initialize() -{ - if (!::Renderer::Initialize()) - return false; - - return true; -} - -void Renderer::Shutdown() -{ - m_swap_chain.reset(); - - ::Renderer::Shutdown(); -} - -std::unique_ptr Renderer::CreateTexture(const TextureConfig& config, +std::unique_ptr Gfx::CreateTexture(const TextureConfig& config, std::string_view name) { return DXTexture::Create(config, name); } -std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, +std::unique_ptr Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) { return DXStagingTexture::Create(type, config); } -std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, +std::unique_ptr Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) { return DXFramebuffer::Create(static_cast(color_attachment), @@ -86,12 +68,12 @@ std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture } std::unique_ptr -Renderer::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) +Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name) { return DXShader::CreateFromSource(stage, source, name); } -std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage, +std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length, std::string_view name) { @@ -99,43 +81,36 @@ std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage sta } std::unique_ptr -Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) +Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { return std::make_unique(vtx_decl); } -std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config, +std::unique_ptr Gfx::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data, size_t cache_data_length) { return DXPipeline::Create(config, cache_data, cache_data_length); } -std::unique_ptr Renderer::CreateBoundingBox() const -{ - return std::make_unique(); -} - -void Renderer::Flush() +void Gfx::Flush() { ExecuteCommandList(false); } -void Renderer::WaitForGPUIdle() +void Gfx::WaitForGPUIdle() { ExecuteCommandList(true); } -void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) +void Gfx::ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { // Use a fast path without the shader if both color/alpha are enabled. - const bool fast_color_clear = color_enable && (alpha_enable || !EFBHasAlphaChannel()); + const bool fast_color_clear = color_enable && alpha_enable; if (fast_color_clear || z_enable) { - MathUtil::Rectangle native_rc = ConvertEFBRectangle(rc); - native_rc.ClampUL(0, 0, m_current_framebuffer->GetWidth(), m_current_framebuffer->GetHeight()); - const D3D12_RECT d3d_clear_rc{native_rc.left, native_rc.top, native_rc.right, native_rc.bottom}; + const D3D12_RECT d3d_clear_rc{target_rc.left, target_rc.top, target_rc.right, target_rc.bottom}; if (fast_color_clear) { @@ -170,10 +145,10 @@ void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable // Anything left over, fall back to clear triangle. if (color_enable || alpha_enable || z_enable) - ::Renderer::ClearScreen(rc, color_enable, alpha_enable, z_enable, color, z); + ::AbstractGfx::ClearRegion(rc, target_rc, color_enable, alpha_enable, z_enable, color, z); } -void Renderer::SetPipeline(const AbstractPipeline* pipeline) +void Gfx::SetPipeline(const AbstractPipeline* pipeline) { const DXPipeline* dx_pipeline = static_cast(pipeline); if (m_current_pipeline == dx_pipeline) @@ -205,7 +180,7 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) } } -void Renderer::BindFramebuffer(DXFramebuffer* fb) +void Gfx::BindFramebuffer(DXFramebuffer* fb) { if (fb->HasColorBuffer()) { @@ -226,7 +201,7 @@ void Renderer::BindFramebuffer(DXFramebuffer* fb) m_dirty_bits &= ~DirtyState_Framebuffer; } -void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) +void Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer) { if (m_current_framebuffer == framebuffer) return; @@ -235,7 +210,7 @@ void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) m_dirty_bits |= DirtyState_Framebuffer; } -void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) +void Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) { SetFramebuffer(framebuffer); @@ -254,7 +229,7 @@ void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) } } -void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, +void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, float depth_value) { DXFramebuffer* dxfb = static_cast(framebuffer); @@ -273,7 +248,7 @@ void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, } } -void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) +void Gfx::SetScissorRect(const MathUtil::Rectangle& rc) { if (m_state.scissor.left == rc.left && m_state.scissor.right == rc.right && m_state.scissor.top == rc.top && m_state.scissor.bottom == rc.bottom) @@ -288,7 +263,7 @@ void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) m_dirty_bits |= DirtyState_ScissorRect; } -void Renderer::SetTexture(u32 index, const AbstractTexture* texture) +void Gfx::SetTexture(u32 index, const AbstractTexture* texture) { const DXTexture* dxtex = static_cast(texture); if (m_state.textures[index].ptr == dxtex->GetSRVDescriptor().cpu_handle.ptr) @@ -301,7 +276,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) m_dirty_bits |= DirtyState_Textures; } -void Renderer::SetSamplerState(u32 index, const SamplerState& state) +void Gfx::SetSamplerState(u32 index, const SamplerState& state) { if (m_state.samplers.states[index] == state) return; @@ -310,7 +285,7 @@ void Renderer::SetSamplerState(u32 index, const SamplerState& state) m_dirty_bits |= DirtyState_Samplers; } -void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) +void Gfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) { const DXTexture* dxtex = static_cast(texture); if (m_state.compute_image_texture == dxtex) @@ -323,7 +298,7 @@ void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool m_dirty_bits |= DirtyState_ComputeImageTexture; } -void Renderer::UnbindTexture(const AbstractTexture* texture) +void Gfx::UnbindTexture(const AbstractTexture* texture) { const auto srv_shadow_descriptor = static_cast(texture)->GetSRVDescriptor().cpu_handle; @@ -342,7 +317,7 @@ void Renderer::UnbindTexture(const AbstractTexture* texture) } } -void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, +void Gfx::SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) { if (m_state.viewport.TopLeftX == x && m_state.viewport.TopLeftY == y && @@ -361,7 +336,7 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne m_dirty_bits |= DirtyState_Viewport; } -void Renderer::Draw(u32 base_vertex, u32 num_vertices) +void Gfx::Draw(u32 base_vertex, u32 num_vertices) { if (!ApplyState()) return; @@ -369,7 +344,7 @@ void Renderer::Draw(u32 base_vertex, u32 num_vertices) g_dx_context->GetCommandList()->DrawInstanced(num_vertices, 1, base_vertex, 0); } -void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) +void Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) { if (!ApplyState()) return; @@ -381,7 +356,7 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) g_dx_context->GetCommandList()->DrawIndexedInstanced(num_indices, 1, base_index, base_vertex, 0); } -void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, +void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { SetRootSignatures(); @@ -412,13 +387,13 @@ void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groupsize m_dirty_bits |= DirtyState_Pipeline; } -void Renderer::BindBackbuffer(const ClearColor& clear_color) +void Gfx::BindBackbuffer(const ClearColor& clear_color) { CheckForSwapChainChanges(); SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(), clear_color); } -void Renderer::CheckForSwapChainChanges() +void Gfx::CheckForSwapChainChanges() { const bool surface_changed = g_presenter->SurfaceChangedTestAndClear(); const bool surface_resized = @@ -437,11 +412,10 @@ void Renderer::CheckForSwapChainChanges() m_swap_chain->ResizeSwapChain(); } - m_backbuffer_width = m_swap_chain->GetWidth(); - m_backbuffer_height = m_swap_chain->GetHeight(); + g_presenter->SetBackbuffer(m_swap_chain->GetWidth(), m_swap_chain->GetHeight()); } -void Renderer::PresentBackbuffer() +void Gfx::PresentBackbuffer() { m_current_framebuffer = nullptr; @@ -451,10 +425,18 @@ void Renderer::PresentBackbuffer() m_swap_chain->Present(); } -void Renderer::OnConfigChanged(u32 bits) +SurfaceInfo Gfx::GetSurfaceInfo() const { - ::Renderer::OnConfigChanged(bits); + return { + m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, + m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, + m_backbuffer_scale, + m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined + }; +} +void Gfx::OnConfigChanged(u32 bits) +{ // For quad-buffered stereo we need to change the layer count, so recreate the swap chain. if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE) { @@ -475,14 +457,14 @@ void Renderer::OnConfigChanged(u32 bits) g_dx_context->RecreateGXRootSignature(); } -void Renderer::ExecuteCommandList(bool wait_for_completion) +void Gfx::ExecuteCommandList(bool wait_for_completion) { PerfQuery::GetInstance()->ResolveQueries(); g_dx_context->ExecuteCommandList(wait_for_completion); m_dirty_bits = DirtyState_All; } -void Renderer::SetConstantBuffer(u32 index, D3D12_GPU_VIRTUAL_ADDRESS address) +void Gfx::SetConstantBuffer(u32 index, D3D12_GPU_VIRTUAL_ADDRESS address) { if (m_state.constant_buffers[index] == address) return; @@ -491,7 +473,7 @@ void Renderer::SetConstantBuffer(u32 index, D3D12_GPU_VIRTUAL_ADDRESS address) m_dirty_bits |= DirtyState_PS_CBV << index; } -void Renderer::SetTextureDescriptor(u32 index, D3D12_CPU_DESCRIPTOR_HANDLE handle) +void Gfx::SetTextureDescriptor(u32 index, D3D12_CPU_DESCRIPTOR_HANDLE handle) { if (m_state.textures[index].ptr == handle.ptr) return; @@ -500,7 +482,7 @@ void Renderer::SetTextureDescriptor(u32 index, D3D12_CPU_DESCRIPTOR_HANDLE handl m_dirty_bits |= DirtyState_Textures; } -void Renderer::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle) +void Gfx::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle) { if (m_state.ps_uav.ptr == handle.ptr) return; @@ -509,7 +491,7 @@ void Renderer::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle) m_dirty_bits |= DirtyState_PS_UAV; } -void Renderer::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESCRIPTOR_HANDLE srv, +void Gfx::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESCRIPTOR_HANDLE srv, u32 stride, u32 size) { if (m_state.vertex_buffer.BufferLocation != address || @@ -527,7 +509,7 @@ void Renderer::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESC } } -void Renderer::SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format) +void Gfx::SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format) { if (m_state.index_buffer.BufferLocation == address && m_state.index_buffer.SizeInBytes == size && m_state.index_buffer.Format == format) @@ -541,7 +523,7 @@ void Renderer::SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_ m_dirty_bits |= DirtyState_IndexBuffer; } -bool Renderer::ApplyState() +bool Gfx::ApplyState() { if (!m_current_framebuffer || !m_current_pipeline) return false; @@ -637,7 +619,7 @@ bool Renderer::ApplyState() return true; } -void Renderer::SetRootSignatures() +void Gfx::SetRootSignatures() { const u32 dirty_bits = m_dirty_bits; if (dirty_bits & DirtyState_RootSignature) @@ -650,7 +632,7 @@ void Renderer::SetRootSignatures() m_dirty_bits &= ~(DirtyState_RootSignature | DirtyState_ComputeRootSignature); } -void Renderer::SetDescriptorHeaps() +void Gfx::SetDescriptorHeaps() { if (m_dirty_bits & DirtyState_DescriptorHeaps) { @@ -660,7 +642,7 @@ void Renderer::SetDescriptorHeaps() } } -void Renderer::UpdateDescriptorTables() +void Gfx::UpdateDescriptorTables() { // Samplers force a full sync because any of the samplers could be in use. const bool texture_update_failed = @@ -684,7 +666,7 @@ void Renderer::UpdateDescriptorTables() } } -bool Renderer::UpdateSRVDescriptorTable() +bool Gfx::UpdateSRVDescriptorTable() { static constexpr std::array src_sizes = {1, 1, 1, 1, 1, 1, 1, 1}; DescriptorHandle dst_base_handle; @@ -700,7 +682,7 @@ bool Renderer::UpdateSRVDescriptorTable() return true; } -bool Renderer::UpdateSamplerDescriptorTable() +bool Gfx::UpdateSamplerDescriptorTable() { if (!g_dx_context->GetSamplerAllocator()->GetGroupHandle(m_state.samplers, &m_state.sampler_descriptor_base)) @@ -713,7 +695,7 @@ bool Renderer::UpdateSamplerDescriptorTable() return true; } -bool Renderer::UpdateUAVDescriptorTable() +bool Gfx::UpdateUAVDescriptorTable() { // We can skip writing the UAV descriptor if bbox isn't enabled, since it's not used otherwise. if (!g_ActiveConfig.bBBoxEnable) @@ -730,7 +712,7 @@ bool Renderer::UpdateUAVDescriptorTable() return true; } -bool Renderer::UpdateVSSRVDescriptorTable() +bool Gfx::UpdateVSSRVDescriptorTable() { if (!UsesDynamicVertexLoader(m_current_pipeline)) { @@ -748,7 +730,7 @@ bool Renderer::UpdateVSSRVDescriptorTable() return true; } -bool Renderer::UpdateComputeUAVDescriptorTable() +bool Gfx::UpdateComputeUAVDescriptorTable() { DescriptorHandle handle; if (!g_dx_context->GetDescriptorAllocator()->Allocate(1, &handle)) diff --git a/Source/Core/VideoBackends/D3D12/D3D12Renderer.h b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h similarity index 92% rename from Source/Core/VideoBackends/D3D12/D3D12Renderer.h rename to Source/Core/VideoBackends/D3D12/D3D12Gfx.h index 19188a5acf..26dc669ffc 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Renderer.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h @@ -6,9 +6,7 @@ #include #include "VideoBackends/D3D12/DescriptorAllocator.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" -#include "VideoCommon/RenderBase.h" - -class BoundingBox; +#include "VideoCommon/AbstractGfx.h" namespace DX12 { @@ -18,19 +16,16 @@ class DXShader; class DXPipeline; class SwapChain; -class Renderer final : public ::Renderer +class Gfx final : public ::AbstractGfx { public: - Renderer(std::unique_ptr swap_chain, float backbuffer_scale); - ~Renderer() override; + Gfx(std::unique_ptr swap_chain, float backbuffer_scale); + ~Gfx() override; - static Renderer* GetInstance() { return static_cast(g_renderer.get()); } + static Gfx* GetInstance() { return static_cast(g_gfx.get()); } bool IsHeadless() const override; - bool Initialize() override; - void Shutdown() override; - std::unique_ptr CreateTexture(const TextureConfig& config, std::string_view name) override; std::unique_ptr @@ -52,8 +47,8 @@ public: void Flush() override; void WaitForGPUIdle() override; - void ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; @@ -74,6 +69,8 @@ public: void BindBackbuffer(const ClearColor& clear_color = {}) override; void PresentBackbuffer() override; + SurfaceInfo GetSurfaceInfo() const override; + // Completes the current render pass, executes the command buffer, and restores state ready for // next render. Use when you want to kick the current buffer to make room for new data. void ExecuteCommandList(bool wait_for_completion); @@ -98,8 +95,6 @@ public: protected: void OnConfigChanged(u32 bits) override; - std::unique_ptr CreateBoundingBox() const override; - private: static const u32 MAX_TEXTURES = 8; static const u32 NUM_CONSTANT_BUFFERS = 3; @@ -152,6 +147,8 @@ private: bool UpdateComputeUAVDescriptorTable(); bool UpdateSamplerDescriptorTable(); + float m_backbuffer_scale; + // Owned objects std::unique_ptr m_swap_chain; diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp index 06f6f9dbcc..a6c466b646 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp @@ -9,9 +9,10 @@ #include "Common/Logging/Log.h" #include "VideoBackends/D3D12/Common.h" -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/DX12Context.h" #include "VideoCommon/VideoCommon.h" +#include "VideoCommon/RenderBase.h" namespace DX12 { @@ -64,7 +65,7 @@ void PerfQuery::EnableQuery(PerfQueryGroup group) // This is because we can't leave a query open when submitting a command list, and the draw // call itself may need to execute a command list if we run out of descriptors. Note that // this assumes that the caller has bound all required state prior to enabling the query. - Renderer::GetInstance()->ApplyState(); + Gfx::GetInstance()->ApplyState(); if (group == PQG_ZCOMP_ZCOMPLOC || group == PQG_ZCOMP) { @@ -261,7 +262,7 @@ void PerfQuery::PartialFlush(bool resolve, bool blocking) { // Submit a command buffer if there are unresolved queries (to write them to the buffer). if (resolve && m_unresolved_queries > 0) - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); ReadbackQueries(blocking); } diff --git a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp index 3ea1008413..55f13630a9 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp @@ -10,7 +10,7 @@ #include "Core/System.h" -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/D3D12StreamBuffer.h" #include "VideoBackends/D3D12/DX12Context.h" @@ -92,7 +92,7 @@ void VertexManager::ResetBuffer(u32 vertex_stride) { // Flush any pending commands first, so that we can wait on the fences WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in vertex/index buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); // Attempt to allocate again, this may cause a fence wait if (!has_vbuffer_allocation) @@ -129,10 +129,10 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in ADDSTAT(g_stats.this_frame.bytes_vertex_streamed, static_cast(vertex_data_size)); ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast(index_data_size)); - Renderer::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), + Gfx::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), m_vertex_srv.cpu_handle, vertex_stride, m_vertex_stream_buffer.GetSize()); - Renderer::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), + Gfx::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT); } @@ -151,7 +151,7 @@ void VertexManager::UpdateVertexShaderConstants() if (!vertex_shader_manager.dirty || !ReserveConstantStorage()) return; - Renderer::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer()); std::memcpy(m_uniform_stream_buffer.GetCurrentHostPointer(), &vertex_shader_manager.constants, sizeof(VertexShaderConstants)); m_uniform_stream_buffer.CommitMemory(sizeof(VertexShaderConstants)); @@ -167,7 +167,7 @@ void VertexManager::UpdateGeometryShaderConstants() if (!geometry_shader_manager.dirty || !ReserveConstantStorage()) return; - Renderer::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer()); std::memcpy(m_uniform_stream_buffer.GetCurrentHostPointer(), &geometry_shader_manager.constants, sizeof(GeometryShaderConstants)); m_uniform_stream_buffer.CommitMemory(sizeof(GeometryShaderConstants)); @@ -183,7 +183,7 @@ void VertexManager::UpdatePixelShaderConstants() if (!pixel_shader_manager.dirty || !ReserveConstantStorage()) return; - Renderer::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer()); std::memcpy(m_uniform_stream_buffer.GetCurrentHostPointer(), &pixel_shader_manager.constants, sizeof(PixelShaderConstants)); m_uniform_stream_buffer.CommitMemory(sizeof(PixelShaderConstants)); @@ -204,7 +204,7 @@ bool VertexManager::ReserveConstantStorage() // The only places that call constant updates are safe to have state restored. WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in uniform buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); // Since we are on a new command buffer, all constants have been invalidated, and we need // to reupload them. We may as well do this now, since we're issuing a draw anyway. @@ -234,11 +234,11 @@ void VertexManager::UploadAllConstants() } // Update bindings - Renderer::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer() + + Gfx::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer() + pixel_constants_offset); - Renderer::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer() + + Gfx::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer() + vertex_constants_offset); - Renderer::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer() + + Gfx::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer() + geometry_constants_offset); auto& system = Core::System::GetInstance(); @@ -271,12 +271,12 @@ void VertexManager::UploadUtilityUniforms(const void* data, u32 data_size) D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) { WARN_LOG_FMT(VIDEO, "Executing command buffer while waiting for ext space in uniform buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); } - Renderer::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer()); - Renderer::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer()); - Renderer::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer()); + Gfx::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer()); std::memcpy(m_uniform_stream_buffer.GetCurrentHostPointer(), data, data_size); m_uniform_stream_buffer.CommitMemory(data_size); ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, data_size); @@ -293,7 +293,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff { // Try submitting cmdbuffer. WARN_LOG_FMT(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); if (!m_texel_stream_buffer.ReserveMemory(data_size, elem_size)) { PanicAlertFmt("Failed to allocate {} bytes from texel buffer", data_size); @@ -305,7 +305,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff *out_offset = static_cast(m_texel_stream_buffer.GetCurrentOffset()) / elem_size; m_texel_stream_buffer.CommitMemory(data_size); ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, data_size); - Renderer::GetInstance()->SetTextureDescriptor(0, m_texel_buffer_views[format].cpu_handle); + Gfx::GetInstance()->SetTextureDescriptor(0, m_texel_buffer_views[format].cpu_handle); return true; } @@ -323,7 +323,7 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff { // Try submitting cmdbuffer. WARN_LOG_FMT(VIDEO, "Submitting command buffer while waiting for space in texel buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); if (!m_texel_stream_buffer.ReserveMemory(reserve_size, elem_size)) { PanicAlertFmt("Failed to allocate {} bytes from texel buffer", reserve_size); @@ -342,8 +342,8 @@ bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBuff m_texel_stream_buffer.CommitMemory(palette_byte_offset + palette_size); ADDSTAT(g_stats.this_frame.bytes_uniform_streamed, palette_byte_offset + palette_size); - Renderer::GetInstance()->SetTextureDescriptor(0, m_texel_buffer_views[format].cpu_handle); - Renderer::GetInstance()->SetTextureDescriptor(1, m_texel_buffer_views[palette_format].cpu_handle); + Gfx::GetInstance()->SetTextureDescriptor(0, m_texel_buffer_views[format].cpu_handle); + Gfx::GetInstance()->SetTextureDescriptor(1, m_texel_buffer_views[palette_format].cpu_handle); return true; } diff --git a/Source/Core/VideoBackends/D3D12/DX12Texture.cpp b/Source/Core/VideoBackends/D3D12/DX12Texture.cpp index 31951b1e95..1c5db02687 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Texture.cpp +++ b/Source/Core/VideoBackends/D3D12/DX12Texture.cpp @@ -8,7 +8,7 @@ #include "Common/StringUtil.h" #include "VideoBackends/D3D12/Common.h" -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/D3D12StreamBuffer.h" #include "VideoBackends/D3D12/DX12Context.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" @@ -254,7 +254,7 @@ void DXTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* { WARN_LOG_FMT(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Renderer::GetInstance()->ExecuteCommandList(false); + Gfx::GetInstance()->ExecuteCommandList(false); if (!g_dx_context->GetTextureUploadBuffer().ReserveMemory( upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) { @@ -632,7 +632,7 @@ void DXStagingTexture::Flush() // the current list and wait for it to complete. This is the slowest path. Otherwise, if the // command list with the copy has been submitted, we only need to wait for the fence. if (m_completed_fence == g_dx_context->GetCurrentFenceValue()) - Renderer::GetInstance()->ExecuteCommandList(true); + Gfx::GetInstance()->ExecuteCommandList(true); else g_dx_context->WaitForFence(m_completed_fence); } diff --git a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp index 209a74dfc9..dbf51bd7f8 100644 --- a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp +++ b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp @@ -11,8 +11,9 @@ #include "Core/ConfigManager.h" #include "VideoBackends/D3D12/Common.h" +#include "VideoBackends/D3D12/D3D12BoundingBox.h" #include "VideoBackends/D3D12/D3D12PerfQuery.h" -#include "VideoBackends/D3D12/D3D12Renderer.h" +#include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/D3D12SwapChain.h" #include "VideoBackends/D3D12/D3D12VertexManager.h" #include "VideoBackends/D3D12/DX12Context.h" @@ -111,7 +112,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) } FillBackendInfo(); - InitializeShared(); + UpdateActiveConfig(); if (!g_dx_context->CreateGlobalResources()) { @@ -131,31 +132,20 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) } // Create main wrapper instances. - g_renderer = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); - g_vertex_manager = std::make_unique(); - g_shader_cache = std::make_unique(); - g_framebuffer_manager = std::make_unique(); - g_texture_cache = std::make_unique(); - g_perf_query = std::make_unique(); + auto gfx = std::make_unique(std::move(swap_chain), wsi.render_surface_scale); + auto vertex_manager = std::make_unique(); + auto perf_query = std::make_unique(); + auto bounding_box = std::make_unique(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_renderer->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize() || !PerfQuery::GetInstance()->Initialize()) - { - PanicAlertFmtT("Failed to initialize renderer classes"); - Shutdown(); - return false; - } - - g_shader_cache->InitializeShaderCache(); - return true; + return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + std::move(bounding_box)); } void VideoBackend::Shutdown() { // Keep the debug runtime happy... - if (g_renderer) - Renderer::GetInstance()->ExecuteCommandList(true); + if (g_gfx) + Gfx::GetInstance()->ExecuteCommandList(true); ShutdownShared(); DXContext::Destroy(); From 35a69cb1bbce567eeae8fb8eece0062a782e436d Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 03:09:40 +1300 Subject: [PATCH 25/60] Fix missing include --- Source/Core/DolphinNoGUI/PlatformX11.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 3f3ea57bc6..18784499e4 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -19,6 +19,7 @@ static constexpr auto X_None = None; #include #include #include +#include #include #include From 99d3e489ea33fefd61715289959e852f69973117 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 03:40:15 +1300 Subject: [PATCH 26/60] Move BoundingBox out of RenderBase They were essentially just pass-though methods --- .../VideoBackends/Software/SWVertexLoader.cpp | 5 +- Source/Core/VideoCommon/AsyncRequests.cpp | 4 +- Source/Core/VideoCommon/BPStructs.cpp | 10 ++-- Source/Core/VideoCommon/BoundingBox.cpp | 12 ++++- Source/Core/VideoCommon/BoundingBox.h | 12 +++++ Source/Core/VideoCommon/PixelEngine.cpp | 3 +- Source/Core/VideoCommon/PixelShaderGen.cpp | 3 +- Source/Core/VideoCommon/RenderBase.cpp | 48 ------------------- Source/Core/VideoCommon/RenderBase.h | 23 --------- Source/Core/VideoCommon/VertexManagerBase.cpp | 4 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 2 - 11 files changed, 38 insertions(+), 88 deletions(-) diff --git a/Source/Core/VideoBackends/Software/SWVertexLoader.cpp b/Source/Core/VideoBackends/Software/SWVertexLoader.cpp index 9fbf560ecf..903b430aff 100644 --- a/Source/Core/VideoBackends/Software/SWVertexLoader.cpp +++ b/Source/Core/VideoBackends/Software/SWVertexLoader.cpp @@ -18,6 +18,7 @@ #include "VideoBackends/Software/Tev.h" #include "VideoBackends/Software/TransformUnit.h" +#include "VideoCommon/BoundingBox.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/DataReader.h" #include "VideoCommon/IndexGenerator.h" @@ -62,8 +63,8 @@ void SWVertexLoader::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_ } // Flush bounding box here because software overrides the base function - if (g_renderer->IsBBoxEnabled()) - g_renderer->BBoxFlush(); + if (g_bounding_box->IsEnabled()) + g_bounding_box->Flush(); m_setup_unit.Init(primitive_type); Rasterizer::SetTevKonstColors(); diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index aca5be57a4..4ab52845f9 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -6,6 +6,8 @@ #include #include "Core/System.h" + +#include "VideoCommon/BoundingBox.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" @@ -157,7 +159,7 @@ void AsyncRequests::HandleEvent(const AsyncRequests::Event& e) break; case Event::BBOX_READ: - *e.bbox.data = g_renderer->BBoxRead(e.bbox.index); + *e.bbox.data = g_bounding_box->Get(e.bbox.index); break; case Event::FIFO_RESET: diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 4e1114e87d..8979031d3d 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -285,7 +285,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, if (PE_copy.copy_to_xfb == 1) { // Make sure we disable Bounding box to match the side effects of the non-failure path - g_renderer->BBoxDisable(pixel_shader_manager); + g_bounding_box->Disable(pixel_shader_manager); } return; @@ -316,7 +316,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, // We should be able to get away with deactivating the current bbox tracking // here. Not sure if there's a better spot to put this. // the number of lines copied is determined by the y scale * source efb height - g_renderer->BBoxDisable(pixel_shader_manager); + g_bounding_box->Disable(pixel_shader_manager); float yScale; if (PE_copy.scale_invert) @@ -484,10 +484,10 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, case BPMEM_CLEARBBOX2: { const u8 offset = bp.address & 2; - g_renderer->BBoxEnable(pixel_shader_manager); + g_bounding_box->Enable(pixel_shader_manager); - g_renderer->BBoxWrite(offset, bp.newvalue & 0x3ff); - g_renderer->BBoxWrite(offset + 1, bp.newvalue >> 10); + g_bounding_box->Set(offset, bp.newvalue & 0x3ff); + g_bounding_box->Set(offset + 1, bp.newvalue >> 10); } return; case BPMEM_TEXINVALIDATE: diff --git a/Source/Core/VideoCommon/BoundingBox.cpp b/Source/Core/VideoCommon/BoundingBox.cpp index 0429559846..ffc85d824c 100644 --- a/Source/Core/VideoCommon/BoundingBox.cpp +++ b/Source/Core/VideoCommon/BoundingBox.cpp @@ -29,7 +29,7 @@ void BoundingBox::Disable(PixelShaderManager& pixel_shader_manager) void BoundingBox::Flush() { - if (!g_ActiveConfig.backend_info.bSupportsBBox) + if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) return; m_is_valid = false; @@ -76,6 +76,9 @@ u16 BoundingBox::Get(u32 index) { ASSERT(index < NUM_BBOX_VALUES); + if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) + return m_bounding_box_fallback[index]; + if (!m_is_valid) Readback(); @@ -86,6 +89,12 @@ void BoundingBox::Set(u32 index, u16 value) { ASSERT(index < NUM_BBOX_VALUES); + if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) + { + m_bounding_box_fallback[index] = value; + return; + } + if (m_is_valid && m_values[index] == value) return; @@ -98,6 +107,7 @@ void BoundingBox::Set(u32 index, u16 value) // Nonetheless, it has been designed to be as safe as possible. void BoundingBox::DoState(PointerWrap& p) { + p.DoArray(m_bounding_box_fallback); p.Do(m_is_active); p.DoArray(m_values); p.DoArray(m_dirty); diff --git a/Source/Core/VideoCommon/BoundingBox.h b/Source/Core/VideoCommon/BoundingBox.h index d8d2508716..ed4861d6ad 100644 --- a/Source/Core/VideoCommon/BoundingBox.h +++ b/Source/Core/VideoCommon/BoundingBox.h @@ -49,6 +49,18 @@ private: std::array m_values = {}; std::array m_dirty = {}; bool m_is_valid = true; + + // Nintendo's SDK seems to write "default" bounding box values before every draw (1023 0 1023 0 + // are the only values encountered so far, which happen to be the extents allowed by the BP + // registers) to reset the registers for comparison in the pixel engine, and presumably to detect + // whether GX has updated the registers with real values. + // + // We can store these values when Bounding Box emulation is disabled and return them on read, + // which the game will interpret as "no pixels have been drawn" + // + // This produces much better results than just returning garbage, which can cause games like + // Ultimate Spider-Man to crash + std::array m_bounding_box_fallback = {}; }; extern std::unique_ptr g_bounding_box; diff --git a/Source/Core/VideoCommon/PixelEngine.cpp b/Source/Core/VideoCommon/PixelEngine.cpp index 82464339e4..9817e38c07 100644 --- a/Source/Core/VideoCommon/PixelEngine.cpp +++ b/Source/Core/VideoCommon/PixelEngine.cpp @@ -20,7 +20,6 @@ #include "VideoCommon/BoundingBox.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/PerfQueryBase.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" namespace PixelEngine @@ -148,7 +147,7 @@ void PixelEngineManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) { mmio->Register(base | (PE_BBOX_LEFT + 2 * i), MMIO::ComplexRead([i](Core::System& system, u32) { - g_renderer->BBoxDisable(system.GetPixelShaderManager()); + g_bounding_box->Disable(system.GetPixelShaderManager()); return g_video_backend->Video_GetBoundingBox(i); }), MMIO::InvalidWrite()); diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 1d7cc375d6..aa81601362 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -16,7 +16,6 @@ #include "VideoCommon/DriverDetails.h" #include "VideoCommon/LightingShaderGen.h" #include "VideoCommon/NativeVertexFormat.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VideoCommon.h" @@ -180,7 +179,7 @@ PixelShaderUid GetPixelShaderUid() uid_data->genMode_numindstages = bpmem.genMode.numindstages; uid_data->genMode_numtevstages = bpmem.genMode.numtevstages; uid_data->genMode_numtexgens = bpmem.genMode.numtexgens; - uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && g_renderer->IsBBoxEnabled(); + uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && g_bounding_box->IsEnabled(); uid_data->rgba6_format = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor; uid_data->dither = bpmem.blendmode.dither && uid_data->rgba6_format; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 159dd6baaf..1bd0420a48 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -95,11 +95,6 @@ bool Renderer::Initialize() return true; } -void Renderer::Shutdown() -{ - g_bounding_box.reset(); -} - void Renderer::BeginUtilityDrawing() { g_vertex_manager->Flush(); @@ -151,48 +146,6 @@ void Renderer::ReinterpretPixelData(EFBReinterpretType convtype) g_framebuffer_manager->ReinterpretPixelData(convtype); } -bool Renderer::IsBBoxEnabled() const -{ - return g_bounding_box->IsEnabled(); -} - -void Renderer::BBoxEnable(PixelShaderManager& pixel_shader_manager) -{ - g_bounding_box->Enable(pixel_shader_manager); -} - -void Renderer::BBoxDisable(PixelShaderManager& pixel_shader_manager) -{ - g_bounding_box->Disable(pixel_shader_manager); -} - -u16 Renderer::BBoxRead(u32 index) -{ - if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) - return m_bounding_box_fallback[index]; - - return g_bounding_box->Get(index); -} - -void Renderer::BBoxWrite(u32 index, u16 value) -{ - if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) - { - m_bounding_box_fallback[index] = value; - return; - } - - g_bounding_box->Set(index, value); -} - -void Renderer::BBoxFlush() -{ - if (!g_ActiveConfig.bBBoxEnable || !g_ActiveConfig.backend_info.bSupportsBBox) - return; - - g_bounding_box->Flush(); -} - u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) { if (type == EFBAccessType::PeekColor) @@ -716,7 +669,6 @@ void Renderer::DoState(PointerWrap& p) p.Do(m_last_xfb_width); p.Do(m_last_xfb_stride); p.Do(m_last_xfb_height); - p.DoArray(m_bounding_box_fallback); g_bounding_box->DoState(p); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 6a9d86387c..6776110492 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -33,7 +33,6 @@ class AbstractPipeline; class AbstractShader; class AbstractTexture; class AbstractStagingTexture; -class BoundingBox; class NativeVertexFormat; class PixelShaderManager; class PointerWrap; @@ -68,7 +67,6 @@ public: virtual ~Renderer(); virtual bool Initialize(); - virtual void Shutdown(); void BeginUtilityDrawing(); void EndUtilityDrawing(); @@ -99,13 +97,6 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - bool IsBBoxEnabled() const; - void BBoxEnable(PixelShaderManager& pixel_shader_manager); - void BBoxDisable(PixelShaderManager& pixel_shader_manager); - u16 BBoxRead(u32 index); - void BBoxWrite(u32 index, u16 value); - void BBoxFlush(); - // Finish up the current frame, print some stats void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); @@ -152,20 +143,6 @@ private: u32 m_last_xfb_stride = 0; u32 m_last_xfb_height = 0; - std::unique_ptr m_bounding_box; - - // Nintendo's SDK seems to write "default" bounding box values before every draw (1023 0 1023 0 - // are the only values encountered so far, which happen to be the extents allowed by the BP - // registers) to reset the registers for comparison in the pixel engine, and presumably to detect - // whether GX has updated the registers with real values. - // - // We can store these values when Bounding Box emulation is disabled and return them on read, - // which the game will interpret as "no pixels have been drawn" - // - // This produces much better results than just returning garbage, which can cause games like - // Ultimate Spider-Man to crash - std::array m_bounding_box_fallback = {}; - Common::Flag m_force_reload_textures; GraphicsModManager m_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 5f6b8a310e..71e766c000 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -324,10 +324,10 @@ void VertexManagerBase::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 nu void VertexManagerBase::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex) { // If bounding box is enabled, we need to flush any changes first, then invalidate what we have. - if (g_renderer->IsBBoxEnabled() && g_ActiveConfig.bBBoxEnable && + if (g_bounding_box->IsEnabled() && g_ActiveConfig.bBBoxEnable && g_ActiveConfig.backend_info.bSupportsBBox) { - g_renderer->BBoxFlush(); + g_bounding_box->Flush(); } g_gfx->DrawIndexed(base_index, num_indices, base_vertex); diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 5119ba5ed1..144c215ad9 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -395,8 +395,6 @@ void VideoBackendBase::ShutdownShared() if (g_shader_cache) g_shader_cache->Shutdown(); - if (g_renderer) - g_renderer->Shutdown(); if (g_texture_cache) g_texture_cache->Shutdown(); From 55d15bdd6ea04c0f0862a1a23dc4859b3e30ad64 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 04:02:55 +1300 Subject: [PATCH 27/60] Move utiltily drawing out of RenderBase --- Source/Core/VideoCommon/AbstractGfx.cpp | 9 +++++---- Source/Core/VideoCommon/RenderBase.cpp | 12 ------------ Source/Core/VideoCommon/RenderBase.h | 2 -- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp index 17c95b863a..74fb12ddbf 100644 --- a/Source/Core/VideoCommon/AbstractGfx.cpp +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -7,6 +7,7 @@ #include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/ShaderCache.h" @@ -22,14 +23,14 @@ bool AbstractGfx::IsHeadless() const void AbstractGfx::BeginUtilityDrawing() { - if (g_renderer) - g_renderer->BeginUtilityDrawing(); + g_vertex_manager->Flush(); } void AbstractGfx::EndUtilityDrawing() { - if (g_renderer) - g_renderer->EndUtilityDrawing(); + // Reset framebuffer/scissor/viewport. Pipeline will be reset at next draw. + g_framebuffer_manager->BindEFBFramebuffer(); + BPFunctions::SetScissorAndViewport(); } void AbstractGfx::SetFramebuffer(AbstractFramebuffer* framebuffer) diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 1bd0420a48..89e84d2d6e 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -95,18 +95,6 @@ bool Renderer::Initialize() return true; } -void Renderer::BeginUtilityDrawing() -{ - g_vertex_manager->Flush(); -} - -void Renderer::EndUtilityDrawing() -{ - // Reset framebuffer/scissor/viewport. Pipeline will be reset at next draw. - g_framebuffer_manager->BindEFBFramebuffer(); - BPFunctions::SetScissorAndViewport(); -} - bool Renderer::EFBHasAlphaChannel() const { return m_prev_efb_format == PixelFormat::RGBA6_Z24; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 6776110492..2a154c4f30 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -68,8 +68,6 @@ public: virtual bool Initialize(); - void BeginUtilityDrawing(); - void EndUtilityDrawing(); // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } From ca5ec13e138d60d2fbac114450527c654caed702 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 05:01:05 +1300 Subject: [PATCH 28/60] Move GraphicsMod out of RenderBase --- .../Runtime/GraphicsModManager.cpp | 26 +++++++++++++++ .../Runtime/GraphicsModManager.h | 4 +++ Source/Core/VideoCommon/RenderBase.cpp | 32 ++----------------- Source/Core/VideoCommon/RenderBase.h | 7 ---- Source/Core/VideoCommon/TextureCacheBase.cpp | 7 ++-- Source/Core/VideoCommon/VertexManagerBase.cpp | 3 +- .../Core/VideoCommon/VertexShaderManager.cpp | 5 +-- Source/Core/VideoCommon/VideoBackendBase.cpp | 8 +++-- 8 files changed, 47 insertions(+), 45 deletions(-) diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp index 7441f210fa..4b5f5dbd84 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp @@ -10,10 +10,15 @@ #include "Common/Logging/Log.h" #include "Common/VariantUtil.h" +#include "Core/ConfigManager.h" + #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" #include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h" #include "VideoCommon/TextureInfo.h" +#include "VideoCommon/VideoConfig.h" + +std::unique_ptr g_graphics_mod_manager; class GraphicsModManager::DecoratedAction final : public GraphicsModAction { @@ -64,6 +69,27 @@ private: GraphicsModConfig m_mod; }; +bool GraphicsModManager::Initialize() +{ + if (g_ActiveConfig.bGraphicMods) + { + // If a config change occurred in a previous session, + // remember the old change count value. By setting + // our current change count to the old value, we + // avoid loading the stale data when we + // check for config changes. + const u32 old_game_mod_changes = g_ActiveConfig.graphics_mod_config ? + g_ActiveConfig.graphics_mod_config->GetChangeCount() : + 0; + g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID()); + g_ActiveConfig.graphics_mod_config->Load(); + g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes); + g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); + } + + return true; +} + const std::vector& GraphicsModManager::GetProjectionActions(ProjectionType projection_type) const { diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h index 1775e3b0e3..035ee48a36 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h @@ -19,6 +19,8 @@ class GraphicsModGroupConfig; class GraphicsModManager { public: + bool Initialize(); + const std::vector& GetProjectionActions(ProjectionType projection_type) const; const std::vector& GetProjectionTextureActions(ProjectionType projection_type, @@ -52,3 +54,5 @@ private: std::unordered_set m_groups; }; + +extern std::unique_ptr g_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 89e84d2d6e..942006a07f 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -45,7 +45,7 @@ #include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FreeLookCamera.h" -#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/PixelEngine.h" @@ -74,27 +74,6 @@ Renderer::Renderer() Renderer::~Renderer() = default; -bool Renderer::Initialize() -{ - if (g_ActiveConfig.bGraphicMods) - { - // If a config change occurred in a previous session, - // remember the old change count value. By setting - // our current change count to the old value, we - // avoid loading the stale data when we - // check for config changes. - const u32 old_game_mod_changes = g_ActiveConfig.graphics_mod_config ? - g_ActiveConfig.graphics_mod_config->GetChangeCount() : - 0; - g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID()); - g_ActiveConfig.graphics_mod_config->Load(); - g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes); - m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config); - } - - return true; -} - bool Renderer::EFBHasAlphaChannel() const { return m_prev_efb_format == PixelFormat::RGBA6_Z24; @@ -338,7 +317,7 @@ void Renderer::CheckForConfigChanges() if (g_ActiveConfig.graphics_mod_config && (old_game_mod_changes != g_ActiveConfig.graphics_mod_config->GetChangeCount())) { - m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config); + g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); } // Update texture cache settings with any changed options. @@ -541,7 +520,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 if (g_ActiveConfig.bGraphicMods) { - m_graphics_mod_manager.EndOfFrame(); + g_graphics_mod_manager->EndOfFrame(); } g_framebuffer_manager->EndOfFrame(); @@ -675,8 +654,3 @@ void Renderer::DoState(PointerWrap& p) g_frame_dumper->DoState(p); #endif } - -const GraphicsModManager& Renderer::GetGraphicsModManager() const -{ - return m_graphics_mod_manager; -} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 2a154c4f30..c605177b00 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -25,7 +25,6 @@ #include "Common/CommonTypes.h" #include "Common/Flag.h" #include "Common/MathUtil.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/RenderState.h" class AbstractFramebuffer; @@ -66,8 +65,6 @@ public: Renderer(); virtual ~Renderer(); - virtual bool Initialize(); - // Ideal internal resolution - multiple of the native EFB resolution int GetTargetWidth() const { return m_target_width; } int GetTargetHeight() const { return m_target_height; } @@ -111,8 +108,6 @@ public: // Will forcibly reload all textures on the next swap void ForceReloadTextures(); - const GraphicsModManager& GetGraphicsModManager() const; - protected: std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); @@ -142,8 +137,6 @@ private: u32 m_last_xfb_height = 0; Common::Flag m_force_reload_textures; - - GraphicsModManager m_graphics_mod_manager; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index e991669950..21b58d931e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -40,6 +40,7 @@ #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/HiresTextures.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelShaderManager.h" @@ -1291,7 +1292,7 @@ TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info) GraphicsModActionData::TextureLoad texture_load{entry->texture_info_name}; for (const auto action : - g_renderer->GetGraphicsModManager().GetTextureLoadActions(entry->texture_info_name)) + g_graphics_mod_manager->GetTextureLoadActions(entry->texture_info_name)) { action->OnTextureLoad(&texture_load); } @@ -2210,7 +2211,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( info.m_texture_format = baseFormat; if (is_xfb_copy) { - for (const auto action : g_renderer->GetGraphicsModManager().GetXFBActions(info)) + for (const auto action : g_graphics_mod_manager->GetXFBActions(info)) { action->OnXFB(); } @@ -2219,7 +2220,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( { bool skip = false; GraphicsModActionData::EFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h}; - for (const auto action : g_renderer->GetGraphicsModManager().GetEFBActions(info)) + for (const auto action : g_graphics_mod_manager->GetEFBActions(info)) { action->OnEFB(&efb); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 71e766c000..959b080510 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -24,6 +24,7 @@ #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/OpcodeDecoding.h" @@ -556,7 +557,7 @@ void VertexManagerBase::Flush() bool skip = false; GraphicsModActionData::DrawStarted draw_started{&skip}; for (const auto action : - g_renderer->GetGraphicsModManager().GetDrawStartedActions(texture_name)) + g_graphics_mod_manager->GetDrawStartedActions(texture_name)) { action->OnDrawStarted(&draw_started); } diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index deda07fd65..3779051b6b 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -22,6 +22,7 @@ #include "VideoCommon/CPMemory.h" #include "VideoCommon/FreeLookCamera.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" @@ -387,14 +388,14 @@ void VertexShaderManager::SetConstants(const std::vector& textures) if (g_ActiveConfig.bGraphicMods) { for (const auto action : - g_renderer->GetGraphicsModManager().GetProjectionActions(xfmem.projection.type)) + g_graphics_mod_manager->GetProjectionActions(xfmem.projection.type)) { projection_actions.push_back(action); } for (const auto& texture : textures) { - for (const auto action : g_renderer->GetGraphicsModManager().GetProjectionTextureActions( + for (const auto action : g_graphics_mod_manager->GetProjectionTextureActions( xfmem.projection.type, texture)) { projection_actions.push_back(action); diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 144c215ad9..31bacc804c 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -49,6 +49,7 @@ #include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelEngine.h" @@ -357,6 +358,7 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, g_frame_dumper = std::make_unique(); g_framebuffer_manager = std::make_unique(); g_shader_cache = std::make_unique(); + g_graphics_mod_manager = std::make_unique(); auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); @@ -371,9 +373,9 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, TMEM::Init(); if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_perf_query->Initialize() || !g_renderer->Initialize() || - !g_presenter->Initialize() || !g_framebuffer_manager->Initialize() || - !g_texture_cache->Initialize() || !g_bounding_box->Initialize()) + !g_perf_query->Initialize() || !g_presenter->Initialize() || + !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || + !g_bounding_box->Initialize() || !g_graphics_mod_manager->Initialize()) { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); From 9d125a6e432d3fe050097dd2a5b13b13ec26e180 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 05:06:25 +1300 Subject: [PATCH 29/60] Move ConfigChanged out of RenderBase There is this nice VideoConfig file that's perfect for it --- .../VideoBackends/Software/SWRenderer.cpp | 1 + Source/Core/VideoCommon/AbstractGfx.h | 13 -- Source/Core/VideoCommon/RenderBase.cpp | 104 ---------------- Source/Core/VideoCommon/RenderBase.h | 5 +- Source/Core/VideoCommon/VideoConfig.cpp | 114 ++++++++++++++++++ Source/Core/VideoCommon/VideoConfig.h | 15 +++ 6 files changed, 132 insertions(+), 120 deletions(-) diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 7440fba1b3..d537820c95 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -6,6 +6,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/MsgHandler.h" #include "Core/HW/Memmap.h" #include "Core/System.h" diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index d34690ec01..bdbcdc1001 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -37,19 +37,6 @@ namespace VideoCommon class AsyncShaderCompiler; } -// Bitmask containing information about which configuration has changed for the backend. -enum ConfigChangeBits : u32 -{ - CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), - CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), - CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), - CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), - CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), - CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), - CONFIG_CHANGE_BIT_VSYNC = (1 << 6), - CONFIG_CHANGE_BIT_BBOX = (1 << 7) -}; - using ClearColor = std::array; class AbstractGfx diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 942006a07f..dea1cca349 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -44,7 +44,6 @@ #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" -#include "VideoCommon/FreeLookCamera.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PerformanceMetrics.h" @@ -52,7 +51,6 @@ #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" #include "VideoCommon/ShaderCache.h" -#include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -65,11 +63,7 @@ Renderer::Renderer() m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} { UpdateActiveConfig(); - FreeLook::UpdateActiveConfig(); CalculateTargetSize(); - - m_is_game_widescreen = SConfig::GetInstance().bWii && Config::Get(Config::SYSCONF_WIDESCREEN); - g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type); } Renderer::~Renderer() = default; @@ -288,104 +282,6 @@ bool Renderer::CalculateTargetSize() return false; } -void Renderer::CheckForConfigChanges() -{ - const ShaderHostConfig old_shader_host_config = ShaderHostConfig::GetCurrent(); - const StereoMode old_stereo = g_ActiveConfig.stereo_mode; - const u32 old_multisamples = g_ActiveConfig.iMultisamples; - const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; - const int old_efb_access_tile_size = g_ActiveConfig.iEFBAccessTileSize; - const auto old_texture_filtering_mode = g_ActiveConfig.texture_filtering_mode; - const bool old_vsync = g_ActiveConfig.bVSyncActive; - const bool old_bbox = g_ActiveConfig.bBBoxEnable; - const u32 old_game_mod_changes = - g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0; - const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods; - - UpdateActiveConfig(); - FreeLook::UpdateActiveConfig(); - g_vertex_manager->OnConfigChange(); - - g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type); - - if (g_ActiveConfig.bGraphicMods && !old_graphics_mods_enabled) - { - g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID()); - g_ActiveConfig.graphics_mod_config->Load(); - } - - if (g_ActiveConfig.graphics_mod_config && - (old_game_mod_changes != g_ActiveConfig.graphics_mod_config->GetChangeCount())) - { - g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); - } - - // Update texture cache settings with any changed options. - g_texture_cache->OnConfigChanged(g_ActiveConfig); - - // EFB tile cache doesn't need to notify the backend. - if (old_efb_access_tile_size != g_ActiveConfig.iEFBAccessTileSize) - g_framebuffer_manager->SetEFBCacheTileSize(std::max(g_ActiveConfig.iEFBAccessTileSize, 0)); - - // Determine which (if any) settings have changed. - ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); - u32 changed_bits = 0; - if (old_shader_host_config.bits != new_host_config.bits) - changed_bits |= CONFIG_CHANGE_BIT_HOST_CONFIG; - if (old_stereo != g_ActiveConfig.stereo_mode) - changed_bits |= CONFIG_CHANGE_BIT_STEREO_MODE; - if (old_multisamples != g_ActiveConfig.iMultisamples) - changed_bits |= CONFIG_CHANGE_BIT_MULTISAMPLES; - if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) - changed_bits |= CONFIG_CHANGE_BIT_ANISOTROPY; - if (old_texture_filtering_mode != g_ActiveConfig.texture_filtering_mode) - changed_bits |= CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING; - if (old_vsync != g_ActiveConfig.bVSyncActive) - changed_bits |= CONFIG_CHANGE_BIT_VSYNC; - if (old_bbox != g_ActiveConfig.bBBoxEnable) - changed_bits |= CONFIG_CHANGE_BIT_BBOX; - if (CalculateTargetSize()) - changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; - - g_presenter->CheckForConfigChanges(changed_bits); - - // No changes? - if (changed_bits == 0) - return; - - // Notify the backend of the changes, if any. - g_gfx->OnConfigChanged(changed_bits); - - // If there's any shader changes, wait for the GPU to finish before destroying anything. - if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) - { - g_gfx->WaitForGPUIdle(); - g_gfx->SetPipeline(nullptr); - } - - // Framebuffer changed? - if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | - CONFIG_CHANGE_BIT_TARGET_SIZE)) - { - g_framebuffer_manager->RecreateEFBFramebuffer(); - } - - // Reload shaders if host config has changed. - if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) - { - OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); - g_vertex_manager->InvalidatePipelineObject(); - g_shader_cache->SetHostConfig(new_host_config); - g_shader_cache->Reload(); - g_framebuffer_manager->RecompileShaders(); - } - - // Viewport and scissor rect have to be reset since they will be scaled differently. - if (changed_bits & CONFIG_CHANGE_BIT_TARGET_SIZE) - { - BPFunctions::SetScissorAndViewport(); - } -} MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle& rc) const { diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index c605177b00..054516ae01 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -108,11 +108,10 @@ public: // Will forcibly reload all textures on the next swap void ForceReloadTextures(); -protected: - std::tuple CalculateTargetScale(int x, int y) const; bool CalculateTargetSize(); - void CheckForConfigChanges(); +protected: + std::tuple CalculateTargetScale(int x, int y) const; void CheckFifoRecording(); void RecordVideoMemory(); diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index f37d8e9595..8b8a73e6cf 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -8,12 +8,27 @@ #include "Common/CPUDetect.h" #include "Common/CommonTypes.h" #include "Common/StringUtil.h" + #include "Core/Config/GraphicsSettings.h" #include "Core/Config/MainSettings.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Movie.h" + +#include "VideoCommon/AbstractGfx.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/DriverDetails.h" +#include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/FreeLookCamera.h" +#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/Present.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/ShaderGenCommon.h" +#include "VideoCommon/TextureCacheBase.h" +#include "VideoCommon/VertexManagerBase.h" + #include "VideoCommon/VideoCommon.h" VideoConfig g_Config; @@ -227,3 +242,102 @@ u32 VideoConfig::GetShaderPrecompilerThreads() const else return 1; } + +void CheckForConfigChanges() +{ + const ShaderHostConfig old_shader_host_config = ShaderHostConfig::GetCurrent(); + const StereoMode old_stereo = g_ActiveConfig.stereo_mode; + const u32 old_multisamples = g_ActiveConfig.iMultisamples; + const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; + const int old_efb_access_tile_size = g_ActiveConfig.iEFBAccessTileSize; + const auto old_texture_filtering_mode = g_ActiveConfig.texture_filtering_mode; + const bool old_vsync = g_ActiveConfig.bVSyncActive; + const bool old_bbox = g_ActiveConfig.bBBoxEnable; + const u32 old_game_mod_changes = + g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0; + const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods; + + UpdateActiveConfig(); + FreeLook::UpdateActiveConfig(); + g_vertex_manager->OnConfigChange(); + + g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type); + + if (g_ActiveConfig.bGraphicMods && !old_graphics_mods_enabled) + { + g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID()); + g_ActiveConfig.graphics_mod_config->Load(); + } + + if (g_ActiveConfig.graphics_mod_config && + (old_game_mod_changes != g_ActiveConfig.graphics_mod_config->GetChangeCount())) + { + g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); + } + + // Update texture cache settings with any changed options. + g_texture_cache->OnConfigChanged(g_ActiveConfig); + + // EFB tile cache doesn't need to notify the backend. + if (old_efb_access_tile_size != g_ActiveConfig.iEFBAccessTileSize) + g_framebuffer_manager->SetEFBCacheTileSize(std::max(g_ActiveConfig.iEFBAccessTileSize, 0)); + + // Determine which (if any) settings have changed. + ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); + u32 changed_bits = 0; + if (old_shader_host_config.bits != new_host_config.bits) + changed_bits |= CONFIG_CHANGE_BIT_HOST_CONFIG; + if (old_stereo != g_ActiveConfig.stereo_mode) + changed_bits |= CONFIG_CHANGE_BIT_STEREO_MODE; + if (old_multisamples != g_ActiveConfig.iMultisamples) + changed_bits |= CONFIG_CHANGE_BIT_MULTISAMPLES; + if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + changed_bits |= CONFIG_CHANGE_BIT_ANISOTROPY; + if (old_texture_filtering_mode != g_ActiveConfig.texture_filtering_mode) + changed_bits |= CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING; + if (old_vsync != g_ActiveConfig.bVSyncActive) + changed_bits |= CONFIG_CHANGE_BIT_VSYNC; + if (old_bbox != g_ActiveConfig.bBBoxEnable) + changed_bits |= CONFIG_CHANGE_BIT_BBOX; + if (g_renderer->CalculateTargetSize()) + changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; + + g_presenter->CheckForConfigChanges(changed_bits); + + // No changes? + if (changed_bits == 0) + return; + + // Notify the backend of the changes, if any. + g_gfx->OnConfigChanged(changed_bits); + + // If there's any shader changes, wait for the GPU to finish before destroying anything. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) + { + g_gfx->WaitForGPUIdle(); + g_gfx->SetPipeline(nullptr); + } + + // Framebuffer changed? + if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | + CONFIG_CHANGE_BIT_TARGET_SIZE)) + { + g_framebuffer_manager->RecreateEFBFramebuffer(); + } + + // Reload shaders if host config has changed. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) + { + OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); + g_vertex_manager->InvalidatePipelineObject(); + g_shader_cache->SetHostConfig(new_host_config); + g_shader_cache->Reload(); + g_framebuffer_manager->RecompileShaders(); + } + + // Viewport and scissor rect have to be reset since they will be scaled differently. + if (changed_bits & CONFIG_CHANGE_BIT_TARGET_SIZE) + { + BPFunctions::SetScissorAndViewport(); + } +} diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 184e599a58..2c05a6d4ae 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -59,6 +59,20 @@ enum class TriState : int Auto }; + +// Bitmask containing information about which configuration has changed for the backend. +enum ConfigChangeBits : u32 +{ + CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0), + CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1), + CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2), + CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3), + CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), + CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), + CONFIG_CHANGE_BIT_VSYNC = (1 << 6), + CONFIG_CHANGE_BIT_BBOX = (1 << 7) +}; + // NEVER inherit from this class. struct VideoConfig final { @@ -307,3 +321,4 @@ extern VideoConfig g_ActiveConfig; // Called every frame. void UpdateActiveConfig(); +void CheckForConfigChanges(); From d6cd8de1a7c28629ee7680ee20082608b5f84a29 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 05:06:47 +1300 Subject: [PATCH 30/60] Delete unused EFBHasAlphaChannel function --- Source/Core/VideoCommon/RenderBase.cpp | 5 ----- Source/Core/VideoCommon/RenderBase.h | 1 - 2 files changed, 6 deletions(-) diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index dea1cca349..706e0ac58c 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -68,11 +68,6 @@ Renderer::Renderer() Renderer::~Renderer() = default; -bool Renderer::EFBHasAlphaChannel() const -{ - return m_prev_efb_format == PixelFormat::RGBA6_Z24; -} - void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 054516ae01..ea38b71b98 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -100,7 +100,6 @@ public: PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } - bool EFBHasAlphaChannel() const; bool UseVertexDepthRange() const; void DoState(PointerWrap& p); From 154cb4f722a9996a4134463e64c85c6bfa25d8bf Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 22:36:25 +1300 Subject: [PATCH 31/60] Introduce an Event system to VideoCommon A lot of the remaining complexity in Renderer is the massive Swap function which tries to handle a bunch of FrameBegin/FrameEnd events. Rather than create a new place for it. This event system will try to distribute it all over the place --- Source/Core/Common/CMakeLists.txt | 2 + Source/Core/Common/EventHook.h | 79 ++++++++++++++++++++++++ Source/Core/Common/StringLiteral.h | 17 ++++++ Source/Core/DolphinLib.props | 3 + Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/VideoEvents.h | 84 ++++++++++++++++++++++++++ 6 files changed, 186 insertions(+) create mode 100644 Source/Core/Common/EventHook.h create mode 100644 Source/Core/Common/StringLiteral.h create mode 100644 Source/Core/VideoCommon/VideoEvents.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 6e49f7b7a6..bab78766da 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -46,6 +46,7 @@ add_library(common EnumFormatter.h EnumMap.h Event.h + EventHook.h FatFsUtil.cpp FatFsUtil.h FileSearch.cpp @@ -115,6 +116,7 @@ add_library(common SocketContext.cpp SocketContext.h SPSCQueue.h + StringLiteral.h StringUtil.cpp StringUtil.h SymbolDB.cpp diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/EventHook.h new file mode 100644 index 0000000000..265013827a --- /dev/null +++ b/Source/Core/Common/EventHook.h @@ -0,0 +1,79 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/Logging/Log.h" +#include "Common/StringLiteral.h" + +#include +#include +#include +#include +#include + +// A hookable event system. + +// Define Events in a header as: +// +// using MyLoveyEvent = Event<"My lovely event", std::string>; +// +// Register listeners anywhere you need them as: +// EventHook myHook = MyLoveyEvent::Register([](std::string foo) { +// // Do something +// }, "Name of the hook"); +// +// The hook will be automatically unregistered when the EventHook object goes out of scope. +// Trigger events by doing: +// +// MyLoveyEvent::Trigger("Hello world"); +// + +struct HookBase +{ + virtual ~HookBase() = default; +}; + +using EventHook = std::unique_ptr; + +template +class Event +{ +public: + using CallbackType = std::function; + +private: + struct HookImpl : public HookBase + { + ~HookImpl() override { Event::Remove(this); } + HookImpl(CallbackType callback, std::string name) : m_fn(callback), m_name(name){ } + CallbackType m_fn; + std::string m_name; + }; +public: + + // Returns a handle that will unregister the listener when destroyed. + static EventHook Register(CallbackType callback, std::string name) + { + DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); + auto handle = std::make_unique(callback, name); + m_listeners.push_back(handle.get()); + return handle; + } + + static void Trigger(CallbackArgs... args) + { + for (auto& handle : m_listeners) + handle->m_fn(args...); + } + +private: + static void Remove(HookImpl* handle) + { + auto it = std::find(m_listeners.begin(), m_listeners.end(), handle); + if (it != m_listeners.end()) + m_listeners.erase(it); + } + + inline static std::vector m_listeners = {}; +}; diff --git a/Source/Core/Common/StringLiteral.h b/Source/Core/Common/StringLiteral.h new file mode 100644 index 0000000000..6227d48ed6 --- /dev/null +++ b/Source/Core/Common/StringLiteral.h @@ -0,0 +1,17 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +// A useful template for passing string literals as arguments to templates +// from: https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/ +template +struct StringLiteral { + consteval StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; +}; diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 9bf63bbb51..470d303e21 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -46,6 +46,7 @@ + @@ -145,6 +146,7 @@ + @@ -713,6 +715,7 @@ + diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 4507904afd..5428facc55 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -163,6 +163,7 @@ add_library(videocommon VertexShaderManager.h VideoBackendBase.cpp VideoBackendBase.h + VideoEvents.h VideoCommon.h VideoConfig.cpp VideoConfig.h diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h new file mode 100644 index 0000000000..bb95296ebd --- /dev/null +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -0,0 +1,84 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/EventHook.h" + + +// Called when certain video config setting are changed +using ConfigChangedEvent = Event<"ConfigChanged", u32>; + +// An event called just before the first draw call of a frame +using BeforeFrameEvent = Event<"BeforeFrame">; + +// An event called after the frame XFB copy begins processing on the host GPU. +// Useful for "once per frame" usecases. +// Note: In a few rare cases, games do multiple XFB copies per frame and join them while presenting. +// If this matters to your usecase, you should use BeforePresent instead. +using AfterFrameEvent = Event<"AfterFrame">; + +struct PresentInfo +{ + enum class PresentReason + { + Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy + VideoInterface, // VideoInterface has triggered a present with a new frame + VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame + }; + + // The number of (unique) frames since the emulated console booted + u64 frame_count; + + + // The number of presents since the video backend was initialized. + // never goes backwards. + u64 present_count; + + // The frame is identical to the previous frame + PresentReason reason; + + // The exact emulated time of the when real hardware would have presented this frame + // FIXME: Immediate should predict the timestamp of this present + u64 emulated_timestamp; + + // TODO: + // u64 intended_present_time; + + // AfterPresent only: The actual time the frame was presented + u64 actual_present_time = 0; + + enum class PresentTimeAccuracy + { + // The Driver/OS has given us an exact timestamp of when the first line of the frame started + // scanning out to the monitor + PresentOnScreenExact, + + // An approximate timestamp of scanout. + PresentOnScreen, + + // Dolphin doesn't have visibility of the present time. But the present operation has + // been queued with the GPU driver and will happen in the near future. + PresentInProgress, + + // Not implemented + Unimplemented, + }; + + // Accuracy of actual_present_time + PresentTimeAccuracy present_time_accuracy = PresentTimeAccuracy::Unimplemented; +}; + +// An event called just as a frame is queued for presentation. +// The exact timing of this event depends on the "Immediately Present XFB" option. +// +// If enabled, this event will trigger immediately after AfterFrame +// If disabled, this event won't trigger until the emulated interface starts drawing out a new frame. +// +// frame_count: The number of frames +using BeforePresentEvent = Event<"BeforePresent", PresentInfo&>; + +// An event that is triggered after a frame is presented. +// The exact timing of this event depends on backend/driver support. +using AfterPresentEvent = Event<"AfterPresent", PresentInfo&>; From 2a18b34a73f4f1bc798dab0a5eec46c222ec7429 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 30 Jan 2023 23:59:54 +1300 Subject: [PATCH 32/60] Wire up frame before/after events --- Source/Core/VideoCommon/AsyncRequests.cpp | 6 +- Source/Core/VideoCommon/BPStructs.cpp | 12 +- Source/Core/VideoCommon/Present.cpp | 123 ++++++++++++------ Source/Core/VideoCommon/Present.h | 14 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 6 + Source/Core/VideoCommon/VertexManagerBase.h | 1 + 6 files changed, 117 insertions(+), 45 deletions(-) diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index 4ab52845f9..8c254d4e71 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -9,12 +9,14 @@ #include "VideoCommon/BoundingBox.h" #include "VideoCommon/Fifo.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoState.h" +#include "VideoCommon/VideoEvents.h" AsyncRequests AsyncRequests::s_singleton; @@ -154,8 +156,8 @@ void AsyncRequests::HandleEvent(const AsyncRequests::Event& e) break; case Event::SWAP_EVENT: - g_renderer->Swap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, - e.swap_event.fbHeight, e.time); + g_presenter->ViSwap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, + e.swap_event.fbHeight, e.time); break; case Event::BBOX_READ: diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 8979031d3d..865d73dbed 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -31,6 +31,7 @@ #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PerfQueryBase.h" +#include "VideoCommon/Present.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/RenderBase.h" @@ -42,6 +43,7 @@ #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" using namespace BPFunctions; @@ -340,14 +342,16 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top, bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients()); - // This stays in to signal end of a "frame" - g_renderer->RenderToXFB(destAddr, srcRect, destStride, height, s_gammaLUT[PE_copy.gamma]); + // This is as closest as we have to an "end of the frame" + // It works 99% of the time. + AfterFrameEvent::Trigger(); + if (g_ActiveConfig.bImmediateXFB) { // below div two to convert from bytes to pixels - it expects width, not stride - g_renderer->Swap(destAddr, destStride / 2, destStride, height, - Core::System::GetInstance().GetCoreTiming().GetTicks()); + g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, + Core::System::GetInstance().GetCoreTiming().GetTicks()); } else { diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 159fe080af..69deb32e2c 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -16,6 +16,7 @@ #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" std::unique_ptr g_presenter; @@ -60,6 +61,88 @@ bool Presenter::Initialize() return true; } +bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height) +{ + ReleaseXFBContentLock(); + + m_xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); + + m_xfb_entry->AcquireContentLock(); + if (m_last_xfb_id == m_xfb_entry->id) + return false; + + m_last_xfb_id = m_xfb_entry->id; + + return true; +} + +void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) +{ + bool unique = FetchXFB(xfb_addr, fb_width, fb_stride, fb_height); + + PresentInfo present_info; + present_info.emulated_timestamp = ticks; + if (unique) + { + present_info.frame_count = m_frame_count++; + present_info.reason = PresentInfo::PresentReason::VideoInterface; + } + else + { + present_info.frame_count = m_frame_count - 1; + present_info.reason = PresentInfo::PresentReason::VideoInterfaceDuplicate; + } + present_info.present_count = m_present_count; + + BeforePresentEvent::Trigger(present_info); + + if (unique || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) + { + Present(); + ProcessFrameDumping(ticks); + } + else + { + g_gfx->Flush(); + } +} + +void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) +{ + FetchXFB(xfb_addr, fb_width, fb_stride, fb_height); + + PresentInfo present_info; + present_info.emulated_timestamp = ticks; + present_info.frame_count = m_frame_count++; + present_info.reason = PresentInfo::PresentReason::Immediate; + present_info.present_count = m_present_count++; + + Present(); + ProcessFrameDumping(ticks); +} + +void Presenter::ProcessFrameDumping(u64 ticks) const +{ + if (g_frame_dumper->IsFrameDumping()) + { + MathUtil::Rectangle target_rect; + if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) + { + target_rect = GetTargetRectangle(); + } + else + { + int width, height; + std::tie(width, height) = + CalculateOutputDimensions(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); + target_rect = MathUtil::Rectangle(0, 0, width, height); + } + + g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, + m_frame_count); + } +} + void Presenter::SetBackbuffer(int backbuffer_width, int backbuffer_height) { m_backbuffer_width = backbuffer_width; @@ -80,8 +163,7 @@ void Presenter::CheckForConfigChanges(u32 changed_bits) { // Check for post-processing shader changes. Done up here as it doesn't affect anything outside // the post-processor. Note that options are applied every frame, so no need to check those. - if (m_post_processor && - m_post_processor->GetConfig()->GetShader() != g_ActiveConfig.sPostProcessingShader) + if (changed_bits & ConfigChangeBits::CONFIG_CHANGE_BIT_POST_PROCESSING_SHADER && m_post_processor) { // The existing shader must not be in use when it's destroyed g_gfx->WaitForGPUIdle(); @@ -404,43 +486,10 @@ void Presenter::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, } } -bool Presenter::SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb_rect, u64 ticks, - int frame_count) -{ - m_xfb_entry = std::move(xfb_entry); - m_xfb_rect = xfb_rect; - bool is_duplicate_frame = m_last_xfb_id == m_xfb_entry->id; - m_last_xfb_id = m_xfb_entry->id; - - if (!is_duplicate_frame || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) - { - Present(); - - if (g_frame_dumper->IsFrameDumping()) - { - MathUtil::Rectangle target_rect; - if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) - { - target_rect = GetTargetRectangle(); - } - else - { - int width, height; - std::tie(width, height) = - CalculateOutputDimensions(m_xfb_rect.GetWidth(), m_xfb_rect.GetHeight()); - target_rect = MathUtil::Rectangle(0, 0, width, height); - } - - g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, - frame_count); - } - } - - return is_duplicate_frame; -} - void Presenter::Present() { + m_present_count++; + if (g_gfx->IsHeadless() || (!m_onscreen_ui && !m_xfb_entry)) return; diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 507c5ee424..4c870c93b0 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -31,8 +31,9 @@ public: Presenter(); virtual ~Presenter(); - bool SubmitXFB(RcTcacheEntry xfb_entry, MathUtil::Rectangle& xfb_rect, u64 ticks, - int frame_count); + void ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); + void ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); + void Present(); void ClearLastXfbId() { m_last_xfb_id = std::numeric_limits::max(); } @@ -86,6 +87,12 @@ public: const MathUtil::Rectangle& GetTargetRectangle() const { return m_target_rectangle; } private: + // Fetches the XFB texture from the texture cache. + // Returns true the contents have changed since last time + bool FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height); + + void ProcessFrameDumping(u64 ticks) const; + std::tuple CalculateOutputDimensions(int width, int height) const; std::tuple ApplyStandardAspectCrop(float width, float height) const; std::tuple ScaleToDisplayAspectRatio(int width, int height) const; @@ -120,6 +127,9 @@ private: std::unique_ptr m_post_processor; std::unique_ptr m_onscreen_ui; + + u64 m_frame_count = 0; + u64 m_present_count = 0; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 959b080510..34fe374d25 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -417,6 +417,12 @@ void VertexManagerBase::Flush() m_is_flushed = true; + if (m_draw_counter == 0) + { + // This is more or less the start of the Frame + BeforeFrameEvent::Trigger(); + } + if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens || xfmem.numChan.numColorChans != bpmem.genMode.numcolchans) { diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 3b8180c5d2..b2cb8b54bd 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -13,6 +13,7 @@ #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/ShaderCache.h" +#include "VideoCommon/VideoEvents.h" class DataReader; class NativeVertexFormat; From 0da69055d9f41d838bdf94b66805540c746eaa7d Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 00:46:10 +1300 Subject: [PATCH 33/60] Split out everying remaining from Swap --- Source/Core/Core/Core.cpp | 8 + Source/Core/Core/FifoPlayer/FifoRecorder.cpp | 46 ++++- Source/Core/Core/FifoPlayer/FifoRecorder.h | 5 + .../DynamicInputTextureManager.cpp | 6 +- Source/Core/VideoCommon/FrameDumper.cpp | 1 + Source/Core/VideoCommon/FrameDumper.h | 3 + .../Core/VideoCommon/FramebufferManager.cpp | 2 + Source/Core/VideoCommon/FramebufferManager.h | 3 + .../Runtime/GraphicsModManager.cpp | 2 + .../Runtime/GraphicsModManager.h | 6 +- Source/Core/VideoCommon/Present.cpp | 6 + Source/Core/VideoCommon/RenderBase.cpp | 177 +++--------------- Source/Core/VideoCommon/RenderBase.h | 24 ++- Source/Core/VideoCommon/ShaderCache.cpp | 1 + Source/Core/VideoCommon/ShaderCache.h | 3 + Source/Core/VideoCommon/Statistics.cpp | 17 ++ Source/Core/VideoCommon/TextureCacheBase.cpp | 17 ++ Source/Core/VideoCommon/TextureCacheBase.h | 11 ++ Source/Core/VideoCommon/VertexManagerBase.cpp | 7 + Source/Core/VideoCommon/VertexManagerBase.h | 2 + Source/Core/VideoCommon/VideoConfig.cpp | 5 + 21 files changed, 182 insertions(+), 170 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index e4d2af12ab..8a2d3185c9 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -91,6 +91,7 @@ #include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" +#include "VideoCommon/VideoEvents.h" #ifdef ANDROID #include "jni/AndroidCommon/IDCache.h" @@ -132,6 +133,13 @@ static thread_local bool tls_is_gpu_thread = false; static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi); +static EventHook s_frame_presented = AfterPresentEvent::Register([](auto& present_info) { + const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); + // The denominator should always be > 0 but if it's not, just return 1 + const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; + Core::Callback_FramePresented(last_speed); +}, "Core Frame Presented"); + bool GetIsThrottlerTempDisabled() { return s_is_throttler_temp_disabled; diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp index 0d12481420..0ec2232843 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp @@ -14,8 +14,13 @@ #include "Core/HW/Memmap.h" #include "Core/System.h" +#include "VideoCommon/BPMemory.h" +#include "VideoCommon/CommandProcessor.h" #include "VideoCommon/OpcodeDecoding.h" +#include "VideoCommon/TextureDecoder.h" #include "VideoCommon/XFStructs.h" +#include "VideoCommon/XFMemory.h" +#include "VideoCommon/VideoEvents.h" class FifoRecorder::FifoRecordAnalyzer : public OpcodeDecoder::Callback { @@ -249,6 +254,40 @@ void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; + + m_end_of_frame_event = AfterFrameEvent::Register([this] { + const bool was_recording = OpcodeDecoder::g_record_fifo_data; + OpcodeDecoder::g_record_fifo_data = IsRecording(); + + if (!OpcodeDecoder::g_record_fifo_data) + return; + + if (!was_recording) + { + RecordInitialVideoMemory(); + } + + auto& system = Core::System::GetInstance(); + auto& command_processor = system.GetCommandProcessor(); + const auto& fifo = command_processor.GetFifo(); + EndFrame(fifo.CPBase.load(std::memory_order_relaxed), + fifo.CPEnd.load(std::memory_order_relaxed)); + }, "FifoRecorder::EndFrame"); +} + +void FifoRecorder::RecordInitialVideoMemory() +{ + const u32* bpmem_ptr = reinterpret_cast(&bpmem); + u32 cpmem[256] = {}; + // The FIFO recording format splits XF memory into xfmem and xfregs; follow + // that split here. + const u32* xfmem_ptr = reinterpret_cast(&xfmem); + const u32* xfregs_ptr = reinterpret_cast(&xfmem) + FifoDataFile::XF_MEM_SIZE; + u32 xfregs_size = sizeof(XFMemory) / 4 - FifoDataFile::XF_MEM_SIZE; + + g_main_cp_state.FillCPMemoryArray(cpmem); + + SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, texMem); } void FifoRecorder::StopRecording() @@ -391,11 +430,14 @@ void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd) m_SkipFutureData = true; // Signal video backend that it should not call this function when the next frame ends m_IsRecording = false; + + // Remove our frame end callback + m_end_of_frame_event.reset(); } } void FifoRecorder::SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* xfMem, - const u32* xfRegs, u32 xfRegsSize, const u8* texMem) + const u32* xfRegs, u32 xfRegsSize, const u8* texMem_ptr) { std::lock_guard lk(m_mutex); @@ -408,7 +450,7 @@ void FifoRecorder::SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize); memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4); - memcpy(m_File->GetTexMem(), texMem, FifoDataFile::TEX_MEM_SIZE); + memcpy(m_File->GetTexMem(), texMem_ptr, FifoDataFile::TEX_MEM_SIZE); } m_record_analyzer = std::make_unique(this, cpMem); diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.h b/Source/Core/Core/FifoPlayer/FifoRecorder.h index 3a28d05bce..6c26ba5934 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.h +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.h @@ -9,6 +9,7 @@ #include #include "Common/Assert.h" +#include "Common/EventHook.h" #include "Core/FifoPlayer/FifoDataFile.h" class FifoRecorder @@ -50,6 +51,8 @@ public: private: class FifoRecordAnalyzer; + void RecordInitialVideoMemory(); + // Accessed from both GUI and video threads std::recursive_mutex m_mutex; @@ -72,4 +75,6 @@ private: std::vector m_FifoData; std::vector m_Ram; std::vector m_ExRam; + + EventHook m_end_of_frame_event; }; diff --git a/Source/Core/InputCommon/DynamicInputTextureManager.cpp b/Source/Core/InputCommon/DynamicInputTextureManager.cpp index 7b7811801c..8a325f8f7e 100644 --- a/Source/Core/InputCommon/DynamicInputTextureManager.cpp +++ b/Source/Core/InputCommon/DynamicInputTextureManager.cpp @@ -13,7 +13,7 @@ #include "InputCommon/DynamicInputTextures/DITConfiguration.h" #include "VideoCommon/HiresTextures.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/TextureCacheBase.h" namespace InputCommon { @@ -48,7 +48,7 @@ void DynamicInputTextureManager::GenerateTextures(const IniFile& file, any_dirty |= configuration.GenerateTextures(file, controller_names); } - if (any_dirty && g_renderer && Core::GetState() != Core::State::Starting) - g_renderer->ForceReloadTextures(); + if (any_dirty && g_texture_cache && Core::GetState() != Core::State::Starting) + g_texture_cache->ForceReloadTextures(); } } // namespace InputCommon diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index 35431a4abf..bb0cb2c378 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -28,6 +28,7 @@ static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) FrameDumper::FrameDumper() { + m_frame_end_handle = AfterFrameEvent::Register([this] { FlushFrameDump(); }, "FrameDumper"); } FrameDumper::~FrameDumper() diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index f2c53a28b9..8006739671 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -10,6 +10,7 @@ #include "Common/Thread.h" #include "VideoCommon/FrameDumpFFMpeg.h" +#include "VideoCommon/VideoEvents.h" class AbstractStagingTexture; class AbstractTexture; @@ -116,6 +117,8 @@ private: Common::Event m_screenshot_completed; std::mutex m_screenshot_lock; std::string m_screenshot_name; + + EventHook m_frame_end_handle; }; extern std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 861536813e..3032de574f 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -79,6 +79,8 @@ bool FramebufferManager::Initialize() return false; } + m_end_of_frame_event = AfterFrameEvent::Register([this] { EndOfFrame(); }, "FramebufferManager"); + return true; } diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index dd073dda12..405ef1fc3a 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -16,6 +16,7 @@ #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoEvents.h" class NativeVertexFormat; class PointerWrap; @@ -213,6 +214,8 @@ protected: std::unique_ptr m_depth_poke_pipeline; std::vector m_color_poke_vertices; std::vector m_depth_poke_vertices; + + EventHook m_end_of_frame_event; }; extern std::unique_ptr g_framebuffer_manager; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp index 4b5f5dbd84..368e7adba4 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp @@ -85,6 +85,8 @@ bool GraphicsModManager::Initialize() g_ActiveConfig.graphics_mod_config->Load(); g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes); g_graphics_mod_manager->Load(*g_ActiveConfig.graphics_mod_config); + + m_end_of_frame_event = AfterFrameEvent::Register([this] { EndOfFrame(); }, "ModManager"); } return true; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h index 035ee48a36..aeb55f17d1 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h @@ -14,6 +14,7 @@ #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" #include "VideoCommon/TextureInfo.h" #include "VideoCommon/XFMemory.h" +#include "VideoCommon/VideoEvents.h" class GraphicsModGroupConfig; class GraphicsModManager @@ -34,9 +35,8 @@ public: void Load(const GraphicsModGroupConfig& config); - void EndOfFrame(); - private: + void EndOfFrame(); void Reset(); class DecoratedAction; @@ -53,6 +53,8 @@ private: std::unordered_map, FBInfoHasher> m_xfb_target_to_actions; std::unordered_set m_groups; + + EventHook m_end_of_frame_event; }; extern std::unique_ptr g_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 69deb32e2c..73532367f9 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -100,6 +100,8 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, { Present(); ProcessFrameDumping(ticks); + + AfterPresentEvent::Trigger(present_info); } else { @@ -117,8 +119,12 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; + BeforePresentEvent::Trigger(present_info); + Present(); ProcessFrameDumping(ticks); + + AfterPresentEvent::Trigger(present_info); } void Presenter::ProcessFrameDumping(u64 ticks) const diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 706e0ac58c..69555f9919 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -64,6 +64,10 @@ Renderer::Renderer() { UpdateActiveConfig(); CalculateTargetSize(); + UpdateWidescreen(); + // VertexManager doesn't maintain statistics in Wii mode. + if (!SConfig::GetInstance().bWii) + m_update_widescreen_handle = AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } Renderer::~Renderer() = default; @@ -202,15 +206,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } -void Renderer::RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle& sourceRc, u32 fbStride, - u32 fbHeight, float Gamma) -{ - CheckFifoRecording(); - - if (!fbStride || !fbHeight) - return; -} - unsigned int Renderer::GetEFBScale() const { return m_efb_scale; @@ -288,60 +283,39 @@ MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle return result; } -void Renderer::CheckFifoRecording() +void Renderer::UpdateWidescreen() { - const bool was_recording = OpcodeDecoder::g_record_fifo_data; - OpcodeDecoder::g_record_fifo_data = FifoRecorder::GetInstance().IsRecording(); + if (SConfig::GetInstance().bWii) + m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - if (!OpcodeDecoder::g_record_fifo_data) - return; + // suggested_aspect_mode overrides SYSCONF_WIDESCREEN + if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; - if (!was_recording) + // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. + if (!g_ActiveConfig.bWidescreenHack) { - RecordVideoMemory(); + const auto aspect_mode = g_ActiveConfig.aspect_mode; + if (aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; } - - auto& system = Core::System::GetInstance(); - auto& command_processor = system.GetCommandProcessor(); - const auto& fifo = command_processor.GetFifo(); - FifoRecorder::GetInstance().EndFrame(fifo.CPBase.load(std::memory_order_relaxed), - fifo.CPEnd.load(std::memory_order_relaxed)); -} - -void Renderer::RecordVideoMemory() -{ - const u32* bpmem_ptr = reinterpret_cast(&bpmem); - u32 cpmem[256] = {}; - // The FIFO recording format splits XF memory into xfmem and xfregs; follow - // that split here. - const u32* xfmem_ptr = reinterpret_cast(&xfmem); - const u32* xfregs_ptr = reinterpret_cast(&xfmem) + FifoDataFile::XF_MEM_SIZE; - u32 xfregs_size = sizeof(XFMemory) / 4 - FifoDataFile::XF_MEM_SIZE; - - g_main_cp_state.FillCPMemoryArray(cpmem); - - FifoRecorder::GetInstance().SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, - texMem); -} - -void Renderer::ForceReloadTextures() -{ - m_force_reload_textures.Set(); } // Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. void Renderer::UpdateWidescreenHeuristic() { - // VertexManager maintains no statistics in Wii mode. - if (SConfig::GetInstance().bWii) - return; - const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount(); // If suggested_aspect_mode (GameINI) is configured don't use heuristic. if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto) return; + UpdateWidescreen(); + // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) @@ -382,109 +356,16 @@ void Renderer::UpdateWidescreenHeuristic() m_was_orthographically_anamorphic = ortho_looks_anamorphic; } +void Renderer::OnConfigChanged(u32 bits) +{ + if (bits & CONFIG_CHANGE_BIT_ASPECT_RATIO) + UpdateWidescreen(); +} + void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - if (SConfig::GetInstance().bWii) - m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - - // suggested_aspect_mode overrides SYSCONF_WIDESCREEN - if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - - // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. - if (!g_ActiveConfig.bWidescreenHack) - { - const auto aspect_mode = g_ActiveConfig.aspect_mode; - if (aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - } - UpdateWidescreenHeuristic(); - - // Ensure the last frame was written to the dump. - // This is required even if frame dumping has stopped, since the frame dump is one frame - // behind the renderer. - g_frame_dumper->FlushFrameDump(); - - if (g_ActiveConfig.bGraphicMods) - { - g_graphics_mod_manager->EndOfFrame(); - } - - g_framebuffer_manager->EndOfFrame(); - if (xfb_addr && fb_width && fb_stride && fb_height) { - // Get the current XFB from texture cache - - g_presenter->ReleaseXFBContentLock(); - - MathUtil::Rectangle xfb_rect; - RcTcacheEntry xfb_entry = - g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); - - bool is_duplicate_frame = - g_presenter->SubmitXFB(std::move(xfb_entry), xfb_rect, ticks, m_frame_count); - - if (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame) - { - if (!is_duplicate_frame) - { - DolphinAnalytics::PerformanceSample perf_sample; - perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); - perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; - perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; - DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); - - // Begin new frame - m_frame_count++; - g_stats.ResetFrame(); - } - - g_shader_cache->RetrieveAsyncShaders(); - g_vertex_manager->OnEndFrame(); - - // We invalidate the pipeline object at the start of the frame. - // This is for the rare case where only a single pipeline configuration is used, - // and hybrid ubershaders have compiled the specialized shader, but without any - // state changes the specialized shader will not take over. - g_vertex_manager->InvalidatePipelineObject(); - - if (m_force_reload_textures.TestAndClear()) - { - g_texture_cache->ForceReload(); - } - else - { - // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame - // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending - // copies. - g_texture_cache->FlushEFBCopies(); - } - - if (!is_duplicate_frame) - { - // Remove stale EFB/XFB copies. - g_texture_cache->Cleanup(m_frame_count); - const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); - // The denominator should always be > 0 but if it's not, just return 1 - const double last_speed = - last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; - Core::Callback_FramePresented(last_speed); - } - - // Handle any config changes, this gets propagated to the backend. - CheckForConfigChanges(); - g_Config.iSaveTargetId = 0; - } - else - { - g_gfx->Flush(); - } - // Update our last xfb values m_last_xfb_addr = xfb_addr; m_last_xfb_ticks = ticks; @@ -492,10 +373,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 m_last_xfb_stride = fb_stride; m_last_xfb_height = fb_height; } - else - { - g_gfx->Flush(); - } } bool Renderer::UseVertexDepthRange() const diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index ea38b71b98..520acdb8e4 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -26,6 +26,7 @@ #include "Common/Flag.h" #include "Common/MathUtil.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/VideoEvents.h" class AbstractFramebuffer; class AbstractPipeline; @@ -86,8 +87,6 @@ public: void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); - void RenderToXFB(u32 xfbAddr, const MathUtil::Rectangle& sourceRc, u32 fbStride, - u32 fbHeight, float Gamma = 1.0f); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); @@ -95,7 +94,6 @@ public: // Finish up the current frame, print some stats void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); - void UpdateWidescreenHeuristic(); bool IsGameWidescreen() const { return m_is_game_widescreen; } PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } @@ -104,16 +102,17 @@ public: bool UseVertexDepthRange() const; void DoState(PointerWrap& p); - // Will forcibly reload all textures on the next swap - void ForceReloadTextures(); - bool CalculateTargetSize(); -protected: - std::tuple CalculateTargetScale(int x, int y) const; + int m_frame_count = 0; - void CheckFifoRecording(); - void RecordVideoMemory(); + void OnConfigChanged(u32 bits); + +protected: + void UpdateWidescreen(); + void UpdateWidescreenHeuristic(); + + std::tuple CalculateTargetScale(int x, int y) const; bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; @@ -122,8 +121,6 @@ protected: int m_target_width = 1; int m_target_height = 1; - int m_frame_count = 0; - private: PixelFormat m_prev_efb_format; unsigned int m_efb_scale = 1; @@ -134,7 +131,8 @@ private: u32 m_last_xfb_stride = 0; u32 m_last_xfb_height = 0; - Common::Flag m_force_reload_textures; + EventHook m_update_widescreen_handle; + EventHook m_config_changed_handle; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 1e46f3527b..cc6e9fb86a 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -46,6 +46,7 @@ bool ShaderCache::Initialize() return false; m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); + m_frame_end_handler = AfterFrameEvent::Register([this] { RetrieveAsyncShaders(); }, "RetreiveAsyncShaders"); return true; } diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h index 2721cd7319..f0982b3104 100644 --- a/Source/Core/VideoCommon/ShaderCache.h +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -30,6 +30,7 @@ #include "VideoCommon/UberShaderPixel.h" #include "VideoCommon/UberShaderVertex.h" #include "VideoCommon/VertexShaderGen.h" +#include "VideoCommon/VideoEvents.h" class NativeVertexFormat; enum class AbstractTextureFormat : u32; @@ -250,6 +251,8 @@ private: // Texture decoding shaders std::map, std::unique_ptr> m_texture_decoding_shaders; + + EventHook m_frame_end_handler; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index a6b7eb8167..a1ab6122ab 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -8,11 +8,28 @@ #include +#include "Core/DolphinAnalytics.h" +#include "Core/HW/SystemTimers.h" + #include "VideoCommon/BPFunctions.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" Statistics g_stats; + +static EventHook s_before_frame_event = BeforeFrameEvent::Register([] { + g_stats.ResetFrame(); +}, "Statistics::ResetFrame"); + +static EventHook s_after_frame_event = AfterFrameEvent::Register([] { + DolphinAnalytics::PerformanceSample perf_sample; + perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); + perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; + perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; + DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); +}, "Statistics::PerformanceSample"); + static bool clear_scissors; void Statistics::ResetFrame() diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 21b58d931e..6029f09c5c 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -769,6 +769,23 @@ void TextureCacheBase::DoLoadState(PointerWrap& p) } } +void TextureCacheBase::OnFrameEnd() +{ + if (m_force_reload_textures.TestAndClear()) + { + ForceReload(); + } + else + { + // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame + // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending + // copies. + g_texture_cache->FlushEFBCopies(); + } + + g_texture_cache->Cleanup(g_renderer->m_frame_count); +} + void TCacheEntry::DoState(PointerWrap& p) { p.Do(addr); diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 8b9010a50e..c38f2578b9 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -18,12 +18,15 @@ #include "Common/BitSet.h" #include "Common/CommonTypes.h" +#include "Common/Flag.h" #include "Common/MathUtil.h" + #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/TextureInfo.h" +#include "VideoCommon/VideoEvents.h" class AbstractFramebuffer; class AbstractStagingTexture; @@ -288,6 +291,9 @@ public: static bool AllCopyFilterCoefsNeeded(const std::array& coefficients); static bool CopyFilterCanOverflow(const std::array& coefficients); + // Will forcibly reload all textures when the frame next ends + void ForceReloadTextures() { m_force_reload_textures.Set(); } + protected: // Decodes the specified data to the GPU texture specified by entry. // Returns false if the configuration is not supported. @@ -428,6 +434,11 @@ private: // We store this in the class so that the same staging texture can be used for multiple // readbacks, saving the overhead of allocating a new buffer every time. std::unique_ptr m_readback_texture; + + void OnFrameEnd(); + + Common::Flag m_force_reload_textures; + EventHook m_frame_event = AfterFrameEvent::Register([this] { OnFrameEnd(); }, "TextureCache"); }; extern std::unique_ptr g_texture_cache; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 34fe374d25..895ddb1372 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -105,6 +105,7 @@ VertexManagerBase::~VertexManagerBase() = default; bool VertexManagerBase::Initialize() { + m_frame_end_event = AfterFrameEvent::Register([this] { OnEndFrame();}, "VertexManagerBase"); m_index_generator.Init(); m_cpu_cull.Init(); return true; @@ -996,4 +997,10 @@ void VertexManagerBase::OnEndFrame() #endif m_cpu_accesses_this_frame.clear(); + + // We invalidate the pipeline object at the start of the frame. + // This is for the rare case where only a single pipeline configuration is used, + // and hybrid ubershaders have compiled the specialized shader, but without any + // state changes the specialized shader will not take over. + InvalidatePipelineObject(); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index b2cb8b54bd..18e0f1d402 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -229,6 +229,8 @@ private: std::vector m_cpu_accesses_this_frame; std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; + + EventHook m_frame_end_event; }; extern std::unique_ptr g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 8b8a73e6cf..86cfc97ade 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -341,3 +341,8 @@ void CheckForConfigChanges() BPFunctions::SetScissorAndViewport(); } } + +static EventHook s_check_config_event = AfterFrameEvent::Register([] { + CheckForConfigChanges(); + +}, "CheckForConfigChanges"); From 3ae78b8e762787a3114baeabec2dd0178562ed1c Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 00:49:23 +1300 Subject: [PATCH 34/60] Also use events for config changed --- Source/Core/VideoBackends/D3D/D3DGfx.cpp | 2 ++ Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp | 2 ++ Source/Core/VideoBackends/Metal/MTLGfx.mm | 2 ++ Source/Core/VideoBackends/OGL/OGLGfx.cpp | 2 ++ Source/Core/VideoBackends/Vulkan/VKGfx.cpp | 2 ++ Source/Core/VideoBackends/Vulkan/VKMain.cpp | 2 +- Source/Core/VideoCommon/AbstractGfx.cpp | 15 +++++++++++++ Source/Core/VideoCommon/AbstractGfx.h | 3 ++- Source/Core/VideoCommon/RenderBase.cpp | 3 +++ Source/Core/VideoCommon/VideoConfig.cpp | 22 +++++++++++--------- Source/Core/VideoCommon/VideoConfig.h | 4 +++- 11 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.cpp b/Source/Core/VideoBackends/D3D/D3DGfx.cpp index dc391f08b3..32dc21fb6a 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.cpp +++ b/Source/Core/VideoBackends/D3D/D3DGfx.cpp @@ -169,6 +169,8 @@ void Gfx::PresentBackbuffer() void Gfx::OnConfigChanged(u32 bits) { + AbstractGfx::OnConfigChanged(bits); + // Quad-buffer changes require swap chain recreation. if (bits & CONFIG_CHANGE_BIT_STEREO_MODE && m_swap_chain) m_swap_chain->SetStereo(SwapChain::WantsStereo()); diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp index ea9610ae34..68b0436134 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp @@ -437,6 +437,8 @@ SurfaceInfo Gfx::GetSurfaceInfo() const void Gfx::OnConfigChanged(u32 bits) { + AbstractGfx::OnConfigChanged(bits); + // For quad-buffered stereo we need to change the layer count, so recreate the swap chain. if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE) { diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.mm b/Source/Core/VideoBackends/Metal/MTLGfx.mm index 2d44156102..f1e307d808 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.mm +++ b/Source/Core/VideoBackends/Metal/MTLGfx.mm @@ -273,6 +273,8 @@ void Metal::Gfx::WaitForGPUIdle() void Metal::Gfx::OnConfigChanged(u32 bits) { + AbstractGfx::OnConfigChanged(bits); + if (bits & CONFIG_CHANGE_BIT_VSYNC) [m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive]; diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.cpp b/Source/Core/VideoBackends/OGL/OGLGfx.cpp index 88e544c235..ec0dd3181e 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.cpp +++ b/Source/Core/VideoBackends/OGL/OGLGfx.cpp @@ -436,6 +436,8 @@ void OGLGfx::PresentBackbuffer() void OGLGfx::OnConfigChanged(u32 bits) { + AbstractGfx::OnConfigChanged(bits); + if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC)) m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive); diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp index f2aeaef0ca..35d6174cec 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp @@ -369,6 +369,8 @@ void VKGfx::CheckForSurfaceResize() void VKGfx::OnConfigChanged(u32 bits) { + AbstractGfx::OnConfigChanged(bits); + if (bits & CONFIG_CHANGE_BIT_HOST_CONFIG) g_object_cache->ReloadPipelineCache(); diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index 721887af7a..7521915897 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -239,7 +239,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) auto perf_query = std::make_unique(); auto bounding_box = std::make_unique(); - return !InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), + return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), std::move(bounding_box)); } diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp index 74fb12ddbf..6ec74210c9 100644 --- a/Source/Core/VideoCommon/AbstractGfx.cpp +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -16,6 +16,11 @@ std::unique_ptr g_gfx; +AbstractGfx::AbstractGfx() +{ + ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "AbstractGfx"); +} + bool AbstractGfx::IsHeadless() const { return true; @@ -132,6 +137,16 @@ std::unique_ptr AbstractGfx::CreateAsyncShader return std::make_unique(); } +void AbstractGfx::OnConfigChanged(u32 changed_bits) +{ + // If there's any shader changes, wait for the GPU to finish before destroying anything. + if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) + { + WaitForGPUIdle(); + SetPipeline(nullptr); + } +} + bool AbstractGfx::UseGeometryShaderForUI() const { // OpenGL doesn't render to a 2-layer backbuffer like D3D/Vulkan for quad-buffered stereo, diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index bdbcdc1001..be9708c12f 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -42,6 +42,7 @@ using ClearColor = std::array; class AbstractGfx { public: + AbstractGfx(); virtual ~AbstractGfx() = default; virtual bool IsHeadless() const = 0; @@ -149,7 +150,7 @@ public: virtual std::unique_ptr CreateAsyncShaderCompiler(); // Called when the configuration changes, and backend structures need to be updated. - virtual void OnConfigChanged(u32 bits) {} + virtual void OnConfigChanged(u32 changed_bits); // Returns true if a layer-expanding geometry shader should be used when rendering the user // interface and final XFB. diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 69555f9919..0e8bb41393 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -65,6 +65,9 @@ Renderer::Renderer() UpdateActiveConfig(); CalculateTargetSize(); UpdateWidescreen(); + + m_config_changed_handle = ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); + // VertexManager doesn't maintain statistics in Wii mode. if (!SConfig::GetInstance().bWii) m_update_widescreen_handle = AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 86cfc97ade..7f710730bc 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -256,6 +256,9 @@ void CheckForConfigChanges() const u32 old_game_mod_changes = g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0; const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods; + const AspectMode old_suggested_aspect_mode = g_ActiveConfig.suggested_aspect_mode; + const bool old_widescreen_hack = g_ActiveConfig.bWidescreenHack; + const auto old_post_processing_shader = g_ActiveConfig.sPostProcessingShader; UpdateActiveConfig(); FreeLook::UpdateActiveConfig(); @@ -301,22 +304,21 @@ void CheckForConfigChanges() changed_bits |= CONFIG_CHANGE_BIT_BBOX; if (g_renderer->CalculateTargetSize()) changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; - - g_presenter->CheckForConfigChanges(changed_bits); + if (old_suggested_aspect_mode != g_ActiveConfig.suggested_aspect_mode) + changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO; + if (old_widescreen_hack != g_ActiveConfig.bWidescreenHack) + changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO; + if (old_post_processing_shader != g_ActiveConfig.sPostProcessingShader) + changed_bits |= CONFIG_CHANGE_BIT_POST_PROCESSING_SHADER; // No changes? if (changed_bits == 0) return; - // Notify the backend of the changes, if any. - g_gfx->OnConfigChanged(changed_bits); + // Notify all listeners + ConfigChangedEvent::Trigger(changed_bits); - // If there's any shader changes, wait for the GPU to finish before destroying anything. - if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) - { - g_gfx->WaitForGPUIdle(); - g_gfx->SetPipeline(nullptr); - } + // TODO: Move everything else to the ConfigChanged event // Framebuffer changed? if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 2c05a6d4ae..c86ec647bc 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -70,7 +70,9 @@ enum ConfigChangeBits : u32 CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4), CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5), CONFIG_CHANGE_BIT_VSYNC = (1 << 6), - CONFIG_CHANGE_BIT_BBOX = (1 << 7) + CONFIG_CHANGE_BIT_BBOX = (1 << 7), + CONFIG_CHANGE_BIT_ASPECT_RATIO = (1 << 8), + CONFIG_CHANGE_BIT_POST_PROCESSING_SHADER = (1 << 9), }; // NEVER inherit from this class. From 3be63221c7a1003ce6177e823da5ec399cd51015 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 00:59:17 +1300 Subject: [PATCH 35/60] Renderer still needs to track swaps for savestates --- Source/Core/VideoCommon/AsyncRequests.cpp | 2 ++ Source/Core/VideoCommon/BPStructs.cpp | 5 +++-- Source/Core/VideoCommon/RenderBase.cpp | 13 +++++++------ Source/Core/VideoCommon/RenderBase.h | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index 8c254d4e71..7cbb134f7e 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -158,6 +158,8 @@ void AsyncRequests::HandleEvent(const AsyncRequests::Event& e) case Event::SWAP_EVENT: g_presenter->ViSwap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, e.swap_event.fbHeight, e.time); + g_renderer->TrackSwaps(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, + e.swap_event.fbHeight, e.time); break; case Event::BBOX_READ: diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 865d73dbed..1985ac6527 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -350,8 +350,9 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, if (g_ActiveConfig.bImmediateXFB) { // below div two to convert from bytes to pixels - it expects width, not stride - g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, - Core::System::GetInstance().GetCoreTiming().GetTicks()); + u64 ticks = Core::System::GetInstance().GetCoreTiming().GetTicks(); + g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, ticks); + g_renderer->TrackSwaps(destAddr, destStride / 2, destStride, height, ticks); } else { diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 0e8bb41393..e252921c9f 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -365,7 +365,7 @@ void Renderer::OnConfigChanged(u32 bits) UpdateWidescreen(); } -void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) +void Renderer::TrackSwaps(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { if (xfb_addr && fb_width && fb_stride && fb_height) { @@ -412,13 +412,14 @@ void Renderer::DoState(PointerWrap& p) if (p.IsReadMode()) { - // Force the next xfb to be displayed. - g_presenter->ClearLastXfbId(); - m_was_orthographically_anamorphic = false; - // And actually display it. - Swap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height, m_last_xfb_ticks); + // This technically counts as the end of the frame + AfterFrameEvent::Trigger(); + + // re-display the most recent XFB + g_presenter->ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, + m_last_xfb_height, m_last_xfb_ticks); } #if defined(HAVE_FFMPEG) diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 520acdb8e4..cceddea638 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -91,8 +91,8 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - // Finish up the current frame, print some stats - void Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); + // Track swaps for save-states + void TrackSwaps(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); bool IsGameWidescreen() const { return m_is_game_widescreen; } From ec8f46b02c21fec9b24518e6788a4df413baf8f9 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 01:03:46 +1300 Subject: [PATCH 36/60] Expose Renderer's Framecount We don't want to move it, because we want to complete this refactor without changing savestate version --- Source/Core/VideoCommon/Present.cpp | 8 ++++---- Source/Core/VideoCommon/Present.h | 1 - Source/Core/VideoCommon/RenderBase.h | 5 ++++- Source/Core/VideoCommon/TextureCacheBase.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 73532367f9..0612d96d3b 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -84,12 +84,12 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, present_info.emulated_timestamp = ticks; if (unique) { - present_info.frame_count = m_frame_count++; + present_info.frame_count = g_renderer->FrameCountIncrement(); present_info.reason = PresentInfo::PresentReason::VideoInterface; } else { - present_info.frame_count = m_frame_count - 1; + present_info.frame_count = g_renderer->FrameCount() - 1; // Previous frame present_info.reason = PresentInfo::PresentReason::VideoInterfaceDuplicate; } present_info.present_count = m_present_count; @@ -115,7 +115,7 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ PresentInfo present_info; present_info.emulated_timestamp = ticks; - present_info.frame_count = m_frame_count++; + present_info.frame_count = g_renderer->FrameCountIncrement(); present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; @@ -145,7 +145,7 @@ void Presenter::ProcessFrameDumping(u64 ticks) const } g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, - m_frame_count); + g_renderer->FrameCount()); } } diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 4c870c93b0..5e84d5aada 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -128,7 +128,6 @@ private: std::unique_ptr m_post_processor; std::unique_ptr m_onscreen_ui; - u64 m_frame_count = 0; u64 m_present_count = 0; }; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index cceddea638..209d915975 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -104,7 +104,8 @@ public: bool CalculateTargetSize(); - int m_frame_count = 0; + int FrameCount() const { return m_frame_count; } + int FrameCountIncrement() { return m_frame_count++; } void OnConfigChanged(u32 bits); @@ -131,6 +132,8 @@ private: u32 m_last_xfb_stride = 0; u32 m_last_xfb_height = 0; + int m_frame_count = 0; + EventHook m_update_widescreen_handle; EventHook m_config_changed_handle; }; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 6029f09c5c..947b2e5965 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -783,7 +783,7 @@ void TextureCacheBase::OnFrameEnd() g_texture_cache->FlushEFBCopies(); } - g_texture_cache->Cleanup(g_renderer->m_frame_count); + g_texture_cache->Cleanup(g_renderer->FrameCount()); } void TCacheEntry::DoState(PointerWrap& p) From abfc75f3629fb0fcef42682d25249595923c7771 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 01:19:19 +1300 Subject: [PATCH 37/60] Cleanup headers --- Source/Android/jni/MainAndroid.cpp | 1 - Source/Core/Core/Core.cpp | 1 - Source/Core/DolphinNoGUI/PlatformFBDev.cpp | 1 - .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 1 - Source/Core/DolphinQt/Settings.cpp | 1 - .../Core/VideoBackends/Vulkan/StateTracker.h | 1 - .../Core/VideoBackends/Vulkan/VKPerfQuery.cpp | 1 + Source/Core/VideoCommon/FrameDumper.cpp | 1 - Source/Core/VideoCommon/RenderBase.cpp | 22 ++++------------ Source/Core/VideoCommon/RenderBase.h | 26 ------------------- Source/Core/VideoCommon/VertexManagerBase.cpp | 1 - Source/Core/VideoCommon/VideoBackendBase.cpp | 2 +- 12 files changed, 7 insertions(+), 52 deletions(-) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index d305878f99..7902a9973f 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -60,7 +60,6 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" #include "jni/AndroidCommon/AndroidCommon.h" diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 8a2d3185c9..495ffc6ba4 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -89,7 +89,6 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoEvents.h" diff --git a/Source/Core/DolphinNoGUI/PlatformFBDev.cpp b/Source/Core/DolphinNoGUI/PlatformFBDev.cpp index d965c6f99b..0b08bc110f 100644 --- a/Source/Core/DolphinNoGUI/PlatformFBDev.cpp +++ b/Source/Core/DolphinNoGUI/PlatformFBDev.cpp @@ -21,7 +21,6 @@ #include #include #include -#include "VideoCommon/RenderBase.h" namespace { diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 31a8de1488..4dec815de9 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -61,7 +61,6 @@ #include "VideoCommon/NetPlayChatUI.h" #include "VideoCommon/NetPlayGolfUI.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" namespace diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index 26e108e80a..bfc2f36ea1 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -46,7 +46,6 @@ #include "VideoCommon/NetPlayChatUI.h" #include "VideoCommon/NetPlayGolfUI.h" -#include "VideoCommon/RenderBase.h" Settings::Settings() { diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index eb5e4676de..d90496631c 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -9,7 +9,6 @@ #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/Constants.h" -#include "VideoCommon/RenderBase.h" namespace Vulkan { diff --git a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp index c03d110eac..d362deaf37 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp @@ -15,6 +15,7 @@ #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VulkanContext.h" +#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoCommon.h" namespace Vulkan diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index bb0cb2c378..f0c62c4c17 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -16,7 +16,6 @@ #include "VideoCommon/AbstractTexture.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" static bool DumpFrameToPNG(const FrameData& frame, const std::string& file_name) diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index e252921c9f..b02738d037 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -23,38 +23,27 @@ #include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/Config/Config.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Core/Config/GraphicsSettings.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" -#include "Core/Core.h" -#include "Core/DolphinAnalytics.h" -#include "Core/FifoPlayer/FifoRecorder.h" -#include "Core/FreeLookConfig.h" -#include "Core/HW/SystemTimers.h" #include "Core/System.h" -#include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractGfx.h" -#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/BPMemory.h" #include "VideoCommon/BoundingBox.h" -#include "VideoCommon/CommandProcessor.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/FrameDumper.h" -#include "VideoCommon/FramebufferManager.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" -#include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/PerformanceMetrics.h" + #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" -#include "VideoCommon/ShaderCache.h" -#include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoCommon.h" +#include "VideoCommon/XFMemory.h" std::unique_ptr g_renderer; @@ -62,7 +51,6 @@ Renderer::Renderer() : m_prev_efb_format{PixelFormat::INVALID_FMT}, m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} { - UpdateActiveConfig(); CalculateTargetSize(); UpdateWidescreen(); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 209d915975..0468681e21 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -13,43 +13,17 @@ #pragma once -#include #include -#include -#include -#include -#include #include -#include #include "Common/CommonTypes.h" -#include "Common/Flag.h" #include "Common/MathUtil.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoEvents.h" -class AbstractFramebuffer; -class AbstractPipeline; -class AbstractShader; -class AbstractTexture; -class AbstractStagingTexture; -class NativeVertexFormat; -class PixelShaderManager; class PointerWrap; -struct ComputePipelineConfig; -struct AbstractPipelineConfig; -struct PortableVertexDeclaration; -struct TextureConfig; -enum class AbstractTextureFormat : u32; -enum class ShaderStage; enum class EFBAccessType; enum class EFBReinterpretType; -enum class StagingTextureType; - -namespace VideoCommon -{ -class AsyncShaderCompiler; -} struct EfbPokeData { diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 895ddb1372..a1058c77c7 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -30,7 +30,6 @@ #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PerfQueryBase.h" #include "VideoCommon/PixelShaderManager.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureInfo.h" diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 31bacc804c..3eeceb3c5e 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -97,7 +97,7 @@ void VideoBackendBase::Video_ExitLoop() void VideoBackendBase::Video_OutputXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - if (m_initialized && g_renderer && !g_ActiveConfig.bImmediateXFB) + if (m_initialized && g_presenter && !g_ActiveConfig.bImmediateXFB) { auto& system = Core::System::GetInstance(); system.GetFifo().SyncGPU(Fifo::SyncGPUReason::Swap); From b753641dd44c927e52c4b478f0ace8bfdf17a2ff Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 14:34:36 +1300 Subject: [PATCH 38/60] Add some descriptions to new classes --- Source/Core/VideoCommon/AbstractGfx.h | 5 +++++ Source/Core/VideoCommon/OnScreenUI.h | 2 ++ Source/Core/VideoCommon/Present.h | 2 ++ Source/Core/VideoCommon/RenderBase.h | 16 +++++----------- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index be9708c12f..e59270871c 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -39,6 +39,11 @@ class AsyncShaderCompiler; using ClearColor = std::array; +// AbstractGfx is the root of Dolphin's Graphics API abstraction layer. +// +// Abstract knows nothing about the internals of the GameCube/Wii, that is all handled elsewhere in +// VideoCommon. + class AbstractGfx { public: diff --git a/Source/Core/VideoCommon/OnScreenUI.h b/Source/Core/VideoCommon/OnScreenUI.h index ea8eeb6630..e90d1c2132 100644 --- a/Source/Core/VideoCommon/OnScreenUI.h +++ b/Source/Core/VideoCommon/OnScreenUI.h @@ -16,6 +16,8 @@ class AbstractPipeline; namespace VideoCommon { + +// OnScreenUI handles all the ImGui rendering. class OnScreenUI { public: diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 5e84d5aada..fb4799898c 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -23,6 +23,8 @@ namespace VideoCommon class OnScreenUI; class PostProcessing; +// Presenter is a class that deals with putting the final XFB on the screen. +// It also handles the ImGui UI and post-processing. class Presenter { public: diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 0468681e21..d2e0036563 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -1,15 +1,6 @@ // Copyright 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -// --------------------------------------------------------------------------------------------- -// GC graphics pipeline -// --------------------------------------------------------------------------------------------- -// 3d commands are issued through the fifo. The GPU draws to the 2MB EFB. -// The efb can be copied back into ram in two forms: as textures or as XFB. -// The XFB is the region in RAM that the VI chip scans out to the television. -// So, after all rendering to EFB is done, the image is copied into one of two XFBs in RAM. -// Next frame, that one is scanned out and the other one gets the copy. = double buffering. -// --------------------------------------------------------------------------------------------- #pragma once @@ -32,8 +23,11 @@ struct EfbPokeData }; // Renderer really isn't a very good name for this class - it's more like "Misc". -// The long term goal is to get rid of this class and replace it with others that make -// more sense. +// It used to be a massive mess, but almost everything has been refactored out. +// +// Most of the remaining functionality is related to EFB and Internal Resolution scaling +// +// It also handles the Widescreen heuristic, frame counting and tracking xfb across save states. class Renderer { public: From e0090024119184238032923987af1c8d7167b7b5 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 15:44:38 +1300 Subject: [PATCH 39/60] Refactor ClearRegion And fix bug where opengl was getting the wrong coordinates --- Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp | 4 +- Source/Core/VideoBackends/D3D12/D3D12Gfx.h | 2 +- Source/Core/VideoBackends/Metal/MTLGfx.h | 2 +- Source/Core/VideoBackends/Metal/MTLGfx.mm | 5 +- Source/Core/VideoBackends/OGL/OGLGfx.cpp | 3 +- Source/Core/VideoBackends/OGL/OGLGfx.h | 2 +- Source/Core/VideoBackends/Software/SWGfx.cpp | 3 +- Source/Core/VideoBackends/Software/SWGfx.h | 2 +- Source/Core/VideoBackends/Vulkan/VKGfx.cpp | 5 +- Source/Core/VideoBackends/Vulkan/VKGfx.h | 2 +- Source/Core/VideoCommon/AbstractGfx.cpp | 30 +++++++++- Source/Core/VideoCommon/AbstractGfx.h | 3 +- Source/Core/VideoCommon/BPFunctions.cpp | 2 +- .../Core/VideoCommon/FramebufferManager.cpp | 59 ++++++++++--------- Source/Core/VideoCommon/FramebufferManager.h | 4 +- Source/Core/VideoCommon/RenderBase.cpp | 29 --------- Source/Core/VideoCommon/RenderBase.h | 2 - 17 files changed, 77 insertions(+), 82 deletions(-) diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp index 68b0436134..e1c82353b2 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp @@ -103,7 +103,7 @@ void Gfx::WaitForGPUIdle() ExecuteCommandList(true); } -void Gfx::ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, +void Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { // Use a fast path without the shader if both color/alpha are enabled. @@ -145,7 +145,7 @@ void Gfx::ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectan // Anything left over, fall back to clear triangle. if (color_enable || alpha_enable || z_enable) - ::AbstractGfx::ClearRegion(rc, target_rc, color_enable, alpha_enable, z_enable, color, z); + ::AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z); } void Gfx::SetPipeline(const AbstractPipeline* pipeline) diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h index 26dc669ffc..e7eb015b2f 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h @@ -47,7 +47,7 @@ public: void Flush() override; void WaitForGPUIdle() override; - void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.h b/Source/Core/VideoBackends/Metal/MTLGfx.h index 4e2dca5662..b4c3a3d0e8 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.h +++ b/Source/Core/VideoBackends/Metal/MTLGfx.h @@ -47,7 +47,7 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.mm b/Source/Core/VideoBackends/Metal/MTLGfx.mm index f1e307d808..ba7d64e333 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.mm +++ b/Source/Core/VideoBackends/Metal/MTLGfx.mm @@ -285,8 +285,7 @@ void Metal::Gfx::OnConfigChanged(u32 bits) } } -void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, +void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { u32 framebuffer_width = m_current_framebuffer->GetWidth(); @@ -332,7 +331,7 @@ void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& rc, } g_state_tracker->EnableEncoderLabel(false); - g_framebuffer_manager->ClearEFB(rc, color_enable, alpha_enable, z_enable, color, z); + AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z); g_state_tracker->EnableEncoderLabel(true); } diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.cpp b/Source/Core/VideoBackends/OGL/OGLGfx.cpp index ec0dd3181e..880b213fb8 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.cpp +++ b/Source/Core/VideoBackends/OGL/OGLGfx.cpp @@ -373,8 +373,7 @@ void OGLGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const Clea glDepthMask(m_current_depth_state.updateenable); } -void OGLGfx::ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, bool colorEnable, +void OGLGfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) { u32 clear_mask = 0; diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.h b/Source/Core/VideoBackends/OGL/OGLGfx.h index 8d92766387..671d78ee4a 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.h +++ b/Source/Core/VideoBackends/OGL/OGLGfx.h @@ -42,7 +42,7 @@ public: void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) override; void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; - void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + void ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetTexture(u32 index, const AbstractTexture* texture) override; diff --git a/Source/Core/VideoBackends/Software/SWGfx.cpp b/Source/Core/VideoBackends/Software/SWGfx.cpp index 7e8c234540..d4758a07fa 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.cpp +++ b/Source/Core/VideoBackends/Software/SWGfx.cpp @@ -107,8 +107,7 @@ void SWGfx::ShowImage(const AbstractTexture* source_texture, m_window->ShowImage(source_texture, source_rc); } -void SWGfx::ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, bool colorEnable, +void SWGfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) { EfbCopy::ClearEfb(); diff --git a/Source/Core/VideoBackends/Software/SWGfx.h b/Source/Core/VideoBackends/Software/SWGfx.h index 1337d89525..6e5b328b5c 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.h +++ b/Source/Core/VideoBackends/Software/SWGfx.h @@ -46,7 +46,7 @@ public: void SetScissorRect(const MathUtil::Rectangle& rc) override; - void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + void ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; private: diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp index 35d6174cec..569fbaa32b 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.cpp @@ -100,8 +100,7 @@ void VKGfx::SetPipeline(const AbstractPipeline* pipeline) StateTracker::GetInstance()->SetPipeline(static_cast(pipeline)); } -void VKGfx::ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, bool color_enable, +void VKGfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) { VkRect2D target_vk_rc = { @@ -191,7 +190,7 @@ void VKGfx::ClearRegion(const MathUtil::Rectangle& rc, if (!color_enable && !alpha_enable && !z_enable) return; - g_framebuffer_manager->ClearEFB(rc, color_enable, alpha_enable, z_enable, color, z); + AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z); } void VKGfx::Flush() diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.h b/Source/Core/VideoBackends/Vulkan/VKGfx.h index 8cab073386..8275038479 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.h +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.h @@ -53,7 +53,7 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearRegion(const MathUtil::Rectangle& rc, const MathUtil::Rectangle& target_rc, + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; diff --git a/Source/Core/VideoCommon/AbstractGfx.cpp b/Source/Core/VideoCommon/AbstractGfx.cpp index 6ec74210c9..6e1c56aa20 100644 --- a/Source/Core/VideoCommon/AbstractGfx.cpp +++ b/Source/Core/VideoCommon/AbstractGfx.cpp @@ -54,11 +54,35 @@ void AbstractGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, m_current_framebuffer = framebuffer; } -void AbstractGfx::ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, bool colorEnable, +void AbstractGfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) { - g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z); + // This is a generic fallback for any ClearRegion operations that backends don't support. + // It simply draws a Quad. + + BeginUtilityDrawing(); + + // Set up uniforms. + struct Uniforms + { + float clear_color[4]; + float clear_depth; + float padding1, padding2, padding3; + }; + static_assert(std::is_standard_layout::value); + Uniforms uniforms = {{static_cast((color >> 16) & 0xFF) / 255.0f, + static_cast((color >> 8) & 0xFF) / 255.0f, + static_cast((color >> 0) & 0xFF) / 255.0f, + static_cast((color >> 24) & 0xFF) / 255.0f}, + static_cast(z & 0xFFFFFF) / 16777216.0f}; + if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange) + uniforms.clear_depth = 1.0f - uniforms.clear_depth; + g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); + + g_gfx->SetPipeline(g_framebuffer_manager->GetClearPipeline(colorEnable, alphaEnable, zEnable)); + g_gfx->SetViewportAndScissor(target_rc); + g_gfx->Draw(0, 3); + EndUtilityDrawing(); } void AbstractGfx::SetViewportAndScissor(const MathUtil::Rectangle& rect, float min_depth, diff --git a/Source/Core/VideoCommon/AbstractGfx.h b/Source/Core/VideoCommon/AbstractGfx.h index e59270871c..916bcd4e11 100644 --- a/Source/Core/VideoCommon/AbstractGfx.h +++ b/Source/Core/VideoCommon/AbstractGfx.h @@ -82,8 +82,7 @@ public: virtual void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f); - virtual void ClearRegion(const MathUtil::Rectangle& rc, - const MathUtil::Rectangle& target_rc, bool colorEnable, + virtual void ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z); // Drawing with currently-bound pipeline state. diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 808e7959e5..d022f46103 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -342,7 +342,7 @@ void ClearScreen(const MathUtil::Rectangle& rc) color = RGBA8ToRGB565ToRGBA8(color); z = Z24ToZ16ToZ24(z); } - g_renderer->ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z); + g_framebuffer_manager->ClearEFB(rc, colorEnable, alphaEnable, zEnable, color, z); } } diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 3032de574f..5696ebb2b8 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -16,6 +16,7 @@ #include "VideoCommon/AbstractShader.h" #include "VideoCommon/AbstractStagingTexture.h" #include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferShaderGen.h" #include "VideoCommon/RenderBase.h" @@ -787,36 +788,34 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index, bool async data.tiles[tile_index].present = true; } -void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool clear_color, - bool clear_alpha, bool clear_z, u32 color, u32 z) +void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool color_enable, + bool alpha_enable, bool z_enable, u32 color, u32 z) { FlushEFBPokes(); FlagPeekCacheAsOutOfDate(); - g_gfx->BeginUtilityDrawing(); - // Set up uniforms. - struct Uniforms + // Native -> EFB coordinates + MathUtil::Rectangle target_rc = g_renderer->ConvertEFBRectangle(rc); + target_rc = g_gfx->ConvertFramebufferRectangle(target_rc, m_efb_framebuffer.get()); + target_rc.ClampUL(0, 0, g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight()); + + // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha + // channel to 0xFF. + // On backends that don't allow masking Alpha clears, this allows us to use the fast path + // almost all the time + if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 || + bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 || + bpmem.zcontrol.pixel_format == PixelFormat::Z24) { - float clear_color[4]; - float clear_depth; - float padding1, padding2, padding3; - }; - static_assert(std::is_standard_layout::value); - Uniforms uniforms = {{static_cast((color >> 16) & 0xFF) / 255.0f, - static_cast((color >> 8) & 0xFF) / 255.0f, - static_cast((color >> 0) & 0xFF) / 255.0f, - static_cast((color >> 24) & 0xFF) / 255.0f}, - static_cast(z & 0xFFFFFF) / 16777216.0f}; - if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange) - uniforms.clear_depth = 1.0f - uniforms.clear_depth; - g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms)); + // Force alpha writes, and clear the alpha channel. + alpha_enable = true; + color &= 0x00FFFFFF; + } - const auto target_rc = g_gfx->ConvertFramebufferRectangle(g_renderer->ConvertEFBRectangle(rc), - m_efb_framebuffer.get()); - g_gfx->SetPipeline(m_efb_clear_pipelines[clear_color][clear_alpha][clear_z].get()); - g_gfx->SetViewportAndScissor(target_rc); - g_gfx->Draw(0, 3); - g_gfx->EndUtilityDrawing(); + g_gfx->ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z); + + // Scissor rect must be restored. + BPFunctions::SetScissorAndViewport(); } bool FramebufferManager::CompileClearPipelines() @@ -849,9 +848,9 @@ bool FramebufferManager::CompileClearPipelines() config.depth_state.testenable = depth_enable != 0; config.depth_state.updateenable = depth_enable != 0; - m_efb_clear_pipelines[color_enable][alpha_enable][depth_enable] = + m_clear_pipelines[color_enable][alpha_enable][depth_enable] = g_gfx->CreatePipeline(config); - if (!m_efb_clear_pipelines[color_enable][alpha_enable][depth_enable]) + if (!m_clear_pipelines[color_enable][alpha_enable][depth_enable]) return false; } } @@ -868,12 +867,18 @@ void FramebufferManager::DestroyClearPipelines() { for (u32 depth_enable = 0; depth_enable < 2; depth_enable++) { - m_efb_clear_pipelines[color_enable][alpha_enable][depth_enable].reset(); + m_clear_pipelines[color_enable][alpha_enable][depth_enable].reset(); } } } } +AbstractPipeline* FramebufferManager::GetClearPipeline(bool colorEnable, bool alphaEnable, + bool zEnable) const +{ + return m_clear_pipelines[colorEnable][alphaEnable][zEnable].get(); +} + void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color) { // Flush if we exceeded the number of vertices per batch. diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index 405ef1fc3a..c3871c8e15 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -95,6 +95,8 @@ public: void ClearEFB(const MathUtil::Rectangle& rc, bool clear_color, bool clear_alpha, bool clear_z, u32 color, u32 z); + AbstractPipeline* GetClearPipeline(bool clear_color, bool clear_alpha, bool clear_z) const; + // Reads a framebuffer value back from the GPU. This may block if the cache is not current. u32 PeekEFBColor(u32 x, u32 y); float PeekEFBDepth(u32 x, u32 y); @@ -206,7 +208,7 @@ protected: // EFB clear pipelines // Indexed by [color_write_enabled][alpha_write_enabled][depth_write_enabled] std::array, 2>, 2>, 2> - m_efb_clear_pipelines; + m_clear_pipelines; // EFB poke drawing setup std::unique_ptr m_poke_vertex_format; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index b02738d037..6c991c74ae 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -63,35 +63,6 @@ Renderer::Renderer() Renderer::~Renderer() = default; -void Renderer::ClearScreen(const MathUtil::Rectangle& rc, bool color_enable, bool alpha_enable, - bool z_enable, u32 color, u32 z) -{ - g_framebuffer_manager->FlushEFBPokes(); - g_framebuffer_manager->FlagPeekCacheAsOutOfDate(); - - // Native -> EFB coordinates - MathUtil::Rectangle target_rc = Renderer::ConvertEFBRectangle(rc); - target_rc.ClampUL(0, 0, m_target_width, m_target_height); - - // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha - // channel to 0xFF. - // On backends that don't allow masking Alpha clears, this allows us to use the fast path - // almost all the time - if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 || - bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 || - bpmem.zcontrol.pixel_format == PixelFormat::Z24) - { - // Force alpha writes, and clear the alpha channel. - alpha_enable = true; - color &= 0x00FFFFFF; - } - - g_gfx->ClearRegion(rc, target_rc, color_enable, alpha_enable, z_enable, color, z); - - // Scissor rect must be restored. - BPFunctions::SetScissorAndViewport(); -} - void Renderer::ReinterpretPixelData(EFBReinterpretType convtype) { g_framebuffer_manager->ReinterpretPixelData(convtype); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index d2e0036563..c1c00aa382 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -52,8 +52,6 @@ public: float EFBToScaledXf(float x) const; float EFBToScaledYf(float y) const; - void ClearScreen(const MathUtil::Rectangle& rc, bool colorEnable, bool alphaEnable, - bool zEnable, u32 color, u32 z); virtual void ReinterpretPixelData(EFBReinterpretType convtype); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); From 11de923dcb7e6a7e1b29e5b15cb2bf71ede22b97 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 17:26:46 +1300 Subject: [PATCH 40/60] Move xfb tracking and IR scaling out of RenderBase --- Source/Core/Core/State.cpp | 2 +- .../Core/VideoBackends/D3D/D3DPerfQuery.cpp | 8 +- .../VideoBackends/D3D12/D3D12PerfQuery.cpp | 4 +- .../Core/VideoBackends/Metal/MTLPerfQuery.mm | 2 +- .../Core/VideoBackends/OGL/OGLPerfQuery.cpp | 6 +- .../Core/VideoBackends/Vulkan/VKPerfQuery.cpp | 6 +- Source/Core/VideoCommon/AsyncRequests.cpp | 2 - Source/Core/VideoCommon/BPFunctions.cpp | 10 +- Source/Core/VideoCommon/BPStructs.cpp | 1 - .../Core/VideoCommon/FramebufferManager.cpp | 85 ++++++++++--- Source/Core/VideoCommon/FramebufferManager.h | 23 +++- .../Core/VideoCommon/PixelShaderManager.cpp | 6 +- Source/Core/VideoCommon/Present.cpp | 55 ++++++-- Source/Core/VideoCommon/Present.h | 16 ++- Source/Core/VideoCommon/RenderBase.cpp | 117 +----------------- Source/Core/VideoCommon/RenderBase.h | 48 +------ Source/Core/VideoCommon/TextureCacheBase.cpp | 56 ++++----- .../Core/VideoCommon/VertexShaderManager.cpp | 5 +- Source/Core/VideoCommon/VideoConfig.cpp | 23 +++- Source/Core/VideoCommon/VideoState.cpp | 11 ++ 20 files changed, 238 insertions(+), 248 deletions(-) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 4392182a22..9f1a1c2a5a 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -96,7 +96,7 @@ static size_t s_state_writes_in_queue; static std::condition_variable s_state_write_queue_is_empty; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 157; // Last changed in PR 11183 +constexpr u32 STATE_VERSION = 158; // Last changed in PR ??? // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, diff --git a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp index e1535f7c32..d72b88c0fb 100644 --- a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp @@ -114,8 +114,8 @@ void PerfQuery::FlushOne() // NOTE: Reported pixel metrics should be referenced to native resolution // TODO: Dropping the lower 2 bits from this count should be closer to actual // hardware behavior when drawing triangles. - const u64 native_res_result = result * EFB_WIDTH / g_renderer->GetTargetWidth() * EFB_HEIGHT / - g_renderer->GetTargetHeight(); + const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / + g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); @@ -143,8 +143,8 @@ void PerfQuery::WeakFlush() if (hr == S_OK) { // NOTE: Reported pixel metrics should be referenced to native resolution - const u64 native_res_result = result * EFB_WIDTH / g_renderer->GetTargetWidth() * EFB_HEIGHT / - g_renderer->GetTargetHeight(); + const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / + g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].store(static_cast(native_res_result), std::memory_order_relaxed); diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp index a6c466b646..17cb1da2d3 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp @@ -245,8 +245,8 @@ void PerfQuery::AccumulateQueriesFromBuffer(u32 query_count) // NOTE: Reported pixel metrics should be referenced to native resolution const u64 native_res_result = static_cast(result) * EFB_WIDTH / - g_renderer->GetTargetWidth() * EFB_HEIGHT / - g_renderer->GetTargetHeight(); + g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / + g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); } diff --git a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm index cd65b37b58..4bf37ccd22 100644 --- a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm +++ b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm @@ -78,7 +78,7 @@ void Metal::PerfQuery::ReturnResults(const u64* data, const PerfQueryGroup* grou for (size_t i = 0; i < count; ++i) { u64 native_res_result = data[i] * (EFB_WIDTH * EFB_HEIGHT) / - (g_renderer->GetTargetWidth() * g_renderer->GetTargetHeight()); + (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); native_res_result /= g_ActiveConfig.iMultisamples; diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp index cae5f71297..a5bd155afb 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp @@ -9,7 +9,7 @@ #include "Common/GL/GLExtensions/GLExtensions.h" #include "VideoBackends/OGL/OGLGfx.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -165,7 +165,7 @@ void PerfQueryGL::FlushOne() // TODO: Dropping the lower 2 bits from this count should be closer to actual // hardware behavior when drawing triangles. result = static_cast(result) * EFB_WIDTH * EFB_HEIGHT / - (g_renderer->GetTargetWidth() * g_renderer->GetTargetHeight()); + (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); // Adjust for multisampling if (g_ActiveConfig.iMultisamples > 1) @@ -265,7 +265,7 @@ void PerfQueryGLESNV::FlushOne() // TODO: Dropping the lower 2 bits from this count should be closer to actual // hardware behavior when drawing triangles. const u64 native_res_result = static_cast(result) * EFB_WIDTH * EFB_HEIGHT / - (g_renderer->GetTargetWidth() * g_renderer->GetTargetHeight()); + (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); diff --git a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp index d362deaf37..d16c8c379b 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp @@ -15,7 +15,7 @@ #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/VKGfx.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" namespace Vulkan @@ -219,8 +219,8 @@ void PerfQuery::ReadbackQueries(u32 query_count) // NOTE: Reported pixel metrics should be referenced to native resolution const u64 native_res_result = static_cast(m_query_result_buffer[i]) * EFB_WIDTH / - g_renderer->GetTargetWidth() * EFB_HEIGHT / - g_renderer->GetTargetHeight(); + g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / + g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); } diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index 7cbb134f7e..8c254d4e71 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -158,8 +158,6 @@ void AsyncRequests::HandleEvent(const AsyncRequests::Event& e) case Event::SWAP_EVENT: g_presenter->ViSwap(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, e.swap_event.fbHeight, e.time); - g_renderer->TrackSwaps(e.swap_event.xfbAddr, e.swap_event.fbWidth, e.swap_event.fbStride, - e.swap_event.fbHeight, e.time); break; case Event::BBOX_READ: diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index d022f46103..ef705a50fd 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -198,7 +198,7 @@ void SetScissorAndViewport() { auto native_rc = ComputeScissorRects().Best(); - auto target_rc = g_renderer->ConvertEFBRectangle(native_rc.rect); + auto target_rc = g_framebuffer_manager->ConvertEFBRectangle(native_rc.rect); auto converted_rc = g_gfx->ConvertFramebufferRectangle(target_rc, g_gfx->GetCurrentFramebuffer()); g_gfx->SetScissorRect(converted_rc); @@ -216,10 +216,10 @@ void SetScissorAndViewport() raw_height = std::round(raw_height); } - float x = g_renderer->EFBToScaledXf(raw_x); - float y = g_renderer->EFBToScaledYf(raw_y); - float width = g_renderer->EFBToScaledXf(raw_width); - float height = g_renderer->EFBToScaledYf(raw_height); + float x = g_framebuffer_manager->EFBToScaledXf(raw_x); + float y = g_framebuffer_manager->EFBToScaledYf(raw_y); + float width = g_framebuffer_manager->EFBToScaledXf(raw_width); + float height = g_framebuffer_manager->EFBToScaledYf(raw_height); float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; float max_depth = xfmem.viewport.farZ / 16777216.0f; if (width < 0.f) diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 1985ac6527..6d50377950 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -352,7 +352,6 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, // below div two to convert from bytes to pixels - it expects width, not stride u64 ticks = Core::System::GetInstance().GetCoreTiming().GetTicks(); g_presenter->ImmediateSwap(destAddr, destStride / 2, destStride, height, ticks); - g_renderer->TrackSwaps(destAddr, destStride / 2, destStride, height, ticks); } else { diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 5696ebb2b8..38cab15f00 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -10,6 +10,7 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Core/Config/GraphicsSettings.h" +#include "Core/System.h" #include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractPipeline.h" @@ -19,7 +20,8 @@ #include "VideoCommon/BPFunctions.h" #include "VideoCommon/DriverDetails.h" #include "VideoCommon/FramebufferShaderGen.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -145,16 +147,16 @@ static u32 CalculateEFBLayers() return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? 2 : 1; } -TextureConfig FramebufferManager::GetEFBColorTextureConfig() +TextureConfig FramebufferManager::GetEFBColorTextureConfig(u32 width, u32 height) { - return TextureConfig(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), 1, + return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples, GetEFBColorFormat(), AbstractTextureFlag_RenderTarget); } -TextureConfig FramebufferManager::GetEFBDepthTextureConfig() +TextureConfig FramebufferManager::GetEFBDepthTextureConfig(u32 width, u32 height) { - return TextureConfig(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), 1, + return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples, GetEFBDepthFormat(), AbstractTextureFlag_RenderTarget); } @@ -169,10 +171,65 @@ FramebufferState FramebufferManager::GetEFBFramebufferState() const return ret; } +MathUtil::Rectangle +FramebufferManager::ConvertEFBRectangle(const MathUtil::Rectangle& rc) const +{ + MathUtil::Rectangle result; + result.left = EFBToScaledX(rc.left); + result.top = EFBToScaledY(rc.top); + result.right = EFBToScaledX(rc.right); + result.bottom = EFBToScaledY(rc.bottom); + return result; +} + +unsigned int FramebufferManager::GetEFBScale() const +{ + return m_efb_scale; +} + +int FramebufferManager::EFBToScaledX(int x) const +{ + return x * static_cast(m_efb_scale); +} + +int FramebufferManager::EFBToScaledY(int y) const +{ + return y * static_cast(m_efb_scale); +} + +float FramebufferManager::EFBToScaledXf(float x) const +{ + return x * ((float)GetEFBWidth() / (float)EFB_WIDTH); +} + +float FramebufferManager::EFBToScaledYf(float y) const +{ + return y * ((float)GetEFBHeight() / (float)EFB_HEIGHT); +} + +std::tuple FramebufferManager::CalculateTargetSize() +{ + if (g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL) + m_efb_scale = g_presenter->AutoIntegralScale(); + else + m_efb_scale = g_ActiveConfig.iEFBScale; + + const u32 max_size = g_ActiveConfig.backend_info.MaxTextureSize; + if (max_size < EFB_WIDTH * m_efb_scale) + m_efb_scale = max_size / EFB_WIDTH; + + u32 new_efb_width = std::max(EFB_WIDTH * static_cast(m_efb_scale), 1u); + u32 new_efb_height = std::max(EFB_HEIGHT * static_cast(m_efb_scale), 1u); + + return std::make_tuple(new_efb_width, new_efb_height); +} + bool FramebufferManager::CreateEFBFramebuffer() { - const TextureConfig efb_color_texture_config = GetEFBColorTextureConfig(); - const TextureConfig efb_depth_texture_config = GetEFBDepthTextureConfig(); + auto [width, height] = CalculateTargetSize(); + + const TextureConfig efb_color_texture_config = GetEFBColorTextureConfig(width, height); + const TextureConfig efb_depth_texture_config = GetEFBDepthTextureConfig(width, height); // We need a second texture to swap with for changing pixel formats m_efb_color_texture = g_gfx->CreateTexture(efb_color_texture_config, "EFB color texture"); @@ -634,7 +691,7 @@ void FramebufferManager::DestroyReadbackPipelines() bool FramebufferManager::CreateReadbackFramebuffer() { - if (g_renderer->GetEFBScale() != 1) + if (GetEFBScale() != 1) { const TextureConfig color_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH, IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, @@ -655,7 +712,7 @@ bool FramebufferManager::CreateReadbackFramebuffer() (IsUsingTiledEFBCache() && !g_ActiveConfig.backend_info.bSupportsPartialDepthCopies) || !AbstractTexture::IsCompatibleDepthAndColorFormats(m_efb_depth_texture->GetFormat(), GetEFBDepthCopyFormat()) || - g_renderer->GetEFBScale() != 1) + GetEFBScale() != 1) { const TextureConfig depth_config(IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_WIDTH, IsUsingTiledEFBCache() ? m_efb_cache_tile_size : EFB_HEIGHT, 1, @@ -732,10 +789,10 @@ void FramebufferManager::PopulateEFBCache(bool depth, u32 tile_index, bool async // Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on. EFBCacheData& data = depth ? m_efb_depth_cache : m_efb_color_cache; const MathUtil::Rectangle rect = GetEFBCacheTileRect(tile_index); - const MathUtil::Rectangle native_rect = g_renderer->ConvertEFBRectangle(rect); + const MathUtil::Rectangle native_rect = ConvertEFBRectangle(rect); AbstractTexture* src_texture = depth ? ResolveEFBDepthTexture(native_rect) : ResolveEFBColorTexture(native_rect); - if (g_renderer->GetEFBScale() != 1 || force_intermediate_copy) + if (GetEFBScale() != 1 || force_intermediate_copy) { // Downsample from internal resolution to 1x. // TODO: This won't produce correct results at IRs above 2x. More samples are required. @@ -795,9 +852,9 @@ void FramebufferManager::ClearEFB(const MathUtil::Rectangle& rc, bool color FlagPeekCacheAsOutOfDate(); // Native -> EFB coordinates - MathUtil::Rectangle target_rc = g_renderer->ConvertEFBRectangle(rc); + MathUtil::Rectangle target_rc = ConvertEFBRectangle(rc); target_rc = g_gfx->ConvertFramebufferRectangle(target_rc, m_efb_framebuffer.get()); - target_rc.ClampUL(0, 0, g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight()); + target_rc.ClampUL(0, 0, m_efb_framebuffer->GetWidth(), m_efb_framebuffer->GetWidth()); // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha // channel to 0xFF. @@ -925,7 +982,7 @@ void FramebufferManager::CreatePokeVertices(std::vector* destinat // GPU will expand the point to a quad. const float cs_x = (static_cast(x) + 0.5f) * cs_pixel_width - 1.0f; const float cs_y = 1.0f - (static_cast(y) + 0.5f) * cs_pixel_height; - const float point_size = static_cast(g_renderer->GetEFBScale()); + const float point_size = static_cast(GetEFBScale()); destination_list->push_back({{cs_x, cs_y, z, point_size}, color}); return; } diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index c3871c8e15..0eb5d4aa93 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" #include "Common/EnumFormatter.h" @@ -54,8 +55,8 @@ public: static AbstractTextureFormat GetEFBColorFormat(); static AbstractTextureFormat GetEFBDepthFormat(); static AbstractTextureFormat GetEFBDepthCopyFormat(); - static TextureConfig GetEFBColorTextureConfig(); - static TextureConfig GetEFBDepthTextureConfig(); + static TextureConfig GetEFBColorTextureConfig(u32 width, u32 height); + static TextureConfig GetEFBDepthTextureConfig(u32 width, u32 height); // Accessors. AbstractTexture* GetEFBColorTexture() const { return m_efb_color_texture.get(); } @@ -69,6 +70,20 @@ public: bool IsEFBStereo() const { return m_efb_color_texture->GetLayers() > 1; } FramebufferState GetEFBFramebufferState() const; + // EFB coordinate conversion functions + // Use this to convert a whole native EFB rect to backbuffer coordinates + MathUtil::Rectangle ConvertEFBRectangle(const MathUtil::Rectangle& rc) const; + + unsigned int GetEFBScale() const; + + // Use this to upscale native EFB coordinates to IDEAL internal resolution + int EFBToScaledX(int x) const; + int EFBToScaledY(int y) const; + + // Floating point versions of the above - only use them if really necessary + float EFBToScaledXf(float x) const; + float EFBToScaledYf(float y) const; + // First-time setup. bool Initialize(); @@ -172,9 +187,13 @@ protected: void DrawPokeVertices(const EFBPokeVertex* vertices, u32 vertex_count, const AbstractPipeline* pipeline); + std::tuple CalculateTargetSize(); + void DoLoadState(PointerWrap& p); void DoSaveState(PointerWrap& p); + float m_efb_scale = 0.0f; + std::unique_ptr m_efb_color_texture; std::unique_ptr m_efb_convert_color_texture; std::unique_ptr m_efb_depth_texture; diff --git a/Source/Core/VideoCommon/PixelShaderManager.cpp b/Source/Core/VideoCommon/PixelShaderManager.cpp index 35db2c6684..c929db6995 100644 --- a/Source/Core/VideoCommon/PixelShaderManager.cpp +++ b/Source/Core/VideoCommon/PixelShaderManager.cpp @@ -7,7 +7,7 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" @@ -74,7 +74,7 @@ void PixelShaderManager::Dirty() // Any constants that can changed based on settings should be re-calculated m_fog_range_adjusted_changed = true; - SetEfbScaleChanged(g_renderer->EFBToScaledXf(1), g_renderer->EFBToScaledYf(1)); + SetEfbScaleChanged(g_framebuffer_manager->EFBToScaledXf(1), g_framebuffer_manager->EFBToScaledYf(1)); SetFogParamChanged(); dirty = true; @@ -103,7 +103,7 @@ void PixelShaderManager::SetConstants() // TODO: Shouldn't this be EFBToScaledXf? constants.fogf[2] = ScreenSpaceCenter; constants.fogf[3] = - static_cast(g_renderer->EFBToScaledX(static_cast(2.0f * xfmem.viewport.wd))); + static_cast(g_framebuffer_manager->EFBToScaledX(static_cast(2.0f * xfmem.viewport.wd))); for (size_t i = 0, vec_index = 0; i < std::size(bpmem.fogRange.K); i++) { diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 0612d96d3b..b7a60c7dc6 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -3,6 +3,7 @@ #include "VideoCommon/Present.h" +#include "Common/ChunkFile.h" #include "Core/HW/VideoInterface.h" #include "Core/Host.h" @@ -17,6 +18,7 @@ #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoEvents.h" +#include "Present.h" std::unique_ptr g_presenter; @@ -61,35 +63,39 @@ bool Presenter::Initialize() return true; } -bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height) +bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { ReleaseXFBContentLock(); m_xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); + bool is_duplicate = m_xfb_entry->id == m_last_xfb_id; m_xfb_entry->AcquireContentLock(); - if (m_last_xfb_id == m_xfb_entry->id) - return false; + m_last_xfb_addr = xfb_addr; + m_last_xfb_ticks = ticks; + m_last_xfb_width = fb_width; + m_last_xfb_stride = fb_stride; + m_last_xfb_height = fb_height; m_last_xfb_id = m_xfb_entry->id; - return true; + return is_duplicate; } void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - bool unique = FetchXFB(xfb_addr, fb_width, fb_stride, fb_height); + bool unique = FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); PresentInfo present_info; present_info.emulated_timestamp = ticks; if (unique) { - present_info.frame_count = g_renderer->FrameCountIncrement(); + present_info.frame_count = m_frame_count++; present_info.reason = PresentInfo::PresentReason::VideoInterface; } else { - present_info.frame_count = g_renderer->FrameCount() - 1; // Previous frame + present_info.frame_count = m_frame_count - 1; // Previous frame present_info.reason = PresentInfo::PresentReason::VideoInterfaceDuplicate; } present_info.present_count = m_present_count; @@ -111,11 +117,11 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - FetchXFB(xfb_addr, fb_width, fb_stride, fb_height); + FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); PresentInfo present_info; present_info.emulated_timestamp = ticks; - present_info.frame_count = g_renderer->FrameCountIncrement(); + present_info.frame_count = m_frame_count++; present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; @@ -145,7 +151,7 @@ void Presenter::ProcessFrameDumping(u64 ticks) const } g_frame_dumper->DumpCurrentFrame(m_xfb_entry->texture.get(), m_xfb_rect, target_rect, ticks, - g_renderer->FrameCount()); + m_frame_count); } } @@ -307,6 +313,13 @@ void* Presenter::GetNewSurfaceHandle() return handle; } +u32 Presenter::AutoIntegralScale() const +{ + // Calculate a scale based on the window size + u32 width = EFB_WIDTH * m_target_rectangle.GetWidth() / m_last_xfb_width; + u32 height = EFB_HEIGHT * m_target_rectangle.GetHeight() / m_last_xfb_height; + return std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1); +} void Presenter::SetWindowSize(int width, int height) { const auto [out_width, out_height] = g_presenter->CalculateOutputDimensions(width, height); @@ -577,4 +590,26 @@ void Presenter::SetMousePress(u32 button_mask) m_onscreen_ui->SetMousePress(button_mask); } +void Presenter::DoState(PointerWrap& p) +{ + p.Do(m_frame_count); + p.Do(m_last_xfb_ticks); + p.Do(m_last_xfb_addr); + p.Do(m_last_xfb_width); + p.Do(m_last_xfb_stride); + p.Do(m_last_xfb_height); + + if (p.IsReadMode()) + { + // This technically counts as the end of the frame + AfterFrameEvent::Trigger(); + + // re-display the most recent XFB + ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, + m_last_xfb_height, m_last_xfb_ticks); + } + +} + + } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index fb4799898c..235dba11d4 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -8,6 +8,7 @@ #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoCommon.h" #include #include @@ -47,6 +48,7 @@ public: int GetBackbufferWidth() const { return m_backbuffer_width; } int GetBackbufferHeight() const { return m_backbuffer_height; } float GetBackbufferScale() const { return m_backbuffer_scale; } + u32 AutoIntegralScale() const; AbstractTextureFormat GetBackbufferFormat() const { return m_backbuffer_format; } void SetWindowSize(int width, int height); void SetBackbuffer(int backbuffer_width, int backbuffer_height); @@ -86,12 +88,16 @@ public: void SetMousePos(float x, float y); void SetMousePress(u32 button_mask); + int FrameCount() const { return m_frame_count; } + + void DoState(PointerWrap& p); + const MathUtil::Rectangle& GetTargetRectangle() const { return m_target_rectangle; } private: // Fetches the XFB texture from the texture cache. // Returns true the contents have changed since last time - bool FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height); + bool FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); void ProcessFrameDumping(u64 ticks) const; @@ -130,7 +136,15 @@ private: std::unique_ptr m_post_processor; std::unique_ptr m_onscreen_ui; + u64 m_frame_count = 0; u64 m_present_count = 0; + + // XFB tracking + u64 m_last_xfb_ticks = 0; + u32 m_last_xfb_addr = 0; + u32 m_last_xfb_width = MAX_XFB_WIDTH; + u32 m_last_xfb_stride = 0; + u32 m_last_xfb_height = MAX_XFB_HEIGHT; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 6c991c74ae..c30495f9d1 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -37,7 +37,6 @@ #include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" -#include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -48,10 +47,8 @@ std::unique_ptr g_renderer; Renderer::Renderer() - : m_prev_efb_format{PixelFormat::INVALID_FMT}, - m_last_xfb_width{MAX_XFB_WIDTH}, m_last_xfb_height{MAX_XFB_HEIGHT} + : m_prev_efb_format{PixelFormat::INVALID_FMT} { - CalculateTargetSize(); UpdateWidescreen(); m_config_changed_handle = ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); @@ -168,83 +165,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } -unsigned int Renderer::GetEFBScale() const -{ - return m_efb_scale; -} - -int Renderer::EFBToScaledX(int x) const -{ - return x * static_cast(m_efb_scale); -} - -int Renderer::EFBToScaledY(int y) const -{ - return y * static_cast(m_efb_scale); -} - -float Renderer::EFBToScaledXf(float x) const -{ - return x * ((float)GetTargetWidth() / (float)EFB_WIDTH); -} - -float Renderer::EFBToScaledYf(float y) const -{ - return y * ((float)GetTargetHeight() / (float)EFB_HEIGHT); -} - -std::tuple Renderer::CalculateTargetScale(int x, int y) const -{ - return std::make_tuple(x * static_cast(m_efb_scale), y * static_cast(m_efb_scale)); -} - -// return true if target size changed -bool Renderer::CalculateTargetSize() -{ - if (g_ActiveConfig.iEFBScale == EFB_SCALE_AUTO_INTEGRAL) - { - auto target_rectangle = g_presenter->GetTargetRectangle(); - // Set a scale based on the window size - int width = EFB_WIDTH * target_rectangle.GetWidth() / m_last_xfb_width; - int height = EFB_HEIGHT * target_rectangle.GetHeight() / m_last_xfb_height; - m_efb_scale = std::max((width - 1) / EFB_WIDTH + 1, (height - 1) / EFB_HEIGHT + 1); - } - else - { - m_efb_scale = g_ActiveConfig.iEFBScale; - } - - const u32 max_size = g_ActiveConfig.backend_info.MaxTextureSize; - if (max_size < EFB_WIDTH * m_efb_scale) - m_efb_scale = max_size / EFB_WIDTH; - - auto [new_efb_width, new_efb_height] = CalculateTargetScale(EFB_WIDTH, EFB_HEIGHT); - new_efb_width = std::max(new_efb_width, 1); - new_efb_height = std::max(new_efb_height, 1); - - if (new_efb_width != m_target_width || new_efb_height != m_target_height) - { - m_target_width = new_efb_width; - m_target_height = new_efb_height; - auto& system = Core::System::GetInstance(); - auto& pixel_shader_manager = system.GetPixelShaderManager(); - pixel_shader_manager.SetEfbScaleChanged(EFBToScaledXf(1), EFBToScaledYf(1)); - return true; - } - return false; -} - - -MathUtil::Rectangle Renderer::ConvertEFBRectangle(const MathUtil::Rectangle& rc) const -{ - MathUtil::Rectangle result; - result.left = EFBToScaledX(rc.left); - result.top = EFBToScaledY(rc.top); - result.right = EFBToScaledX(rc.right); - result.bottom = EFBToScaledY(rc.bottom); - return result; -} - void Renderer::UpdateWidescreen() { if (SConfig::GetInstance().bWii) @@ -324,20 +244,7 @@ void Renderer::OnConfigChanged(u32 bits) UpdateWidescreen(); } -void Renderer::TrackSwaps(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) -{ - if (xfb_addr && fb_width && fb_stride && fb_height) - { - // Update our last xfb values - m_last_xfb_addr = xfb_addr; - m_last_xfb_ticks = ticks; - m_last_xfb_width = fb_width; - m_last_xfb_stride = fb_stride; - m_last_xfb_height = fb_height; - } -} - -bool Renderer::UseVertexDepthRange() const +bool Renderer::UseVertexDepthRange() { // We can't compute the depth range in the vertex shader if we don't support depth clamp. if (!g_ActiveConfig.backend_info.bSupportsDepthClamp) @@ -359,29 +266,9 @@ bool Renderer::UseVertexDepthRange() const void Renderer::DoState(PointerWrap& p) { p.Do(m_is_game_widescreen); - p.Do(m_frame_count); - p.Do(m_prev_efb_format); - p.Do(m_last_xfb_ticks); - p.Do(m_last_xfb_addr); - p.Do(m_last_xfb_width); - p.Do(m_last_xfb_stride); - p.Do(m_last_xfb_height); - - g_bounding_box->DoState(p); if (p.IsReadMode()) { m_was_orthographically_anamorphic = false; - - // This technically counts as the end of the frame - AfterFrameEvent::Trigger(); - - // re-display the most recent XFB - g_presenter->ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, - m_last_xfb_height, m_last_xfb_ticks); } - -#if defined(HAVE_FFMPEG) - g_frame_dumper->DoState(p); -#endif } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index c1c00aa382..52415c0f36 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -25,80 +25,38 @@ struct EfbPokeData // Renderer really isn't a very good name for this class - it's more like "Misc". // It used to be a massive mess, but almost everything has been refactored out. // -// Most of the remaining functionality is related to EFB and Internal Resolution scaling -// -// It also handles the Widescreen heuristic, frame counting and tracking xfb across save states. +// All that's left is Widescreen tracking, pixel format and a thin abstraction layer +// for VideoSoftware to intercept EFB accesses. class Renderer { public: Renderer(); virtual ~Renderer(); - // Ideal internal resolution - multiple of the native EFB resolution - int GetTargetWidth() const { return m_target_width; } - int GetTargetHeight() const { return m_target_height; } - - // EFB coordinate conversion functions - // Use this to convert a whole native EFB rect to backbuffer coordinates - MathUtil::Rectangle ConvertEFBRectangle(const MathUtil::Rectangle& rc) const; - - unsigned int GetEFBScale() const; - - // Use this to upscale native EFB coordinates to IDEAL internal resolution - int EFBToScaledX(int x) const; - int EFBToScaledY(int y) const; - - // Floating point versions of the above - only use them if really necessary - float EFBToScaledXf(float x) const; - float EFBToScaledYf(float y) const; - virtual void ReinterpretPixelData(EFBReinterpretType convtype); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - // Track swaps for save-states - void TrackSwaps(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); - bool IsGameWidescreen() const { return m_is_game_widescreen; } PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } - bool UseVertexDepthRange() const; + static bool UseVertexDepthRange(); void DoState(PointerWrap& p); - bool CalculateTargetSize(); - - int FrameCount() const { return m_frame_count; } - int FrameCountIncrement() { return m_frame_count++; } - void OnConfigChanged(u32 bits); protected: void UpdateWidescreen(); void UpdateWidescreenHeuristic(); - std::tuple CalculateTargetScale(int x, int y) const; - bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; - // The framebuffer size - int m_target_width = 1; - int m_target_height = 1; - private: PixelFormat m_prev_efb_format; - unsigned int m_efb_scale = 1; - - u64 m_last_xfb_ticks = 0; - u32 m_last_xfb_addr = 0; - u32 m_last_xfb_width = 0; - u32 m_last_xfb_stride = 0; - u32 m_last_xfb_height = 0; - - int m_frame_count = 0; EventHook m_update_widescreen_handle; EventHook m_config_changed_handle; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 947b2e5965..02601baaf4 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -44,7 +44,7 @@ #include "VideoCommon/HiresTextures.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelShaderManager.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Present.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TMEM.h" @@ -783,7 +783,7 @@ void TextureCacheBase::OnFrameEnd() g_texture_cache->FlushEFBCopies(); } - g_texture_cache->Cleanup(g_renderer->FrameCount()); + g_texture_cache->Cleanup(g_presenter->FrameCount()); } void TCacheEntry::DoState(PointerWrap& p) @@ -921,17 +921,17 @@ RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_ entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight()) { ScaleTextureCacheEntryTo(entry_to_update, - g_renderer->EFBToScaledX(entry_to_update->native_width), - g_renderer->EFBToScaledY(entry_to_update->native_height)); - ScaleTextureCacheEntryTo(entry, g_renderer->EFBToScaledX(entry->native_width), - g_renderer->EFBToScaledY(entry->native_height)); + g_framebuffer_manager->EFBToScaledX(entry_to_update->native_width), + g_framebuffer_manager->EFBToScaledY(entry_to_update->native_height)); + ScaleTextureCacheEntryTo(entry, g_framebuffer_manager->EFBToScaledX(entry->native_width), + g_framebuffer_manager->EFBToScaledY(entry->native_height)); - src_x = g_renderer->EFBToScaledX(src_x); - src_y = g_renderer->EFBToScaledY(src_y); - dst_x = g_renderer->EFBToScaledX(dst_x); - dst_y = g_renderer->EFBToScaledY(dst_y); - copy_width = g_renderer->EFBToScaledX(copy_width); - copy_height = g_renderer->EFBToScaledY(copy_height); + src_x = g_framebuffer_manager->EFBToScaledX(src_x); + src_y = g_framebuffer_manager->EFBToScaledY(src_y); + dst_x = g_framebuffer_manager->EFBToScaledX(dst_x); + dst_y = g_framebuffer_manager->EFBToScaledY(dst_y); + copy_width = g_framebuffer_manager->EFBToScaledX(copy_width); + copy_height = g_framebuffer_manager->EFBToScaledY(copy_height); } // If the source rectangle is outside of what we actually have in VRAM, skip the copy. @@ -1091,7 +1091,7 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex, // that have arbitrary contents, eg. are used for fog effects where the // distance they kick in at is important to preserve at any resolution. // Correct this with the upscaling factor of custom textures. - s32 lod_offset = std::log2(g_renderer->GetEFBScale() / custom_tex_scale) * 256.f; + s32 lod_offset = std::log2(g_framebuffer_manager->GetEFBScale() / custom_tex_scale) * 256.f; state.tm0.lod_bias = std::clamp(state.tm0.lod_bias + lod_offset, -32768, 32767); // Anisotropic also pushes mips farther away so it cannot be used either @@ -1957,8 +1957,8 @@ void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry) // copies to be stitched together. if (create_upscaled_copy) { - ScaleTextureCacheEntryTo(stitched_entry, g_renderer->EFBToScaledX(stitched_entry->native_width), - g_renderer->EFBToScaledY(stitched_entry->native_height)); + ScaleTextureCacheEntryTo(stitched_entry, g_framebuffer_manager->EFBToScaledX(stitched_entry->native_width), + g_framebuffer_manager->EFBToScaledY(stitched_entry->native_height)); } for (TCacheEntry* entry : candidates) @@ -1993,17 +1993,17 @@ void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry) // Scale to internal resolution. if (entry->native_width != entry->GetWidth()) { - src_x = g_renderer->EFBToScaledX(src_x); - src_y = g_renderer->EFBToScaledY(src_y); - src_width = g_renderer->EFBToScaledX(src_width); - src_height = g_renderer->EFBToScaledY(src_height); + src_x = g_framebuffer_manager->EFBToScaledX(src_x); + src_y = g_framebuffer_manager->EFBToScaledY(src_y); + src_width = g_framebuffer_manager->EFBToScaledX(src_width); + src_height = g_framebuffer_manager->EFBToScaledY(src_height); } if (create_upscaled_copy) { - dst_x = g_renderer->EFBToScaledX(dst_x); - dst_y = g_renderer->EFBToScaledY(dst_y); - dst_width = g_renderer->EFBToScaledX(dst_width); - dst_height = g_renderer->EFBToScaledY(dst_height); + dst_x = g_framebuffer_manager->EFBToScaledX(dst_x); + dst_y = g_framebuffer_manager->EFBToScaledY(dst_y); + dst_width = g_framebuffer_manager->EFBToScaledX(dst_width); + dst_height = g_framebuffer_manager->EFBToScaledY(dst_height); } // If the source rectangle is outside of what we actually have in VRAM, skip the copy. @@ -2183,8 +2183,8 @@ void TextureCacheBase::CopyRenderTargetToTexture( // For the latter, we keep the EFB resolution for the virtual XFB blit. u32 tex_w = width; u32 tex_h = height; - u32 scaled_tex_w = g_renderer->EFBToScaledX(width); - u32 scaled_tex_h = g_renderer->EFBToScaledY(height); + u32 scaled_tex_w = g_framebuffer_manager->EFBToScaledX(width); + u32 scaled_tex_h = g_framebuffer_manager->EFBToScaledY(height); if (scaleByHalf) { @@ -2269,7 +2269,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( // TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more // complex down filtering to average all pixels and produce the correct result. const bool linear_filter = - !is_depth_copy && (scaleByHalf || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f); + !is_depth_copy && (scaleByHalf || g_framebuffer_manager->GetEFBScale() != 1 || y_scale > 1.0f); RcTcacheEntry entry; if (copy_to_vram) @@ -2803,7 +2803,7 @@ void TextureCacheBase::CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_c return; } - const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); + const auto scaled_src_rect = g_framebuffer_manager->ConvertEFBRectangle(src_rect); const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle( scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer()); AbstractTexture* src_texture = @@ -2877,7 +2877,7 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& return; } - const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect); + const auto scaled_src_rect = g_framebuffer_manager->ConvertEFBRectangle(src_rect); const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle( scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer()); AbstractTexture* src_texture = diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 3779051b6b..f2a203cfe1 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -20,6 +20,7 @@ #include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FreeLookCamera.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" @@ -339,10 +340,10 @@ void VertexShaderManager::SetConstants(const std::vector& textures) const bool bUseVertexRounding = g_ActiveConfig.UseVertexRounding(); const float viewport_width = bUseVertexRounding ? (2.f * xfmem.viewport.wd) : - g_renderer->EFBToScaledXf(2.f * xfmem.viewport.wd); + g_framebuffer_manager->EFBToScaledXf(2.f * xfmem.viewport.wd); const float viewport_height = bUseVertexRounding ? (2.f * xfmem.viewport.ht) : - g_renderer->EFBToScaledXf(2.f * xfmem.viewport.ht); + g_framebuffer_manager->EFBToScaledXf(2.f * xfmem.viewport.ht); const float pixel_size_x = 2.f / viewport_width; const float pixel_size_y = 2.f / viewport_height; constants.pixelcentercorrection[0] = pixel_center_correction * pixel_size_x; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 7f710730bc..21f006af4a 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -14,6 +14,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Movie.h" +#include "Core/System.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPFunctions.h" @@ -24,7 +25,7 @@ #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VertexManagerBase.h" @@ -253,6 +254,7 @@ void CheckForConfigChanges() const auto old_texture_filtering_mode = g_ActiveConfig.texture_filtering_mode; const bool old_vsync = g_ActiveConfig.bVSyncActive; const bool old_bbox = g_ActiveConfig.bBBoxEnable; + const int old_efb_scale = g_ActiveConfig.iEFBScale; const u32 old_game_mod_changes = g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0; const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods; @@ -302,7 +304,7 @@ void CheckForConfigChanges() changed_bits |= CONFIG_CHANGE_BIT_VSYNC; if (old_bbox != g_ActiveConfig.bBBoxEnable) changed_bits |= CONFIG_CHANGE_BIT_BBOX; - if (g_renderer->CalculateTargetSize()) + if (old_efb_scale != g_ActiveConfig.iEFBScale) changed_bits |= CONFIG_CHANGE_BIT_TARGET_SIZE; if (old_suggested_aspect_mode != g_ActiveConfig.suggested_aspect_mode) changed_bits |= CONFIG_CHANGE_BIT_ASPECT_RATIO; @@ -315,10 +317,7 @@ void CheckForConfigChanges() if (changed_bits == 0) return; - // Notify all listeners - ConfigChangedEvent::Trigger(changed_bits); - - // TODO: Move everything else to the ConfigChanged event + float old_scale = g_framebuffer_manager->GetEFBScale(); // Framebuffer changed? if (changed_bits & (CONFIG_CHANGE_BIT_MULTISAMPLES | CONFIG_CHANGE_BIT_STEREO_MODE | @@ -327,6 +326,13 @@ void CheckForConfigChanges() g_framebuffer_manager->RecreateEFBFramebuffer(); } + if (old_scale != g_framebuffer_manager->GetEFBScale()) + { + auto& system = Core::System::GetInstance(); + auto& pixel_shader_manager = system.GetPixelShaderManager(); + pixel_shader_manager.Dirty(); + } + // Reload shaders if host config has changed. if (changed_bits & (CONFIG_CHANGE_BIT_HOST_CONFIG | CONFIG_CHANGE_BIT_MULTISAMPLES)) { @@ -342,6 +348,11 @@ void CheckForConfigChanges() { BPFunctions::SetScissorAndViewport(); } + + // Notify all listeners + ConfigChangedEvent::Trigger(changed_bits); + + // TODO: Move everything else to the ConfigChanged event } static EventHook s_check_config_event = AfterFrameEvent::Register([] { diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 75e9e4d3bc..6981e14adb 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -7,15 +7,18 @@ #include "Common/ChunkFile.h" #include "Core/System.h" +#include "VideoCommon/BoundingBox.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/BPStructs.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/FrameDumper.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/TMEM.h" #include "VideoCommon/TextureCacheBase.h" @@ -95,6 +98,14 @@ void VideoCommon_DoState(PointerWrap& p) g_renderer->DoState(p); p.DoMarker("Renderer"); + g_presenter->DoState(p); + g_frame_dumper->DoState(p); + p.DoMarker("Presenter"); + + g_bounding_box->DoState(p); + p.DoMarker("Bounding Box"); + + // Refresh state. if (p.IsReadMode()) { From 31cfe8250d5a27bd586136d0b71b09736861ad7d Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 17:29:16 +1300 Subject: [PATCH 41/60] Lint fixes --- Source/Core/Common/EventHook.h | 6 +- Source/Core/Common/StringLiteral.h | 9 +-- Source/Core/Core/Core.cpp | 14 ++-- Source/Core/Core/FifoPlayer/FifoRecorder.cpp | 36 ++++----- Source/Core/VideoBackends/D3D/D3DGfx.cpp | 32 ++++---- Source/Core/VideoBackends/D3D/D3DMain.cpp | 4 +- .../Core/VideoBackends/D3D/D3DPerfQuery.cpp | 8 +- Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp | 38 +++++----- Source/Core/VideoBackends/D3D12/D3D12Gfx.h | 4 +- .../VideoBackends/D3D12/D3D12PerfQuery.cpp | 2 +- .../D3D12/D3D12VertexManager.cpp | 12 +-- .../Core/VideoBackends/D3D12/VideoBackend.cpp | 2 +- Source/Core/VideoBackends/Metal/MTLGfx.h | 4 +- Source/Core/VideoBackends/Metal/MTLGfx.mm | 55 ++++++-------- .../Core/VideoBackends/Metal/MTLPerfQuery.mm | 5 +- Source/Core/VideoBackends/OGL/OGLGfx.h | 4 +- .../Core/VideoBackends/OGL/OGLPerfQuery.cpp | 5 +- Source/Core/VideoBackends/Software/SWGfx.h | 4 +- Source/Core/VideoBackends/Vulkan/VKGfx.h | 4 +- Source/Core/VideoBackends/Vulkan/VKMain.cpp | 2 +- Source/Core/VideoCommon/AsyncRequests.cpp | 2 +- Source/Core/VideoCommon/BPStructs.cpp | 3 +- .../Core/VideoCommon/FramebufferManager.cpp | 13 ++-- Source/Core/VideoCommon/FramebufferManager.h | 3 +- .../Runtime/GraphicsModManager.h | 2 +- Source/Core/VideoCommon/OnScreenUI.h | 1 - .../Core/VideoCommon/PixelShaderManager.cpp | 7 +- Source/Core/VideoCommon/Present.cpp | 15 ++-- Source/Core/VideoCommon/RenderBase.cpp | 15 ++-- Source/Core/VideoCommon/RenderBase.h | 1 - Source/Core/VideoCommon/ShaderCache.cpp | 3 +- Source/Core/VideoCommon/Statistics.cpp | 21 +++--- Source/Core/VideoCommon/TextureCacheBase.cpp | 14 ++-- Source/Core/VideoCommon/VertexManagerBase.cpp | 5 +- .../Core/VideoCommon/VertexShaderManager.cpp | 7 +- Source/Core/VideoCommon/VideoConfig.cpp | 11 +-- Source/Core/VideoCommon/VideoConfig.h | 1 - Source/Core/VideoCommon/VideoEvents.h | 75 +++++++++---------- Source/Core/VideoCommon/VideoState.cpp | 5 +- 39 files changed, 219 insertions(+), 235 deletions(-) diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/EventHook.h index 265013827a..903ff3ae36 100644 --- a/Source/Core/Common/EventHook.h +++ b/Source/Core/Common/EventHook.h @@ -36,7 +36,7 @@ struct HookBase using EventHook = std::unique_ptr; -template +template class Event { public: @@ -46,12 +46,12 @@ private: struct HookImpl : public HookBase { ~HookImpl() override { Event::Remove(this); } - HookImpl(CallbackType callback, std::string name) : m_fn(callback), m_name(name){ } + HookImpl(CallbackType callback, std::string name) : m_fn(callback), m_name(name) {} CallbackType m_fn; std::string m_name; }; -public: +public: // Returns a handle that will unregister the listener when destroyed. static EventHook Register(CallbackType callback, std::string name) { diff --git a/Source/Core/Common/StringLiteral.h b/Source/Core/Common/StringLiteral.h index 6227d48ed6..33ecee31e5 100644 --- a/Source/Core/Common/StringLiteral.h +++ b/Source/Core/Common/StringLiteral.h @@ -7,11 +7,10 @@ // A useful template for passing string literals as arguments to templates // from: https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/ -template -struct StringLiteral { - consteval StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } +template +struct StringLiteral +{ + consteval StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } char value[N]; }; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 495ffc6ba4..f49261483d 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -132,12 +132,14 @@ static thread_local bool tls_is_gpu_thread = false; static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi); -static EventHook s_frame_presented = AfterPresentEvent::Register([](auto& present_info) { - const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); - // The denominator should always be > 0 but if it's not, just return 1 - const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; - Core::Callback_FramePresented(last_speed); -}, "Core Frame Presented"); +static EventHook s_frame_presented = AfterPresentEvent::Register( + [](auto& present_info) { + const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); + // The denominator should always be > 0 but if it's not, just return 1 + const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; + Core::Callback_FramePresented(last_speed); + }, + "Core Frame Presented"); bool GetIsThrottlerTempDisabled() { diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp index 0ec2232843..15063c3f02 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp @@ -18,9 +18,9 @@ #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/TextureDecoder.h" -#include "VideoCommon/XFStructs.h" -#include "VideoCommon/XFMemory.h" #include "VideoCommon/VideoEvents.h" +#include "VideoCommon/XFMemory.h" +#include "VideoCommon/XFStructs.h" class FifoRecorder::FifoRecordAnalyzer : public OpcodeDecoder::Callback { @@ -255,24 +255,26 @@ void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; - m_end_of_frame_event = AfterFrameEvent::Register([this] { - const bool was_recording = OpcodeDecoder::g_record_fifo_data; - OpcodeDecoder::g_record_fifo_data = IsRecording(); + m_end_of_frame_event = AfterFrameEvent::Register( + [this] { + const bool was_recording = OpcodeDecoder::g_record_fifo_data; + OpcodeDecoder::g_record_fifo_data = IsRecording(); - if (!OpcodeDecoder::g_record_fifo_data) - return; + if (!OpcodeDecoder::g_record_fifo_data) + return; - if (!was_recording) - { - RecordInitialVideoMemory(); - } + if (!was_recording) + { + RecordInitialVideoMemory(); + } - auto& system = Core::System::GetInstance(); - auto& command_processor = system.GetCommandProcessor(); - const auto& fifo = command_processor.GetFifo(); - EndFrame(fifo.CPBase.load(std::memory_order_relaxed), - fifo.CPEnd.load(std::memory_order_relaxed)); - }, "FifoRecorder::EndFrame"); + auto& system = Core::System::GetInstance(); + auto& command_processor = system.GetCommandProcessor(); + const auto& fifo = command_processor.GetFifo(); + EndFrame(fifo.CPBase.load(std::memory_order_relaxed), + fifo.CPEnd.load(std::memory_order_relaxed)); + }, + "FifoRecorder::EndFrame"); } void FifoRecorder::RecordInitialVideoMemory() diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.cpp b/Source/Core/VideoBackends/D3D/D3DGfx.cpp index 32dc21fb6a..35be881e01 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.cpp +++ b/Source/Core/VideoBackends/D3D/D3DGfx.cpp @@ -50,19 +50,19 @@ bool Gfx::IsHeadless() const } std::unique_ptr Gfx::CreateTexture(const TextureConfig& config, - std::string_view name) + std::string_view name) { return DXTexture::Create(config, name); } std::unique_ptr Gfx::CreateStagingTexture(StagingTextureType type, - const TextureConfig& config) + const TextureConfig& config) { return DXStagingTexture::Create(type, config); } std::unique_ptr Gfx::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) + AbstractTexture* depth_attachment) { return DXFramebuffer::Create(static_cast(color_attachment), static_cast(depth_attachment)); @@ -78,16 +78,15 @@ Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::str return DXShader::CreateFromBytecode(stage, std::move(*bytecode), name); } -std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, - const void* data, size_t length, - std::string_view name) +std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, + size_t length, std::string_view name) { return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length), name); } std::unique_ptr Gfx::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) + const void* cache_data, + size_t cache_data_length) { return DXPipeline::Create(config); } @@ -129,7 +128,7 @@ void Gfx::SetScissorRect(const MathUtil::Rectangle& rc) } void Gfx::SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) + float far_depth) { // TODO: Move to stateman const CD3D11_VIEWPORT vp(x, y, width, height, near_depth, far_depth); @@ -149,7 +148,7 @@ void Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) } void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, - u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) + u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { D3D::stateman->SetComputeShader(static_cast(shader)->GetD3DComputeShader()); D3D::stateman->SyncComputeBindings(); @@ -222,8 +221,8 @@ void Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) SetFramebuffer(framebuffer); } -void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) +void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, + float depth_value) { SetFramebuffer(framebuffer); D3D::stateman->Apply(); @@ -286,12 +285,9 @@ bool Gfx::IsFullscreen() const SurfaceInfo Gfx::GetSurfaceInfo() const { - return { - m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, - m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, - m_backbuffer_scale, - m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined - }; + return {m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, + m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, m_backbuffer_scale, + m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined}; } } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/D3DMain.cpp b/Source/Core/VideoBackends/D3D/D3DMain.cpp index ae2e0cd959..8256674ad5 100644 --- a/Source/Core/VideoBackends/D3D/D3DMain.cpp +++ b/Source/Core/VideoBackends/D3D/D3DMain.cpp @@ -13,8 +13,8 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBoundingBox.h" -#include "VideoBackends/D3D/D3DPerfQuery.h" #include "VideoBackends/D3D/D3DGfx.h" +#include "VideoBackends/D3D/D3DPerfQuery.h" #include "VideoBackends/D3D/D3DSwapChain.h" #include "VideoBackends/D3D/D3DVertexManager.h" #include "VideoBackends/D3DCommon/D3DCommon.h" @@ -161,7 +161,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) auto bounding_box = std::make_unique(); return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), - std::move(bounding_box)); + std::move(bounding_box)); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp index d72b88c0fb..bef3834dc5 100644 --- a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp @@ -114,8 +114,8 @@ void PerfQuery::FlushOne() // NOTE: Reported pixel metrics should be referenced to native resolution // TODO: Dropping the lower 2 bits from this count should be closer to actual // hardware behavior when drawing triangles. - const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / - g_framebuffer_manager->GetEFBHeight(); + const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * + EFB_HEIGHT / g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); @@ -143,8 +143,8 @@ void PerfQuery::WeakFlush() if (hr == S_OK) { // NOTE: Reported pixel metrics should be referenced to native resolution - const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / - g_framebuffer_manager->GetEFBHeight(); + const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * + EFB_HEIGHT / g_framebuffer_manager->GetEFBHeight(); m_results[entry.query_group].store(static_cast(native_res_result), std::memory_order_relaxed); diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp index e1c82353b2..b3ade2e5d3 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.cpp @@ -49,19 +49,19 @@ bool Gfx::IsHeadless() const } std::unique_ptr Gfx::CreateTexture(const TextureConfig& config, - std::string_view name) + std::string_view name) { return DXTexture::Create(config, name); } std::unique_ptr Gfx::CreateStagingTexture(StagingTextureType type, - const TextureConfig& config) + const TextureConfig& config) { return DXStagingTexture::Create(type, config); } std::unique_ptr Gfx::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) + AbstractTexture* depth_attachment) { return DXFramebuffer::Create(static_cast(color_attachment), static_cast(depth_attachment)); @@ -73,9 +73,8 @@ Gfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::str return DXShader::CreateFromSource(stage, source, name); } -std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, - const void* data, size_t length, - std::string_view name) +std::unique_ptr Gfx::CreateShaderFromBinary(ShaderStage stage, const void* data, + size_t length, std::string_view name) { return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length), name); } @@ -87,8 +86,8 @@ Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) } std::unique_ptr Gfx::CreatePipeline(const AbstractPipelineConfig& config, - const void* cache_data, - size_t cache_data_length) + const void* cache_data, + size_t cache_data_length) { return DXPipeline::Create(config, cache_data, cache_data_length); } @@ -103,8 +102,8 @@ void Gfx::WaitForGPUIdle() ExecuteCommandList(true); } -void Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, - bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) +void Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, + bool alpha_enable, bool z_enable, u32 color, u32 z) { // Use a fast path without the shader if both color/alpha are enabled. const bool fast_color_clear = color_enable && alpha_enable; @@ -229,8 +228,8 @@ void Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) } } -void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) +void Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, + float depth_value) { DXFramebuffer* dxfb = static_cast(framebuffer); BindFramebuffer(dxfb); @@ -318,7 +317,7 @@ void Gfx::UnbindTexture(const AbstractTexture* texture) } void Gfx::SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) + float far_depth) { if (m_state.viewport.TopLeftX == x && m_state.viewport.TopLeftY == y && m_state.viewport.Width == width && m_state.viewport.Height == height && @@ -357,7 +356,7 @@ void Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) } void Gfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y, - u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) + u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) { SetRootSignatures(); SetDescriptorHeaps(); @@ -427,12 +426,9 @@ void Gfx::PresentBackbuffer() SurfaceInfo Gfx::GetSurfaceInfo() const { - return { - m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, - m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, - m_backbuffer_scale, - m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined - }; + return {m_swap_chain ? static_cast(m_swap_chain->GetWidth()) : 0, + m_swap_chain ? static_cast(m_swap_chain->GetHeight()) : 0, m_backbuffer_scale, + m_swap_chain ? m_swap_chain->GetFormat() : AbstractTextureFormat::Undefined}; } void Gfx::OnConfigChanged(u32 bits) @@ -494,7 +490,7 @@ void Gfx::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle) } void Gfx::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, D3D12_CPU_DESCRIPTOR_HANDLE srv, - u32 stride, u32 size) + u32 stride, u32 size) { if (m_state.vertex_buffer.BufferLocation != address || m_state.vertex_buffer.StrideInBytes != stride || m_state.vertex_buffer.SizeInBytes != size) diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h index e7eb015b2f..95774dd71d 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h @@ -47,8 +47,8 @@ public: void Flush() override; void WaitForGPUIdle() override; - void ClearRegion(const MathUtil::Rectangle& target_rc, - bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, + bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp index 17cb1da2d3..d7ada1516a 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp @@ -11,8 +11,8 @@ #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/DX12Context.h" -#include "VideoCommon/VideoCommon.h" #include "VideoCommon/RenderBase.h" +#include "VideoCommon/VideoCommon.h" namespace DX12 { diff --git a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp index 55f13630a9..03e88e3867 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.cpp @@ -130,10 +130,10 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in ADDSTAT(g_stats.this_frame.bytes_index_streamed, static_cast(index_data_size)); Gfx::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), - m_vertex_srv.cpu_handle, vertex_stride, - m_vertex_stream_buffer.GetSize()); + m_vertex_srv.cpu_handle, vertex_stride, + m_vertex_stream_buffer.GetSize()); Gfx::GetInstance()->SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), - m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT); + m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT); } void VertexManager::UploadUniforms() @@ -235,11 +235,11 @@ void VertexManager::UploadAllConstants() // Update bindings Gfx::GetInstance()->SetConstantBuffer(0, m_uniform_stream_buffer.GetCurrentGPUPointer() + - pixel_constants_offset); + pixel_constants_offset); Gfx::GetInstance()->SetConstantBuffer(1, m_uniform_stream_buffer.GetCurrentGPUPointer() + - vertex_constants_offset); + vertex_constants_offset); Gfx::GetInstance()->SetConstantBuffer(2, m_uniform_stream_buffer.GetCurrentGPUPointer() + - geometry_constants_offset); + geometry_constants_offset); auto& system = Core::System::GetInstance(); auto& pixel_shader_manager = system.GetPixelShaderManager(); diff --git a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp index dbf51bd7f8..afc5bb96e8 100644 --- a/Source/Core/VideoBackends/D3D12/VideoBackend.cpp +++ b/Source/Core/VideoBackends/D3D12/VideoBackend.cpp @@ -12,8 +12,8 @@ #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3D12/D3D12BoundingBox.h" -#include "VideoBackends/D3D12/D3D12PerfQuery.h" #include "VideoBackends/D3D12/D3D12Gfx.h" +#include "VideoBackends/D3D12/D3D12PerfQuery.h" #include "VideoBackends/D3D12/D3D12SwapChain.h" #include "VideoBackends/D3D12/D3D12VertexManager.h" #include "VideoBackends/D3D12/DX12Context.h" diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.h b/Source/Core/VideoBackends/Metal/MTLGfx.h index b4c3a3d0e8..a7d5e170b6 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.h +++ b/Source/Core/VideoBackends/Metal/MTLGfx.h @@ -47,8 +47,8 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearRegion(const MathUtil::Rectangle& target_rc, - bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, + bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.mm b/Source/Core/VideoBackends/Metal/MTLGfx.mm index ba7d64e333..d6621396ec 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.mm +++ b/Source/Core/VideoBackends/Metal/MTLGfx.mm @@ -18,8 +18,7 @@ #include -Metal::Gfx::Gfx(MRCOwned layer) - : m_layer(std::move(layer)) +Metal::Gfx::Gfx(MRCOwned layer) : m_layer(std::move(layer)) { UpdateActiveConfig(); [m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive]; @@ -38,7 +37,7 @@ bool Metal::Gfx::IsHeadless() const // MARK: Texture Creation std::unique_ptr Metal::Gfx::CreateTexture(const TextureConfig& config, - std::string_view name) + std::string_view name) { @autoreleasepool { @@ -94,8 +93,7 @@ Metal::Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& c } std::unique_ptr -Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, - AbstractTexture* depth_attachment) +Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) { AbstractTexture* const either_attachment = color_attachment ? color_attachment : depth_attachment; return std::make_unique( @@ -107,8 +105,8 @@ Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, // MARK: Pipeline Creation std::unique_ptr Metal::Gfx::CreateShaderFromSource(ShaderStage stage, - std::string_view source, - std::string_view name) + std::string_view source, + std::string_view name) { std::optional msl = Util::TranslateShaderToMSL(stage, source); if (!msl.has_value()) @@ -121,9 +119,8 @@ std::unique_ptr Metal::Gfx::CreateShaderFromSource(ShaderStage s } std::unique_ptr Metal::Gfx::CreateShaderFromBinary(ShaderStage stage, - const void* data, - size_t length, - std::string_view name) + const void* data, size_t length, + std::string_view name) { return CreateShaderFromMSL(stage, std::string(static_cast(data), length), {}, name); } @@ -154,10 +151,9 @@ static NSString* GenericShaderName(ShaderStage stage) // clang-format on -std::unique_ptr Metal::Gfx::CreateShaderFromMSL(ShaderStage stage, - std::string msl, - std::string_view glsl, - std::string_view name) +std::unique_ptr Metal::Gfx::CreateShaderFromMSL(ShaderStage stage, std::string msl, + std::string_view glsl, + std::string_view name) { @autoreleasepool { @@ -247,9 +243,9 @@ Metal::Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) } } -std::unique_ptr -Metal::Gfx::CreatePipeline(const AbstractPipelineConfig& config, const void* cache_data, - size_t cache_data_length) +std::unique_ptr Metal::Gfx::CreatePipeline(const AbstractPipelineConfig& config, + const void* cache_data, + size_t cache_data_length) { return g_object_cache->CreatePipeline(config); } @@ -285,8 +281,8 @@ void Metal::Gfx::OnConfigChanged(u32 bits) } } -void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, - bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) +void Metal::Gfx::ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, + bool alpha_enable, bool z_enable, u32 color, u32 z) { u32 framebuffer_width = m_current_framebuffer->GetWidth(); u32 framebuffer_height = m_current_framebuffer->GetHeight(); @@ -362,7 +358,7 @@ void Metal::Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) } void Metal::Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, - const ClearColor& color_value, float depth_value) + const ClearColor& color_value, float depth_value) { @autoreleasepool { @@ -400,7 +396,7 @@ void Metal::Gfx::UnbindTexture(const AbstractTexture* texture) } void Metal::Gfx::SetViewport(float x, float y, float width, float height, float near_depth, - float far_depth) + float far_depth) { g_state_tracker->SetViewport(x, y, width, height, near_depth, far_depth); } @@ -422,8 +418,8 @@ void Metal::Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) } void Metal::Gfx::DispatchComputeShader(const AbstractShader* shader, // - u32 groupsize_x, u32 groupsize_y, u32 groupsize_z, - u32 groups_x, u32 groups_y, u32 groups_z) + u32 groupsize_x, u32 groupsize_y, u32 groupsize_z, + u32 groups_x, u32 groups_y, u32 groups_z) { @autoreleasepool { @@ -491,7 +487,8 @@ void Metal::Gfx::SetupSurface() [m_layer setDrawableSize:{static_cast(info.width), static_cast(info.height)}]; - TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format, AbstractTextureFlag_RenderTarget); + TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format, + AbstractTextureFlag_RenderTarget); m_bb_texture = std::make_unique(nullptr, cfg); m_backbuffer = std::make_unique(m_bb_texture.get(), nullptr, // info.width, info.height, 1, 1); @@ -502,16 +499,12 @@ void Metal::Gfx::SetupSurface() SurfaceInfo Metal::Gfx::GetSurfaceInfo() const { - if (!m_layer) // Headless + if (!m_layer) // Headless return {}; CGSize size = [m_layer bounds].size; const float scale = [m_layer contentsScale]; - return { - static_cast(size.width * scale), - static_cast(size.height * scale), - scale, - Util::ToAbstract([m_layer pixelFormat]) - }; + return {static_cast(size.width * scale), static_cast(size.height * scale), scale, + Util::ToAbstract([m_layer pixelFormat])}; } diff --git a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm index 4bf37ccd22..76bddc1dce 100644 --- a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm +++ b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm @@ -77,8 +77,9 @@ void Metal::PerfQuery::ReturnResults(const u64* data, const PerfQueryGroup* grou { for (size_t i = 0; i < count; ++i) { - u64 native_res_result = data[i] * (EFB_WIDTH * EFB_HEIGHT) / - (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); + u64 native_res_result = + data[i] * (EFB_WIDTH * EFB_HEIGHT) / + (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); native_res_result /= g_ActiveConfig.iMultisamples; diff --git a/Source/Core/VideoBackends/OGL/OGLGfx.h b/Source/Core/VideoBackends/OGL/OGLGfx.h index 671d78ee4a..410b7d562e 100644 --- a/Source/Core/VideoBackends/OGL/OGLGfx.h +++ b/Source/Core/VideoBackends/OGL/OGLGfx.h @@ -42,8 +42,8 @@ public: void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) override; void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; - void ClearRegion(const MathUtil::Rectangle& target_rc, - bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, + bool zEnable, u32 color, u32 z) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp index a5bd155afb..6303464251 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp @@ -264,8 +264,9 @@ void PerfQueryGLESNV::FlushOne() // NOTE: Reported pixel metrics should be referenced to native resolution // TODO: Dropping the lower 2 bits from this count should be closer to actual // hardware behavior when drawing triangles. - const u64 native_res_result = static_cast(result) * EFB_WIDTH * EFB_HEIGHT / - (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); + const u64 native_res_result = + static_cast(result) * EFB_WIDTH * EFB_HEIGHT / + (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); m_results[entry.query_group].fetch_add(static_cast(native_res_result), std::memory_order_relaxed); diff --git a/Source/Core/VideoBackends/Software/SWGfx.h b/Source/Core/VideoBackends/Software/SWGfx.h index 6e5b328b5c..74ead3a323 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.h +++ b/Source/Core/VideoBackends/Software/SWGfx.h @@ -46,8 +46,8 @@ public: void SetScissorRect(const MathUtil::Rectangle& rc) override; - void ClearRegion(const MathUtil::Rectangle& target_rc, - bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& target_rc, bool colorEnable, bool alphaEnable, + bool zEnable, u32 color, u32 z) override; private: std::unique_ptr m_window; diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.h b/Source/Core/VideoBackends/Vulkan/VKGfx.h index 8275038479..8217533f06 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.h +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.h @@ -53,8 +53,8 @@ public: void WaitForGPUIdle() override; void OnConfigChanged(u32 bits) override; - void ClearRegion(const MathUtil::Rectangle& target_rc, - bool color_enable, bool alpha_enable, bool z_enable, u32 color, u32 z) override; + void ClearRegion(const MathUtil::Rectangle& target_rc, bool color_enable, bool alpha_enable, + bool z_enable, u32 color, u32 z) override; void SetPipeline(const AbstractPipeline* pipeline) override; void SetFramebuffer(AbstractFramebuffer* framebuffer) override; diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index 7521915897..053b592c14 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -240,7 +240,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) auto bounding_box = std::make_unique(); return InitializeShared(std::move(gfx), std::move(vertex_manager), std::move(perf_query), - std::move(bounding_box)); + std::move(bounding_box)); } void VideoBackend::Shutdown() diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index 8c254d4e71..ad854a3dce 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -15,8 +15,8 @@ #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" -#include "VideoCommon/VideoState.h" #include "VideoCommon/VideoEvents.h" +#include "VideoCommon/VideoState.h" AsyncRequests AsyncRequests::s_singleton; diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 6d50377950..f5383042a0 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -31,9 +31,9 @@ #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PerfQueryBase.h" -#include "VideoCommon/Present.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TMEM.h" @@ -346,7 +346,6 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, // It works 99% of the time. AfterFrameEvent::Trigger(); - if (g_ActiveConfig.bImmediateXFB) { // below div two to convert from bytes to pixels - it expects width, not stride diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 38cab15f00..a987624f1a 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -149,16 +149,14 @@ static u32 CalculateEFBLayers() TextureConfig FramebufferManager::GetEFBColorTextureConfig(u32 width, u32 height) { - return TextureConfig(width, height, 1, - CalculateEFBLayers(), g_ActiveConfig.iMultisamples, GetEFBColorFormat(), - AbstractTextureFlag_RenderTarget); + return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples, + GetEFBColorFormat(), AbstractTextureFlag_RenderTarget); } TextureConfig FramebufferManager::GetEFBDepthTextureConfig(u32 width, u32 height) { - return TextureConfig(width, height, 1, - CalculateEFBLayers(), g_ActiveConfig.iMultisamples, GetEFBDepthFormat(), - AbstractTextureFlag_RenderTarget); + return TextureConfig(width, height, 1, CalculateEFBLayers(), g_ActiveConfig.iMultisamples, + GetEFBDepthFormat(), AbstractTextureFlag_RenderTarget); } FramebufferState FramebufferManager::GetEFBFramebufferState() const @@ -905,8 +903,7 @@ bool FramebufferManager::CompileClearPipelines() config.depth_state.testenable = depth_enable != 0; config.depth_state.updateenable = depth_enable != 0; - m_clear_pipelines[color_enable][alpha_enable][depth_enable] = - g_gfx->CreatePipeline(config); + m_clear_pipelines[color_enable][alpha_enable][depth_enable] = g_gfx->CreatePipeline(config); if (!m_clear_pipelines[color_enable][alpha_enable][depth_enable]) return false; } diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index 0eb5d4aa93..cc41c0ffba 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -226,8 +226,7 @@ protected: // EFB clear pipelines // Indexed by [color_write_enabled][alpha_write_enabled][depth_write_enabled] - std::array, 2>, 2>, 2> - m_clear_pipelines; + std::array, 2>, 2>, 2> m_clear_pipelines; // EFB poke drawing setup std::unique_ptr m_poke_vertex_format; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h index aeb55f17d1..9115d8f45d 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h @@ -13,8 +13,8 @@ #include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" #include "VideoCommon/TextureInfo.h" -#include "VideoCommon/XFMemory.h" #include "VideoCommon/VideoEvents.h" +#include "VideoCommon/XFMemory.h" class GraphicsModGroupConfig; class GraphicsModManager diff --git a/Source/Core/VideoCommon/OnScreenUI.h b/Source/Core/VideoCommon/OnScreenUI.h index e90d1c2132..43dc6144d1 100644 --- a/Source/Core/VideoCommon/OnScreenUI.h +++ b/Source/Core/VideoCommon/OnScreenUI.h @@ -16,7 +16,6 @@ class AbstractPipeline; namespace VideoCommon { - // OnScreenUI handles all the ImGui rendering. class OnScreenUI { diff --git a/Source/Core/VideoCommon/PixelShaderManager.cpp b/Source/Core/VideoCommon/PixelShaderManager.cpp index c929db6995..94b37be4e8 100644 --- a/Source/Core/VideoCommon/PixelShaderManager.cpp +++ b/Source/Core/VideoCommon/PixelShaderManager.cpp @@ -74,7 +74,8 @@ void PixelShaderManager::Dirty() // Any constants that can changed based on settings should be re-calculated m_fog_range_adjusted_changed = true; - SetEfbScaleChanged(g_framebuffer_manager->EFBToScaledXf(1), g_framebuffer_manager->EFBToScaledYf(1)); + SetEfbScaleChanged(g_framebuffer_manager->EFBToScaledXf(1), + g_framebuffer_manager->EFBToScaledYf(1)); SetFogParamChanged(); dirty = true; @@ -102,8 +103,8 @@ void PixelShaderManager::SetConstants() // so to simplify I use the hi coefficient as K in the shader taking 256 as the scale // TODO: Shouldn't this be EFBToScaledXf? constants.fogf[2] = ScreenSpaceCenter; - constants.fogf[3] = - static_cast(g_framebuffer_manager->EFBToScaledX(static_cast(2.0f * xfmem.viewport.wd))); + constants.fogf[3] = static_cast( + g_framebuffer_manager->EFBToScaledX(static_cast(2.0f * xfmem.viewport.wd))); for (size_t i = 0, vec_index = 0; i < std::size(bpmem.fogRange.K); i++) { diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index b7a60c7dc6..d5fa440417 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -9,6 +9,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "Present.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/FrameDumper.h" #include "VideoCommon/OnScreenUI.h" @@ -18,7 +19,6 @@ #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoEvents.h" -#include "Present.h" std::unique_ptr g_presenter; @@ -67,7 +67,8 @@ bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_heigh { ReleaseXFBContentLock(); - m_xfb_entry = g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); + m_xfb_entry = + g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); bool is_duplicate = m_xfb_entry->id == m_last_xfb_id; m_xfb_entry->AcquireContentLock(); @@ -95,7 +96,7 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, } else { - present_info.frame_count = m_frame_count - 1; // Previous frame + present_info.frame_count = m_frame_count - 1; // Previous frame present_info.reason = PresentInfo::PresentReason::VideoInterfaceDuplicate; } present_info.present_count = m_present_count; @@ -308,7 +309,7 @@ void Presenter::ResizeSurface() void* Presenter::GetNewSurfaceHandle() { - void *handle = m_new_surface_handle; + void* handle = m_new_surface_handle; m_new_surface_handle = nullptr; return handle; } @@ -605,11 +606,9 @@ void Presenter::DoState(PointerWrap& p) AfterFrameEvent::Trigger(); // re-display the most recent XFB - ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, - m_last_xfb_height, m_last_xfb_ticks); + ImmediateSwap(m_last_xfb_addr, m_last_xfb_width, m_last_xfb_stride, m_last_xfb_height, + m_last_xfb_ticks); } - } - } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index c30495f9d1..d324dacac6 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -31,31 +31,32 @@ #include "Core/System.h" #include "VideoCommon/AbstractGfx.h" +#include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/BoundingBox.h" -#include "VideoCommon/BPFunctions.h" #include "VideoCommon/FrameDumper.h" - #include "VideoCommon/FramebufferManager.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/Present.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" -#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoCommon.h" +#include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" std::unique_ptr g_renderer; -Renderer::Renderer() - : m_prev_efb_format{PixelFormat::INVALID_FMT} +Renderer::Renderer() : m_prev_efb_format{PixelFormat::INVALID_FMT} { UpdateWidescreen(); - m_config_changed_handle = ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); + m_config_changed_handle = + ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); // VertexManager doesn't maintain statistics in Wii mode. if (!SConfig::GetInstance().bWii) - m_update_widescreen_handle = AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); + m_update_widescreen_handle = + AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } Renderer::~Renderer() = default; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 52415c0f36..afdfffccb9 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -1,7 +1,6 @@ // Copyright 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #pragma once #include diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index cc6e9fb86a..26af6b65d1 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -46,7 +46,8 @@ bool ShaderCache::Initialize() return false; m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler(); - m_frame_end_handler = AfterFrameEvent::Register([this] { RetrieveAsyncShaders(); }, "RetreiveAsyncShaders"); + m_frame_end_handler = + AfterFrameEvent::Register([this] { RetrieveAsyncShaders(); }, "RetreiveAsyncShaders"); return true; } diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index a1ab6122ab..ecca851968 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -18,17 +18,18 @@ Statistics g_stats; -static EventHook s_before_frame_event = BeforeFrameEvent::Register([] { - g_stats.ResetFrame(); -}, "Statistics::ResetFrame"); +static EventHook s_before_frame_event = + BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame"); -static EventHook s_after_frame_event = AfterFrameEvent::Register([] { - DolphinAnalytics::PerformanceSample perf_sample; - perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); - perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; - perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; - DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); -}, "Statistics::PerformanceSample"); +static EventHook s_after_frame_event = AfterFrameEvent::Register( + [] { + DolphinAnalytics::PerformanceSample perf_sample; + perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); + perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims; + perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls; + DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample)); + }, + "Statistics::PerformanceSample"); static bool clear_scissors; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 02601baaf4..ce14075552 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -773,7 +773,7 @@ void TextureCacheBase::OnFrameEnd() { if (m_force_reload_textures.TestAndClear()) { - ForceReload(); + ForceReload(); } else { @@ -920,9 +920,9 @@ RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_ entry_to_update->native_height != entry_to_update->GetHeight() || entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight()) { - ScaleTextureCacheEntryTo(entry_to_update, - g_framebuffer_manager->EFBToScaledX(entry_to_update->native_width), - g_framebuffer_manager->EFBToScaledY(entry_to_update->native_height)); + ScaleTextureCacheEntryTo( + entry_to_update, g_framebuffer_manager->EFBToScaledX(entry_to_update->native_width), + g_framebuffer_manager->EFBToScaledY(entry_to_update->native_height)); ScaleTextureCacheEntryTo(entry, g_framebuffer_manager->EFBToScaledX(entry->native_width), g_framebuffer_manager->EFBToScaledY(entry->native_height)); @@ -1957,7 +1957,8 @@ void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry) // copies to be stitched together. if (create_upscaled_copy) { - ScaleTextureCacheEntryTo(stitched_entry, g_framebuffer_manager->EFBToScaledX(stitched_entry->native_width), + ScaleTextureCacheEntryTo(stitched_entry, + g_framebuffer_manager->EFBToScaledX(stitched_entry->native_width), g_framebuffer_manager->EFBToScaledY(stitched_entry->native_height)); } @@ -2269,7 +2270,8 @@ void TextureCacheBase::CopyRenderTargetToTexture( // TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more // complex down filtering to average all pixels and produce the correct result. const bool linear_filter = - !is_depth_copy && (scaleByHalf || g_framebuffer_manager->GetEFBScale() != 1 || y_scale > 1.0f); + !is_depth_copy && + (scaleByHalf || g_framebuffer_manager->GetEFBScale() != 1 || y_scale > 1.0f); RcTcacheEntry entry; if (copy_to_vram) diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index a1058c77c7..9686809c88 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -104,7 +104,7 @@ VertexManagerBase::~VertexManagerBase() = default; bool VertexManagerBase::Initialize() { - m_frame_end_event = AfterFrameEvent::Register([this] { OnEndFrame();}, "VertexManagerBase"); + m_frame_end_event = AfterFrameEvent::Register([this] { OnEndFrame(); }, "VertexManagerBase"); m_index_generator.Init(); m_cpu_cull.Init(); return true; @@ -562,8 +562,7 @@ void VertexManagerBase::Flush() { bool skip = false; GraphicsModActionData::DrawStarted draw_started{&skip}; - for (const auto action : - g_graphics_mod_manager->GetDrawStartedActions(texture_name)) + for (const auto action : g_graphics_mod_manager->GetDrawStartedActions(texture_name)) { action->OnDrawStarted(&draw_started); } diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index f2a203cfe1..4177f3b834 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -388,16 +388,15 @@ void VertexShaderManager::SetConstants(const std::vector& textures) std::vector projection_actions; if (g_ActiveConfig.bGraphicMods) { - for (const auto action : - g_graphics_mod_manager->GetProjectionActions(xfmem.projection.type)) + for (const auto action : g_graphics_mod_manager->GetProjectionActions(xfmem.projection.type)) { projection_actions.push_back(action); } for (const auto& texture : textures) { - for (const auto action : g_graphics_mod_manager->GetProjectionTextureActions( - xfmem.projection.type, texture)) + for (const auto action : + g_graphics_mod_manager->GetProjectionTextureActions(xfmem.projection.type, texture)) { projection_actions.push_back(action); } diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 21f006af4a..0c209a9686 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -24,8 +24,8 @@ #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/Present.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Present.h" #include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VertexManagerBase.h" @@ -355,7 +355,8 @@ void CheckForConfigChanges() // TODO: Move everything else to the ConfigChanged event } -static EventHook s_check_config_event = AfterFrameEvent::Register([] { - CheckForConfigChanges(); - -}, "CheckForConfigChanges"); +static EventHook s_check_config_event = AfterFrameEvent::Register( + [] { + CheckForConfigChanges(); + }, + "CheckForConfigChanges"); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index c86ec647bc..1e5e2f683e 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -59,7 +59,6 @@ enum class TriState : int Auto }; - // Bitmask containing information about which configuration has changed for the backend. enum ConfigChangeBits : u32 { diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index bb95296ebd..a2e0760075 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -6,7 +6,6 @@ #include "Common/CommonTypes.h" #include "Common/EventHook.h" - // Called when certain video config setting are changed using ConfigChangedEvent = Event<"ConfigChanged", u32>; @@ -21,60 +20,60 @@ using AfterFrameEvent = Event<"AfterFrame">; struct PresentInfo { - enum class PresentReason - { - Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy - VideoInterface, // VideoInterface has triggered a present with a new frame - VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame - }; + enum class PresentReason + { + Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy + VideoInterface, // VideoInterface has triggered a present with a new frame + VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame + }; - // The number of (unique) frames since the emulated console booted - u64 frame_count; + // The number of (unique) frames since the emulated console booted + u64 frame_count; + // The number of presents since the video backend was initialized. + // never goes backwards. + u64 present_count; - // The number of presents since the video backend was initialized. - // never goes backwards. - u64 present_count; + // The frame is identical to the previous frame + PresentReason reason; - // The frame is identical to the previous frame - PresentReason reason; + // The exact emulated time of the when real hardware would have presented this frame + // FIXME: Immediate should predict the timestamp of this present + u64 emulated_timestamp; - // The exact emulated time of the when real hardware would have presented this frame - // FIXME: Immediate should predict the timestamp of this present - u64 emulated_timestamp; + // TODO: + // u64 intended_present_time; - // TODO: - // u64 intended_present_time; + // AfterPresent only: The actual time the frame was presented + u64 actual_present_time = 0; - // AfterPresent only: The actual time the frame was presented - u64 actual_present_time = 0; + enum class PresentTimeAccuracy + { + // The Driver/OS has given us an exact timestamp of when the first line of the frame started + // scanning out to the monitor + PresentOnScreenExact, - enum class PresentTimeAccuracy - { - // The Driver/OS has given us an exact timestamp of when the first line of the frame started - // scanning out to the monitor - PresentOnScreenExact, + // An approximate timestamp of scanout. + PresentOnScreen, - // An approximate timestamp of scanout. - PresentOnScreen, + // Dolphin doesn't have visibility of the present time. But the present operation has + // been queued with the GPU driver and will happen in the near future. + PresentInProgress, - // Dolphin doesn't have visibility of the present time. But the present operation has - // been queued with the GPU driver and will happen in the near future. - PresentInProgress, + // Not implemented + Unimplemented, + }; - // Not implemented - Unimplemented, - }; - - // Accuracy of actual_present_time - PresentTimeAccuracy present_time_accuracy = PresentTimeAccuracy::Unimplemented; + // Accuracy of actual_present_time + PresentTimeAccuracy present_time_accuracy = PresentTimeAccuracy::Unimplemented; }; // An event called just as a frame is queued for presentation. // The exact timing of this event depends on the "Immediately Present XFB" option. // // If enabled, this event will trigger immediately after AfterFrame -// If disabled, this event won't trigger until the emulated interface starts drawing out a new frame. +// If disabled, this event won't trigger until the emulated interface starts drawing out a new +// frame. // // frame_count: The number of frames using BeforePresentEvent = Event<"BeforePresent", PresentInfo&>; diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 6981e14adb..5aae09e354 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -7,14 +7,14 @@ #include "Common/ChunkFile.h" #include "Core/System.h" -#include "VideoCommon/BoundingBox.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/BPStructs.h" +#include "VideoCommon/BoundingBox.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" -#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FrameDumper.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" @@ -105,7 +105,6 @@ void VideoCommon_DoState(PointerWrap& p) g_bounding_box->DoState(p); p.DoMarker("Bounding Box"); - // Refresh state. if (p.IsReadMode()) { From 9b5397abdb643b5d40211e71f7a58bb3814c29c4 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 17:58:54 +1300 Subject: [PATCH 42/60] Move WidescreenHeuristic to it's own class It's about the only thing left in renderer --- Source/Core/DolphinLib.props | 2 + Source/Core/VideoCommon/CMakeLists.txt | 2 + Source/Core/VideoCommon/Present.cpp | 8 +- Source/Core/VideoCommon/RenderBase.cpp | 100 +--------------- Source/Core/VideoCommon/RenderBase.h | 14 --- Source/Core/VideoCommon/VideoBackendBase.cpp | 3 + Source/Core/VideoCommon/VideoState.cpp | 4 + Source/Core/VideoCommon/Widescreen.cpp | 113 +++++++++++++++++++ Source/Core/VideoCommon/Widescreen.h | 34 ++++++ 9 files changed, 163 insertions(+), 117 deletions(-) create mode 100644 Source/Core/VideoCommon/Widescreen.cpp create mode 100644 Source/Core/VideoCommon/Widescreen.h diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 470d303e21..780f8da7b9 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -717,6 +717,7 @@ + @@ -1306,6 +1307,7 @@ + diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 5428facc55..632120ee09 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -169,6 +169,8 @@ add_library(videocommon VideoConfig.h VideoState.cpp VideoState.h + Widescreen.cpp + Widescreen.h XFMemory.cpp XFMemory.h XFStructs.cpp diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index d5fa440417..0e7f6b96d9 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -14,11 +14,11 @@ #include "VideoCommon/FrameDumper.h" #include "VideoCommon/OnScreenUI.h" #include "VideoCommon/PostProcessing.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoEvents.h" +#include "VideoCommon/Widescreen.h" std::unique_ptr g_presenter; @@ -246,7 +246,7 @@ float Presenter::CalculateDrawAspectRatio() const const float aspect_ratio = VideoInterface::GetAspectRatio(); if (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) + (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) { return AspectToWidescreen(aspect_ratio); } @@ -346,7 +346,7 @@ std::tuple Presenter::ApplyStandardAspectCrop(float width, float h const float current_aspect = width / height; const float expected_aspect = (aspect_mode == AspectMode::AnalogWide || - (aspect_mode == AspectMode::Auto && g_renderer->IsGameWidescreen())) ? + (aspect_mode == AspectMode::Auto && g_widescreen->IsGameWidescreen())) ? (16.0f / 9.0f) : (4.0f / 3.0f); if (current_aspect > expected_aspect) @@ -373,7 +373,7 @@ void Presenter::UpdateDrawRectangle() if (g_ActiveConfig.bWidescreenHack) { float source_aspect = VideoInterface::GetAspectRatio(); - if (g_renderer && g_renderer->IsGameWidescreen()) + if (g_widescreen->IsGameWidescreen()) source_aspect = AspectToWidescreen(source_aspect); const float adjust = source_aspect / draw_aspect_ratio; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index d324dacac6..e597ee1e82 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -26,19 +26,14 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" #include "Core/System.h" -#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" -#include "VideoCommon/BoundingBox.h" -#include "VideoCommon/FrameDumper.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/Present.h" -#include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -48,15 +43,6 @@ std::unique_ptr g_renderer; Renderer::Renderer() : m_prev_efb_format{PixelFormat::INVALID_FMT} { - UpdateWidescreen(); - - m_config_changed_handle = - ConfigChangedEvent::Register([this](u32 bits) { OnConfigChanged(bits); }, "Renderer"); - - // VertexManager doesn't maintain statistics in Wii mode. - if (!SConfig::GetInstance().bWii) - m_update_widescreen_handle = - AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); } Renderer::~Renderer() = default; @@ -166,85 +152,6 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } -void Renderer::UpdateWidescreen() -{ - if (SConfig::GetInstance().bWii) - m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - - // suggested_aspect_mode overrides SYSCONF_WIDESCREEN - if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - - // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. - if (!g_ActiveConfig.bWidescreenHack) - { - const auto aspect_mode = g_ActiveConfig.aspect_mode; - if (aspect_mode == AspectMode::Analog) - m_is_game_widescreen = false; - else if (aspect_mode == AspectMode::AnalogWide) - m_is_game_widescreen = true; - } -} - -// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. -void Renderer::UpdateWidescreenHeuristic() -{ - const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount(); - - // If suggested_aspect_mode (GameINI) is configured don't use heuristic. - if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto) - return; - - UpdateWidescreen(); - - // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. - if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || - g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) - return; - - // Modify the threshold based on which aspect ratio we're already using: - // If the game's in 4:3, it probably won't switch to anamorphic, and vice-versa. - static constexpr u32 TRANSITION_THRESHOLD = 3; - - const auto looks_normal = [](auto& counts) { - return counts.normal_vertex_count > counts.anamorphic_vertex_count * TRANSITION_THRESHOLD; - }; - const auto looks_anamorphic = [](auto& counts) { - return counts.anamorphic_vertex_count > counts.normal_vertex_count * TRANSITION_THRESHOLD; - }; - - const auto& persp = flush_statistics.perspective; - const auto& ortho = flush_statistics.orthographic; - - const auto ortho_looks_anamorphic = looks_anamorphic(ortho); - - if (looks_anamorphic(persp) || ortho_looks_anamorphic) - { - // If either perspective or orthographic projections look anamorphic, it's a safe bet. - m_is_game_widescreen = true; - } - else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho))) - { - // Many widescreen games (or AR/GeckoCodes) use anamorphic perspective projections - // with NON-anamorphic orthographic projections. - // This can cause incorrect changes to 4:3 when perspective projections are temporarily not - // shown. e.g. Animal Crossing's inventory menu. - // Unless we were in a situation which was orthographically anamorphic - // we won't consider orthographic data for changes from 16:9 to 4:3. - m_is_game_widescreen = false; - } - - m_was_orthographically_anamorphic = ortho_looks_anamorphic; -} - -void Renderer::OnConfigChanged(u32 bits) -{ - if (bits & CONFIG_CHANGE_BIT_ASPECT_RATIO) - UpdateWidescreen(); -} - bool Renderer::UseVertexDepthRange() { // We can't compute the depth range in the vertex shader if we don't support depth clamp. @@ -266,10 +173,5 @@ bool Renderer::UseVertexDepthRange() void Renderer::DoState(PointerWrap& p) { - p.Do(m_is_game_widescreen); - - if (p.IsReadMode()) - { - m_was_orthographically_anamorphic = false; - } + p.Do(m_prev_efb_format); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index afdfffccb9..e2ff0d0a06 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -37,28 +37,14 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - bool IsGameWidescreen() const { return m_is_game_widescreen; } - PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } static bool UseVertexDepthRange(); void DoState(PointerWrap& p); - void OnConfigChanged(u32 bits); - -protected: - void UpdateWidescreen(); - void UpdateWidescreenHeuristic(); - - bool m_is_game_widescreen = false; - bool m_was_orthographically_anamorphic = false; - private: PixelFormat m_prev_efb_format; - - EventHook m_update_widescreen_handle; - EventHook m_config_changed_handle; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 3eeceb3c5e..95b58322d2 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -64,6 +64,7 @@ #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoState.h" +#include "VideoCommon/Widescreen.h" VideoBackendBase* g_video_backend = nullptr; @@ -359,6 +360,7 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, g_framebuffer_manager = std::make_unique(); g_shader_cache = std::make_unique(); g_graphics_mod_manager = std::make_unique(); + g_widescreen = std::make_unique(); auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); @@ -407,6 +409,7 @@ void VideoBackendBase::ShutdownShared() g_shader_cache.reset(); g_vertex_manager.reset(); g_renderer.reset(); + g_widescreen.reset(); g_presenter.reset(); g_gfx.reset(); diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 5aae09e354..45b06e4973 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -26,6 +26,7 @@ #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VertexShaderManager.h" +#include "VideoCommon/Widescreen.h" #include "VideoCommon/XFMemory.h" void VideoCommon_DoState(PointerWrap& p) @@ -105,6 +106,9 @@ void VideoCommon_DoState(PointerWrap& p) g_bounding_box->DoState(p); p.DoMarker("Bounding Box"); + g_widescreen->DoState(p); + p.DoMarker("Widescreen"); + // Refresh state. if (p.IsReadMode()) { diff --git a/Source/Core/VideoCommon/Widescreen.cpp b/Source/Core/VideoCommon/Widescreen.cpp new file mode 100644 index 0000000000..ac8fa2fcfd --- /dev/null +++ b/Source/Core/VideoCommon/Widescreen.cpp @@ -0,0 +1,113 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Widescreen.h" + +#include "Common/ChunkFile.h" +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigManager.h" + +#include "VideoCommon/VertexManagerBase.h" + +std::unique_ptr g_widescreen; + +WidescreenManager::WidescreenManager() +{ + Update(); + + m_config_changed = ConfigChangedEvent::Register([this](u32 bits) { + if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO)) + Update(); + }, "Widescreen"); + + // VertexManager doesn't maintain statistics in Wii mode. + if (!SConfig::GetInstance().bWii) + { + m_update_widescreen = + AfterFrameEvent::Register([this] { UpdateWidescreenHeuristic(); }, "WideScreen Heuristic"); + } +} + + +void WidescreenManager::Update() +{ + if (SConfig::GetInstance().bWii) + m_is_game_widescreen = Config::Get(Config::SYSCONF_WIDESCREEN); + + // suggested_aspect_mode overrides SYSCONF_WIDESCREEN + if (g_ActiveConfig.suggested_aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (g_ActiveConfig.suggested_aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; + + // If widescreen hack is disabled override game's AR if UI is set to 4:3 or 16:9. + if (!g_ActiveConfig.bWidescreenHack) + { + const auto aspect_mode = g_ActiveConfig.aspect_mode; + if (aspect_mode == AspectMode::Analog) + m_is_game_widescreen = false; + else if (aspect_mode == AspectMode::AnalogWide) + m_is_game_widescreen = true; + } +} + +// Heuristic to detect if a GameCube game is in 16:9 anamorphic widescreen mode. +void WidescreenManager::UpdateWidescreenHeuristic() +{ + const auto flush_statistics = g_vertex_manager->ResetFlushAspectRatioCount(); + + // If suggested_aspect_mode (GameINI) is configured don't use heuristic. + if (g_ActiveConfig.suggested_aspect_mode != AspectMode::Auto) + return; + + Update(); + + // If widescreen hack isn't active and aspect_mode (UI) is 4:3 or 16:9 don't use heuristic. + if (!g_ActiveConfig.bWidescreenHack && (g_ActiveConfig.aspect_mode == AspectMode::Analog || + g_ActiveConfig.aspect_mode == AspectMode::AnalogWide)) + return; + + // Modify the threshold based on which aspect ratio we're already using: + // If the game's in 4:3, it probably won't switch to anamorphic, and vice-versa. + static constexpr u32 TRANSITION_THRESHOLD = 3; + + const auto looks_normal = [](auto& counts) { + return counts.normal_vertex_count > counts.anamorphic_vertex_count * TRANSITION_THRESHOLD; + }; + const auto looks_anamorphic = [](auto& counts) { + return counts.anamorphic_vertex_count > counts.normal_vertex_count * TRANSITION_THRESHOLD; + }; + + const auto& persp = flush_statistics.perspective; + const auto& ortho = flush_statistics.orthographic; + + const auto ortho_looks_anamorphic = looks_anamorphic(ortho); + + if (looks_anamorphic(persp) || ortho_looks_anamorphic) + { + // If either perspective or orthographic projections look anamorphic, it's a safe bet. + m_is_game_widescreen = true; + } + else if (looks_normal(persp) || (m_was_orthographically_anamorphic && looks_normal(ortho))) + { + // Many widescreen games (or AR/GeckoCodes) use anamorphic perspective projections + // with NON-anamorphic orthographic projections. + // This can cause incorrect changes to 4:3 when perspective projections are temporarily not + // shown. e.g. Animal Crossing's inventory menu. + // Unless we were in a situation which was orthographically anamorphic + // we won't consider orthographic data for changes from 16:9 to 4:3. + m_is_game_widescreen = false; + } + + m_was_orthographically_anamorphic = ortho_looks_anamorphic; +} + +void WidescreenManager::DoState(PointerWrap& p) +{ + p.Do(m_is_game_widescreen); + + if (p.IsReadMode()) + { + m_was_orthographically_anamorphic = false; + } +} \ No newline at end of file diff --git a/Source/Core/VideoCommon/Widescreen.h b/Source/Core/VideoCommon/Widescreen.h new file mode 100644 index 0000000000..437149cb54 --- /dev/null +++ b/Source/Core/VideoCommon/Widescreen.h @@ -0,0 +1,34 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + + +#include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" + +class PointerWrap; + +// This class is responsible for tracking the game's aspect ratio. +class WidescreenManager +{ +public: + WidescreenManager(); + + bool IsGameWidescreen() const { return m_is_game_widescreen; } + + void DoState(PointerWrap& p); +private: + void Update(); + void UpdateWidescreenHeuristic(); + + bool m_is_game_widescreen = false; + bool m_was_orthographically_anamorphic = false; + + EventHook m_update_widescreen; + EventHook m_config_changed; +}; + +extern std::unique_ptr g_widescreen; \ No newline at end of file From 2cfc02a1169653566e277a7e23e69fd5eb628434 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 18:13:47 +1300 Subject: [PATCH 43/60] Move m_prev_efb_format into FramebufferManager --- Source/Core/VideoCommon/BPFunctions.cpp | 4 ++-- Source/Core/VideoCommon/FramebufferManager.cpp | 1 + Source/Core/VideoCommon/FramebufferManager.h | 3 +++ Source/Core/VideoCommon/RenderBase.cpp | 15 --------------- Source/Core/VideoCommon/RenderBase.h | 11 +---------- Source/Core/VideoCommon/VideoState.cpp | 4 ---- 6 files changed, 7 insertions(+), 31 deletions(-) diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index ef705a50fd..5bd4ab241c 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -364,9 +364,9 @@ void OnPixelFormatChange() if (!g_ActiveConfig.bEFBEmulateFormatChanges) return; - const auto old_format = g_renderer->GetPrevPixelFormat(); + const auto old_format = g_framebuffer_manager->GetPrevPixelFormat(); const auto new_format = bpmem.zcontrol.pixel_format; - g_renderer->StorePixelFormat(new_format); + g_framebuffer_manager->StorePixelFormat(new_format); DEBUG_LOG_FMT(VIDEO, "pixelfmt: pixel={}, zc={}", new_format, bpmem.zcontrol.zformat); diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index a987624f1a..21871f8442 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -1092,6 +1092,7 @@ void FramebufferManager::DestroyPokePipelines() void FramebufferManager::DoState(PointerWrap& p) { FlushEFBPokes(); + p.Do(m_prev_efb_format); bool save_efb_state = Config::Get(Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE); p.Do(save_efb_state); diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index cc41c0ffba..087c5023aa 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -105,6 +105,8 @@ public: // Assumes no render pass is currently in progress. // Swaps EFB framebuffers, so re-bind afterwards. bool ReinterpretPixelData(EFBReinterpretType convtype); + PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } + void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } // Clears the EFB using shaders. void ClearEFB(const MathUtil::Rectangle& rc, bool clear_color, bool clear_alpha, @@ -193,6 +195,7 @@ protected: void DoSaveState(PointerWrap& p); float m_efb_scale = 0.0f; + PixelFormat m_prev_efb_format; std::unique_ptr m_efb_color_texture; std::unique_ptr m_efb_convert_color_texture; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index e597ee1e82..f09dfcc048 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -20,20 +20,14 @@ #include -#include "Common/Assert.h" -#include "Common/ChunkFile.h" -#include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Core/ConfigManager.h" #include "Core/System.h" -#include "VideoCommon/BPFunctions.h" -#include "VideoCommon/BPMemory.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PixelEngine.h" -#include "VideoCommon/Present.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -41,10 +35,6 @@ std::unique_ptr g_renderer; -Renderer::Renderer() : m_prev_efb_format{PixelFormat::INVALID_FMT} -{ -} - Renderer::~Renderer() = default; void Renderer::ReinterpretPixelData(EFBReinterpretType convtype) @@ -170,8 +160,3 @@ bool Renderer::UseVertexDepthRange() // in the vertex shader. return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; } - -void Renderer::DoState(PointerWrap& p) -{ - p.Do(m_prev_efb_format); -} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index e2ff0d0a06..0306a00e65 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -24,12 +24,10 @@ struct EfbPokeData // Renderer really isn't a very good name for this class - it's more like "Misc". // It used to be a massive mess, but almost everything has been refactored out. // -// All that's left is Widescreen tracking, pixel format and a thin abstraction layer -// for VideoSoftware to intercept EFB accesses. +// All that's left is a thin abstraction layer for VideoSoftware to intercept EFB accesses. class Renderer { public: - Renderer(); virtual ~Renderer(); virtual void ReinterpretPixelData(EFBReinterpretType convtype); @@ -37,14 +35,7 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } - void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; } - static bool UseVertexDepthRange(); - void DoState(PointerWrap& p); - -private: - PixelFormat m_prev_efb_format; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 45b06e4973..7f005e1474 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -19,7 +19,6 @@ #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/TMEM.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureDecoder.h" @@ -96,9 +95,6 @@ void VideoCommon_DoState(PointerWrap& p) g_texture_cache->DoState(p); p.DoMarker("TextureCache"); - g_renderer->DoState(p); - p.DoMarker("Renderer"); - g_presenter->DoState(p); g_frame_dumper->DoState(p); p.DoMarker("Presenter"); From 5803786beb4a7c1c95ccd96929b16143060dd663 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 18:18:46 +1300 Subject: [PATCH 44/60] Move UseVertexDepthRange() out of Renderer There wasn't really a good place for it, but this will do --- Source/Core/VideoCommon/BPFunctions.cpp | 3 ++- Source/Core/VideoCommon/RenderBase.cpp | 20 ----------------- Source/Core/VideoCommon/RenderBase.h | 2 -- .../Core/VideoCommon/VertexShaderManager.cpp | 22 ++++++++++++++++++- Source/Core/VideoCommon/VertexShaderManager.h | 4 ++++ 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 5bd4ab241c..ad9898b27e 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -18,6 +18,7 @@ #include "VideoCommon/RenderBase.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VertexManagerBase.h" +#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" @@ -246,7 +247,7 @@ void SetScissorAndViewport() max_depth = std::clamp(max_depth, 0.0f, GX_MAX_DEPTH); } - if (g_renderer->UseVertexDepthRange()) + if (VertexShaderManager::UseVertexDepthRange()) { // We need to ensure depth values are clamped the maximum value supported by the console GPU. // Taking into account whether the depth range is inverted or not. diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index f09dfcc048..61820e0c82 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -31,7 +31,6 @@ #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" -#include "VideoCommon/XFMemory.h" std::unique_ptr g_renderer; @@ -141,22 +140,3 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num } } } - -bool Renderer::UseVertexDepthRange() -{ - // We can't compute the depth range in the vertex shader if we don't support depth clamp. - if (!g_ActiveConfig.backend_info.bSupportsDepthClamp) - return false; - - // We need a full depth range if a ztexture is used. - if (bpmem.ztex2.op != ZTexOp::Disabled && !bpmem.zcontrol.early_ztest) - return true; - - // If an inverted depth range is unsupported, we also need to check if the range is inverted. - if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange && xfmem.viewport.zRange < 0.0f) - return true; - - // If an oversized depth range or a ztexture is used, we need to calculate the depth range - // in the vertex shader. - return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; -} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 0306a00e65..23e6895553 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -34,8 +34,6 @@ public: virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); virtual void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points); - - static bool UseVertexDepthRange(); }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 4177f3b834..817b7e5aa9 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -158,6 +158,26 @@ void VertexShaderManager::SetProjectionMatrix() } } +bool VertexShaderManager::UseVertexDepthRange() +{ + // We can't compute the depth range in the vertex shader if we don't support depth clamp. + if (!g_ActiveConfig.backend_info.bSupportsDepthClamp) + return false; + + // We need a full depth range if a ztexture is used. + if (bpmem.ztex2.op != ZTexOp::Disabled && !bpmem.zcontrol.early_ztest) + return true; + + // If an inverted depth range is unsupported, we also need to check if the range is inverted. + if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange && xfmem.viewport.zRange < 0.0f) + return true; + + // If an oversized depth range or a ztexture is used, we need to calculate the depth range + // in the vertex shader. + return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; +} + + // Syncs the shader constant buffers with xfmem // TODO: A cleaner way to control the matrices without making a mess in the parameters field void VertexShaderManager::SetConstants(const std::vector& textures) @@ -356,7 +376,7 @@ void VertexShaderManager::SetConstants(const std::vector& textures) constants.viewport[0] = (2.f * xfmem.viewport.wd); constants.viewport[1] = (2.f * xfmem.viewport.ht); - if (g_renderer->UseVertexDepthRange()) + if (UseVertexDepthRange()) { // Oversized depth ranges are handled in the vertex shader. We need to reverse // the far value to use the reversed-Z trick. diff --git a/Source/Core/VideoCommon/VertexShaderManager.h b/Source/Core/VideoCommon/VertexShaderManager.h index 9a150980da..3b22d43aeb 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.h +++ b/Source/Core/VideoCommon/VertexShaderManager.h @@ -44,9 +44,13 @@ public: // (i.e. VertexShaderManager::SetConstants needs to be called before using this!) void TransformToClipSpace(const float* data, float* out, u32 mtxIdx); + static bool UseVertexDepthRange(); + VertexShaderConstants constants{}; bool dirty = false; + + private: alignas(16) std::array m_projection_matrix; From 4422af1272959fb48418a6a863a62c20730434ac Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 18:22:43 +1300 Subject: [PATCH 45/60] Cleanup headers --- Source/Core/DolphinNoGUI/MainNoGUI.cpp | 1 - Source/Core/DolphinQt/HotkeyScheduler.cpp | 1 - Source/Core/DolphinTool/ToolHeadlessPlatform.cpp | 1 - Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp | 2 +- Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp | 2 +- Source/Core/VideoBackends/Metal/MTLStateTracker.h | 2 +- Source/Core/VideoCommon/BPStructs.cpp | 1 - Source/Core/VideoCommon/RenderBase.h | 5 ----- Source/Core/VideoCommon/VertexShaderManager.cpp | 1 - 9 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 11bbf55da1..e8bb0958ab 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -33,7 +33,6 @@ #include "InputCommon/GCAdapter.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" static std::unique_ptr s_platform; diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 0b80d746a7..1e390c3796 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -43,7 +43,6 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "VideoCommon/OnScreenDisplay.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" diff --git a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp index 4034df5ae0..2bed042148 100644 --- a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp +++ b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp @@ -15,7 +15,6 @@ #include "Core/Host.h" // Begin stubs needed to satisfy Core dependencies -#include "VideoCommon/RenderBase.h" std::vector Host_GetPreferredLocales() { diff --git a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp index bef3834dc5..fe34a8ff45 100644 --- a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp @@ -6,7 +6,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "VideoBackends/D3D/D3DBase.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" namespace DX11 diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp index d7ada1516a..933f118ce9 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp @@ -11,7 +11,7 @@ #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3D12/D3D12Gfx.h" #include "VideoBackends/D3D12/DX12Context.h" -#include "VideoCommon/RenderBase.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" namespace DX12 diff --git a/Source/Core/VideoBackends/Metal/MTLStateTracker.h b/Source/Core/VideoBackends/Metal/MTLStateTracker.h index 3e0cb38afa..f425cd2884 100644 --- a/Source/Core/VideoBackends/Metal/MTLStateTracker.h +++ b/Source/Core/VideoBackends/Metal/MTLStateTracker.h @@ -17,8 +17,8 @@ #include "VideoBackends/Metal/MTLTexture.h" #include "VideoBackends/Metal/MTLUtil.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PerfQueryBase.h" -#include "VideoCommon/RenderBase.h" namespace Metal { diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index f5383042a0..baf3f5fedf 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -34,7 +34,6 @@ #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TMEM.h" #include "VideoCommon/TextureCacheBase.h" diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 23e6895553..dc48b1945d 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -4,14 +4,9 @@ #pragma once #include -#include #include "Common/CommonTypes.h" -#include "Common/MathUtil.h" -#include "VideoCommon/RenderState.h" -#include "VideoCommon/VideoEvents.h" -class PointerWrap; enum class EFBAccessType; enum class EFBReinterpretType; diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 817b7e5aa9..ab55efca8c 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -24,7 +24,6 @@ #include "VideoCommon/FreeLookCamera.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" -#include "VideoCommon/RenderBase.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexManagerBase.h" From 628af9d564e32079fb2f366b256fff9c9927a309 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 19:15:09 +1300 Subject: [PATCH 46/60] Fix builds with FFMPEG disabled --- Source/Core/VideoCommon/FrameDumpFFMpeg.h | 28 +++++++++++++++++++---- Source/Core/VideoCommon/FrameDumper.cpp | 12 +++++++--- Source/Core/VideoCommon/FrameDumper.h | 22 +----------------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Source/Core/VideoCommon/FrameDumpFFMpeg.h b/Source/Core/VideoCommon/FrameDumpFFMpeg.h index 18c98c7720..8431e24274 100644 --- a/Source/Core/VideoCommon/FrameDumpFFMpeg.h +++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.h @@ -10,8 +10,26 @@ struct FrameDumpContext; class PointerWrap; -struct FrameData; -struct FrameState; + +// Holds relevant emulation state during a rendered frame for +// when it is later asynchronously written. +struct FrameState +{ + u64 ticks = 0; + int frame_number = 0; + u32 savestate_index = 0; + int refresh_rate_num = 0; + int refresh_rate_den = 0; +}; + +struct FrameData +{ + const u8* data = nullptr; + int width = 0; + int height = 0; + int stride = 0; + FrameState state; +}; class FFMpegFrameDump { @@ -47,10 +65,10 @@ private: }; #if !defined(HAVE_FFMPEG) -inline FrameDump::FrameDump() = default; -inline FrameDump::~FrameDump() = default; +inline FFMpegFrameDump::FFMpegFrameDump() = default; +inline FFMpegFrameDump::~FFMpegFrameDump() = default; -inline FrameState FrameDump::FetchState(u64 ticks, int frame_number) const +inline FrameState FFMpegFrameDump::FetchState(u64 ticks, int frame_number) const { return {}; } diff --git a/Source/Core/VideoCommon/FrameDumper.cpp b/Source/Core/VideoCommon/FrameDumper.cpp index f0c62c4c17..06bbe1b1c9 100644 --- a/Source/Core/VideoCommon/FrameDumper.cpp +++ b/Source/Core/VideoCommon/FrameDumper.cpp @@ -288,16 +288,16 @@ void FrameDumper::StopFrameDumpToFFMPEG() #else -bool FrameDump::StartFrameDumpToFFMPEG(const FrameData&) +bool FrameDumper::StartFrameDumpToFFMPEG(const FrameData&) { return false; } -void FrameDump::DumpFrameToFFMPEG(const FrameData&) +void FrameDumper::DumpFrameToFFMPEG(const FrameData&) { } -void FrameDump::StopFrameDumpToFFMPEG() +void FrameDumper::StopFrameDumpToFFMPEG() { } @@ -352,4 +352,10 @@ bool FrameDumper::IsFrameDumping() const return false; } +void FrameDumper::DoState(PointerWrap& p) +{ +#ifdef HAVE_FFMPEG + m_ffmpeg_dump.DoState(p); +#endif +} std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index 8006739671..cdf6d9359d 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -16,26 +16,6 @@ class AbstractStagingTexture; class AbstractTexture; class AbstractFramebuffer; -// Holds relevant emulation state during a rendered frame for -// when it is later asynchronously written. -struct FrameState -{ - u64 ticks = 0; - int frame_number = 0; - u32 savestate_index = 0; - int refresh_rate_num = 0; - int refresh_rate_den = 0; -}; - -struct FrameData -{ - const u8* data = nullptr; - int width = 0; - int height = 0; - int stride = 0; - FrameState state; -}; - class FrameDumper { public: @@ -54,7 +34,7 @@ public: bool IsFrameDumping() const; - void DoState(PointerWrap& p) { m_ffmpeg_dump.DoState(p); } + void DoState(PointerWrap& p); private: // NOTE: The methods below are called on the framedumping thread. From f7ad82573683376958bb39ada5d5a37cc98abffc Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 19:16:58 +1300 Subject: [PATCH 47/60] fix fbdev --- Source/Core/DolphinNoGUI/PlatformFBDev.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/DolphinNoGUI/PlatformFBDev.cpp b/Source/Core/DolphinNoGUI/PlatformFBDev.cpp index 0b08bc110f..552a2aa9c4 100644 --- a/Source/Core/DolphinNoGUI/PlatformFBDev.cpp +++ b/Source/Core/DolphinNoGUI/PlatformFBDev.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include From cf9a6f8477a8c0ebfcb344fb9f3af9b97569798b Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 19:21:15 +1300 Subject: [PATCH 48/60] Lint fixes --- Source/Core/VideoBackends/Metal/MTLMain.mm | 9 +++------ Source/Core/VideoBackends/Metal/MTLObjectCache.h | 2 +- Source/Core/VideoBackends/OGL/OGLConfig.h | 2 +- Source/Core/VideoBackends/Software/SWGfx.cpp | 2 +- Source/Core/VideoCommon/VertexShaderManager.cpp | 1 - Source/Core/VideoCommon/VertexShaderManager.h | 2 -- Source/Core/VideoCommon/VideoConfig.cpp | 7 ++----- Source/Core/VideoCommon/Widescreen.cpp | 13 +++++++------ Source/Core/VideoCommon/Widescreen.h | 4 ++-- 9 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Source/Core/VideoBackends/Metal/MTLMain.mm b/Source/Core/VideoBackends/Metal/MTLMain.mm index d7be2cc534..f799d5eea1 100644 --- a/Source/Core/VideoBackends/Metal/MTLMain.mm +++ b/Source/Core/VideoBackends/Metal/MTLMain.mm @@ -17,9 +17,9 @@ #include "Common/MsgHandler.h" #include "VideoBackends/Metal/MTLBoundingBox.h" +#include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLObjectCache.h" #include "VideoBackends/Metal/MTLPerfQuery.h" -#include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLStateTracker.h" #include "VideoBackends/Metal/MTLUtil.h" #include "VideoBackends/Metal/MTLVertexManager.h" @@ -106,11 +106,8 @@ bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi) g_state_tracker = std::make_unique(); return InitializeShared( - std::make_unique(std::move(layer)), - std::make_unique(), - std::make_unique(), - std::make_unique() - ); + std::make_unique(std::move(layer)), std::make_unique(), + std::make_unique(), std::make_unique()); } } diff --git a/Source/Core/VideoBackends/Metal/MTLObjectCache.h b/Source/Core/VideoBackends/Metal/MTLObjectCache.h index 9ae28a6352..f47a513116 100644 --- a/Source/Core/VideoBackends/Metal/MTLObjectCache.h +++ b/Source/Core/VideoBackends/Metal/MTLObjectCache.h @@ -8,8 +8,8 @@ #include "VideoBackends/Metal/MRCHelpers.h" -#include "VideoCommon/RenderState.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/RenderState.h" struct AbstractPipelineConfig; class AbstractPipeline; diff --git a/Source/Core/VideoBackends/OGL/OGLConfig.h b/Source/Core/VideoBackends/OGL/OGLConfig.h index eb3d20e70c..2aece68896 100644 --- a/Source/Core/VideoBackends/OGL/OGLConfig.h +++ b/Source/Core/VideoBackends/OGL/OGLConfig.h @@ -75,4 +75,4 @@ bool PopulateConfig(GLContext* main_gl_context); extern VideoConfig g_ogl_config; -} // namespace OGL \ No newline at end of file +} // namespace OGL diff --git a/Source/Core/VideoBackends/Software/SWGfx.cpp b/Source/Core/VideoBackends/Software/SWGfx.cpp index d4758a07fa..4dfd5dc0f2 100644 --- a/Source/Core/VideoBackends/Software/SWGfx.cpp +++ b/Source/Core/VideoBackends/Software/SWGfx.cpp @@ -128,4 +128,4 @@ void SWGfx::SetScissorRect(const MathUtil::Rectangle& rc) Rasterizer::ScissorChanged(); } -} // namespace SW \ No newline at end of file +} // namespace SW diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index ab55efca8c..2408281da6 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -176,7 +176,6 @@ bool VertexShaderManager::UseVertexDepthRange() return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; } - // Syncs the shader constant buffers with xfmem // TODO: A cleaner way to control the matrices without making a mess in the parameters field void VertexShaderManager::SetConstants(const std::vector& textures) diff --git a/Source/Core/VideoCommon/VertexShaderManager.h b/Source/Core/VideoCommon/VertexShaderManager.h index 3b22d43aeb..320e04985d 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.h +++ b/Source/Core/VideoCommon/VertexShaderManager.h @@ -49,8 +49,6 @@ public: VertexShaderConstants constants{}; bool dirty = false; - - private: alignas(16) std::array m_projection_matrix; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 0c209a9686..dfbc0d3952 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -355,8 +355,5 @@ void CheckForConfigChanges() // TODO: Move everything else to the ConfigChanged event } -static EventHook s_check_config_event = AfterFrameEvent::Register( - [] { - CheckForConfigChanges(); - }, - "CheckForConfigChanges"); +static EventHook s_check_config_event = + AfterFrameEvent::Register([] { CheckForConfigChanges(); }, "CheckForConfigChanges"); diff --git a/Source/Core/VideoCommon/Widescreen.cpp b/Source/Core/VideoCommon/Widescreen.cpp index ac8fa2fcfd..dd1393d309 100644 --- a/Source/Core/VideoCommon/Widescreen.cpp +++ b/Source/Core/VideoCommon/Widescreen.cpp @@ -15,10 +15,12 @@ WidescreenManager::WidescreenManager() { Update(); - m_config_changed = ConfigChangedEvent::Register([this](u32 bits) { - if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO)) - Update(); - }, "Widescreen"); + m_config_changed = ConfigChangedEvent::Register( + [this](u32 bits) { + if (bits & (CONFIG_CHANGE_BIT_ASPECT_RATIO)) + Update(); + }, + "Widescreen"); // VertexManager doesn't maintain statistics in Wii mode. if (!SConfig::GetInstance().bWii) @@ -28,7 +30,6 @@ WidescreenManager::WidescreenManager() } } - void WidescreenManager::Update() { if (SConfig::GetInstance().bWii) @@ -110,4 +111,4 @@ void WidescreenManager::DoState(PointerWrap& p) { m_was_orthographically_anamorphic = false; } -} \ No newline at end of file +} diff --git a/Source/Core/VideoCommon/Widescreen.h b/Source/Core/VideoCommon/Widescreen.h index 437149cb54..78430ca9bf 100644 --- a/Source/Core/VideoCommon/Widescreen.h +++ b/Source/Core/VideoCommon/Widescreen.h @@ -5,7 +5,6 @@ #include - #include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoEvents.h" @@ -20,6 +19,7 @@ public: bool IsGameWidescreen() const { return m_is_game_widescreen; } void DoState(PointerWrap& p); + private: void Update(); void UpdateWidescreenHeuristic(); @@ -31,4 +31,4 @@ private: EventHook m_config_changed; }; -extern std::unique_ptr g_widescreen; \ No newline at end of file +extern std::unique_ptr g_widescreen; From e2de28189782feed870ac367e28b93c6b4458351 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Tue, 31 Jan 2023 22:40:43 +1300 Subject: [PATCH 49/60] Make sure pixel shaders pick up Initial EFB Scale --- .../Core/VideoCommon/PixelShaderManager.cpp | 2 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Core/VideoCommon/PixelShaderManager.cpp b/Source/Core/VideoCommon/PixelShaderManager.cpp index 94b37be4e8..786c5d6177 100644 --- a/Source/Core/VideoCommon/PixelShaderManager.cpp +++ b/Source/Core/VideoCommon/PixelShaderManager.cpp @@ -65,7 +65,7 @@ void PixelShaderManager::Init() } } - dirty = true; + Dirty(); } void PixelShaderManager::Dirty() diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 95b58322d2..7788fe0dfb 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -362,6 +362,16 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, g_graphics_mod_manager = std::make_unique(); g_widescreen = std::make_unique(); + if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || + !g_perf_query->Initialize() || !g_presenter->Initialize() || + !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || + !g_bounding_box->Initialize() || !g_graphics_mod_manager->Initialize()) + { + PanicAlertFmtT("Failed to initialize renderer classes"); + Shutdown(); + return false; + } + auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); command_processor.Init(system); @@ -374,16 +384,6 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, system.GetPixelShaderManager().Init(); TMEM::Init(); - if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || - !g_perf_query->Initialize() || !g_presenter->Initialize() || - !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || - !g_bounding_box->Initialize() || !g_graphics_mod_manager->Initialize()) - { - PanicAlertFmtT("Failed to initialize renderer classes"); - Shutdown(); - return false; - } - g_Config.VerifyValidity(); UpdateActiveConfig(); From 05fad53fa0bc2000316baa0c6de2313b1759b615 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Wed, 1 Feb 2023 13:35:06 +1300 Subject: [PATCH 50/60] Update STATE_VERSION Co-authored-by: BhaaL --- Source/Core/Core/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 9f1a1c2a5a..1329dbe070 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -96,7 +96,7 @@ static size_t s_state_writes_in_queue; static std::condition_variable s_state_write_queue_is_empty; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 158; // Last changed in PR ??? +constexpr u32 STATE_VERSION = 158; // Last changed in PR 11522 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, From 43b6a4901206f49cd5b6f08206543d07c9ee875d Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 13:00:26 +1300 Subject: [PATCH 51/60] Apply suggestions from code review Co-authored-by: Mai --- Source/Core/Common/EventHook.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/EventHook.h index 903ff3ae36..f4d6ea0863 100644 --- a/Source/Core/Common/EventHook.h +++ b/Source/Core/Common/EventHook.h @@ -46,7 +46,7 @@ private: struct HookImpl : public HookBase { ~HookImpl() override { Event::Remove(this); } - HookImpl(CallbackType callback, std::string name) : m_fn(callback), m_name(name) {} + HookImpl(CallbackType callback, std::string name) : m_fn(std::move(callback)), m_name(std::move(name)) {} CallbackType m_fn; std::string m_name; }; @@ -56,12 +56,12 @@ public: static EventHook Register(CallbackType callback, std::string name) { DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); - auto handle = std::make_unique(callback, name); + auto handle = std::make_unique(callback, std::move(name)); m_listeners.push_back(handle.get()); return handle; } - static void Trigger(CallbackArgs... args) + static void Trigger(const CallbackArgs&... args) { for (auto& handle : m_listeners) handle->m_fn(args...); From e0a1631659425948a1e25c159307d81bda961018 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 13:01:18 +1300 Subject: [PATCH 52/60] Add comment about "end of frame" --- Source/Core/VideoCommon/BPStructs.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index baf3f5fedf..8bb312f1ba 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -343,8 +343,19 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, // This is as closest as we have to an "end of the frame" // It works 99% of the time. + // But sometimes games want to render an XFB larger than the EFB's 640x528 pixel resolution + // (especially when using the 3xMSAA mode, which cuts EFB resolution to 640x264). So they + // render multiple sub-frames and arrange the XFB copies in next to each-other in main memory + // so they form a single completed XFB. + // See https://dolphin-emu.org/blog/2017/11/19/hybridxfb/ for examples and more detail. AfterFrameEvent::Trigger(); + // Note: Theoretically, in the future we could track the VI configuration and try to detect + // when an XFB is the last XFB copy of a frame. Not only would we get a clean "end of + // the frame", but we would also be able to use ImmediateXFB even for these games. + // Might also clean up some issues with games doing XFB copies they don't intend to + // display. + if (g_ActiveConfig.bImmediateXFB) { // below div two to convert from bytes to pixels - it expects width, not stride From 60f2b5af7b5b5748f672e968d99338d503b5272e Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 13:18:37 +1300 Subject: [PATCH 53/60] Apply suggestions from code review Co-authored-by: Mai Co-authored-by: BhaaL Co-authored-by: iwubcode --- Source/Core/Common/EventHook.h | 20 ++++++++++++++----- Source/Core/Common/StringLiteral.h | 4 ++++ Source/Core/Core/Core.cpp | 2 +- Source/Core/Core/FifoPlayer/FifoRecorder.h | 2 +- Source/Core/VideoBackends/D3D/D3DGfx.h | 2 +- Source/Core/VideoBackends/Metal/MTLGfx.h | 1 - Source/Core/VideoBackends/Vulkan/VKGfx.h | 3 +-- Source/Core/VideoCommon/FrameDumper.h | 2 +- Source/Core/VideoCommon/FramebufferManager.h | 2 +- .../Runtime/GraphicsModManager.h | 2 +- Source/Core/VideoCommon/ShaderCache.h | 2 +- Source/Core/VideoCommon/Statistics.cpp | 4 ++-- Source/Core/VideoCommon/TextureCacheBase.cpp | 4 ++-- Source/Core/VideoCommon/TextureCacheBase.h | 7 ++++--- Source/Core/VideoCommon/VertexManagerBase.h | 2 +- Source/Core/VideoCommon/VideoConfig.cpp | 2 +- Source/Core/VideoCommon/VideoEvents.h | 10 +++++----- Source/Core/VideoCommon/Widescreen.h | 4 ++-- 18 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/EventHook.h index f4d6ea0863..49a3b9514a 100644 --- a/Source/Core/Common/EventHook.h +++ b/Source/Core/Common/EventHook.h @@ -8,10 +8,13 @@ #include #include +#include #include #include #include +namespace Common +{ // A hookable event system. // Define Events in a header as: @@ -43,10 +46,13 @@ public: using CallbackType = std::function; private: - struct HookImpl : public HookBase + struct HookImpl final : public HookBase { ~HookImpl() override { Event::Remove(this); } - HookImpl(CallbackType callback, std::string name) : m_fn(std::move(callback)), m_name(std::move(name)) {} + HookImpl(CallbackType callback, std::string name) + : m_fn(std::move(callback)), m_name(std::move(name)) + { + } CallbackType m_fn; std::string m_name; }; @@ -55,6 +61,7 @@ public: // Returns a handle that will unregister the listener when destroyed. static EventHook Register(CallbackType callback, std::string name) { + std::lock_guard lock(m_mutex); DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); auto handle = std::make_unique(callback, std::move(name)); m_listeners.push_back(handle.get()); @@ -63,6 +70,7 @@ public: static void Trigger(const CallbackArgs&... args) { + std::lock_guard lock(m_mutex); for (auto& handle : m_listeners) handle->m_fn(args...); } @@ -70,10 +78,12 @@ public: private: static void Remove(HookImpl* handle) { - auto it = std::find(m_listeners.begin(), m_listeners.end(), handle); - if (it != m_listeners.end()) - m_listeners.erase(it); + std::lock_guard lock(m_mutex); + std::erase(m_listeners, handle); } inline static std::vector m_listeners = {}; + inline static std::mutex m_mutex; }; + +} // namespace Common diff --git a/Source/Core/Common/StringLiteral.h b/Source/Core/Common/StringLiteral.h index 33ecee31e5..6da9afd24d 100644 --- a/Source/Core/Common/StringLiteral.h +++ b/Source/Core/Common/StringLiteral.h @@ -5,6 +5,8 @@ #include +namespace Common +{ // A useful template for passing string literals as arguments to templates // from: https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/ template @@ -14,3 +16,5 @@ struct StringLiteral char value[N]; }; + +} // namespace Common diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index f49261483d..1f627705e9 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -132,7 +132,7 @@ static thread_local bool tls_is_gpu_thread = false; static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi); -static EventHook s_frame_presented = AfterPresentEvent::Register( +static Common::EventHook s_frame_presented = AfterPresentEvent::Register( [](auto& present_info) { const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); // The denominator should always be > 0 but if it's not, just return 1 diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.h b/Source/Core/Core/FifoPlayer/FifoRecorder.h index 6c26ba5934..c0a1845471 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.h +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.h @@ -76,5 +76,5 @@ private: std::vector m_Ram; std::vector m_ExRam; - EventHook m_end_of_frame_event; + Common::EventHook m_end_of_frame_event; }; diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.h b/Source/Core/VideoBackends/D3D/D3DGfx.h index 8bbf998e36..853599224c 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.h +++ b/Source/Core/VideoBackends/D3D/D3DGfx.h @@ -16,7 +16,7 @@ class SwapChain; class DXTexture; class DXFramebuffer; -class Gfx : public ::AbstractGfx +class Gfx final : public ::AbstractGfx { public: Gfx(std::unique_ptr swap_chain, float backbuffer_scale); diff --git a/Source/Core/VideoBackends/Metal/MTLGfx.h b/Source/Core/VideoBackends/Metal/MTLGfx.h index a7d5e170b6..22795a0183 100644 --- a/Source/Core/VideoBackends/Metal/MTLGfx.h +++ b/Source/Core/VideoBackends/Metal/MTLGfx.h @@ -69,7 +69,6 @@ public: void BindBackbuffer(const ClearColor& clear_color = {}) override; void PresentBackbuffer() override; - // Returns info about the main surface (aka backbuffer) SurfaceInfo GetSurfaceInfo() const override; private: diff --git a/Source/Core/VideoBackends/Vulkan/VKGfx.h b/Source/Core/VideoBackends/Vulkan/VKGfx.h index 8217533f06..bc254874fd 100644 --- a/Source/Core/VideoBackends/Vulkan/VKGfx.h +++ b/Source/Core/VideoBackends/Vulkan/VKGfx.h @@ -19,7 +19,7 @@ class VKFramebuffer; class VKPipeline; class VKTexture; -class VKGfx : public ::AbstractGfx +class VKGfx final : public ::AbstractGfx { public: VKGfx(std::unique_ptr swap_chain, float backbuffer_scale); @@ -77,7 +77,6 @@ public: void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; - // Returns info about the main surface (aka backbuffer) virtual SurfaceInfo GetSurfaceInfo() const override; // Completes the current render pass, executes the command buffer, and restores state ready for diff --git a/Source/Core/VideoCommon/FrameDumper.h b/Source/Core/VideoCommon/FrameDumper.h index cdf6d9359d..859a4140e3 100644 --- a/Source/Core/VideoCommon/FrameDumper.h +++ b/Source/Core/VideoCommon/FrameDumper.h @@ -98,7 +98,7 @@ private: std::mutex m_screenshot_lock; std::string m_screenshot_name; - EventHook m_frame_end_handle; + Common::EventHook m_frame_end_handle; }; extern std::unique_ptr g_frame_dumper; diff --git a/Source/Core/VideoCommon/FramebufferManager.h b/Source/Core/VideoCommon/FramebufferManager.h index 087c5023aa..7921a70d41 100644 --- a/Source/Core/VideoCommon/FramebufferManager.h +++ b/Source/Core/VideoCommon/FramebufferManager.h @@ -238,7 +238,7 @@ protected: std::vector m_color_poke_vertices; std::vector m_depth_poke_vertices; - EventHook m_end_of_frame_event; + Common::EventHook m_end_of_frame_event; }; extern std::unique_ptr g_framebuffer_manager; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h index 9115d8f45d..ed0063444a 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h @@ -54,7 +54,7 @@ private: std::unordered_set m_groups; - EventHook m_end_of_frame_event; + Common::EventHook m_end_of_frame_event; }; extern std::unique_ptr g_graphics_mod_manager; diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h index f0982b3104..809ebb23eb 100644 --- a/Source/Core/VideoCommon/ShaderCache.h +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -252,7 +252,7 @@ private: // Texture decoding shaders std::map, std::unique_ptr> m_texture_decoding_shaders; - EventHook m_frame_end_handler; + Common::EventHook m_frame_end_handler; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Statistics.cpp b/Source/Core/VideoCommon/Statistics.cpp index ecca851968..231bb470b4 100644 --- a/Source/Core/VideoCommon/Statistics.cpp +++ b/Source/Core/VideoCommon/Statistics.cpp @@ -18,10 +18,10 @@ Statistics g_stats; -static EventHook s_before_frame_event = +static Common::EventHook s_before_frame_event = BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame"); -static EventHook s_after_frame_event = AfterFrameEvent::Register( +static Common::EventHook s_after_frame_event = AfterFrameEvent::Register( [] { DolphinAnalytics::PerformanceSample perf_sample; perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance(); diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index ce14075552..8b5e56af37 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -780,10 +780,10 @@ void TextureCacheBase::OnFrameEnd() // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending // copies. - g_texture_cache->FlushEFBCopies(); + FlushEFBCopies(); } - g_texture_cache->Cleanup(g_presenter->FrameCount()); + Cleanup(g_presenter->FrameCount()); } void TCacheEntry::DoState(PointerWrap& p) diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index c38f2578b9..958d7704fe 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -123,8 +123,8 @@ struct TCacheEntry bool is_efb_copy = false; bool is_custom_tex = false; bool may_have_overlapping_textures = true; - bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary - // content, aren't just downscaled + // indicates that the mips in this texture are arbitrary content, aren't just downscaled + bool has_arbitrary_mips = false; bool should_force_safe_hashing = false; // for XFB bool is_xfb_copy = false; bool is_xfb_container = false; @@ -438,7 +438,8 @@ private: void OnFrameEnd(); Common::Flag m_force_reload_textures; - EventHook m_frame_event = AfterFrameEvent::Register([this] { OnFrameEnd(); }, "TextureCache"); + Common::EventHook m_frame_event = + AfterFrameEvent::Register([this] { OnFrameEnd(); }, "TextureCache"); }; extern std::unique_ptr g_texture_cache; diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 18e0f1d402..775d8e2787 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -230,7 +230,7 @@ private: std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; - EventHook m_frame_end_event; + Common::EventHook m_frame_end_event; }; extern std::unique_ptr g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index dfbc0d3952..38ee680bf3 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -355,5 +355,5 @@ void CheckForConfigChanges() // TODO: Move everything else to the ConfigChanged event } -static EventHook s_check_config_event = +static Common::EventHook s_check_config_event = AfterFrameEvent::Register([] { CheckForConfigChanges(); }, "CheckForConfigChanges"); diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index a2e0760075..57d44cbe59 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -7,16 +7,16 @@ #include "Common/EventHook.h" // Called when certain video config setting are changed -using ConfigChangedEvent = Event<"ConfigChanged", u32>; +using ConfigChangedEvent = Common::Event<"ConfigChanged", u32>; // An event called just before the first draw call of a frame -using BeforeFrameEvent = Event<"BeforeFrame">; +using BeforeFrameEvent = Common::Event<"BeforeFrame">; // An event called after the frame XFB copy begins processing on the host GPU. // Useful for "once per frame" usecases. // Note: In a few rare cases, games do multiple XFB copies per frame and join them while presenting. // If this matters to your usecase, you should use BeforePresent instead. -using AfterFrameEvent = Event<"AfterFrame">; +using AfterFrameEvent = Common::Event<"AfterFrame">; struct PresentInfo { @@ -76,8 +76,8 @@ struct PresentInfo // frame. // // frame_count: The number of frames -using BeforePresentEvent = Event<"BeforePresent", PresentInfo&>; +using BeforePresentEvent = Common::Event<"BeforePresent", PresentInfo&>; // An event that is triggered after a frame is presented. // The exact timing of this event depends on backend/driver support. -using AfterPresentEvent = Event<"AfterPresent", PresentInfo&>; +using AfterPresentEvent = Common::Event<"AfterPresent", PresentInfo&>; diff --git a/Source/Core/VideoCommon/Widescreen.h b/Source/Core/VideoCommon/Widescreen.h index 78430ca9bf..c8dc5847ec 100644 --- a/Source/Core/VideoCommon/Widescreen.h +++ b/Source/Core/VideoCommon/Widescreen.h @@ -27,8 +27,8 @@ private: bool m_is_game_widescreen = false; bool m_was_orthographically_anamorphic = false; - EventHook m_update_widescreen; - EventHook m_config_changed; + Common::EventHook m_update_widescreen; + Common::EventHook m_config_changed; }; extern std::unique_ptr g_widescreen; From 8c8bd0e7ac5fbe8868031ff5c75e387d9df89338 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 13:37:42 +1300 Subject: [PATCH 54/60] Rename to HookableEvent. Because naming conflict --- Source/Core/Common/CMakeLists.txt | 2 +- Source/Core/Common/{EventHook.h => HookableEvent.h} | 6 +++--- Source/Core/Core/FifoPlayer/FifoRecorder.h | 2 +- Source/Core/DolphinLib.props | 2 +- Source/Core/VideoCommon/VideoEvents.h | 12 ++++++------ 5 files changed, 12 insertions(+), 12 deletions(-) rename Source/Core/Common/{EventHook.h => HookableEvent.h} (92%) diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index bab78766da..30248f864f 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -46,7 +46,6 @@ add_library(common EnumFormatter.h EnumMap.h Event.h - EventHook.h FatFsUtil.cpp FatFsUtil.h FileSearch.cpp @@ -63,6 +62,7 @@ add_library(common GekkoDisassembler.h Hash.cpp Hash.h + HookableEvent.h HttpRequest.cpp HttpRequest.h Image.cpp diff --git a/Source/Core/Common/EventHook.h b/Source/Core/Common/HookableEvent.h similarity index 92% rename from Source/Core/Common/EventHook.h rename to Source/Core/Common/HookableEvent.h index 49a3b9514a..cc58c045b5 100644 --- a/Source/Core/Common/EventHook.h +++ b/Source/Core/Common/HookableEvent.h @@ -19,7 +19,7 @@ namespace Common // Define Events in a header as: // -// using MyLoveyEvent = Event<"My lovely event", std::string>; +// using MyLoveyEvent = HookableEvent<"My lovely event", std::string>; // // Register listeners anywhere you need them as: // EventHook myHook = MyLoveyEvent::Register([](std::string foo) { @@ -40,7 +40,7 @@ struct HookBase using EventHook = std::unique_ptr; template -class Event +class HookableEvent { public: using CallbackType = std::function; @@ -48,7 +48,7 @@ public: private: struct HookImpl final : public HookBase { - ~HookImpl() override { Event::Remove(this); } + ~HookImpl() override { HookableEvent::Remove(this); } HookImpl(CallbackType callback, std::string name) : m_fn(std::move(callback)), m_name(std::move(name)) { diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.h b/Source/Core/Core/FifoPlayer/FifoRecorder.h index c0a1845471..b6330c2e28 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.h +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.h @@ -9,7 +9,7 @@ #include #include "Common/Assert.h" -#include "Common/EventHook.h" +#include "Common/HookableEvent.h" #include "Core/FifoPlayer/FifoDataFile.h" class FifoRecorder diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 780f8da7b9..ff461840e3 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -46,7 +46,6 @@ - @@ -110,6 +109,7 @@ + diff --git a/Source/Core/VideoCommon/VideoEvents.h b/Source/Core/VideoCommon/VideoEvents.h index 57d44cbe59..a54d4d9ce7 100644 --- a/Source/Core/VideoCommon/VideoEvents.h +++ b/Source/Core/VideoCommon/VideoEvents.h @@ -4,19 +4,19 @@ #pragma once #include "Common/CommonTypes.h" -#include "Common/EventHook.h" +#include "Common/HookableEvent.h" // Called when certain video config setting are changed -using ConfigChangedEvent = Common::Event<"ConfigChanged", u32>; +using ConfigChangedEvent = Common::HookableEvent<"ConfigChanged", u32>; // An event called just before the first draw call of a frame -using BeforeFrameEvent = Common::Event<"BeforeFrame">; +using BeforeFrameEvent = Common::HookableEvent<"BeforeFrame">; // An event called after the frame XFB copy begins processing on the host GPU. // Useful for "once per frame" usecases. // Note: In a few rare cases, games do multiple XFB copies per frame and join them while presenting. // If this matters to your usecase, you should use BeforePresent instead. -using AfterFrameEvent = Common::Event<"AfterFrame">; +using AfterFrameEvent = Common::HookableEvent<"AfterFrame">; struct PresentInfo { @@ -76,8 +76,8 @@ struct PresentInfo // frame. // // frame_count: The number of frames -using BeforePresentEvent = Common::Event<"BeforePresent", PresentInfo&>; +using BeforePresentEvent = Common::HookableEvent<"BeforePresent", PresentInfo&>; // An event that is triggered after a frame is presented. // The exact timing of this event depends on backend/driver support. -using AfterPresentEvent = Common::Event<"AfterPresent", PresentInfo&>; +using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>; From 59a4b026f6a8259200e88f4356ef988789f7b5c1 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 14:24:26 +1300 Subject: [PATCH 55/60] Better documentation for HookableEvent. --- Source/Core/Common/HookableEvent.h | 49 +++++++++++++++++++----------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/Source/Core/Common/HookableEvent.h b/Source/Core/Common/HookableEvent.h index cc58c045b5..8c87303725 100644 --- a/Source/Core/Common/HookableEvent.h +++ b/Source/Core/Common/HookableEvent.h @@ -15,30 +15,40 @@ namespace Common { -// A hookable event system. - -// Define Events in a header as: -// -// using MyLoveyEvent = HookableEvent<"My lovely event", std::string>; -// -// Register listeners anywhere you need them as: -// EventHook myHook = MyLoveyEvent::Register([](std::string foo) { -// // Do something -// }, "Name of the hook"); -// -// The hook will be automatically unregistered when the EventHook object goes out of scope. -// Trigger events by doing: -// -// MyLoveyEvent::Trigger("Hello world"); -// - struct HookBase { virtual ~HookBase() = default; + +protected: + HookBase() = default; + + // This shouldn't be copied. And since we always wrap it in unique_ptr, no need to move it either + HookBase(const HookBase&) = delete; + HookBase(HookBase&&) = delete; + HookBase& operator=(const HookBase&) = delete; + HookBase& operator=(HookBase&&) = delete; }; +// EventHook is a handle a registered listener holds. +// When the handle is destroyed, the HookableEvent will automatically remove the listener. using EventHook = std::unique_ptr; +// A hookable event system. +// +// Define Events in a header as: +// +// using MyLoveyEvent = HookableEvent<"My lovely event", std::string, u32>; +// +// Register listeners anywhere you need them as: +// EventHook myHook = MyLoveyEvent::Register([](std::string foo, u32 bar) { +// fmt::print("I've been triggered with {} and {}", foo, bar) +// }, "NameOfHook"); +// +// The hook will be automatically unregistered when the EventHook object goes out of scope. +// Trigger events by calling Trigger as: +// +// MyLoveyEvent::Trigger("Hello world", 42); +// template class HookableEvent { @@ -62,6 +72,7 @@ public: static EventHook Register(CallbackType callback, std::string name) { std::lock_guard lock(m_mutex); + DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); auto handle = std::make_unique(callback, std::move(name)); m_listeners.push_back(handle.get()); @@ -71,7 +82,8 @@ public: static void Trigger(const CallbackArgs&... args) { std::lock_guard lock(m_mutex); - for (auto& handle : m_listeners) + + for (const auto& handle : m_listeners) handle->m_fn(args...); } @@ -79,6 +91,7 @@ private: static void Remove(HookImpl* handle) { std::lock_guard lock(m_mutex); + std::erase(m_listeners, handle); } From d3ddd96cee9e8e14fd000bc489cfa84abed1d5a2 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 15:18:10 +1300 Subject: [PATCH 56/60] Make sure m_prev_efb_format is initilized --- Source/Core/VideoCommon/FramebufferManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/FramebufferManager.cpp b/Source/Core/VideoCommon/FramebufferManager.cpp index 21871f8442..4bdc3616a9 100644 --- a/Source/Core/VideoCommon/FramebufferManager.cpp +++ b/Source/Core/VideoCommon/FramebufferManager.cpp @@ -31,7 +31,9 @@ constexpr size_t MAX_POKE_VERTICES = 32768; std::unique_ptr g_framebuffer_manager; -FramebufferManager::FramebufferManager() = default; +FramebufferManager::FramebufferManager() : m_prev_efb_format(PixelFormat::INVALID_FMT) +{ +} FramebufferManager::~FramebufferManager() { From 83b7b01265598067494eca228175e5b5805e36a2 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 15:51:50 +1300 Subject: [PATCH 57/60] Fix XFB duplicate detection Frame duplicate detection was inverted. Huge problem for 60fps games where it would see all frames as "duplicates" and nothing would ever be presented. --- Source/Core/VideoCommon/Present.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 0e7f6b96d9..1f3b8b8ad8 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -80,7 +80,7 @@ bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_heigh m_last_xfb_height = fb_height; m_last_xfb_id = m_xfb_entry->id; - return is_duplicate; + return !is_duplicate; } void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) From b2a31103b43c365d4c48a55b805001bfb07d974e Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 17:42:47 +1300 Subject: [PATCH 58/60] Presenter: Handle blanked frames correctly --- Source/Core/VideoCommon/Present.cpp | 48 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 1f3b8b8ad8..fa3be6acc5 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -66,54 +66,58 @@ bool Presenter::Initialize() bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { ReleaseXFBContentLock(); + u64 old_xfb_id = m_last_xfb_id; - m_xfb_entry = - g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); - bool is_duplicate = m_xfb_entry->id == m_last_xfb_id; - - m_xfb_entry->AcquireContentLock(); + if (fb_width == 0 || fb_height == 0) + { + // Game is blanking the screen + m_xfb_entry.reset(); + m_last_xfb_id = std::numeric_limits::max(); + } + else + { + m_xfb_entry = + g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &m_xfb_rect); + m_last_xfb_id = m_xfb_entry->id; + m_xfb_entry->AcquireContentLock(); + } m_last_xfb_addr = xfb_addr; m_last_xfb_ticks = ticks; m_last_xfb_width = fb_width; m_last_xfb_stride = fb_stride; m_last_xfb_height = fb_height; - m_last_xfb_id = m_xfb_entry->id; - return !is_duplicate; + return old_xfb_id == m_last_xfb_id; } void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) { - bool unique = FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); + bool is_duplicate = FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); PresentInfo present_info; present_info.emulated_timestamp = ticks; - if (unique) - { - present_info.frame_count = m_frame_count++; - present_info.reason = PresentInfo::PresentReason::VideoInterface; - } - else + present_info.present_count = m_present_count++; + if (is_duplicate) { present_info.frame_count = m_frame_count - 1; // Previous frame present_info.reason = PresentInfo::PresentReason::VideoInterfaceDuplicate; } - present_info.present_count = m_present_count; + else + { + present_info.frame_count = m_frame_count++; + present_info.reason = PresentInfo::PresentReason::VideoInterface; + } BeforePresentEvent::Trigger(present_info); - if (unique || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) + if (!is_duplicate || !g_ActiveConfig.bSkipPresentingDuplicateXFBs) { Present(); ProcessFrameDumping(ticks); AfterPresentEvent::Trigger(present_info); } - else - { - g_gfx->Flush(); - } } void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks) @@ -121,7 +125,7 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ FetchXFB(xfb_addr, fb_width, fb_stride, fb_height, ticks); PresentInfo present_info; - present_info.emulated_timestamp = ticks; + present_info.emulated_timestamp = ticks; // TODO: This should be the time of the next VI field present_info.frame_count = m_frame_count++; present_info.reason = PresentInfo::PresentReason::Immediate; present_info.present_count = m_present_count++; @@ -136,7 +140,7 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_ void Presenter::ProcessFrameDumping(u64 ticks) const { - if (g_frame_dumper->IsFrameDumping()) + if (g_frame_dumper->IsFrameDumping() && m_xfb_entry) { MathUtil::Rectangle target_rect; if (!g_ActiveConfig.bInternalResolutionFrameDumps && !g_gfx->IsHeadless()) From 9c1fe59cc9e9c3616416d78e1ed4b37be1bb2f66 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 19:11:31 +1300 Subject: [PATCH 59/60] Insert a more solid abstraction between Qt and Imgui --- Source/Core/DolphinLib.props | 1 + Source/Core/DolphinQt/RenderWidget.cpp | 44 +++++++--------------- Source/Core/DolphinQt/RenderWidget.h | 4 +- Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/OnScreenUI.cpp | 22 +++++++++-- Source/Core/VideoCommon/OnScreenUI.h | 3 +- Source/Core/VideoCommon/OnScreenUIKeyMap.h | 37 ++++++++++++++++++ Source/Core/VideoCommon/Present.cpp | 2 +- Source/Core/VideoCommon/Present.h | 4 +- 9 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 Source/Core/VideoCommon/OnScreenUIKeyMap.h diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index ff461840e3..f7f7606982 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -675,6 +675,7 @@ + diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index cebbd490c3..c39621ad73 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -19,8 +19,6 @@ #include #include -#include - #include "Core/Config/MainSettings.h" #include "Core/Core.h" #include "Core/State.h" @@ -32,6 +30,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "VideoCommon/OnScreenUI.h" #include "VideoCommon/Present.h" #include "VideoCommon/VideoConfig.h" @@ -62,7 +61,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { if (state == Core::State::Running) - SetImGuiKeyMap(); + SetPresenterKeyMap(); }); // We have to use Qt::DirectConnection here because we don't want those signals to get queued @@ -338,7 +337,7 @@ void RenderWidget::SetWaitingForMessageBox(bool waiting_for_message_box) bool RenderWidget::event(QEvent* event) { - PassEventToImGui(event); + PassEventToPresenter(event); switch (event->type()) { @@ -470,7 +469,7 @@ bool RenderWidget::event(QEvent* event) return QWidget::event(event); } -void RenderWidget::PassEventToImGui(const QEvent* event) +void RenderWidget::PassEventToPresenter(const QEvent* event) { if (!Core::IsRunningAndStarted()) return; @@ -498,7 +497,7 @@ void RenderWidget::PassEventToImGui(const QEvent* event) chars = utf8.constData(); } - // Pass the key onto ImGui + // Pass the key onto Presenter (for the imgui UI) g_presenter->SetKey(key, is_down, chars); } break; @@ -529,31 +528,16 @@ void RenderWidget::PassEventToImGui(const QEvent* event) } } -void RenderWidget::SetImGuiKeyMap() +void RenderWidget::SetPresenterKeyMap() { - static std::array, 21> key_map{{ - {ImGuiKey_Tab, Qt::Key_Tab}, - {ImGuiKey_LeftArrow, Qt::Key_Left}, - {ImGuiKey_RightArrow, Qt::Key_Right}, - {ImGuiKey_UpArrow, Qt::Key_Up}, - {ImGuiKey_DownArrow, Qt::Key_Down}, - {ImGuiKey_PageUp, Qt::Key_PageUp}, - {ImGuiKey_PageDown, Qt::Key_PageDown}, - {ImGuiKey_Home, Qt::Key_Home}, - {ImGuiKey_End, Qt::Key_End}, - {ImGuiKey_Insert, Qt::Key_Insert}, - {ImGuiKey_Delete, Qt::Key_Delete}, - {ImGuiKey_Backspace, Qt::Key_Backspace}, - {ImGuiKey_Space, Qt::Key_Space}, - {ImGuiKey_Enter, Qt::Key_Return}, - {ImGuiKey_Escape, Qt::Key_Escape}, - {ImGuiKey_A, Qt::Key_A}, - {ImGuiKey_C, Qt::Key_C}, - {ImGuiKey_V, Qt::Key_V}, - {ImGuiKey_X, Qt::Key_X}, - {ImGuiKey_Y, Qt::Key_Y}, - {ImGuiKey_Z, Qt::Key_Z}, - }}; + static constexpr DolphinKeyMap key_map = { + Qt::Key_Tab, Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down, + Qt::Key_PageUp, Qt::Key_PageDown, Qt::Key_Home, Qt::Key_End, Qt::Key_Insert, + Qt::Key_Delete, Qt::Key_Backspace, Qt::Key_Space, Qt::Key_Return, Qt::Key_Escape, + Qt::Key_Enter, // Keypad enter + Qt::Key_A, Qt::Key_C, Qt::Key_V, Qt::Key_X, Qt::Key_Y, + Qt::Key_Z, + }; g_presenter->SetKeyMap(key_map); } diff --git a/Source/Core/DolphinQt/RenderWidget.h b/Source/Core/DolphinQt/RenderWidget.h index c8ea1e3a84..87235a3986 100644 --- a/Source/Core/DolphinQt/RenderWidget.h +++ b/Source/Core/DolphinQt/RenderWidget.h @@ -39,8 +39,8 @@ private: void OnLockCursorChanged(); void OnKeepOnTopChanged(bool top); void UpdateCursor(); - void PassEventToImGui(const QEvent* event); - void SetImGuiKeyMap(); + void PassEventToPresenter(const QEvent* event); + void SetPresenterKeyMap(); void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 632120ee09..392e53df91 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -89,6 +89,7 @@ add_library(videocommon OnScreenDisplay.h OnScreenUI.cpp OnScreenUI.h + OnScreenUIKeyMap.h OpcodeDecoding.cpp OpcodeDecoding.h PerfQueryBase.cpp diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 291f0c23e2..e2fa2593a7 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -3,6 +3,7 @@ #include "VideoCommon/OnScreenUI.h" +#include "Common/EnumMap.h" #include "Common/Profiler.h" #include "Common/Timer.h" @@ -346,15 +347,30 @@ void OnScreenUI::SetScale(float backbuffer_scale) m_backbuffer_scale = backbuffer_scale; } -void OnScreenUI::SetKeyMap(std::span> key_map) +void OnScreenUI::SetKeyMap(const DolphinKeyMap& key_map) { + // Right now this is a 1:1 mapping. But might not be true later + static constexpr DolphinKeyMap dolphin_to_imgui_map = { + ImGuiKey_Tab, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, + ImGuiKey_DownArrow, ImGuiKey_PageUp, ImGuiKey_PageDown, ImGuiKey_Home, + ImGuiKey_End, ImGuiKey_Insert, ImGuiKey_Delete, ImGuiKey_Backspace, + ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_KeyPadEnter, + ImGuiKey_A, ImGuiKey_C, ImGuiKey_V, ImGuiKey_X, + ImGuiKey_Y, ImGuiKey_Z, + }; + static_assert(dolphin_to_imgui_map.size() == ImGuiKey_COUNT); // Fail if ImGui adds keys + auto lock = GetImGuiLock(); if (!ImGui::GetCurrentContext()) return; - for (auto [imgui_key, qt_key] : key_map) - ImGui::GetIO().KeyMap[imgui_key] = (qt_key & 0x1FF); + for (int dolphin_key = 0; dolphin_key <= static_cast(DolphinKey::Z); dolphin_key++) + { + int imgui_key = dolphin_to_imgui_map[DolphinKey(dolphin_key)]; + if (imgui_key >= 0) + ImGui::GetIO().KeyMap[imgui_key] = (key_map[DolphinKey(dolphin_key)] & 0x1FF); + } } void OnScreenUI::SetKey(u32 key, bool is_down, const char* chars) diff --git a/Source/Core/VideoCommon/OnScreenUI.h b/Source/Core/VideoCommon/OnScreenUI.h index 43dc6144d1..a0becbc126 100644 --- a/Source/Core/VideoCommon/OnScreenUI.h +++ b/Source/Core/VideoCommon/OnScreenUI.h @@ -9,6 +9,7 @@ #include #include "Common/CommonTypes.h" +#include "VideoCommon/OnScreenUIKeyMap.h" class NativeVertexFormat; class AbstractTexture; @@ -52,7 +53,7 @@ public: void Finalize(); // Receive keyboard and mouse from QT - void SetKeyMap(std::span> key_map); + void SetKeyMap(const DolphinKeyMap& key_map); void SetKey(u32 key, bool is_down, const char* chars); void SetMousePos(float x, float y); void SetMousePress(u32 button_mask); diff --git a/Source/Core/VideoCommon/OnScreenUIKeyMap.h b/Source/Core/VideoCommon/OnScreenUIKeyMap.h new file mode 100644 index 0000000000..c637f3938b --- /dev/null +++ b/Source/Core/VideoCommon/OnScreenUIKeyMap.h @@ -0,0 +1,37 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Common/EnumMap.h" + +// The main point of this is to allow other parts of dolphin to set ImGui's key map without +// having to import ImGui headers. +// But the idea is that it can be expanded in the future with more keys to support more things. +enum class DolphinKey +{ + Tab, + LeftArrow, + RightArrow, + UpArrow, + DownArrow, + PageUp, + PageDown, + Home, + End, + Insert, + Delete, + Backspace, + Space, + Enter, + Escape, + KeyPadEnter, + A, // for text edit CTRL+A: select all + C, // for text edit CTRL+C: copy + V, // for text edit CTRL+V: paste + X, // for text edit CTRL+X: cut + Y, // for text edit CTRL+Y: redo + Z, // for text edit CTRL+Z: undo +}; + +using DolphinKeyMap = Common::EnumMap; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index fa3be6acc5..068c4693fa 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -571,7 +571,7 @@ void Presenter::Present() g_gfx->EndUtilityDrawing(); } -void Presenter::SetKeyMap(std::span> key_map) +void Presenter::SetKeyMap(const DolphinKeyMap& key_map) { if (m_onscreen_ui) m_onscreen_ui->SetKeyMap(key_map); diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 235dba11d4..eee98d0c1a 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -6,6 +6,7 @@ #include "Common/Flag.h" #include "Common/MathUtil.h" +#include "VideoCommon/OnScreenUIKeyMap.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureConfig.h" #include "VideoCommon/VideoCommon.h" @@ -18,6 +19,7 @@ class AbstractTexture; struct SurfaceInfo; +enum class DolphinKey; namespace VideoCommon { @@ -82,7 +84,7 @@ public: bool SurfaceChangedTestAndClear() { return m_surface_changed.TestAndClear(); } void* GetNewSurfaceHandle(); - void SetKeyMap(std::span> key_map); + void SetKeyMap(const DolphinKeyMap& key_map); void SetKey(u32 key, bool is_down, const char* chars); void SetMousePos(float x, float y); From 5c1b3ac61d7e660a9d1c206dbd0e615d637d3272 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 3 Feb 2023 20:00:57 +1300 Subject: [PATCH 60/60] Hook up Presenter's ConfigChanged function Fixes issue with post-processing not working --- Source/Core/VideoCommon/Present.cpp | 4 +++- Source/Core/VideoCommon/Present.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 068c4693fa..181dfea140 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -31,6 +31,8 @@ static float AspectToWidescreen(float aspect) Presenter::Presenter() { + m_config_changed = + ConfigChangedEvent::Register([this](u32 bits) { ConfigChanged(bits); }, "Presenter"); } Presenter::~Presenter() @@ -176,7 +178,7 @@ void Presenter::SetBackbuffer(SurfaceInfo info) UpdateDrawRectangle(); } -void Presenter::CheckForConfigChanges(u32 changed_bits) +void Presenter::ConfigChanged(u32 changed_bits) { // Check for post-processing shader changes. Done up here as it doesn't affect anything outside // the post-processor. Note that options are applied every frame, so no need to check those. diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index eee98d0c1a..14d2104a65 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -44,7 +44,7 @@ public: bool Initialize(); - void CheckForConfigChanges(u32 changed_bits); + void ConfigChanged(u32 changed_bits); // Display resolution int GetBackbufferWidth() const { return m_backbuffer_width; } @@ -147,6 +147,8 @@ private: u32 m_last_xfb_width = MAX_XFB_WIDTH; u32 m_last_xfb_stride = 0; u32 m_last_xfb_height = MAX_XFB_HEIGHT; + + Common::EventHook m_config_changed; }; } // namespace VideoCommon