update blobs
This commit is contained in:
@@ -12,6 +12,7 @@ qml_module(ZShell-blobs
|
||||
|
||||
qt_add_shaders(ZShell-blobs "blob_shaders"
|
||||
BATCHABLE OPTIMIZED NOHLSL NOMSL
|
||||
GLSL "300es,330"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
shaders/blob.frag
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
|
||||
"BlobMaterial packs excludeMask into a float slot via memcpy");
|
||||
|
||||
QSGMaterialType* BlobMaterial::type() const {
|
||||
static QSGMaterialType s_type;
|
||||
return &s_type;
|
||||
@@ -82,8 +85,11 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto& r = mat->m_rects[i];
|
||||
const int base = 160 + i * 80;
|
||||
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
|
||||
float maskAsFloat;
|
||||
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
|
||||
const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
|
||||
const float d1[4] = { 0.0f, r.offsetX, r.offsetY, r.minEig };
|
||||
const float d1[4] = { maskAsFloat, r.offsetX, r.offsetY, r.minEig };
|
||||
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
|
||||
memcpy(buf->data() + base, d0, 16);
|
||||
memcpy(buf->data() + base + 16, d1, 16);
|
||||
|
||||
@@ -14,6 +14,9 @@ struct BlobRectData {
|
||||
float screenHalfX = 0, screenHalfY = 0;
|
||||
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
|
||||
float radius[4] = { 0, 0, 0, 0 };
|
||||
// Bitmask of indices in this rect's m_cachedRects that mutually exclude (or are excluded by) this rect.
|
||||
// Used by the shader to skip smin between excluded pairs.
|
||||
int excludeMask = 0;
|
||||
};
|
||||
|
||||
class BlobMaterial : public QSGMaterial {
|
||||
|
||||
@@ -149,6 +149,10 @@ void BlobShape::updatePolish() {
|
||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
||||
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
||||
|
||||
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
|
||||
QVector<BlobShape*> rectShapes;
|
||||
rectShapes.reserve(m_group->shapes().size());
|
||||
|
||||
for (BlobShape* other : m_group->shapes()) {
|
||||
if (other->isInvertedRect())
|
||||
continue;
|
||||
@@ -210,12 +214,29 @@ void BlobShape::updatePolish() {
|
||||
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
||||
|
||||
m_cachedRects.append(r);
|
||||
rectShapes.append(other);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvertedRect())
|
||||
m_cachedMyIndex = -1;
|
||||
|
||||
// Compute pairwise exclude masks. Bit j in entry i is set iff rect i excludes rect j
|
||||
// or rect j excludes rect i. The shader uses this to avoid smin between excluded pairs.
|
||||
const auto cachedCount = m_cachedRects.size();
|
||||
for (qsizetype i = 0; i < cachedCount; ++i) {
|
||||
int mask = 0;
|
||||
BlobShape* si = rectShapes[i];
|
||||
for (qsizetype j = 0; j < cachedCount; ++j) {
|
||||
if (j == i)
|
||||
continue;
|
||||
BlobShape* sj = rectShapes[j];
|
||||
if (si->isExcluded(sj) || sj->isExcluded(si))
|
||||
mask |= (1 << j);
|
||||
}
|
||||
m_cachedRects[i].excludeMask = mask;
|
||||
}
|
||||
|
||||
// Cache inverted rect data
|
||||
m_cachedHasInverted = false;
|
||||
m_cachedInvertedRadius = 0;
|
||||
@@ -270,6 +291,7 @@ void BlobShape::updatePolish() {
|
||||
const auto rectCount = m_cachedRects.size();
|
||||
for (qsizetype i = 0; i < rectCount; ++i) {
|
||||
auto& ri = m_cachedRects[i];
|
||||
const int riExcludeMask = ri.excludeMask;
|
||||
float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f;
|
||||
|
||||
const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh;
|
||||
@@ -280,6 +302,8 @@ void BlobShape::updatePolish() {
|
||||
for (qsizetype j = 0; j < rectCount; ++j) {
|
||||
if (j == i)
|
||||
continue;
|
||||
if (riExcludeMask & (1 << j))
|
||||
continue;
|
||||
const auto& rj = m_cachedRects[j];
|
||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||
|
||||
@@ -63,13 +63,17 @@ float smaxSharpA(float a, float b, float k) {
|
||||
void main() {
|
||||
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
|
||||
|
||||
float mergedSdf = 1e10;
|
||||
// Phase 1: compute per-rect SDF, track owner. We can't smin yet because
|
||||
// excluded pairs need to skip the smooth blend, which requires pairwise pass
|
||||
// below.
|
||||
float dArr[16];
|
||||
int owner = -2;
|
||||
float minDist = 1e10;
|
||||
|
||||
for (int i = 0; i < rectCount; i++) {
|
||||
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
|
||||
vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
|
||||
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
|
||||
vec4 props =
|
||||
rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig
|
||||
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
||||
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
||||
vec4 radii =
|
||||
@@ -81,8 +85,10 @@ void main() {
|
||||
// AABB early-out: skip rects far from this pixel
|
||||
vec2 extent = sh.xy + vec2(smoothFactor * 1.5);
|
||||
if (abs(pixel.x - center.x) > extent.x ||
|
||||
abs(pixel.y - center.y) > extent.y)
|
||||
abs(pixel.y - center.y) > extent.y) {
|
||||
dArr[i] = 1e10;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply pre-computed inverse deformation to the evaluation point
|
||||
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
||||
@@ -138,13 +144,38 @@ void main() {
|
||||
d *= scale;
|
||||
}
|
||||
|
||||
mergedSdf = smin(mergedSdf, d, smoothFactor);
|
||||
dArr[i] = d;
|
||||
if (d < smoothFactor && d < minDist) {
|
||||
minDist = d;
|
||||
owner = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: hard-min baseline over all rects.
|
||||
float mergedSdf = 1e10;
|
||||
for (int i = 0; i < rectCount; i++) {
|
||||
mergedSdf = min(mergedSdf, dArr[i]);
|
||||
}
|
||||
|
||||
// Phase 3: pair-wise smin contributions, skipping excluded pairs. Pair smin
|
||||
// <= min, so taking the min over all non-excluded pair smins gives the
|
||||
// smoothly-merged SDF.
|
||||
for (int i = 0; i < rectCount; i++) {
|
||||
if (dArr[i] >= 1e9)
|
||||
continue;
|
||||
int excludeMask = floatBitsToInt(rectData[i * 5 + 1].x);
|
||||
for (int j = i + 1; j < rectCount; j++) {
|
||||
if (dArr[j] >= 1e9)
|
||||
continue;
|
||||
if ((excludeMask & (1 << j)) != 0)
|
||||
continue;
|
||||
// smin only deviates from min within smoothFactor
|
||||
if (abs(dArr[i] - dArr[j]) >= smoothFactor)
|
||||
continue;
|
||||
mergedSdf = min(mergedSdf, smin(dArr[i], dArr[j], smoothFactor));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInverted != 0) {
|
||||
float dOuter = sdBox(pixel, invertedOuter.xy, invertedOuter.zw) - 1.0;
|
||||
float dInner =
|
||||
|
||||
Reference in New Issue
Block a user