Compare commits

...

16 Commits

Author SHA1 Message Date
vaxerski
9a3bec5d0a [gha] Nix: update inputs 2025-03-28 16:23:50 +00:00
Vaxry
29e2e59fdb version: bump to v0.48.1 2025-03-28 16:16:07 +00:00
Lee Bousfield
1fdb5ba09e xwayland: Cleanup server startup and FDs (#9769) 2025-03-28 16:14:46 +00:00
Lee Bousfield
aa421c2e95 core: Don't damage the entire surface every frame (#9763)
* core: Don't damage the entire surface every frame

* core: Damage buffer on dims or transform change

* core: Use guards for scale and tr equality checks
2025-03-28 16:14:46 +00:00
Vaxry
3c36e083f1 surfacestate: reset buffer bit before applying to current
fixes #9759
2025-03-28 16:14:46 +00:00
Vaxry
3fc0abcb56 workspaces: minor fixes to persistence
fixes #9741
2025-03-28 16:14:46 +00:00
Tom Englund
273f43bda6 internal: fix minor ubsan errors (#9743)
* opengl: check if g_pHyprOpengl exist

on compositor destruction we can hit a race where a CEGLSync destructs
and tries to call eglDestroySyncKHR on a null g_pHyprOpengl.

/src/render/OpenGL.cpp:3019:32: runtime error: member access within null pointer of type 'struct CHyprOpenGLImpl'
     #0 0x555565eed979 in CEGLSync::~CEGLSync() /src/render/OpenGL.cpp:3019
     #1 0x555565f6271e in std::default_delete<CEGLSync>::operator()(CEGLSync*)
     const /usr/lib/gcc/x86_64-pc-linux-gnu/14/include/g++-v14/bits/unique_ptr.h:93

* xdgshell: dont apply state on empty states

setsize can be called before a state has been added to pending,
resulting in calling ApplyState with a empty state.

/src/protocols/XDGShell.cpp:323:11: runtime error: null pointer passed as argument 2, which is declared to never be null
     #0 0x5555659bf67e in CXDGToplevelResource::applyState() /src/protocols/XDGShell.cpp:323
     #1 0x5555659bcedc in CXDGToplevelResource::setSize(Hyprutils::Math::Vector2D const&) /src/protocols/XDGShell.cpp:  256
     #2 0x555563eed0ef in Events::listener_commitWindow(void*, void*) /src/events/Windows.cpp:841
2025-03-28 16:14:46 +00:00
Vaxry
0a3948107a surfacestate: track and apply updated state
fixes #9729
2025-03-28 16:14:46 +00:00
Vaxry
189e18394e opengl: don't attempt to compile cm on gles3.0
also disable the error for the cm shader

fixes #9738
2025-03-28 16:14:46 +00:00
vaxerski
3eb859bb4e pass/rect: include clipBox in opaque calculations
ref #9730 ref #9709
2025-03-28 16:14:46 +00:00
vaxerski
eaa9663057 groupbar: round boxes 2025-03-28 16:14:46 +00:00
Tom Englund
5da8281d68 pass: remove unusued timeline in texpass (#9734)
remove unused timeline and waitpoint in texpass and especially remove
the passing it to renderTextureInternalWithDamage that implicitly
converted it to bool. setting discardActive and allowCustomUV
2025-03-28 16:14:46 +00:00
Arkady Buryakov
d2031ba3e0 Groupbar: apply scaling factor to text (#9731) 2025-03-28 16:14:46 +00:00
Tom Englund
c22f46768c xwl: dont close the fd to early (#9715)
dont close the fd until the wl_event_source is removed, so we dont get
another event triggered with an already closed fd.
2025-03-28 16:14:46 +00:00
Vaxry
66470020a7 seat: avoid sending null surfaces in leave/enter events
ref #9699
2025-03-28 16:14:46 +00:00
UjinT34
ed2f50d5ad renderer: Simplify and fix hdr metadata setting (#9706)
* simplify and fix hdr metadata setting

* keep incorrect(?) cm skip till #9600
2025-03-28 16:14:46 +00:00
20 changed files with 248 additions and 146 deletions

View File

@@ -1 +1 @@
0.48.0
0.48.1

18
flake.lock generated
View File

@@ -238,11 +238,11 @@
]
},
"locked": {
"lastModified": 1741534688,
"narHash": "sha256-EV3945SnjOCuRVbGRghsWx/9D89FyshnSO1Q6/TuQ14=",
"lastModified": 1742984269,
"narHash": "sha256-uz9FaCIbga/gQ5ZG1Hb4HVVjTWT1qjjCAFlCXiaefxg=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "dd1f720cbc2dbb3c71167c9598045dd3261d27b3",
"rev": "7248194a2ce0106ae647b70d0526a96dc9d6ad60",
"type": "github"
},
"original": {
@@ -276,11 +276,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1742069588,
"narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"lastModified": 1742889210,
"narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"rev": "698214a32beb4f4c8e3942372c694f40848b360d",
"type": "github"
},
"original": {
@@ -299,11 +299,11 @@
]
},
"locked": {
"lastModified": 1742058297,
"narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=",
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {

View File

@@ -2627,7 +2627,13 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITO
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, getMonitorFromID(monID), NAME, SPECIAL, isEmpty));
const auto PMONITOR = getMonitorFromID(monID);
if (!PMONITOR) {
Debug::log(ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace");
return nullptr;
}
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty));
PWORKSPACE->m_fAlpha->setValueAndWarp(0);
@@ -3061,6 +3067,8 @@ bool CCompositor::shouldChangePreferredImageDescription() {
}
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
if (!m_pLastMonitor)
return;
for (const auto& rule : rules) {
if (!rule.isPersistent)
@@ -3076,6 +3084,9 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
const auto PMONITOR = getMonitorFromString(rule.monitor);
if (!rule.monitor.empty() && !PMONITOR)
continue; // don't do anything yet, as the monitor is not yet present.
if (!PWORKSPACE) {
WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName;
@@ -3092,7 +3103,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
}
PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false);
createNewWorkspace(id, PMONITOR ? PMONITOR->ID : m_pLastMonitor->ID, wsname, false);
}
if (PWORKSPACE)

View File

@@ -621,14 +621,10 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* no
box.y = std::round(box.y);
CTexPassElement::SRenderData data;
data.tex = texture;
data.box = box.round();
data.syncTimeline = currentCursorImage.waitTimeline;
data.syncPoint = currentCursorImage.waitPoint;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
data.tex = texture;
data.box = box.round();
currentCursorImage.waitTimeline.reset();
currentCursorImage.waitPoint = 0;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);

View File

@@ -148,8 +148,6 @@ class CPointerManager {
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
SP<CSyncTimeline> waitTimeline = nullptr;
uint64_t waitPoint = 0;
} currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors
Vector2D pointerPos = {0, 0};

View File

@@ -75,20 +75,22 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface->pending.buffer && surface->pending.newBuffer && !surface->pending.texture) {
const bool PENDING_HAS_NEW_BUFFER = surface->pending.updated & SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER;
if (!surface->pending.buffer && PENDING_HAS_NEW_BUFFER && !surface->pending.texture) {
removeAllWaiters();
surface->commitPendingState(surface->pending);
return; // null buffer attached.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && surface->current.buffer) {
if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && surface->current.buffer) {
surface->current.bufferDamage.clear();
surface->current.damage.clear();
surface->commitPendingState(surface->current);
return; // no new buffer, but we still have current around and a commit happend, commit current again.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && !surface->current.buffer) {
if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && !surface->current.buffer) {
surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit
return;
}
@@ -109,7 +111,8 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
const auto& state = pendingStates.emplace_back(makeShared<SSurfaceState>(surface->pending));
surface->pending.damage.clear();
surface->pending.bufferDamage.clear();
surface->pending.newBuffer = false;
surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER;
surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
surface->pending.buffer.reset();
state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease();

View File

@@ -319,8 +319,11 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
if (!pendingApply.states.empty()) {
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
}
resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);

View File

@@ -71,12 +71,13 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
pending.offset = {x, y};
pending.newBuffer = true;
pending.offset = {x, y};
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
if (!buffer) {
pending.buffer.reset();
pending.texture.reset();
pending.bufferSize = Vector2D{};
} else {
auto res = CWLBufferResource::fromResource(buffer);
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr;
@@ -85,10 +86,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
}
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{};
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
if (pending.bufferSize != current.bufferSize)
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
@@ -119,11 +117,29 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
commitPendingState(pending);
});
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); });
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); });
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.damage.add(CBox{x, y, w, h});
});
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.bufferDamage.add(CBox{x, y, w, h});
});
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; });
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; });
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) {
if (scale == pending.scale)
return;
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_SCALE | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.scale = scale;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) {
if (tr == pending.transform)
return;
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_TRANSFORM | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.transform = (wl_output_transform)tr;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) {
if (!region) {
@@ -131,6 +147,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_INPUT;
auto RG = CWLRegionResource::fromResource(region);
pending.input = RG->region;
});
@@ -141,13 +159,18 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OPAQUE;
auto RG = CWLRegionResource::fromResource(region);
pending.opaque = RG->region;
});
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
pending.offset = {x, y};
});
}
CWLSurfaceResource::~CWLSurfaceResource() {
@@ -403,13 +426,8 @@ CBox CWLSurfaceResource::extends() {
void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
auto lastTexture = current.texture;
if (state.newBuffer) {
state.newBuffer = false;
current = state;
state.damage.clear();
state.bufferDamage.clear();
state.buffer.reset();
}
current.updateFrom(state);
state.updated = 0;
if (current.buffer) {
if (current.buffer->buffer->isSynchronous())

View File

@@ -22,7 +22,7 @@ bool CWLTouchResource::good() {
}
void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner)
if (!owner || !surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
@@ -145,7 +145,7 @@ bool CWLPointerResource::good() {
}
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -165,7 +165,7 @@ void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2
}
void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -336,7 +336,7 @@ void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
}
void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
@@ -361,7 +361,7 @@ void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
}
void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))

View File

@@ -56,3 +56,38 @@ void SSurfaceState::reset() {
offset = {};
size = {};
}
void SSurfaceState::updateFrom(SSurfaceState& ref) {
updated = ref.updated;
if (ref.updated & SURFACE_UPDATED_BUFFER) {
ref.updated &= ~SURFACE_UPDATED_BUFFER;
*this = ref;
ref.damage.clear();
ref.bufferDamage.clear();
ref.buffer.reset();
} else {
if (ref.updated & SURFACE_UPDATED_DAMAGE) {
damage = ref.damage;
bufferDamage = ref.bufferDamage;
}
if (ref.updated & SURFACE_UPDATED_INPUT)
input = ref.input;
if (ref.updated & SURFACE_UPDATED_OPAQUE)
opaque = ref.opaque;
if (ref.updated & SURFACE_UPDATED_OFFSET)
offset = ref.offset;
if (ref.updated & SURFACE_UPDATED_SCALE)
scale = ref.scale;
if (ref.updated & SURFACE_UPDATED_VIEWPORT)
viewport = ref.viewport;
if (ref.updated & SURFACE_UPDATED_TRANSFORM)
transform = ref.transform;
}
}

View File

@@ -7,6 +7,17 @@ class CHLBufferReference;
class CTexture;
struct SSurfaceState {
enum eUpdatedProperties : uint8_t {
SURFACE_UPDATED_OPAQUE = 1 << 0,
SURFACE_UPDATED_INPUT = 1 << 1,
SURFACE_UPDATED_DAMAGE = 1 << 2,
SURFACE_UPDATED_SCALE = 1 << 3,
SURFACE_UPDATED_BUFFER = 1 << 4,
SURFACE_UPDATED_OFFSET = 1 << 5,
SURFACE_UPDATED_VIEWPORT = 1 << 6,
SURFACE_UPDATED_TRANSFORM = 1 << 7,
};
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
int scale = 1;
@@ -20,12 +31,15 @@ struct SSurfaceState {
Vector2D destination;
CBox source;
} viewport;
bool rejected = false;
bool newBuffer = false;
bool rejected = false;
uint8_t updated = 0; // eUpdatedProperties. Stores what the last update changed
Vector2D sourceSize();
// Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage
CRegion accumulateBufferDamage();
void updateSynchronousTexture(SP<CTexture> lastTexture);
void reset();
// updates this state from a reference state. Mutates the reference state. If a new buffer is committed,
// reference state gets its damage and buffer cleared.
void updateFrom(SSurfaceState& ref);
};

View File

@@ -158,6 +158,7 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
#else
attrs.push_back(EGL_CONTEXT_CLIENT_VERSION);
attrs.push_back(2);
m_eglContextVersion = EGL_CONTEXT_GLES_2_0;
#endif
attrs.push_back(EGL_NONE);
@@ -176,7 +177,8 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
attrs.push_back(0);
attrs.push_back(EGL_NONE);
m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
m_eglContextVersion = EGL_CONTEXT_GLES_3_0;
if (m_pEglContext == EGL_NO_CONTEXT)
RASSERT(false, "EGL: failed to create a context with either GLES3.2 or 3.0");
@@ -564,7 +566,7 @@ EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attr
return image;
}
void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) {
void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program, bool silent) {
GLint maxLength = 0;
if (program)
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
@@ -582,18 +584,19 @@ void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) {
Debug::log(ERR, "Failed to link shader: {}", FULLERROR);
g_pConfigManager->addParseError(FULLERROR);
if (!silent)
g_pConfigManager->addParseError(FULLERROR);
}
GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic);
GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic, bool silent) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic, silent);
if (dynamic) {
if (vertCompiled == 0)
return 0;
} else
RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert);
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic);
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic, silent);
if (dynamic) {
if (fragCompiled == 0)
return 0;
@@ -614,7 +617,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(prog, true);
logShaderError(prog, true, silent);
return 0;
}
} else {
@@ -626,7 +629,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
return prog;
}
GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic) {
GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic, bool silent) {
auto shader = glCreateShader(type);
auto shaderSource = src.c_str();
@@ -639,7 +642,7 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(shader, false);
logShaderError(shader, false, silent);
return 0;
}
} else {
@@ -868,41 +871,43 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower");
#ifndef GLES2
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true);
m_bCMSupported = prog > 0;
if (m_bCMSupported) {
m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
} else {
Debug::log(
ERR,
"WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports about this!");
if (m_eglContextVersion == EGL_CONTEXT_GLES_3_2 /* GLES2 and GLES3.0 can't compile the CM shader */) {
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true, true);
m_bCMSupported = prog > 0;
if (m_bCMSupported) {
m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
} else {
Debug::log(
ERR,
"WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports about this!");
}
}
#endif
@@ -1382,9 +1387,9 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
const auto imageDescription =
m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
const bool skipCM = !*PENABLECM /* CM disabled by the user */
|| !m_RenderData.surface /* No surface - no point in CM */
|| !m_bCMSupported /* CM unsupported - hw failed to compile the shader probably */
const bool skipCM = !*PENABLECM /* CM disabled by the user */
|| !m_RenderData.surface /* FIXME unknown texture settings should be treated as sRGB and go through CM if monitor isn't in sRGB mode */
|| !m_bCMSupported /* CM unsupported - hw failed to compile the shader probably */
|| (imageDescription == m_RenderData.pMonitor->imageDescription) /* Source and target have the same image description */
|| ((*PPASS == 1 || (*PPASS == 2 && imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) && m_RenderData.pMonitor->activeWorkspace &&
m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
@@ -3016,7 +3021,7 @@ CEGLSync::~CEGLSync() {
if (sync == EGL_NO_SYNC_KHR)
return;
if (g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
if (g_pHyprOpenGL && g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
Debug::log(ERR, "eglDestroySyncKHR failed");
}

View File

@@ -276,6 +276,14 @@ class CHyprOpenGLImpl {
} m_sExts;
private:
enum eEGLContextVersion : uint8_t {
EGL_CONTEXT_GLES_2_0 = 0,
EGL_CONTEXT_GLES_3_0,
EGL_CONTEXT_GLES_3_2,
};
eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2;
std::list<GLuint> m_lBuffers;
std::list<GLuint> m_lTextures;
@@ -297,9 +305,9 @@ class CHyprOpenGLImpl {
SP<CTexture> m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; // TODO: don't always load lock
void logShaderError(const GLuint&, bool program = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false);
void logShaderError(const GLuint&, bool program = false, bool silent = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false, bool silent = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false, bool silent = false);
void createBGTextureForMonitor(PHLMONITOR);
void initShaders();
void initDRMFormats();

View File

@@ -1411,11 +1411,16 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}};
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ)
return NO_HDR_METADATA; // empty metadata for SDR
uint8_t eotf = 0;
switch (settings.transferFunction) {
case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now
case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break;
// case CM_TRANSFER_FUNCTION_HLG: eotf = 3; break; TODO check display capabilities first
default: return NO_HDR_METADATA; // empty metadata for SDR
}
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ?
@@ -1429,7 +1434,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = 2,
.eotf = eotf,
.metadata_type = 0,
.display_primaries =
{
@@ -1454,25 +1459,42 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) {
// HDR metadata determined by
// PPASS = 0 monitor settings
// PPASS = 1
// windowed: monitor settings
// fullscreen surface: surface settings FIXME: fullscreen SDR surface passthrough - pass degamma, ctm, gamma if needed
// PPASS = 2
// windowed: monitor settings
// fullscreen SDR surface: monitor settings
// fullscreen HDR surface: surface settings
bool wantHDR = PHDR;
bool hdrIsHandled = false;
if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
const auto SURF =
ROOT_SURF->findFirstPreorder([ROOT_SURF](SP<CWLSurfaceResource> surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); });
const bool wantHDR = PHDR && *PPASS == 2;
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) {
wantHDR = PHDR && *PPASS == 2;
// we have a surface with image description and it's allowed by wantHDR
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription() &&
(!wantHDR || SURF->colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) {
bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW;
if (SURF->colorManagement->needsHdrMetadataUpdate())
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
if (needsHdrMetadataUpdate)
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR)
pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
hdrIsHandled = true;
}
pMonitor->m_previousFSWindow = WINDOW;
} else {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
}
if (!hdrIsHandled) {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR)
pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
pMonitor->m_previousFSWindow.reset();
}
}

View File

@@ -142,7 +142,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
*PINDICATORHEIGHT};
rect.scale(pMonitor->scale);
rect.scale(pMonitor->scale).round();
const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked;
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE;
@@ -278,7 +278,7 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
const CHyprColor COLOR = CHyprColor(*PTEXTCOLOR);
const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT;
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE * monitorScale, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
if (tex)
texSize = tex->m_vSize;
@@ -573,5 +573,5 @@ CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
if (PWORKSPACE && !m_pWindow->m_bPinned)
box.translate(PWORKSPACE->m_vRenderOffset->value());
return box;
return box.round();
}

View File

@@ -33,5 +33,13 @@ std::optional<CBox> CRectPassElement::boundingBox() {
}
CRegion CRectPassElement::opaqueRegion() {
return data.color.a >= 1.F ? boundingBox()->expand(-data.round) : CRegion{};
if (data.color.a < 1.F)
return CRegion{};
CRegion rg = boundingBox()->expand(-data.round);
if (!data.clipBox.empty())
rg.intersect(data.clipBox);
return rg;
}

View File

@@ -22,8 +22,7 @@ void CTexPassElement::draw(const CRegion& damage) {
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection;
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower, data.syncTimeline,
data.syncPoint);
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower);
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
}

View File

@@ -16,8 +16,6 @@ class CTexPassElement : public IPassElement {
int round = 0;
float roundingPower = 2.0f;
bool flipEndFrame = false;
SP<CSyncTimeline> syncTimeline;
int64_t syncPoint = 0;
std::optional<Mat3x3> replaceProjection;
CBox clipBox;
};

View File

@@ -177,8 +177,7 @@ static void startServer(void* data) {
}
static int xwaylandReady(int fd, uint32_t mask, void* data) {
CFileDescriptor xwlFd{fd};
return g_pXWayland->pServer->ready(std::move(xwlFd), mask);
return g_pXWayland->pServer->ready(fd, mask);
}
static bool safeRemove(const std::string& path) {
@@ -353,7 +352,7 @@ bool CXWaylandServer::start() {
return false;
}
waylandFDs[0].take(); // does this leak?
waylandFDs[0].take(); // wl_client owns this fd now
int notify[2] = {-1, -1};
if (pipe(notify) < 0) {
@@ -373,30 +372,24 @@ bool CXWaylandServer::start() {
pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notifyFds[0].get(), WL_EVENT_READABLE, ::xwaylandReady, nullptr);
pipeFd = std::move(notifyFds[0]);
serverPID = fork();
auto serverPID = fork();
if (serverPID < 0) {
Debug::log(ERR, "fork failed");
die();
return false;
} else if (serverPID == 0) {
pid_t pid = fork();
if (pid < 0) {
Debug::log(ERR, "second fork failed");
_exit(1);
} else if (pid == 0)
runXWayland(notifyFds[1]);
runXWayland(notifyFds[1]);
_exit(0);
}
return true;
}
int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
int CXWaylandServer::ready(int fd, uint32_t mask) {
if (mask & WL_EVENT_READABLE) {
// xwayland writes twice
char buf[64];
ssize_t n = read(fd.get(), buf, sizeof(buf));
ssize_t n = read(fd, buf, sizeof(buf));
if (n < 0 && errno != EINTR) {
Debug::log(ERR, "Xwayland: read from displayFd failed");
mask = 0;
@@ -404,14 +397,6 @@ int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
return 1;
}
while (waitpid(serverPID, nullptr, 0) < 0) {
if (errno == EINTR)
continue;
Debug::log(ERR, "Xwayland: waitpid for fork failed");
g_pXWayland->pServer.reset();
return 1;
}
// if we don't have readable here, it failed
if (!(mask & WL_EVENT_READABLE)) {
Debug::log(ERR, "Xwayland: startup failed, not setting up xwm");
@@ -422,6 +407,7 @@ int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
Debug::log(LOG, "XWayland is ready");
wl_event_source_remove(pipeSource);
pipeFd.reset();
pipeSource = nullptr;
// start the wm

View File

@@ -20,7 +20,7 @@ class CXWaylandServer {
bool start();
// called on ready
int ready(Hyprutils::OS::CFileDescriptor fd, uint32_t mask);
int ready(int fd, uint32_t mask);
void die();
@@ -34,8 +34,6 @@ class CXWaylandServer {
bool tryOpenSockets();
void runXWayland(Hyprutils::OS::CFileDescriptor& notifyFD);
pid_t serverPID = 0;
std::string displayName;
int display = -1;
std::array<Hyprutils::OS::CFileDescriptor, 2> xFDs;