renderer: add overscan support

adds basic overscanning by squishing the image. Configurable per-side with px
This commit is contained in:
Vaxry 2025-08-17 18:05:16 +02:00
parent 251288ec59
commit 612a8d95ac
3 changed files with 64 additions and 2 deletions

View file

@ -824,6 +824,10 @@ CConfigManager::CConfigManager() {
m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0}); m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0});
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "overscan_t", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "overscan_l", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "overscan_r", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "overscan_b", Hyprlang::INT{-1});
// keywords // keywords
m_config->registerHandler(&::handleExec, "exec", {false}); m_config->registerHandler(&::handleExec, "exec", {false});
@ -1118,6 +1122,19 @@ std::optional<std::string> CConfigManager::handleMonitorv2(const std::string& ou
if (VAL && VAL->m_bSetByUser) if (VAL && VAL->m_bSetByUser)
parser.rule().maxAvgLuminance = std::any_cast<Hyprlang::INT>(VAL->getValue()); parser.rule().maxAvgLuminance = std::any_cast<Hyprlang::INT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "overscan_t", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().overscanTL.y = std::any_cast<Hyprlang::INT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "overscan_l", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().overscanTL.x = std::any_cast<Hyprlang::INT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "overscan_r", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().overscanBR.x = std::any_cast<Hyprlang::INT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "overscan_b", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().overscanBR.y = std::any_cast<Hyprlang::INT>(VAL->getValue());
auto newrule = parser.rule(); auto newrule = parser.rule();
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; }); std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
@ -3156,6 +3173,9 @@ bool CConfigManager::shouldUseSoftwareCursors(PHLMONITOR pMonitor) {
if (pMonitor->m_tearingState.activelyTearing) if (pMonitor->m_tearingState.activelyTearing)
return true; return true;
if (pMonitor->m_activeMonitorRule.hasOverscan())
return true; // translating hw cursors would be annoying, fuck this.
if (*PINVISIBLE != 0) if (*PINVISIBLE != 0)
return true; return true;

View file

@ -69,8 +69,16 @@ struct SMonitorRule {
int maxLuminance = -1; // >= 0 overrides EDID int maxLuminance = -1; // >= 0 overrides EDID
int maxAvgLuminance = -1; // >= 0 overrides EDID int maxAvgLuminance = -1; // >= 0 overrides EDID
Vector2D overscanTL = {0, 0};
Vector2D overscanBR = {0, 0};
drmModeModeInfo drmMode = {}; drmModeModeInfo drmMode = {};
std::optional<int> vrr; std::optional<int> vrr;
//
bool hasOverscan() {
return overscanTL != Vector2D{0, 0} || overscanBR != Vector2D{0, 0};
}
}; };
class CMonitor; class CMonitor;

View file

@ -834,11 +834,45 @@ void CHyprOpenGLImpl::end() {
m_renderData.outFB->bind(); m_renderData.outFB->bind();
blend(false); blend(false);
if (m_finalScreenShader.program < 1 && !g_pHyprRenderer->m_crashingInProgress) if (m_finalScreenShader.program < 1 && !g_pHyprRenderer->m_crashingInProgress && !m_renderData.pMonitor->m_activeMonitorRule.hasOverscan())
renderTexturePrimitive(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox); renderTexturePrimitive(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox);
else else {
const auto DAMAGE_BACKUP = m_renderData.damage;
const auto FINAL_DAMAGE_BACKUP = m_renderData.finalDamage;
if (m_renderData.pMonitor->m_activeMonitorRule.hasOverscan()) {
// hack to clear undamaged areas
// FIXME: buffers should be cleared once, probably...
const auto OLD_MONBOX = monbox;
monbox.translate(m_renderData.pMonitor->m_activeMonitorRule.overscanTL);
monbox.w -= m_renderData.pMonitor->m_activeMonitorRule.overscanTL.x + m_renderData.pMonitor->m_activeMonitorRule.overscanBR.x;
monbox.h -= m_renderData.pMonitor->m_activeMonitorRule.overscanTL.y + m_renderData.pMonitor->m_activeMonitorRule.overscanBR.y;
m_renderData.damage = CRegion{OLD_MONBOX}.subtract(monbox);
clear(Colors::BLACK);
m_renderData.damage = DAMAGE_BACKUP;
// fix damage here, because we squish the image
m_renderData.damage.scale(
(m_renderData.pMonitor->m_pixelSize - m_renderData.pMonitor->m_activeMonitorRule.overscanTL - m_renderData.pMonitor->m_activeMonitorRule.overscanBR) /
m_renderData.pMonitor->m_pixelSize);
m_renderData.damage.translate(m_renderData.pMonitor->m_activeMonitorRule.overscanTL);
m_renderData.finalDamage.scale(
(m_renderData.pMonitor->m_pixelSize - m_renderData.pMonitor->m_activeMonitorRule.overscanTL - m_renderData.pMonitor->m_activeMonitorRule.overscanBR) /
m_renderData.pMonitor->m_pixelSize);
m_renderData.finalDamage.translate(m_renderData.pMonitor->m_activeMonitorRule.overscanTL);
}
renderTexture(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox, {}); renderTexture(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox, {});
m_renderData.damage = DAMAGE_BACKUP;
m_renderData.finalDamage = FINAL_DAMAGE_BACKUP;
}
blend(true); blend(true);
m_renderData.useNearestNeighbor = false; m_renderData.useNearestNeighbor = false;