[aquamarine] try to fix direct scanout (#6875)

* make linux-dmabuf use monitor primary plane formats for ds

* add support for monitor connects/disconnects in linux-dmabuf

* the weird bug was because ds was global, and disabled on one monitor but enabled on the other

* forgot to remove attemptDirectScanout from hyprRenderer

* add renderer fallback tranche on ds and a few other improvements

* use set when making format table

* mitigate ds artifacting and update linux-dmabuf comments
with this commit artifacting now only occurs when the
client's fps is above the refresh rate of the monitor

* use new backend release infra

* revert earlier artifacting mitigation that broke stuff ;)

* ... i should test before push ;-;

---------

Co-authored-by: Vaxry <vaxry@vaxry.net>
This commit is contained in:
Ikalco 2024-07-19 10:56:13 -05:00 committed by Vaxry
parent 0de9568da5
commit 1f844610d1
9 changed files with 285 additions and 181 deletions

View file

@ -10,6 +10,7 @@
#include "../protocols/DRMLease.hpp"
#include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp>
#include <hyprutils/string/String.hpp>
@ -786,6 +787,54 @@ void CMonitor::scheduleDone() {
doneSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::onDoneSource, this);
}
bool CMonitor::attemptDirectScanout() {
if (!mirrors.empty() || isMirror() || g_pHyprRenderer->m_bDirectScanoutBlocked)
return false; // do not DS if this monitor is being mirrored. Will break the functionality.
if (g_pPointerManager->softwareLockedFor(self.lock()))
return false;
const auto PCANDIDATE = solitaryClient.lock();
if (!PCANDIDATE)
return false;
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.buffer->size != vecPixelSize || PSURFACE->current.transform != transform)
return false;
// we can't scanout shm buffers.
if (!PSURFACE->current.buffer->dmabuf().success)
return false;
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
output->state->setBuffer(PSURFACE->current.buffer);
output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
if (!state.test())
return false;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, this, true);
if (state.commit()) {
if (lastScanout.expired()) {
lastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
} else {
lastScanout.reset();
return false;
}
return true;
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
}

View file

@ -133,6 +133,9 @@ class CMonitor {
// for tearing
PHLWINDOWREF solitaryClient;
// for direct scanout
PHLWINDOWREF lastScanout;
struct {
bool canTear = false;
bool nextRenderTorn = false;
@ -174,6 +177,7 @@ class CMonitor {
int64_t activeSpecialWorkspaceID();
CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;

View file

@ -23,19 +23,66 @@ static std::optional<dev_t> devIDFromFD(int fd) {
return stat.st_rdev;
}
CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches_) {
CDMABUFFormatTable::CDMABUFFormatTable(SDMABUFTranche _rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches_) :
rendererTranche(_rendererTranche), monitorTranches(tranches_) {
std::vector<SDMABUFFormatTableEntry> formatsVec;
std::set<std::pair<uint32_t, uint64_t>> formats;
for (auto& fmt : tranches_.at(0).formats /* FIXME: multigpu */) {
// insert formats into vec if they got inserted into set, meaning they're unique
size_t i = 0;
rendererTranche.indicies.clear();
for (auto& fmt : rendererTranche.formats) {
for (auto& mod : fmt.modifiers) {
formats.insert(std::make_pair<>(fmt.drmFormat, mod));
auto format = std::make_pair<>(fmt.drmFormat, mod);
auto [_, inserted] = formats.insert(format);
if (inserted) {
// if it was inserted into set, then its unique and will have a new index in vec
rendererTranche.indicies.push_back(i++);
formatsVec.push_back(SDMABUFFormatTableEntry{
.fmt = fmt.drmFormat,
.modifier = mod,
});
} else {
// if it wasn't inserted then find its index in vec
auto it =
std::find_if(formatsVec.begin(), formatsVec.end(), [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
rendererTranche.indicies.push_back(it - formatsVec.begin());
}
}
}
tableLen = formats.size() * sizeof(SDMABUFFeedbackTableEntry);
int fds[2] = {0};
allocateSHMFilePair(tableLen, &fds[0], &fds[1]);
for (auto& [monitor, tranche] : monitorTranches) {
tranche.indicies.clear();
for (auto& fmt : tranche.formats) {
for (auto& mod : fmt.modifiers) {
// apparently these can implode on planes, so dont use them
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
continue;
auto format = std::make_pair<>(fmt.drmFormat, mod);
auto [_, inserted] = formats.insert(format);
if (inserted) {
tranche.indicies.push_back(i++);
formatsVec.push_back(SDMABUFFormatTableEntry{
.fmt = fmt.drmFormat,
.modifier = mod,
});
} else {
auto it = std::find_if(formatsVec.begin(), formatsVec.end(),
[fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
tranche.indicies.push_back(it - formatsVec.begin());
}
}
}
}
auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry);
int fds[2] = {0};
allocateSHMFilePair(tableSize, &fds[0], &fds[1]);
auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
if (arr == MAP_FAILED) {
LOGM(ERR, "mmap failed");
@ -46,28 +93,14 @@ CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMAB
close(fds[0]);
std::vector<std::pair<uint32_t, uint64_t>> formatsVec;
for (auto& f : formats) {
formatsVec.emplace_back(f);
}
std::copy(formatsVec.begin(), formatsVec.end(), arr);
size_t i = 0;
for (auto& [fmt, mod] : formatsVec) {
LOGM(TRACE, "Feedback: format table index {}: fmt {} mod {}", i, FormatUtils::drmFormatName(fmt), mod);
arr[i++] = SDMABUFFeedbackTableEntry{
.fmt = fmt,
.modifier = mod,
};
}
munmap(arr, tableSize);
munmap(arr, tableLen);
mainDevice = device;
tableFD = fds[1];
this->formats = formatsVec;
tableFD = fds[1];
}
CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() {
CDMABUFFormatTable::~CDMABUFFormatTable() {
close(tableFD);
}
@ -275,12 +308,9 @@ CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP<CZwpLinuxDmabufFee
resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
resource->sendFormatTable(feedback->tableFD, feedback->tableLen);
// send default feedback
sendDefault();
auto& formatTable = PROTO::linuxDma->formatTable;
resource->sendFormatTable(formatTable->tableFD, formatTable->tableSize);
sendDefaultFeedback();
}
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
@ -291,31 +321,39 @@ bool CLinuxDMABUFFeedbackResource::good() {
return resource->resource();
}
void CLinuxDMABUFFeedbackResource::sendDefault() {
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
void CLinuxDMABUFFeedbackResource::sendTranche(SDMABUFTranche& tranche) {
struct wl_array deviceArr = {
.size = sizeof(tranche.device),
.data = (void*)&tranche.device,
};
resource->sendTrancheTargetDevice(&deviceArr);
resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)tranche.flags);
wl_array indices = {
.size = tranche.indicies.size() * sizeof(tranche.indicies.at(0)),
.data = tranche.indicies.data(),
};
resource->sendTrancheFormats(&indices);
resource->sendTrancheDone();
}
// default tranche is based on renderer (egl)
void CLinuxDMABUFFeedbackResource::sendDefaultFeedback() {
auto mainDevice = PROTO::linuxDma->mainDevice;
auto& formatTable = PROTO::linuxDma->formatTable;
struct wl_array deviceArr = {
.size = sizeof(feedback->mainDevice),
.data = (void*)&feedback->mainDevice,
.size = sizeof(mainDevice),
.data = (void*)&mainDevice,
};
resource->sendMainDevice(&deviceArr);
// Main tranche
resource->sendTrancheTargetDevice(&deviceArr);
// Technically, on a single-gpu system, this is correct I believe.
resource->sendTrancheFlags(ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT);
wl_array indices;
wl_array_init(&indices);
for (size_t i = 0; i < feedback->formats.size(); ++i) {
*((uint16_t*)wl_array_add(&indices, sizeof(uint16_t))) = i;
}
resource->sendTrancheFormats(&indices);
wl_array_release(&indices);
resource->sendTrancheDone();
sendTranche(formatTable->rendererTranche);
resource->sendDone();
lastFeedbackWasScanout = false;
}
CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
@ -366,16 +404,18 @@ bool CLinuxDMABUFResource::good() {
}
void CLinuxDMABUFResource::sendMods() {
for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->formats) {
if (resource->version() < 3) {
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
resource->sendFormat(fmt);
continue;
for (auto& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) {
for (auto& mod : fmt.modifiers) {
if (resource->version() < 3) {
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
resource->sendFormat(fmt.drmFormat);
continue;
}
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF);
}
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF);
}
}
@ -392,19 +432,48 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
mainDevice = *dev;
// FIXME: this will break on multi-gpu
std::vector<Aquamarine::SDRMFormat> aqFormats = g_pHyprOpenGL->getDRMFormats();
//
SDMABufTranche tranche = {
.device = *dev,
.formats = aqFormats,
SDMABUFTranche eglTranche = {
.device = mainDevice,
.flags = 0, // renderer isnt for ds so dont set flag
.formats = g_pHyprOpenGL->getDRMFormats(),
};
std::vector<SDMABufTranche> tches;
tches.push_back(tranche);
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tches;
defaultFeedback = std::make_unique<CCompiledDMABUFFeedback>(*dev, tches);
if (g_pCompositor->m_pAqBackend->hasSession()) {
// this assumes there's only 1 device used for both scanout and rendering
// also that each monitor never changes its primary plane
for (auto& mon : g_pCompositor->m_vMonitors) {
auto tranche = SDMABUFTranche{
.device = mainDevice,
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
.formats = mon->output->getRenderFormats(),
};
tches.push_back(std::make_pair<>(mon, tranche));
}
static auto monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
auto pMonitor = std::any_cast<CMonitor*>(param);
auto mon = pMonitor->self.lock();
auto tranche = SDMABUFTranche{
.device = mainDevice,
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
.formats = mon->output->getRenderFormats(),
};
formatTable->monitorTranches.push_back(std::make_pair<>(mon, tranche));
resetFormatTable();
});
static auto monitorRemoved = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) {
auto pMonitor = std::any_cast<CMonitor*>(param);
auto mon = pMonitor->self.lock();
std::erase_if(formatTable->monitorTranches, [mon](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == mon; });
resetFormatTable();
});
}
formatTable = std::make_unique<CDMABUFFormatTable>(eglTranche, tches);
drmDevice* device = nullptr;
if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) {
@ -429,6 +498,39 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
});
}
void CLinuxDMABufV1Protocol::resetFormatTable() {
if (!formatTable)
return;
LOGM(LOG, "Resetting format table");
// this might be a big copy
auto newFormatTable = std::make_unique<CDMABUFFormatTable>(formatTable->rendererTranche, formatTable->monitorTranches);
for (auto& feedback : m_vFeedbacks) {
feedback->resource->sendFormatTable(newFormatTable->tableFD, newFormatTable->tableSize);
if (feedback->lastFeedbackWasScanout) {
SP<CMonitor> mon;
auto HLSurface = CWLSurface::fromResource(feedback->surface);
if (auto w = HLSurface->getWindow(); w)
if (auto m = g_pCompositor->getMonitorFromID(w->m_iMonitorID); m)
mon = m->self.lock();
if (!mon) {
feedback->sendDefaultFeedback();
return;
}
updateScanoutTranche(feedback->surface, mon);
} else {
feedback->sendDefaultFeedback();
}
}
// delete old table after we sent new one
formatTable = std::move(newFormatTable);
}
CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() {
if (mainDeviceFD >= 0)
close(mainDeviceFD);
@ -461,8 +563,6 @@ void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) {
}
void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor) {
// TODO: iterating isn't particularly efficient, maybe add a system for addons for surfaces
SP<CLinuxDMABUFFeedbackResource> feedbackResource;
for (auto& f : m_vFeedbacks) {
if (f->surface != surface)
@ -479,49 +579,34 @@ void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface
if (!pMonitor) {
LOGM(LOG, "updateScanoutTranche: resetting feedback");
feedbackResource->sendDefault();
feedbackResource->sendDefaultFeedback();
return;
}
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
const auto& monitorTranchePair = std::find_if(formatTable->monitorTranches.begin(), formatTable->monitorTranches.end(),
[pMonitor](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == pMonitor; });
if (monitorTranchePair == formatTable->monitorTranches.end()) {
LOGM(LOG, "updateScanoutTranche: monitor has no tranche");
return;
}
auto& monitorTranche = (*monitorTranchePair).second;
LOGM(LOG, "updateScanoutTranche: sending a scanout tranche");
// send a dedicated scanout tranche that contains formats that:
// - match the format of the output
// - are not linear or implicit
struct wl_array deviceArr = {
.size = sizeof(feedback->mainDevice),
.data = (void*)&feedback->mainDevice,
.size = sizeof(mainDevice),
.data = (void*)&mainDevice,
};
feedbackResource->resource->sendTrancheTargetDevice(&deviceArr);
feedbackResource->resource->sendTrancheFlags(ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT);
feedbackResource->resource->sendMainDevice(&deviceArr);
wl_array indices2;
wl_array_init(&indices2);
const auto PRIMARYFORMATS = pMonitor->output->getBackend()->getRenderFormats();
for (size_t i = 0; i < feedback->formats.size(); ++i) {
if (feedback->formats.at(i).second == DRM_FORMAT_MOD_LINEAR || feedback->formats.at(i).second == DRM_FORMAT_MOD_INVALID)
continue;
const auto PRIM_FORMAT = std::find_if(PRIMARYFORMATS.begin(), PRIMARYFORMATS.end(), [&](const auto& e) { return e.drmFormat == feedback->formats.at(i).first; });
if (PRIM_FORMAT == PRIMARYFORMATS.end())
continue;
if (std::find(PRIM_FORMAT->modifiers.begin(), PRIM_FORMAT->modifiers.end(), feedback->formats.at(i).second) == PRIM_FORMAT->modifiers.end())
continue;
LOGM(TRACE, "updateScanoutTranche: index {}: Format {} with modifier {} aka {} passed", i, FormatUtils::drmFormatName(feedback->formats.at(i).first),
feedback->formats.at(i).second, FormatUtils::drmModifierName(feedback->formats.at(i).second));
*((uint16_t*)wl_array_add(&indices2, sizeof(uint16_t))) = i;
}
feedbackResource->resource->sendTrancheFormats(&indices2);
wl_array_release(&indices2);
feedbackResource->resource->sendTrancheDone();
// prioritize scnaout tranche but have renderer fallback tranche
// also yes formats can be duped here because different tranche flags (ds and no ds)
feedbackResource->sendTranche(monitorTranche);
feedbackResource->sendTranche(formatTable->rendererTranche);
feedbackResource->resource->sendDone();
feedbackResource->lastFeedbackWasScanout = true;
}

View file

@ -32,34 +32,29 @@ class CLinuxDMABuffer {
};
#pragma pack(push, 1)
struct SDMABUFFeedbackTableEntry {
struct SDMABUFFormatTableEntry {
uint32_t fmt = 0;
char pad[4];
uint64_t modifier = 0;
};
#pragma pack(pop)
class SCompiledDMABUFTranche {
dev_t device = 0;
uint32_t flags = 0;
std::vector<uint16_t> indices;
};
struct SDMABufTranche {
struct SDMABUFTranche {
dev_t device = 0;
uint32_t flags = 0;
std::vector<SDRMFormat> formats;
std::vector<uint16_t> indicies;
};
class CCompiledDMABUFFeedback {
class CDMABUFFormatTable {
public:
CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches);
~CCompiledDMABUFFeedback();
CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches);
~CDMABUFFormatTable();
dev_t mainDevice = 0;
int tableFD = -1;
size_t tableLen = 0;
std::vector<std::pair<uint32_t, uint64_t>> formats;
int tableFD = -1;
size_t tableSize = 0;
SDMABUFTranche rendererTranche;
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> monitorTranches;
};
class CLinuxDMABBUFParamsResource {
@ -87,12 +82,14 @@ class CLinuxDMABUFFeedbackResource {
~CLinuxDMABUFFeedbackResource();
bool good();
void sendDefault();
void sendDefaultFeedback();
void sendTranche(SDMABUFTranche& tranche);
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
private:
SP<CZwpLinuxDmabufFeedbackV1> resource;
bool lastFeedbackWasScanout = false;
friend class CLinuxDMABufV1Protocol;
};
@ -122,13 +119,15 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
void destroyResource(CLinuxDMABBUFParamsResource* resource);
void destroyResource(CLinuxDMABuffer* resource);
void resetFormatTable();
//
std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
UP<CCompiledDMABUFFeedback> defaultFeedback;
UP<CDMABUFFormatTable> formatTable;
dev_t mainDevice;
int mainDeviceFD = -1;

View file

@ -443,7 +443,15 @@ void CWLSurfaceResource::commitPendingState() {
// for async buffers, we can only release the buffer once we are unrefing it from current.
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
previousBuffer->sendReleaseWithSurface(self.lock());
if (previousBuffer->lockedByBackend) {
previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) {
previousBuffer->sendReleaseWithSurface(self.lock());
previousBuffer->hlEvents.backendRelease.reset();
bufferReleased = true;
});
} else
previousBuffer->sendReleaseWithSurface(self.lock());
bufferReleased = true;
}
}

View file

@ -5,5 +5,6 @@ void IHLBuffer::sendRelease() {
}
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
resource->sendReleaseWithSurface(surf);
if (resource && resource->good())
resource->sendReleaseWithSurface(surf);
}

View file

@ -22,4 +22,8 @@ class IHLBuffer : public Aquamarine::IBuffer {
SP<CTexture> texture;
bool opaque = false;
SP<CWLBufferResource> resource;
struct {
CHyprSignalListener backendRelease;
} hlEvents;
};

View file

@ -1088,53 +1088,6 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
}
}
bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
if (!pMonitor->mirrors.empty() || pMonitor->isMirror() || m_bDirectScanoutBlocked)
return false; // do not DS if this monitor is being mirrored. Will break the functionality.
if (g_pPointerManager->softwareLockedFor(pMonitor->self.lock()))
return false;
const auto PCANDIDATE = pMonitor->solitaryClient.lock();
if (!PCANDIDATE)
return false;
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.buffer->size != pMonitor->vecPixelSize || PSURFACE->current.transform != pMonitor->transform)
return false;
// we can't scanout shm buffers.
if (!PSURFACE->current.buffer->dmabuf().success)
return false;
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
pMonitor->output->state->setBuffer(PSURFACE->current.buffer);
if (!pMonitor->state.test())
return false;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, pMonitor, true);
if (pMonitor->state.commit()) {
if (m_pLastScanout.expired()) {
m_pLastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
} else {
m_pLastScanout.reset();
return false;
}
return true;
}
void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now();
@ -1214,6 +1167,9 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID);
}
if (!pMonitor->output->needsFrame && pMonitor->forceFullFrames == 0)
return;
// tearing and DS first
bool shouldTear = false;
if (pMonitor->tearingState.nextRenderTorn) {
@ -1239,11 +1195,11 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
}
if (!*PNODIRECTSCANOUT && !shouldTear) {
if (attemptDirectScanout(pMonitor)) {
if (pMonitor->attemptDirectScanout()) {
return;
} else if (!m_pLastScanout.expired()) {
} else if (!pMonitor->lastScanout.expired()) {
Debug::log(LOG, "Left a direct scanout.");
m_pLastScanout.reset();
pMonitor->lastScanout.reset();
}
}

View file

@ -76,19 +76,17 @@ class CHyprRenderer {
// if RENDER_MODE_NORMAL, provided damage will be written to.
// otherwise, it will be the one used.
bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false);
void endRender();
bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false);
void endRender();
bool m_bBlockSurfaceFeedback = false;
bool m_bRenderingSnapshot = false;
PHLWINDOWREF m_pLastScanout;
CMonitor* m_pMostHzMonitor = nullptr;
bool m_bDirectScanoutBlocked = false;
bool m_bBlockSurfaceFeedback = false;
bool m_bRenderingSnapshot = false;
CMonitor* m_pMostHzMonitor = nullptr;
bool m_bDirectScanoutBlocked = false;
DAMAGETRACKINGMODES
damageTrackingModeFromStr(const std::string&);
bool attemptDirectScanout(CMonitor*);
void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, SP<CMonitor> monitor); // nullptr monitor resets
void initiateManualCrash();