mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-13 08:25:59 +01:00
[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:
parent
0de9568da5
commit
1f844610d1
9 changed files with 285 additions and 181 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,6 @@ void IHLBuffer::sendRelease() {
|
|||
}
|
||||
|
||||
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
||||
resource->sendReleaseWithSurface(surf);
|
||||
if (resource && resource->good())
|
||||
resource->sendReleaseWithSurface(surf);
|
||||
}
|
||||
|
|
|
@ -22,4 +22,8 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
|||
SP<CTexture> texture;
|
||||
bool opaque = false;
|
||||
SP<CWLBufferResource> resource;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener backendRelease;
|
||||
} hlEvents;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue