Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
ZoneUniformExtension.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 fuddlesworth
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
8
9#include <QMutex>
10#include <QMutexLocker>
11#include <QtNumeric>
12#include <atomic>
13#include <cmath>
14#include <cstring>
15
16namespace PhosphorRendering {
17
39{
40public:
42 {
43 // Value-initialise rather than memset: the default member initialisers
44 // on ZoneExtensionData make it non-trivially-default-constructible, so a
45 // memset trips -Wclass-memaccess in every including TU. `= {}` zeroes
46 // the arrays AND applies the zoneScale = 1.0f default, which is the
47 // identity the shader needs until the item reports the real
48 // device-pixel ratio. A zeroed scale would multiply every radius and
49 // border width to 0, so a fresh overlay's first frames would render
50 // square-cornered and border-less before the first setScale() lands.
51 m_data = {};
52 }
53
54 int extensionSize() const override
55 {
56 return static_cast<int>(sizeof(m_data));
57 }
58
59 void write(char* buffer, int offset) const override
60 {
61 QMutexLocker lock(&m_mutex);
62 std::memcpy(buffer + offset, &m_data, sizeof(m_data));
63 }
64
65 bool isDirty() const override
66 {
67 return m_dirty.load(std::memory_order_acquire);
68 }
69
70 void clearDirty() override
71 {
72 m_dirty.store(false, std::memory_order_release);
73 }
74
79 void updateFromZones(const QVector<ZoneData>& zones)
80 {
81 QMutexLocker lock(&m_mutex);
82 for (int i = 0; i < MaxZones; ++i) {
83 if (i < zones.size()) {
84 const ZoneData& zone = zones[i];
85 m_data.zoneRects[i][0] = static_cast<float>(zone.rect.x());
86 m_data.zoneRects[i][1] = static_cast<float>(zone.rect.y());
87 m_data.zoneRects[i][2] = static_cast<float>(zone.rect.width());
88 m_data.zoneRects[i][3] = static_cast<float>(zone.rect.height());
89 m_data.zoneFillColors[i][0] = static_cast<float>(zone.fillColor.redF());
90 m_data.zoneFillColors[i][1] = static_cast<float>(zone.fillColor.greenF());
91 m_data.zoneFillColors[i][2] = static_cast<float>(zone.fillColor.blueF());
92 m_data.zoneFillColors[i][3] = static_cast<float>(zone.fillColor.alphaF());
93 m_data.zoneBorderColors[i][0] = static_cast<float>(zone.borderColor.redF());
94 m_data.zoneBorderColors[i][1] = static_cast<float>(zone.borderColor.greenF());
95 m_data.zoneBorderColors[i][2] = static_cast<float>(zone.borderColor.blueF());
96 m_data.zoneBorderColors[i][3] = static_cast<float>(zone.borderColor.alphaF());
97 m_data.zoneParams[i][0] = zone.borderRadius;
98 m_data.zoneParams[i][1] = zone.borderWidth;
99 m_data.zoneParams[i][2] = zone.isHighlighted ? 1.0f : 0.0f;
100 m_data.zoneParams[i][3] = static_cast<float>(zone.zoneNumber);
101 } else {
102 std::memset(m_data.zoneRects[i], 0, sizeof(m_data.zoneRects[i]));
103 std::memset(m_data.zoneFillColors[i], 0, sizeof(m_data.zoneFillColors[i]));
104 std::memset(m_data.zoneBorderColors[i], 0, sizeof(m_data.zoneBorderColors[i]));
105 std::memset(m_data.zoneParams[i], 0, sizeof(m_data.zoneParams[i]));
106 }
107 }
108 m_dirty.store(true, std::memory_order_release);
109 }
110
130 [[nodiscard]] bool setScale(float scale)
131 {
132 // Reject values that would make the shader silently disappear rather
133 // than degrade. The GLSL multiplies every radius and border width by
134 // this: a NaN propagates through clamp() and takes the whole zone with
135 // it, and zero or a negative collapses the corner to square with a
136 // hairline border. Neither reads as a fault, so a bad value would be
137 // mistaken for a design choice. A device-pixel ratio is always
138 // positive and finite, so anything else is a caller bug; keep the last
139 // good scale instead of rendering a lie.
140 if (!(scale > 0.0f) || std::isinf(scale)) {
141 return false;
142 }
143 QMutexLocker lock(&m_mutex);
144 // qFuzzyCompare is undefined with a zero operand. Safe here only
145 // because the guard above rejects zero/negative/NaN and zoneScale is
146 // seeded to 1.0f, so neither argument can be 0.
147 if (qFuzzyCompare(m_data.zoneScale, scale)) {
148 return true;
149 }
150 m_data.zoneScale = scale;
151 m_dirty.store(true, std::memory_order_release);
152 return true;
153 }
154
155private:
157 struct alignas(16) ZoneExtensionData
158 {
159 float zoneRects[MaxZones][4];
160 float zoneFillColors[MaxZones][4];
161 float zoneBorderColors[MaxZones][4];
162 float zoneParams[MaxZones][4];
163 // Defaulted to identity for the same reason ZoneShaderUniforms::zoneScale
164 // is, and the constructor now relies on it: a zero scale multiplies
165 // every corner radius and border width to nothing, so a zone renders
166 // square-cornered and border-less rather than failing visibly. The two
167 // structs are asserted binary-identical below, so both need it.
168 float zoneScale = 1.0f;
169 float _pad_after_zoneScale[3] = {};
170 };
171
172 static_assert(sizeof(ZoneExtensionData) == kZoneExtensionBytes,
173 "ZoneExtensionData must be MaxZones * 4 arrays * 4 floats, plus the "
174 "zoneScale scalar and its std140 tail pad");
175 // Field offsets must match the GLSL UBO layout in common.glsl exactly.
176 // Reordering or inserting fields here silently breaks shader rendering
177 // (no compile error, just wrong colors/positions on every zone).
178 static_assert(offsetof(ZoneExtensionData, zoneRects) == 0, "zoneRects must be first");
179 static_assert(offsetof(ZoneExtensionData, zoneFillColors) == sizeof(float[MaxZones][4]),
180 "zoneFillColors must follow zoneRects with no gap");
181 static_assert(offsetof(ZoneExtensionData, zoneBorderColors) == 2 * sizeof(float[MaxZones][4]),
182 "zoneBorderColors must follow zoneFillColors with no gap");
183 static_assert(offsetof(ZoneExtensionData, zoneParams) == 3 * sizeof(float[MaxZones][4]),
184 "zoneParams must follow zoneBorderColors with no gap");
185 static_assert(offsetof(ZoneExtensionData, zoneScale) == 4 * sizeof(float[MaxZones][4]),
186 "zoneScale must follow zoneParams with no gap");
187 // Verify ZoneExtensionData is binary-identical to the zone region of
188 // ZoneShaderUniforms (BaseUniforms + this == ZoneShaderUniforms layout).
189 static_assert(sizeof(ZoneExtensionData) == sizeof(ZoneShaderUniforms) - sizeof(PhosphorShaders::BaseUniforms),
190 "ZoneExtensionData size must match ZoneShaderUniforms zone region size");
191
192 ZoneExtensionData m_data;
193 mutable QMutex m_mutex;
194 std::atomic<bool> m_dirty{true};
195};
196
197} // namespace PhosphorRendering
IUniformExtension implementation for zone data.
Definition ZoneUniformExtension.h:39
ZoneUniformExtension()
Definition ZoneUniformExtension.h:41
bool setScale(float scale)
Set the logical-to-device scale the shader applies to the lengths in zoneParams (corner radius,...
Definition ZoneUniformExtension.h:130
void write(char *buffer, int offset) const override
Write extension data into buffer starting at offset.
Definition ZoneUniformExtension.h:59
bool isDirty() const override
Whether the extension data has changed since the last write.
Definition ZoneUniformExtension.h:65
int extensionSize() const override
Size in bytes of the extension region (must respect std140 alignment).
Definition ZoneUniformExtension.h:54
void updateFromZones(const QVector< ZoneData > &zones)
Update zone data from a vector of ZoneData.
Definition ZoneUniformExtension.h:79
void clearDirty() override
Mark as clean after a successful write.
Definition ZoneUniformExtension.h:70
Interface for appending custom uniform data after the base UBO layout.
Definition IUniformExtension.h:18
Definition ShaderCompiler.h:15
constexpr int MaxZones
Maximum number of zones the zone-aware UBO supports.
Definition ZoneShaderCommon.h:20
constexpr std::size_t kZoneExtensionBytes
Size of the zone extension region: four vec4[MaxZones] arrays, plus the uZoneScale scalar and the std...
Definition ZoneShaderCommon.h:64
Per-zone payload pushed into the UBO each frame.
Definition ZoneShaderCommon.h:84
float borderWidth
Definition ZoneShaderCommon.h:89
QRectF rect
Definition ZoneShaderCommon.h:85
QColor borderColor
Definition ZoneShaderCommon.h:87
int zoneNumber
Definition ZoneShaderCommon.h:91
QColor fillColor
Definition ZoneShaderCommon.h:86
float borderRadius
Definition ZoneShaderCommon.h:88
bool isHighlighted
Definition ZoneShaderCommon.h:90
GPU uniform buffer layout following std140 rules (base region).
Definition BaseUniforms.h:37