mirror of https://github.com/hyprwm/Hyprland
[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
|
@ -10,6 +10,7 @@
|
||||||
#include "../protocols/DRMLease.hpp"
|
#include "../protocols/DRMLease.hpp"
|
||||||
#include "../protocols/core/Output.hpp"
|
#include "../protocols/core/Output.hpp"
|
||||||
#include "../managers/PointerManager.hpp"
|
#include "../managers/PointerManager.hpp"
|
||||||
|
#include "../protocols/core/Compositor.hpp"
|
||||||
#include "sync/SyncTimeline.hpp"
|
#include "sync/SyncTimeline.hpp"
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
#include <hyprutils/string/String.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);
|
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) {
|
CMonitorState::CMonitorState(CMonitor* owner) {
|
||||||
m_pOwner = owner;
|
m_pOwner = owner;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,9 @@ class CMonitor {
|
||||||
// for tearing
|
// for tearing
|
||||||
PHLWINDOWREF solitaryClient;
|
PHLWINDOWREF solitaryClient;
|
||||||
|
|
||||||
|
// for direct scanout
|
||||||
|
PHLWINDOWREF lastScanout;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool canTear = false;
|
bool canTear = false;
|
||||||
bool nextRenderTorn = false;
|
bool nextRenderTorn = false;
|
||||||
|
@ -174,6 +177,7 @@ class CMonitor {
|
||||||
int64_t activeSpecialWorkspaceID();
|
int64_t activeSpecialWorkspaceID();
|
||||||
CBox logicalBox();
|
CBox logicalBox();
|
||||||
void scheduleDone();
|
void scheduleDone();
|
||||||
|
bool attemptDirectScanout();
|
||||||
|
|
||||||
bool m_bEnabled = false;
|
bool m_bEnabled = false;
|
||||||
bool m_bRenderingInitPassed = false;
|
bool m_bRenderingInitPassed = false;
|
||||||
|
|
|
@ -23,19 +23,66 @@ static std::optional<dev_t> devIDFromFD(int fd) {
|
||||||
return stat.st_rdev;
|
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;
|
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) {
|
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);
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry);
|
||||||
|
|
||||||
int fds[2] = {0};
|
int fds[2] = {0};
|
||||||
allocateSHMFilePair(tableLen, &fds[0], &fds[1]);
|
allocateSHMFilePair(tableSize, &fds[0], &fds[1]);
|
||||||
|
|
||||||
auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
|
auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
|
||||||
|
|
||||||
if (arr == MAP_FAILED) {
|
if (arr == MAP_FAILED) {
|
||||||
LOGM(ERR, "mmap failed");
|
LOGM(ERR, "mmap failed");
|
||||||
|
@ -46,28 +93,14 @@ CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMAB
|
||||||
|
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
|
|
||||||
std::vector<std::pair<uint32_t, uint64_t>> formatsVec;
|
std::copy(formatsVec.begin(), formatsVec.end(), arr);
|
||||||
for (auto& f : formats) {
|
|
||||||
formatsVec.emplace_back(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t i = 0;
|
munmap(arr, tableSize);
|
||||||
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, tableLen);
|
|
||||||
|
|
||||||
mainDevice = device;
|
|
||||||
tableFD = fds[1];
|
tableFD = fds[1];
|
||||||
this->formats = formatsVec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() {
|
CDMABUFFormatTable::~CDMABUFFormatTable() {
|
||||||
close(tableFD);
|
close(tableFD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,12 +308,9 @@ CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP<CZwpLinuxDmabufFee
|
||||||
resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||||
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||||
|
|
||||||
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
|
auto& formatTable = PROTO::linuxDma->formatTable;
|
||||||
|
resource->sendFormatTable(formatTable->tableFD, formatTable->tableSize);
|
||||||
resource->sendFormatTable(feedback->tableFD, feedback->tableLen);
|
sendDefaultFeedback();
|
||||||
|
|
||||||
// send default feedback
|
|
||||||
sendDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
|
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
|
||||||
|
@ -291,31 +321,39 @@ bool CLinuxDMABUFFeedbackResource::good() {
|
||||||
return resource->resource();
|
return resource->resource();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLinuxDMABUFFeedbackResource::sendDefault() {
|
void CLinuxDMABUFFeedbackResource::sendTranche(SDMABUFTranche& tranche) {
|
||||||
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
|
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 = {
|
struct wl_array deviceArr = {
|
||||||
.size = sizeof(feedback->mainDevice),
|
.size = sizeof(mainDevice),
|
||||||
.data = (void*)&feedback->mainDevice,
|
.data = (void*)&mainDevice,
|
||||||
};
|
};
|
||||||
resource->sendMainDevice(&deviceArr);
|
resource->sendMainDevice(&deviceArr);
|
||||||
|
|
||||||
// Main tranche
|
sendTranche(formatTable->rendererTranche);
|
||||||
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();
|
|
||||||
|
|
||||||
resource->sendDone();
|
resource->sendDone();
|
||||||
|
|
||||||
|
lastFeedbackWasScanout = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
|
CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
|
||||||
|
@ -366,16 +404,18 @@ bool CLinuxDMABUFResource::good() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLinuxDMABUFResource::sendMods() {
|
void CLinuxDMABUFResource::sendMods() {
|
||||||
for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->formats) {
|
for (auto& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) {
|
||||||
|
for (auto& mod : fmt.modifiers) {
|
||||||
if (resource->version() < 3) {
|
if (resource->version() < 3) {
|
||||||
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
|
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
|
||||||
resource->sendFormat(fmt);
|
resource->sendFormat(fmt.drmFormat);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
|
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
|
||||||
|
|
||||||
resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF);
|
resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,19 +432,48 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
|
||||||
|
|
||||||
mainDevice = *dev;
|
mainDevice = *dev;
|
||||||
|
|
||||||
// FIXME: this will break on multi-gpu
|
SDMABUFTranche eglTranche = {
|
||||||
std::vector<Aquamarine::SDRMFormat> aqFormats = g_pHyprOpenGL->getDRMFormats();
|
.device = mainDevice,
|
||||||
|
.flags = 0, // renderer isnt for ds so dont set flag
|
||||||
//
|
.formats = g_pHyprOpenGL->getDRMFormats(),
|
||||||
SDMABufTranche tranche = {
|
|
||||||
.device = *dev,
|
|
||||||
.formats = aqFormats,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<SDMABufTranche> tches;
|
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tches;
|
||||||
tches.push_back(tranche);
|
|
||||||
|
|
||||||
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;
|
drmDevice* device = nullptr;
|
||||||
if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) {
|
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() {
|
CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() {
|
||||||
if (mainDeviceFD >= 0)
|
if (mainDeviceFD >= 0)
|
||||||
close(mainDeviceFD);
|
close(mainDeviceFD);
|
||||||
|
@ -461,8 +563,6 @@ void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor) {
|
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;
|
SP<CLinuxDMABUFFeedbackResource> feedbackResource;
|
||||||
for (auto& f : m_vFeedbacks) {
|
for (auto& f : m_vFeedbacks) {
|
||||||
if (f->surface != surface)
|
if (f->surface != surface)
|
||||||
|
@ -479,49 +579,34 @@ void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface
|
||||||
|
|
||||||
if (!pMonitor) {
|
if (!pMonitor) {
|
||||||
LOGM(LOG, "updateScanoutTranche: resetting feedback");
|
LOGM(LOG, "updateScanoutTranche: resetting feedback");
|
||||||
feedbackResource->sendDefault();
|
feedbackResource->sendDefaultFeedback();
|
||||||
return;
|
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");
|
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 = {
|
struct wl_array deviceArr = {
|
||||||
.size = sizeof(feedback->mainDevice),
|
.size = sizeof(mainDevice),
|
||||||
.data = (void*)&feedback->mainDevice,
|
.data = (void*)&mainDevice,
|
||||||
};
|
};
|
||||||
feedbackResource->resource->sendTrancheTargetDevice(&deviceArr);
|
feedbackResource->resource->sendMainDevice(&deviceArr);
|
||||||
feedbackResource->resource->sendTrancheFlags(ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT);
|
|
||||||
|
|
||||||
wl_array indices2;
|
// prioritize scnaout tranche but have renderer fallback tranche
|
||||||
wl_array_init(&indices2);
|
// also yes formats can be duped here because different tranche flags (ds and no ds)
|
||||||
const auto PRIMARYFORMATS = pMonitor->output->getBackend()->getRenderFormats();
|
feedbackResource->sendTranche(monitorTranche);
|
||||||
|
feedbackResource->sendTranche(formatTable->rendererTranche);
|
||||||
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();
|
|
||||||
|
|
||||||
feedbackResource->resource->sendDone();
|
feedbackResource->resource->sendDone();
|
||||||
|
|
||||||
|
feedbackResource->lastFeedbackWasScanout = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,34 +32,29 @@ class CLinuxDMABuffer {
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct SDMABUFFeedbackTableEntry {
|
struct SDMABUFFormatTableEntry {
|
||||||
uint32_t fmt = 0;
|
uint32_t fmt = 0;
|
||||||
char pad[4];
|
char pad[4];
|
||||||
uint64_t modifier = 0;
|
uint64_t modifier = 0;
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
class SCompiledDMABUFTranche {
|
struct SDMABUFTranche {
|
||||||
dev_t device = 0;
|
|
||||||
uint32_t flags = 0;
|
|
||||||
std::vector<uint16_t> indices;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDMABufTranche {
|
|
||||||
dev_t device = 0;
|
dev_t device = 0;
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
std::vector<SDRMFormat> formats;
|
std::vector<SDRMFormat> formats;
|
||||||
|
std::vector<uint16_t> indicies;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CCompiledDMABUFFeedback {
|
class CDMABUFFormatTable {
|
||||||
public:
|
public:
|
||||||
CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches);
|
CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches);
|
||||||
~CCompiledDMABUFFeedback();
|
~CDMABUFFormatTable();
|
||||||
|
|
||||||
dev_t mainDevice = 0;
|
|
||||||
int tableFD = -1;
|
int tableFD = -1;
|
||||||
size_t tableLen = 0;
|
size_t tableSize = 0;
|
||||||
std::vector<std::pair<uint32_t, uint64_t>> formats;
|
SDMABUFTranche rendererTranche;
|
||||||
|
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> monitorTranches;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CLinuxDMABBUFParamsResource {
|
class CLinuxDMABBUFParamsResource {
|
||||||
|
@ -87,12 +82,14 @@ class CLinuxDMABUFFeedbackResource {
|
||||||
~CLinuxDMABUFFeedbackResource();
|
~CLinuxDMABUFFeedbackResource();
|
||||||
|
|
||||||
bool good();
|
bool good();
|
||||||
void sendDefault();
|
void sendDefaultFeedback();
|
||||||
|
void sendTranche(SDMABUFTranche& tranche);
|
||||||
|
|
||||||
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
|
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SP<CZwpLinuxDmabufFeedbackV1> resource;
|
SP<CZwpLinuxDmabufFeedbackV1> resource;
|
||||||
|
bool lastFeedbackWasScanout = false;
|
||||||
|
|
||||||
friend class CLinuxDMABufV1Protocol;
|
friend class CLinuxDMABufV1Protocol;
|
||||||
};
|
};
|
||||||
|
@ -122,13 +119,15 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
|
||||||
void destroyResource(CLinuxDMABBUFParamsResource* resource);
|
void destroyResource(CLinuxDMABBUFParamsResource* resource);
|
||||||
void destroyResource(CLinuxDMABuffer* resource);
|
void destroyResource(CLinuxDMABuffer* resource);
|
||||||
|
|
||||||
|
void resetFormatTable();
|
||||||
|
|
||||||
//
|
//
|
||||||
std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
|
std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
|
||||||
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
|
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
|
||||||
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
|
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
|
||||||
std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
|
std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
|
||||||
|
|
||||||
UP<CCompiledDMABUFFeedback> defaultFeedback;
|
UP<CDMABUFFormatTable> formatTable;
|
||||||
dev_t mainDevice;
|
dev_t mainDevice;
|
||||||
int mainDeviceFD = -1;
|
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.
|
// for async buffers, we can only release the buffer once we are unrefing it from current.
|
||||||
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
|
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
|
||||||
|
if (previousBuffer->lockedByBackend) {
|
||||||
|
previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) {
|
||||||
previousBuffer->sendReleaseWithSurface(self.lock());
|
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||||
|
previousBuffer->hlEvents.backendRelease.reset();
|
||||||
|
bufferReleased = true;
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||||
|
|
||||||
bufferReleased = true;
|
bufferReleased = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,6 @@ void IHLBuffer::sendRelease() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
||||||
|
if (resource && resource->good())
|
||||||
resource->sendReleaseWithSurface(surf);
|
resource->sendReleaseWithSurface(surf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,8 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
||||||
SP<CTexture> texture;
|
SP<CTexture> texture;
|
||||||
bool opaque = false;
|
bool opaque = false;
|
||||||
SP<CWLBufferResource> resource;
|
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) {
|
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 renderStart = std::chrono::high_resolution_clock::now();
|
||||||
static std::chrono::high_resolution_clock::time_point renderStartOverlay = 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);
|
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pMonitor->output->needsFrame && pMonitor->forceFullFrames == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// tearing and DS first
|
// tearing and DS first
|
||||||
bool shouldTear = false;
|
bool shouldTear = false;
|
||||||
if (pMonitor->tearingState.nextRenderTorn) {
|
if (pMonitor->tearingState.nextRenderTorn) {
|
||||||
|
@ -1239,11 +1195,11 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*PNODIRECTSCANOUT && !shouldTear) {
|
if (!*PNODIRECTSCANOUT && !shouldTear) {
|
||||||
if (attemptDirectScanout(pMonitor)) {
|
if (pMonitor->attemptDirectScanout()) {
|
||||||
return;
|
return;
|
||||||
} else if (!m_pLastScanout.expired()) {
|
} else if (!pMonitor->lastScanout.expired()) {
|
||||||
Debug::log(LOG, "Left a direct scanout.");
|
Debug::log(LOG, "Left a direct scanout.");
|
||||||
m_pLastScanout.reset();
|
pMonitor->lastScanout.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,14 +81,12 @@ class CHyprRenderer {
|
||||||
|
|
||||||
bool m_bBlockSurfaceFeedback = false;
|
bool m_bBlockSurfaceFeedback = false;
|
||||||
bool m_bRenderingSnapshot = false;
|
bool m_bRenderingSnapshot = false;
|
||||||
PHLWINDOWREF m_pLastScanout;
|
|
||||||
CMonitor* m_pMostHzMonitor = nullptr;
|
CMonitor* m_pMostHzMonitor = nullptr;
|
||||||
bool m_bDirectScanoutBlocked = false;
|
bool m_bDirectScanoutBlocked = false;
|
||||||
|
|
||||||
DAMAGETRACKINGMODES
|
DAMAGETRACKINGMODES
|
||||||
damageTrackingModeFromStr(const std::string&);
|
damageTrackingModeFromStr(const std::string&);
|
||||||
|
|
||||||
bool attemptDirectScanout(CMonitor*);
|
|
||||||
void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, SP<CMonitor> monitor); // nullptr monitor resets
|
void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, SP<CMonitor> monitor); // nullptr monitor resets
|
||||||
void initiateManualCrash();
|
void initiateManualCrash();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue