Latte: Enable colorbuffer optimization if gfx packs are aware

The optimization for colorbuffer resolution introduced in PR #706 is now enabled. This optimization changes the resolution of certain framebuffer textures, which may conflict with the texture resolution rules set by some graphic packs. As a result, if a graphic pack that specifies texture resolution rules is in use, the optimization will automatically be turned off to prevent any issues.

To circumvent this, graphic packs can now include the setting "colorbufferOptimizationAware = true" in their rules.txt. This setting indicates that the pack has been updated to handle the resolution changes introduced by the optimization. Cemu will allow the optimization to remain enabled if resolution packs have this flag set.
This commit is contained in:
Exzap 2024-03-25 21:02:37 +01:00
parent 4d148b3696
commit 4b7d2f88ae
6 changed files with 41 additions and 13 deletions

View File

@ -280,6 +280,10 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
m_enabled = m_default_enabled; m_enabled = m_default_enabled;
} }
auto option_allowRendertargetSizeOptimization = rules.FindOption("colorbufferOptimizationAware");
if (option_allowRendertargetSizeOptimization)
m_allowRendertargetSizeOptimization = boost::iequals(*option_allowRendertargetSizeOptimization, "true") || boost::iequals(*option_allowRendertargetSizeOptimization, "1");
auto option_vendorFilter = rules.FindOption("vendorFilter"); auto option_vendorFilter = rules.FindOption("vendorFilter");
if (option_vendorFilter) if (option_vendorFilter)
{ {

View File

@ -113,6 +113,7 @@ public:
const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy
const std::string& GetDescription() const { return m_description; } const std::string& GetDescription() const { return m_description; }
bool IsDefaultEnabled() const { return m_default_enabled; } bool IsDefaultEnabled() const { return m_default_enabled; }
bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; }
void SetEnabled(bool state) { m_enabled = state; } void SetEnabled(bool state) { m_enabled = state; }
@ -217,6 +218,8 @@ private:
bool m_default_enabled = false; bool m_default_enabled = false;
bool m_allowRendertargetSizeOptimization = false; // gfx pack supports framebuffers with non-padded sizes, which is an optional optimization introduced with Cemu 2.0-74
// filter // filter
std::optional<RendererAPI> m_renderer_api; std::optional<RendererAPI> m_renderer_api;
std::optional<GfxVendor> m_gfx_vendor; std::optional<GfxVendor> m_gfx_vendor;

View File

@ -25,6 +25,8 @@ struct LatteGPUState_t
// context control // context control
uint32 contextControl0; uint32 contextControl0;
uint32 contextControl1; uint32 contextControl1;
// optional features
bool allowFramebufferSizeOptimization{false}; // allow using scissor box as size hint to determine non-padded rendertarget size
// draw context // draw context
struct struct
{ {

View File

@ -267,14 +267,15 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN
// colorbuffer width/height has to be padded to 8/32 alignment but the actual resolution might be smaller // colorbuffer width/height has to be padded to 8/32 alignment but the actual resolution might be smaller
// use the scissor box as a clue to figure out the original resolution if possible // use the scissor box as a clue to figure out the original resolution if possible
#if 0 if(LatteGPUState.allowFramebufferSizeOptimization)
uint32 scissorBoxWidth = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_X(); {
uint32 scissorBoxHeight = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y(); uint32 scissorBoxWidth = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_X();
if (((scissorBoxWidth + 7) & ~7) == colorBufferWidth) uint32 scissorBoxHeight = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y();
colorBufferWidth = scissorBoxWidth; if (((scissorBoxWidth + 7) & ~7) == colorBufferWidth)
if (((colorBufferHeight + 31) & ~31) == colorBufferHeight) colorBufferWidth = scissorBoxWidth;
colorBufferHeight = scissorBoxHeight; if (((colorBufferHeight + 31) & ~31) == colorBufferHeight)
#endif colorBufferHeight = scissorBoxHeight;
}
// log resolution changes if the above heuristic takes effect // log resolution changes if the above heuristic takes effect
// this is useful to find resolutions which need to be updated in gfx pack texture rules // this is useful to find resolutions which need to be updated in gfx pack texture rules
@ -303,7 +304,7 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN
if (colorBufferView == nullptr) if (colorBufferView == nullptr)
{ {
// create color buffer view // create color buffer view
colorBufferView = LatteTexture_CreateMapping(colorBufferPhysMem, 0, colorBufferWidth, colorBufferHeight, (viewFirstSlice + viewNumSlices), colorBufferPitch, colorBufferTileMode, colorBufferSwizzle>>8, viewFirstMip, 1, viewFirstSlice, viewNumSlices, (Latte::E_GX2SURFFMT)colorBufferFormat, (viewFirstSlice + viewNumSlices)>1? Latte::E_DIM::DIM_2D_ARRAY: Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, false); colorBufferView = LatteTexture_CreateMapping(colorBufferPhysMem, 0, colorBufferWidth, colorBufferHeight, (viewFirstSlice + viewNumSlices), colorBufferPitch, colorBufferTileMode, colorBufferSwizzle>>8, viewFirstMip, 1, viewFirstSlice, viewNumSlices, (Latte::E_GX2SURFFMT)colorBufferFormat, (viewFirstSlice + viewNumSlices)>1? Latte::E_DIM::DIM_2D_ARRAY: Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, false, true);
LatteGPUState.repeatTextureInitialization = true; LatteGPUState.repeatTextureInitialization = true;
checkForTextureChanges = false; checkForTextureChanges = false;
} }
@ -582,7 +583,7 @@ bool LatteMRT::UpdateCurrentFBO()
if (!depthBufferView) if (!depthBufferView)
{ {
// create new depth buffer view and if it doesn't exist then also create the texture // create new depth buffer view and if it doesn't exist then also create the texture
depthBufferView = LatteTexture_CreateMapping(depthBufferPhysMem, 0, depthBufferWidth, depthBufferHeight, depthBufferViewFirstSlice+1, depthBufferPitch, depthBufferTileMode, depthBufferSwizzle, 0, 1, depthBufferViewFirstSlice, 1, depthBufferFormat, depthBufferViewFirstSlice > 0 ? Latte::E_DIM::DIM_2D_ARRAY : Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, true); depthBufferView = LatteTexture_CreateMapping(depthBufferPhysMem, 0, depthBufferWidth, depthBufferHeight, depthBufferViewFirstSlice+1, depthBufferPitch, depthBufferTileMode, depthBufferSwizzle, 0, 1, depthBufferViewFirstSlice, 1, depthBufferFormat, depthBufferViewFirstSlice > 0 ? Latte::E_DIM::DIM_2D_ARRAY : Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, true, true);
LatteGPUState.repeatTextureInitialization = true; LatteGPUState.repeatTextureInitialization = true;
} }
else else

View File

@ -1,5 +1,4 @@
#include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/HW/Latte/Core/LatteDraw.h"
#include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Core/LatteShader.h"
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
#include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Core/LatteTexture.h"
@ -9,6 +8,8 @@
#include "Cafe/GraphicPack/GraphicPack2.h" #include "Cafe/GraphicPack/GraphicPack2.h"
#include <boost/container/small_vector.hpp>
struct TexMemOccupancyEntry struct TexMemOccupancyEntry
{ {
uint32 addrStart; uint32 addrStart;
@ -963,7 +964,7 @@ void LatteTexture_RecreateTextureWithDifferentMipSliceCount(LatteTexture* textur
} }
// create new texture representation // create new texture representation
// if allowCreateNewDataTexture is true, a new texture will be created if necessary. If it is false, only existing textures may be used, except if a data-compatible version of the requested texture already exists and it's not view compatible // if allowCreateNewDataTexture is true, a new texture will be created if necessary. If it is false, only existing textures may be used, except if a data-compatible version of the requested texture already exists and it's not view compatible (todo - we should differentiate between Latte compatible views and renderer compatible)
// the returned view will map to the provided mip and slice range within the created texture, this is to match the behavior of lookupSliceEx // the returned view will map to the provided mip and slice range within the created texture, this is to match the behavior of lookupSliceEx
LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, sint32 width, sint32 height, sint32 depth, sint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, sint32 firstMip, sint32 numMip, sint32 firstSlice, sint32 numSlice, Latte::E_GX2SURFFMT format, Latte::E_DIM dimBase, Latte::E_DIM dimView, bool isDepth, bool allowCreateNewDataTexture) LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, sint32 width, sint32 height, sint32 depth, sint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, sint32 firstMip, sint32 numMip, sint32 firstSlice, sint32 numSlice, Latte::E_GX2SURFFMT format, Latte::E_DIM dimBase, Latte::E_DIM dimView, bool isDepth, bool allowCreateNewDataTexture)
{ {
@ -980,7 +981,7 @@ LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, si
// todo, depth and numSlice are redundant // todo, depth and numSlice are redundant
sint32 sliceCount = firstSlice + numSlice; sint32 sliceCount = firstSlice + numSlice;
std::vector<LatteTexture*> list_overlappingTextures; boost::container::small_vector<LatteTexture*, 16> list_overlappingTextures;
for (sint32 sliceIndex = 0; sliceIndex < sliceCount; sliceIndex++) for (sint32 sliceIndex = 0; sliceIndex < sliceCount; sliceIndex++)
{ {
sint32 mipIndex = 0; sint32 mipIndex = 0;

View File

@ -175,6 +175,23 @@ int Latte_ThreadEntry()
// before doing anything with game specific shaders, we need to wait for graphic packs to finish loading // before doing anything with game specific shaders, we need to wait for graphic packs to finish loading
GraphicPack2::WaitUntilReady(); GraphicPack2::WaitUntilReady();
// if legacy packs are enabled we cannot use the colorbuffer resolution optimization
LatteGPUState.allowFramebufferSizeOptimization = true;
for(auto& pack : GraphicPack2::GetActiveGraphicPacks())
{
if(pack->AllowRendertargetSizeOptimization())
continue;
for(auto& rule : pack->GetTextureRules())
{
if(rule.filter_settings.width >= 0 || rule.filter_settings.height >= 0 || rule.filter_settings.depth >= 0 ||
rule.overwrite_settings.width >= 0 || rule.overwrite_settings.height >= 0 || rule.overwrite_settings.depth >= 0)
{
LatteGPUState.allowFramebufferSizeOptimization = false;
cemuLog_log(LogType::Force, "Graphic pack {} prevents rendertarget size optimization.", pack->GetName());
break;
}
}
}
// load disk shader cache // load disk shader cache
LatteShaderCache_Load(); LatteShaderCache_Load();
// init registers // init registers