QSGRenderNode for fullscreen-quad shader rendering via Qt RHI (Vulkan / OpenGL) More...
#include <phosphor-rendering/include/PhosphorRendering/ShaderNodeRhi.h>
Public Member Functions | |
| ShaderNodeRhi (QQuickItem *item, std::unique_ptr< PhosphorShaders::IUboProfile > profile=nullptr) | |
| ~ShaderNodeRhi () override | |
| void | invalidateItem () |
| Notify the render node that its owning item is being destroyed. | |
| bool | hasValidItem () const |
| Whether this node still holds a usable back-pointer to its item. | |
| const std::shared_ptr< ShaderNodeLiveness > & | liveness () const |
| The liveness block to hand to ShaderEffect::registerRenderNode. | |
| QSGRenderNode::StateFlags | changedStates () const override |
| QSGRenderNode::RenderingFlags | flags () const override |
| QRectF | rect () const override |
| void | prepare () override |
| void | render (const RenderState *state) override |
| void | releaseResources () override |
| void | preprocess () override |
| Stock-parity dependency pull (mirrors QSGRhiShaderEffectNode): bring the source provider's layer texture current before this node renders. | |
| void | setUniformExtension (std::shared_ptr< PhosphorShaders::IUniformExtension > extension) |
| std::shared_ptr< PhosphorShaders::IUniformExtension > | uniformExtension () const |
| Access the currently-installed uniform extension (may be nullptr). | |
| void | setTime (double time) |
| void | setTimeDelta (float delta) |
| void | setFrame (int frame) |
| void | setResolution (float width, float height) |
| void | setMousePosition (const QPointF &pos) |
| void | setIsReversed (bool reverse) |
| Direction signal for asymmetric leg rendering. | |
| void | setCustomParams (int index, const QVector4D ¶ms) |
| void | setCustomColor (int index, const QColor &color) |
| void | setSurfaceOpacity (float opacity) |
| Per-surface inputs consumed by a SurfaceUniformProfile. | |
| void | setSurfaceScale (float scale) |
| void | setSurfaceFocused (bool focused) |
| void | setSurfaceSize (float width, float height) |
| void | setBackdropRect (float x, float y, float w, float h) |
| The slice of the bound backdrop this surface samples, normalized texture coords (xy = min, zw = size). | |
| void | setSurfaceFrameTopLeft (float x, float y) |
| void | setSurfaceFrameSize (float width, float height) |
| void | setAppField0 (int value) |
| Write the consumer's two int slots inside BaseUniforms (offsets 88, 92). | |
| void | setAppField1 (int value) |
| bool | setExtraBinding (int binding, QRhiTexture *texture, QRhiSampler *sampler) |
| Bind a consumer-owned texture/sampler at the given binding number. | |
| bool | removeExtraBinding (int binding) |
| void | setAudioSpectrum (const QVector< float > &spectrum) |
| void | setUserTexture (int slot, const QImage &image) |
| void | setUserTextureWrap (int slot, const QString &wrap) |
| Set per-slot sampler address mode. | |
| void | setWallpaperTexture (const QImage &image) |
| void | setUseWallpaper (bool use) |
| void | setUseDepthBuffer (bool use) |
| void | setSourceTextureProvider (QSGTextureProvider *provider) |
Live texture-provider override for user-texture slot 0 (SRB binding 7 / uTexture0). | |
| QSGTextureProvider * | sourceTextureProvider () const |
| void | setBufferShaderPath (const QString &path) |
| void | setBufferShaderPaths (const QStringList &paths) |
| void | setBufferFeedback (bool enable) |
| void | setBufferScale (qreal scale) |
| void | setHalfFloatBuffers (bool enable) |
Buffer-pass texel format: RGBA16F when true (the default — safe for HDR, signed-data, and feedback buffers), RGBA8 when a pack's metadata declares its buffers hold plain clamped colour ("halfFloatBuffers": false). | |
| void | setBufferWrap (const QString &wrap) |
| void | setBufferWraps (const QStringList &wraps) |
| void | setBufferFilter (const QString &filter) |
| void | setBufferFilters (const QStringList &filters) |
| bool | loadVertexShader (const QString &path) |
| bool | loadFragmentShader (const QString &path) |
| void | setVertexShaderSource (const QString &source) |
| void | setFragmentShaderSource (const QString &source) |
| bool | isShaderReady () const |
| QString | shaderError () const |
| void | invalidateShader () |
| void | invalidateUniforms () |
| void | setShaderIncludePaths (const QStringList &paths) |
| void | setParamPreamble (const QString &preamble) |
Set the generated named-parameter preamble that is spliced after the fragment shader's #version line at load time (via PhosphorShaders::spliceAfterVersion), so authors read a parameter by name instead of hand-decoding a customParams[N].xyzw lane. | |
| void | setEntryScaffold (const QString &prologue, const QList< PhosphorShaders::EntryCandidate > &candidates) |
| Install the entry-point scaffold for the fragment stage. | |
Static Public Member Functions | |
| static QString | normalizeWrapMode (const QString &wrap) |
| Normalize wrap mode string to "clamp", "repeat", or "mirror" (static helper, safe to call from any thread — operates on its arguments only). | |
| static QString | normalizeFilterMode (const QString &filter) |
| Normalize filter mode string to "nearest", "linear", or "mipmap". | |
| static QRhiSampler::AddressMode | wrapModeToRhiAddress (const QString &wrap) |
| Map a normalized wrap-mode string to QRhiSampler::AddressMode. | |
Protected Member Functions | |
| QRhi * | safeRhi () const |
| Thread-safe QRhi accessor. | |
| void | retractLiveness () noexcept |
| Retract this node from its liveness block. | |
QSGRenderNode for fullscreen-quad shader rendering via Qt RHI (Vulkan / OpenGL)
Generalized render node extracted from Phosphor's ZoneShaderNodeRhi. Manages a Shadertoy-compatible UBO (a profile-supplied layout, BaseUniforms by default), multipass buffer system, texture bindings (audio, user, wallpaper, depth), and shader baking.
Application-specific UBO data is appended via IUniformExtension. Application-specific texture bindings use setExtraBinding() / removeExtraBinding().
Uses QRhi and QShaderBaker (runtime SPIR-V + GLSL 330 bake). Requires Qt 6.6+ (commandBuffer(), renderTarget()).
One sanctioned entry point runs on the render thread OUTSIDE the sync phase: releaseResources(), reached via ShaderEffect::releaseIdleGraphicsResources' QQuickWindow::NoStage render job while the GUI thread is NOT blocked. That is safe because no GUI-thread path mutates node members directly — every ShaderEffect setter stages into the item's own members and defers the node push to the next sync — so the job cannot race a concurrent member write.
(Setters on the sibling ShaderEffect class are a different story — those run on the GUI thread and stage their changes into ShaderEffect's own members, to be pushed down to this node during the next sync phase.)
|
explicit |
| item | Owning QQuickItem (must outlive the node until invalidateItem() is called). |
| profile | Pluggable UBO profile. The default (nullptr) installs a BaseUniformProfile so every existing caller — including ZoneShaderNodeRhi's ShaderNodeRhi(item) forward — keeps the legacy overlay/animation UBO (BaseUniforms, currently 672 bytes) unchanged. The surface-decoration runtime passes a SurfaceUniformProfile here to reuse the engine with the leaner surface UBO. UBO size is always profile-defined (m_uboProfile->baseSize()), never hard-coded. |
|
override |
|
override |
|
override |
|
inline |
Whether this node still holds a usable back-pointer to its item.
False once invalidateItem() has run. This is the node's central threading invariant — every m_item dereference is gated on it — so it is exposed for callers that need to assert the teardown contract held (chiefly: that the item severed the back-pointer before it was freed). Thread-safe, but only a snapshot: do not use it to gate a dereference the node does not own.
| void PhosphorRendering::ShaderNodeRhi::invalidateItem | ( | ) |
Notify the render node that its owning item is being destroyed.
Called from the owning QQuickItem destructor on the GUI thread. After this call, the render node will no longer dereference m_item. Thread-safe: uses an atomic flag checked by prepare()/render().
| void PhosphorRendering::ShaderNodeRhi::invalidateShader | ( | ) |
| void PhosphorRendering::ShaderNodeRhi::invalidateUniforms | ( | ) |
| bool PhosphorRendering::ShaderNodeRhi::isShaderReady | ( | ) | const |
|
inline |
The liveness block to hand to ShaderEffect::registerRenderNode.
Valid for the node's whole lifetime and beyond — the block itself outlives the node, which nulls its own pointer inside it on destruction. See ShaderNodeLiveness.
| bool PhosphorRendering::ShaderNodeRhi::loadFragmentShader | ( | const QString & | path | ) |
| bool PhosphorRendering::ShaderNodeRhi::loadVertexShader | ( | const QString & | path | ) |
|
static |
Normalize filter mode string to "nearest", "linear", or "mipmap".
|
static |
Normalize wrap mode string to "clamp", "repeat", or "mirror" (static helper, safe to call from any thread — operates on its arguments only).
Unknown / empty inputs fall back to "clamp" — that fallback is load-bearing for legacy callers that pass an empty string when no wrap is configured.
|
override |
|
override |
Stock-parity dependency pull (mirrors QSGRhiShaderEffectNode): bring the source provider's layer texture current before this node renders.
Without it, a ShaderEffectSource this node samples only re-grabs when its OWN subtree is dirtied — a dependency the scene graph cannot see through a raw provider pointer — so a node captured into an enclosing layer (SurfaceAnimator's transition capture) can freeze on an empty first grab for a whole animation leg. Enabled via UsePreprocess in the constructor.
|
override |
|
override |
| bool PhosphorRendering::ShaderNodeRhi::removeExtraBinding | ( | int | binding | ) |
|
override |
|
protectednoexcept |
Retract this node from its liveness block.
MUST be the first statement of EVERY subclass destructor, not just this class's. The most-derived destructor body and its member teardown run before ~ShaderNodeRhi, so a retract that happened only in the base would leave ShaderEffect::withTrackedNode able to dispatch a virtual call into a node whose derived half is already destroyed.
Idempotent (nulling an already-null pointer under the same mutex), so the base destructor's own call after a subclass has already retracted costs one uncontended lock and nothing else. Takes ShaderNodeLiveness::mutex, so it must not be called with m_itemMutex held. See ShaderNodeLiveness for the full ordering.
|
protected |
Thread-safe QRhi accessor.
commandBuffer()->rhi() so the post-invalidateItem() guard stays uniform. | void PhosphorRendering::ShaderNodeRhi::setAppField0 | ( | int | value | ) |
Write the consumer's two int slots inside BaseUniforms (offsets 88, 92).
See PhosphorShaders::BaseUniforms for the full escape-hatch rationale. Updating these fields is cheap: the library uploads only the 8-byte K_APP_FIELDS region rather than the full scene header.
| void PhosphorRendering::ShaderNodeRhi::setAppField1 | ( | int | value | ) |
| void PhosphorRendering::ShaderNodeRhi::setAudioSpectrum | ( | const QVector< float > & | spectrum | ) |
| void PhosphorRendering::ShaderNodeRhi::setBackdropRect | ( | float | x, |
| float | y, | ||
| float | w, | ||
| float | h | ||
| ) |
The slice of the bound backdrop this surface samples, normalized texture coords (xy = min, zw = size).
Defaults to the whole texture.
Only a host that binds ONE shared image across many surfaces needs this — a daemon or preview host handing every surface the same desktop wallpaper. The compositor's capture already covers the window's own canvas, so it leaves the default alone.
| void PhosphorRendering::ShaderNodeRhi::setBufferFeedback | ( | bool | enable | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferFilter | ( | const QString & | filter | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferFilters | ( | const QStringList & | filters | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferScale | ( | qreal | scale | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferShaderPath | ( | const QString & | path | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferShaderPaths | ( | const QStringList & | paths | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferWrap | ( | const QString & | wrap | ) |
| void PhosphorRendering::ShaderNodeRhi::setBufferWraps | ( | const QStringList & | wraps | ) |
| void PhosphorRendering::ShaderNodeRhi::setCustomColor | ( | int | index, |
| const QColor & | color | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setCustomParams | ( | int | index, |
| const QVector4D & | params | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setEntryScaffold | ( | const QString & | prologue, |
| const QList< PhosphorShaders::EntryCandidate > & | candidates | ||
| ) |
Install the entry-point scaffold for the fragment stage.
When set and the loaded fragment source does NOT define main(), loadFragmentShader assembles prologue + source and appends the generated main() of the first candidates entry function the source defines (via PhosphorShaders::composeEntryPoint), BEFORE include expansion — so the prologue's #include is resolved. A source that defines its own main() is left untouched (every bundled pack today). The scaffold is folded into the bake-cache key, and MUST match what the warm-bake applies for the same shader. Empty prologue + empty candidates (the default, e.g. the animation path) disables assembly entirely — a strict no-op.
| bool PhosphorRendering::ShaderNodeRhi::setExtraBinding | ( | int | binding, |
| QRhiTexture * | texture, | ||
| QRhiSampler * | sampler | ||
| ) |
Bind a consumer-owned texture/sampler at the given binding number.
binding collides with a library- managed slot (0, 2-12) or falls outside the supported range. A true return for an identical (binding, texture, sampler) triple is a no-op — the SRB/pipeline is NOT rebuilt when nothing actually changed. | void PhosphorRendering::ShaderNodeRhi::setFragmentShaderSource | ( | const QString & | source | ) |
| void PhosphorRendering::ShaderNodeRhi::setFrame | ( | int | frame | ) |
| void PhosphorRendering::ShaderNodeRhi::setHalfFloatBuffers | ( | bool | enable | ) |
Buffer-pass texel format: RGBA16F when true (the default — safe for HDR, signed-data, and feedback buffers), RGBA8 when a pack's metadata declares its buffers hold plain clamped colour ("halfFloatBuffers": false).
| void PhosphorRendering::ShaderNodeRhi::setIsReversed | ( | bool | reverse | ) |
Direction signal for asymmetric leg rendering.
Forward through to BaseUniforms::iIsReversed at offset 660. SurfaceAnimator pushes this from the leg's isShowLeg flag (false = reverse = hide leg); kwin-effect parity is handled directly via setUniform on the kwin path.
| void PhosphorRendering::ShaderNodeRhi::setMousePosition | ( | const QPointF & | pos | ) |
| void PhosphorRendering::ShaderNodeRhi::setParamPreamble | ( | const QString & | preamble | ) |
Set the generated named-parameter preamble that is spliced after the fragment shader's #version line at load time (via PhosphorShaders::spliceAfterVersion), so authors read a parameter by name instead of hand-decoding a customParams[N].xyzw lane.
The empty default is a no-op splice, so non-adopting shaders bake byte-identically to before. The preamble is folded into the bake-cache key, so two effects sharing a .frag but differing in generated defines never collide, and a metadata param edit (which changes the preamble without touching the .frag mtime) still invalidates the cache.
| void PhosphorRendering::ShaderNodeRhi::setResolution | ( | float | width, |
| float | height | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setShaderIncludePaths | ( | const QStringList & | paths | ) |
| void PhosphorRendering::ShaderNodeRhi::setSourceTextureProvider | ( | QSGTextureProvider * | provider | ) |
Live texture-provider override for user-texture slot 0 (SRB binding 7 / uTexture0).
When set, every SRB rebuild reads provider->texture()->rhiTexture() and binds that — superseding whatever QImage was uploaded via setUserTexture(0, ...). The provider must outlive the node OR be cleared with nullptr before destruction; we hold a QPointer so a torn-down provider nulls out cleanly. Designed for QQuickItem::textureProvider() returns from layer-enabled items, where the underlying QSGTexture identity changes when the FBO is recreated (resize, device-loss); the SRB-rebuild detection in prepare() notices that change and refreshes the binding without the consumer having to re-call this setter every frame.
| void PhosphorRendering::ShaderNodeRhi::setSurfaceFocused | ( | bool | focused | ) |
| void PhosphorRendering::ShaderNodeRhi::setSurfaceFrameSize | ( | float | width, |
| float | height | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setSurfaceFrameTopLeft | ( | float | x, |
| float | y | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setSurfaceOpacity | ( | float | opacity | ) |
Per-surface inputs consumed by a SurfaceUniformProfile.
These feed the surface-only fields of UboFrameState that a surface UBO profile reads (opacity, logical→device scale, focus, surface/frame geometry in device px). A BaseUniformProfile ignores them, so the overlay/animation path is unaffected — the members default to the same values as UboFrameState. A border or rounded-corner pack needs the real surface/frame geometry to know where its edges are, so the host (SurfaceShaderItem) must push these each frame from updatePaintNode.
| void PhosphorRendering::ShaderNodeRhi::setSurfaceScale | ( | float | scale | ) |
| void PhosphorRendering::ShaderNodeRhi::setSurfaceSize | ( | float | width, |
| float | height | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setTime | ( | double | time | ) |
| void PhosphorRendering::ShaderNodeRhi::setTimeDelta | ( | float | delta | ) |
| void PhosphorRendering::ShaderNodeRhi::setUniformExtension | ( | std::shared_ptr< PhosphorShaders::IUniformExtension > | extension | ) |
| void PhosphorRendering::ShaderNodeRhi::setUseDepthBuffer | ( | bool | use | ) |
| void PhosphorRendering::ShaderNodeRhi::setUserTexture | ( | int | slot, |
| const QImage & | image | ||
| ) |
| void PhosphorRendering::ShaderNodeRhi::setUserTextureWrap | ( | int | slot, |
| const QString & | wrap | ||
| ) |
Set per-slot sampler address mode.
Accepts "clamp", "repeat", or "mirror" (case-sensitive); other values fall back to "clamp" via normalizeWrapMode.
| void PhosphorRendering::ShaderNodeRhi::setUseWallpaper | ( | bool | use | ) |
| void PhosphorRendering::ShaderNodeRhi::setVertexShaderSource | ( | const QString & | source | ) |
| void PhosphorRendering::ShaderNodeRhi::setWallpaperTexture | ( | const QImage & | image | ) |
| QString PhosphorRendering::ShaderNodeRhi::shaderError | ( | ) | const |
|
inline |
|
inline |
Access the currently-installed uniform extension (may be nullptr).
|
static |
Map a normalized wrap-mode string to QRhiSampler::AddressMode.
Single source of truth for sampler address-mode selection so callers can't drift on the "mirror" / "repeat" / "clamp" mapping.