diff --git a/CMakeLists.txt b/CMakeLists.txt
index 322ee607ae84..998fad49f87a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2744,6 +2744,10 @@ if(QML)
target_link_libraries(mixxx-qml-lib PUBLIC mixxx-proto)
target_link_libraries(mixxx-qml-libplugin PUBLIC mixxx-proto)
+ # Rendergraph
+ add_subdirectory(src/rendergraph/scenegraph)
+ target_link_libraries(mixxx-qml-lib PUBLIC rendergraph_sg)
+
target_precompile_headers(mixxx-qml-lib PUBLIC
${MIXXX_COMMON_PRECOMPILED_HEADER}
)
@@ -2764,6 +2768,7 @@ if(QML)
res/qml/Mixxx/Controls/WaveformOverviewHotcueMarker.qml
res/qml/Mixxx/Controls/WaveformOverviewMarker.qml
res/qml/Mixxx/Controls/WaveformOverview.qml
+ res/qml/Mixxx/Controls/WaveformDisplay.qml
)
target_link_libraries(mixxx-qml-lib PRIVATE mixxx-qml-mixxxcontrolsplugin)
@@ -2786,6 +2791,8 @@ if(QML)
src/qml/qmlvisibleeffectsmodel.cpp
src/qml/qmlchainpresetmodel.cpp
src/qml/qmlwaveformoverview.cpp
+ src/qml/qmlwaveformdisplay.cpp
+ src/waveform/renderers/waveformdisplayrange.cpp
# The following sources need to be in this target to get QML_ELEMENT properly interpreted
src/control/controlmodel.cpp
src/control/controlsortfiltermodel.cpp
@@ -3804,6 +3811,11 @@ if(VINYLCONTROL)
target_link_libraries(mixxx-lib PRIVATE mixxx-xwax)
endif()
+# rendergraph
+add_subdirectory(src/rendergraph/opengl)
+add_subdirectory(res/shaders/rendergraph)
+target_link_libraries(mixxx-lib PUBLIC rendergraph_gl)
+
# WavPack audio file support
find_package(wavpack)
default_option(WAVPACK "WavPack audio file support" "wavpack_FOUND")
diff --git a/res/mixxx.qrc b/res/mixxx.qrc
index 8011fe39f943..c21ec6a8008b 100644
--- a/res/mixxx.qrc
+++ b/res/mixxx.qrc
@@ -96,5 +96,9 @@
shaders/passthrough.vert
shaders/rgbsignal.frag
shaders/stackedsignal.frag
+ shaders/rendergraph/endoftrack.frag.gl
+ shaders/rendergraph/endoftrack.vert.gl
+ shaders/rendergraph/texture.frag.gl
+ shaders/rendergraph/texture.vert.gl
diff --git a/res/qml/Mixxx/Controls/WaveformDisplay.qml b/res/qml/Mixxx/Controls/WaveformDisplay.qml
new file mode 100644
index 000000000000..3ed11593cadd
--- /dev/null
+++ b/res/qml/Mixxx/Controls/WaveformDisplay.qml
@@ -0,0 +1,7 @@
+import Mixxx 1.0 as Mixxx
+
+Mixxx.WaveformDisplay {
+ id: root
+
+ player: Mixxx.PlayerManager.getPlayer(root.group)
+}
diff --git a/res/qml/WaveformDisplay.qml b/res/qml/WaveformDisplay.qml
new file mode 100644
index 000000000000..2dbcd17d74d7
--- /dev/null
+++ b/res/qml/WaveformDisplay.qml
@@ -0,0 +1,16 @@
+import "." as Skin
+import Mixxx 1.0 as Mixxx
+import Mixxx.Controls 1.0 as MixxxControls
+import QtQuick 2.12
+import "Theme"
+
+Item {
+ id: root
+
+ required property string group
+
+ MixxxControls.WaveformDisplay {
+ anchors.fill: parent
+ group: root.group
+ }
+}
diff --git a/res/shaders/rendergraph/CMakeLists.txt b/res/shaders/rendergraph/CMakeLists.txt
new file mode 100644
index 000000000000..3241c5a84c96
--- /dev/null
+++ b/res/shaders/rendergraph/CMakeLists.txt
@@ -0,0 +1,33 @@
+set(shaders
+ endoftrack.frag
+ endoftrack.vert
+ pattern.frag
+ pattern.vert
+ rgb.frag
+ rgb.vert
+ rgba.frag
+ rgba.vert
+ texture.frag
+ texture.vert
+ unicolor.frag
+ unicolor.vert
+)
+
+qt6_add_shaders(rendergraph_sg "shaders-sg"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /shaders/rendergraph
+ FILES
+ ${shaders}
+)
+
+include(generated_shaders_gl.cmake)
+
+qt_add_resources(rendergraph_gl "shaders-gl"
+ PREFIX
+ /shaders/rendergraph
+ FILES
+ ${generated_shaders_gl}
+)
diff --git a/res/shaders/rendergraph/README b/res/shaders/rendergraph/README
new file mode 100644
index 000000000000..04bb5086e24a
--- /dev/null
+++ b/res/shaders/rendergraph/README
@@ -0,0 +1,9 @@
+Generate the GLSL shaders from the spirv shaders by running
+
+generate_shaders_gl.pl
+
+(Make sure qsb and spirv commands are in your path. E.g:
+export PATH=$PATH:~/VulkanSDK/1.3.283.0/macOS/bin:~/Qt/6.7.2/macos/bin
+)
+
+Since Qt 6.6 we should be able to access this programmatically with QShader, but for now I do it manually
diff --git a/res/shaders/rendergraph/endoftrack.frag b/res/shaders/rendergraph/endoftrack.frag
new file mode 100644
index 000000000000..0f6712e87d5d
--- /dev/null
+++ b/res/shaders/rendergraph/endoftrack.frag
@@ -0,0 +1,17 @@
+#version 440
+
+layout(location = 0) in float vGradient;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ vec4 color;
+}
+ubuf;
+
+void main() {
+ float minAlpha = 0.5 * ubuf.color.w;
+ float maxAlpha = 0.83 * ubuf.color.w;
+ float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient));
+ // premultiple alpha
+ fragColor = vec4(ubuf.color.xyz * alpha, alpha);
+}
diff --git a/res/shaders/rendergraph/endoftrack.frag.gl b/res/shaders/rendergraph/endoftrack.frag.gl
new file mode 100644
index 000000000000..78de913751a3
--- /dev/null
+++ b/res/shaders/rendergraph/endoftrack.frag.gl
@@ -0,0 +1,19 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ vec4 color;
+};
+
+uniform buf ubuf;
+
+varying float vGradient;
+
+void main()
+{
+ float minAlpha = 0.5 * ubuf.color.w;
+ float maxAlpha = 0.829999983310699462890625 * ubuf.color.w;
+ float alpha = mix(minAlpha, maxAlpha, max(0.0, vGradient));
+ gl_FragData[0] = vec4(ubuf.color.xyz * alpha, alpha);
+}
diff --git a/res/shaders/rendergraph/endoftrack.vert b/res/shaders/rendergraph/endoftrack.vert
new file mode 100644
index 000000000000..101cdb324e4e
--- /dev/null
+++ b/res/shaders/rendergraph/endoftrack.vert
@@ -0,0 +1,10 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in float gradient;
+layout(location = 0) out float vGradient;
+
+void main() {
+ vGradient = gradient;
+ gl_Position = position;
+}
diff --git a/res/shaders/rendergraph/endoftrack.vert.gl b/res/shaders/rendergraph/endoftrack.vert.gl
new file mode 100644
index 000000000000..da4626bbf7c5
--- /dev/null
+++ b/res/shaders/rendergraph/endoftrack.vert.gl
@@ -0,0 +1,12 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+varying float vGradient;
+attribute float gradient;
+attribute vec4 position;
+
+void main()
+{
+ vGradient = gradient;
+ gl_Position = position;
+}
diff --git a/res/shaders/rendergraph/generate_shaders_gl.pl b/res/shaders/rendergraph/generate_shaders_gl.pl
new file mode 100755
index 000000000000..c528eb947475
--- /dev/null
+++ b/res/shaders/rendergraph/generate_shaders_gl.pl
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+my @files = (glob("*.vert"),glob("*.frag"));
+
+open(GENERATED,">generated_shaders_gl.cmake");
+print(GENERATED "set(generated_shaders_gl\n");
+for $file (@files)
+{
+ system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb");
+ open(INFILE,"qsb --dump /tmp/$$-$file.qsb|");
+ open(OUTFILE,">$file.gl");
+ $ok = 0;
+ $comment_added = 0;
+ print "Generating $file.gl from $file\n";
+ while ()
+ {
+ if ($in_shader_block == 2)
+ {
+ if (m/^\*\*/)
+ {
+ $in_shader_block = 0;
+ $ok = 1;
+ }
+ else
+ {
+ if (!$comment_added)
+ {
+ if (!m/^#/)
+ {
+ print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n");
+ $comment_added = 1;
+ }
+ }
+ print OUTFILE "$_";
+ }
+ }
+ elsif ($in_shader_block == 1)
+ {
+ chomp($_);
+ if ($_ eq "Contents:")
+ {
+ $in_shader_block = 2;
+ }
+ }
+ else
+ {
+ chomp($_);
+ if ($_ eq "Shader 1: GLSL 120 [Standard]")
+ {
+ $in_shader_block = 1;
+ }
+ }
+ }
+ close INFILE;
+ close OUTFILE;
+ if($ok)
+ {
+ print(GENERATED " $file.gl\n");
+ }
+ else
+ {
+ print STDERR "Failed to generated $file.gl";
+ unlink("$file.gl")
+ }
+ unlink("/tmp/$$-$file.qsb");
+}
+print(GENERATED ")\n");
+close GENERATED;
diff --git a/res/shaders/rendergraph/generated_shaders_gl.cmake b/res/shaders/rendergraph/generated_shaders_gl.cmake
new file mode 100644
index 000000000000..951c85c3669a
--- /dev/null
+++ b/res/shaders/rendergraph/generated_shaders_gl.cmake
@@ -0,0 +1,14 @@
+set(generated_shaders_gl
+ endoftrack.vert.gl
+ pattern.vert.gl
+ rgb.vert.gl
+ rgba.vert.gl
+ texture.vert.gl
+ unicolor.vert.gl
+ endoftrack.frag.gl
+ pattern.frag.gl
+ rgb.frag.gl
+ rgba.frag.gl
+ texture.frag.gl
+ unicolor.frag.gl
+)
diff --git a/res/shaders/rendergraph/pattern.frag b/res/shaders/rendergraph/pattern.frag
new file mode 100644
index 000000000000..5aa3d1556b1e
--- /dev/null
+++ b/res/shaders/rendergraph/pattern.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(binding = 1) uniform sampler2D texture1;
+layout(location = 0) in vec2 vTexcoord;
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = texture(texture1, fract(vTexcoord));
+}
diff --git a/res/shaders/rendergraph/pattern.frag.gl b/res/shaders/rendergraph/pattern.frag.gl
new file mode 100644
index 000000000000..376c71668ba4
--- /dev/null
+++ b/res/shaders/rendergraph/pattern.frag.gl
@@ -0,0 +1,11 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+uniform sampler2D texture1;
+
+varying vec2 vTexcoord;
+
+void main()
+{
+ gl_FragData[0] = texture2D(texture1, fract(vTexcoord));
+}
diff --git a/res/shaders/rendergraph/pattern.vert b/res/shaders/rendergraph/pattern.vert
new file mode 100644
index 000000000000..07b3d7f1f3ba
--- /dev/null
+++ b/res/shaders/rendergraph/pattern.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+}
+ubuf;
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+layout(location = 0) out vec2 vTexcoord;
+
+void main() {
+ vTexcoord = texcoord;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/pattern.vert.gl b/res/shaders/rendergraph/pattern.vert.gl
new file mode 100644
index 000000000000..a3d58014be32
--- /dev/null
+++ b/res/shaders/rendergraph/pattern.vert.gl
@@ -0,0 +1,19 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+};
+
+uniform buf ubuf;
+
+varying vec2 vTexcoord;
+attribute vec2 texcoord;
+attribute vec4 position;
+
+void main()
+{
+ vTexcoord = texcoord;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/rgb.frag b/res/shaders/rendergraph/rgb.frag
new file mode 100644
index 000000000000..0a808489f5b2
--- /dev/null
+++ b/res/shaders/rendergraph/rgb.frag
@@ -0,0 +1,8 @@
+#version 440
+
+layout(location = 0) in vec3 vColor;
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = vec4(vColor, 1.0);
+}
diff --git a/res/shaders/rendergraph/rgb.frag.gl b/res/shaders/rendergraph/rgb.frag.gl
new file mode 100644
index 000000000000..b8a61f8682f9
--- /dev/null
+++ b/res/shaders/rendergraph/rgb.frag.gl
@@ -0,0 +1,9 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+varying vec3 vColor;
+
+void main()
+{
+ gl_FragData[0] = vec4(vColor, 1.0);
+}
diff --git a/res/shaders/rendergraph/rgb.vert b/res/shaders/rendergraph/rgb.vert
new file mode 100644
index 000000000000..6568d01f187c
--- /dev/null
+++ b/res/shaders/rendergraph/rgb.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+}
+ubuf;
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+layout(location = 0) out vec3 vColor;
+
+void main() {
+ vColor = color;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/rgb.vert.gl b/res/shaders/rendergraph/rgb.vert.gl
new file mode 100644
index 000000000000..53e86e4501cc
--- /dev/null
+++ b/res/shaders/rendergraph/rgb.vert.gl
@@ -0,0 +1,19 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+};
+
+uniform buf ubuf;
+
+varying vec3 vColor;
+attribute vec3 color;
+attribute vec4 position;
+
+void main()
+{
+ vColor = color;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/rgba.frag b/res/shaders/rendergraph/rgba.frag
new file mode 100644
index 000000000000..5cf90a770eae
--- /dev/null
+++ b/res/shaders/rendergraph/rgba.frag
@@ -0,0 +1,8 @@
+#version 440
+
+layout(location = 0) in vec4 vColor;
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = vec4(vColor.xyz * vColor.w, vColor.w); // premultiple alpha
+}
diff --git a/res/shaders/rendergraph/rgba.frag.gl b/res/shaders/rendergraph/rgba.frag.gl
new file mode 100644
index 000000000000..a831457b9681
--- /dev/null
+++ b/res/shaders/rendergraph/rgba.frag.gl
@@ -0,0 +1,9 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+varying vec4 vColor;
+
+void main()
+{
+ gl_FragData[0] = vec4(vColor.xyz * vColor.w, vColor.w);
+}
diff --git a/res/shaders/rendergraph/rgba.vert b/res/shaders/rendergraph/rgba.vert
new file mode 100644
index 000000000000..d5ce8b2d9db6
--- /dev/null
+++ b/res/shaders/rendergraph/rgba.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+}
+ubuf;
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 color;
+layout(location = 0) out vec4 vColor;
+
+void main() {
+ vColor = color;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/rgba.vert.gl b/res/shaders/rendergraph/rgba.vert.gl
new file mode 100644
index 000000000000..df2bcf93236e
--- /dev/null
+++ b/res/shaders/rendergraph/rgba.vert.gl
@@ -0,0 +1,19 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+};
+
+uniform buf ubuf;
+
+varying vec4 vColor;
+attribute vec4 color;
+attribute vec4 position;
+
+void main()
+{
+ vColor = color;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/texture.frag b/res/shaders/rendergraph/texture.frag
new file mode 100644
index 000000000000..bbe37bccd69c
--- /dev/null
+++ b/res/shaders/rendergraph/texture.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(binding = 1) uniform sampler2D texture1;
+layout(location = 0) in vec2 vTexcoord;
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = texture(texture1, vTexcoord);
+}
diff --git a/res/shaders/rendergraph/texture.frag.gl b/res/shaders/rendergraph/texture.frag.gl
new file mode 100644
index 000000000000..b2d03f1352c6
--- /dev/null
+++ b/res/shaders/rendergraph/texture.frag.gl
@@ -0,0 +1,11 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+uniform sampler2D texture1;
+
+varying vec2 vTexcoord;
+
+void main()
+{
+ gl_FragData[0] = texture2D(texture1, vTexcoord);
+}
diff --git a/res/shaders/rendergraph/texture.vert b/res/shaders/rendergraph/texture.vert
new file mode 100644
index 000000000000..07b3d7f1f3ba
--- /dev/null
+++ b/res/shaders/rendergraph/texture.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+}
+ubuf;
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+layout(location = 0) out vec2 vTexcoord;
+
+void main() {
+ vTexcoord = texcoord;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/texture.vert.gl b/res/shaders/rendergraph/texture.vert.gl
new file mode 100644
index 000000000000..a3d58014be32
--- /dev/null
+++ b/res/shaders/rendergraph/texture.vert.gl
@@ -0,0 +1,19 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+};
+
+uniform buf ubuf;
+
+varying vec2 vTexcoord;
+attribute vec2 texcoord;
+attribute vec4 position;
+
+void main()
+{
+ vTexcoord = texcoord;
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/texture.vert.qsb b/res/shaders/rendergraph/texture.vert.qsb
new file mode 100644
index 000000000000..84fa7e916fe4
Binary files /dev/null and b/res/shaders/rendergraph/texture.vert.qsb differ
diff --git a/res/shaders/rendergraph/unicolor.frag b/res/shaders/rendergraph/unicolor.frag
new file mode 100644
index 000000000000..e4a8535e3492
--- /dev/null
+++ b/res/shaders/rendergraph/unicolor.frag
@@ -0,0 +1,13 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+}
+ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main() {
+ fragColor = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); // premultiple alpha
+}
diff --git a/res/shaders/rendergraph/unicolor.frag.gl b/res/shaders/rendergraph/unicolor.frag.gl
new file mode 100644
index 000000000000..bfef503120bd
--- /dev/null
+++ b/res/shaders/rendergraph/unicolor.frag.gl
@@ -0,0 +1,15 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+ vec4 color;
+};
+
+uniform buf ubuf;
+
+void main()
+{
+ gl_FragData[0] = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w);
+}
diff --git a/res/shaders/rendergraph/unicolor.vert b/res/shaders/rendergraph/unicolor.vert
new file mode 100644
index 000000000000..9e268c18fbaa
--- /dev/null
+++ b/res/shaders/rendergraph/unicolor.vert
@@ -0,0 +1,13 @@
+#version 440
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+}
+ubuf;
+
+layout(location = 0) in vec4 position;
+
+void main() {
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/res/shaders/rendergraph/unicolor.vert.gl b/res/shaders/rendergraph/unicolor.vert.gl
new file mode 100644
index 000000000000..d126f3dab584
--- /dev/null
+++ b/res/shaders/rendergraph/unicolor.vert.gl
@@ -0,0 +1,17 @@
+#version 120
+//// GENERATED - EDITS WILL BE OVERWRITTEN
+
+struct buf
+{
+ mat4 matrix;
+ vec4 color;
+};
+
+uniform buf ubuf;
+
+attribute vec4 position;
+
+void main()
+{
+ gl_Position = ubuf.matrix * position;
+}
diff --git a/src/qml/qmlplayermanagerproxy.cpp b/src/qml/qmlplayermanagerproxy.cpp
index d8c3fc4930e5..2544b34f3ad5 100644
--- a/src/qml/qmlplayermanagerproxy.cpp
+++ b/src/qml/qmlplayermanagerproxy.cpp
@@ -14,7 +14,7 @@ QmlPlayerManagerProxy::QmlPlayerManagerProxy(
: QObject(parent), m_pPlayerManager(pPlayerManager) {
}
-QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) {
+QmlPlayerProxy* QmlPlayerManagerProxy::getPlayer(const QString& group) {
BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(group);
if (!pPlayer) {
qWarning() << "PlayerManagerProxy failed to find player for group" << group;
diff --git a/src/qml/qmlplayermanagerproxy.h b/src/qml/qmlplayermanagerproxy.h
index e0226da802f6..004123e0bd05 100644
--- a/src/qml/qmlplayermanagerproxy.h
+++ b/src/qml/qmlplayermanagerproxy.h
@@ -18,7 +18,7 @@ class QmlPlayerManagerProxy : public QObject {
std::shared_ptr pPlayerManager,
QObject* parent = nullptr);
- Q_INVOKABLE QObject* getPlayer(const QString& deck);
+ Q_INVOKABLE QmlPlayerProxy* getPlayer(const QString& deck);
Q_INVOKABLE void loadLocationIntoNextAvailableDeck(const QString& location, bool play = false);
Q_INVOKABLE void loadLocationUrlIntoNextAvailableDeck(
const QUrl& locationUrl, bool play = false);
diff --git a/src/qml/qmlwaveformdisplay.cpp b/src/qml/qmlwaveformdisplay.cpp
new file mode 100644
index 000000000000..7bca2d3f88c2
--- /dev/null
+++ b/src/qml/qmlwaveformdisplay.cpp
@@ -0,0 +1,263 @@
+#include "qml/qmlwaveformdisplay.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "mixer/basetrackplayer.h"
+#include "moc_qmlwaveformdisplay.cpp"
+#include "qml/qmlplayerproxy.h"
+#include "rendergraph/shadercache.h"
+#include "waveform/renderers/allshader/waveformrenderbackground.h"
+#include "waveform/renderers/allshader/waveformrenderbeat.h"
+#include "waveform/renderers/allshader/waveformrendererendoftrack.h"
+#include "waveform/renderers/allshader/waveformrendererfiltered.h"
+#include "waveform/renderers/allshader/waveformrendererhsv.h"
+#include "waveform/renderers/allshader/waveformrendererpreroll.h"
+#include "waveform/renderers/allshader/waveformrendererrgb.h"
+#include "waveform/renderers/allshader/waveformrenderersimple.h"
+#include "waveform/renderers/allshader/waveformrendererslipmode.h"
+#include "waveform/renderers/allshader/waveformrendererstem.h"
+#include "waveform/renderers/allshader/waveformrenderertextured.h"
+#include "waveform/renderers/allshader/waveformrendermark.h"
+#include "waveform/renderers/allshader/waveformrendermarkrange.h"
+
+namespace {
+void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) {
+ pNode->appendChildNode(std::unique_ptr(pChild));
+}
+void appendChildTo(std::unique_ptr& pNode, rendergraph::Node* pChild) {
+ pNode->appendChildNode(std::unique_ptr(pChild));
+}
+} // namespace
+
+namespace mixxx {
+namespace qml {
+
+QmlWaveformDisplay::QmlWaveformDisplay(QQuickItem* parent)
+ : QQuickItem(parent),
+ m_pPlayer(nullptr) {
+
+ setFlag(QQuickItem::ItemHasContents, true);
+
+ connect(this, &QmlWaveformDisplay::windowChanged, this, &QmlWaveformDisplay::slotWindowChanged);
+
+ auto pTopNode = std::make_unique();
+ auto pOpacityNode = std::make_unique();
+
+ appendChildTo(pTopNode, addRenderer());
+ appendChildTo(pOpacityNode, addRenderer());
+ appendChildTo(pOpacityNode, addRenderer());
+ m_pWaveformRenderMarkRange = addRenderer();
+ appendChildTo(pOpacityNode, m_pWaveformRenderMarkRange);
+
+#ifdef __STEM__
+ // The following two renderers work in tandem: if the rendered waveform is
+ // for a stem track, WaveformRendererSignalBase will skip rendering and let
+ // WaveformRendererStem do the rendering, and vice-versa.
+ appendChildTo(pOpacityNode, addRenderer());
+#endif
+ allshader::WaveformRendererSignalBase* waveformSignalRenderer =
+ addWaveformSignalRenderer(
+ type, options, ::WaveformRendererAbstract::Play);
+ appendChildTo(pOpacityNode, waveformSignalRenderer);
+
+ appendChildTo(pOpacityNode, addRenderer());
+ m_pWaveformRenderMark = addRenderer();
+ appendChildTo(pOpacityNode, m_pWaveformRenderMark);
+
+ // if the signal renderer supports slip, we add it again, now for slip, together with the
+ // other slip renderers
+ if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) {
+ // The following renderer will add an overlay waveform if a slip is in progress
+ appendChildTo(pOpacityNode, addRenderer());
+ appendChildTo(pOpacityNode,
+ addRenderer(
+ ::WaveformRendererAbstract::Slip));
+#ifdef __STEM__
+ appendChildTo(pOpacityNode,
+ addRenderer(
+ ::WaveformRendererAbstract::Slip));
+#endif
+ appendChildTo(pOpacityNode,
+ addWaveformSignalRenderer(
+ type, options, ::WaveformRendererAbstract::Slip));
+ appendChildTo(pOpacityNode,
+ addRenderer(
+ ::WaveformRendererAbstract::Slip));
+ appendChildTo(pOpacityNode,
+ addRenderer(
+ ::WaveformRendererAbstract::Slip));
+ }
+
+ m_pOpacityNode = pOpacityNode.get();
+ pTopNode->appendChildNode(std::move(pOpacityNode));
+
+ m_pGraph = std::move(pTopNode);
+}
+
+QmlWaveformDisplay::~QmlWaveformDisplay() {
+ m_pGraph.reset();
+ rendergraph::ShaderCache::purge();
+}
+
+void QmlWaveformDisplay::slotWindowChanged(QQuickWindow* window) {
+ connect(window, &QQuickWindow::frameSwapped, this, &QmlWaveformDisplay::slotFrameSwapped);
+ m_timer.restart();
+}
+
+void QmlWaveformDisplay::slotFrameSwapped() {
+ m_timer.restart();
+}
+
+void QmlWaveformDisplay::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
+ m_geometryChanged = true;
+ update();
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+}
+
+QSGNode* QmlWaveformDisplay::updatePaintNode(QSGNode* old, QQuickItem::UpdatePaintNodeData*) {
+ QSGRectangleNode* clipNode;
+
+ if (!old) {
+ // clipNode = window()->createRectangleNode();
+ // clipNode->setColor(QColor(0, 0, 0, 255));
+ // clipNode->setRect(boundingRect());
+
+ // m_node = std::make_unique();
+ // m_node->appendChildNode(std::make_unique());
+ // m_node->appendChildNode(std::make_unique());
+ // m_node->appendChildNode(std::make_unique());
+
+ // {
+ // QImage img(":/example/images/test.png");
+ // auto context = rendergraph::createSgContext(window());
+ // static_cast(m_node->lastChild())
+ // ->setTexture(std::make_unique(
+ // *context, img));
+ // }
+
+ // clipNode->appendChildNode(rendergraph::sgNode(m_node.get()));
+ m_pOpacityNode->setOpacity(shouldOnlyDrawBackground() ? 0.f : 1.f);
+
+ m_pWaveformRenderMark->update();
+ m_pWaveformRenderMarkRange->update();
+
+ m_pGraph->preprocess();
+ m_pGraph->render();
+
+ old = clipNode;
+ } else {
+ clipNode = static_cast(old);
+ }
+
+ if (m_geometryChanged) {
+ clipNode->setRect(boundingRect());
+ m_geometryChanged = false;
+ }
+
+ return clipNode;
+}
+
+QmlPlayerProxy* QmlWaveformDisplay::getPlayer() const {
+ return m_pPlayer;
+}
+
+void QmlWaveformDisplay::setPlayer(QmlPlayerProxy* pPlayer) {
+ if (m_pPlayer == pPlayer) {
+ return;
+ }
+
+ if (m_pPlayer != nullptr) {
+ m_pPlayer->internalTrackPlayer()->disconnect(this);
+ }
+
+ m_pPlayer = pPlayer;
+
+ if (m_pPlayer != nullptr) {
+ setCurrentTrack(m_pPlayer->internalTrackPlayer()->getLoadedTrack());
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::newTrackLoaded,
+ this,
+ &QmlWaveformDisplay::slotTrackLoaded);
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::loadingTrack,
+ this,
+ &QmlWaveformDisplay::slotTrackLoading);
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::playerEmpty,
+ this,
+ &QmlWaveformDisplay::slotTrackUnloaded);
+ }
+
+ emit playerChanged();
+ update();
+}
+
+void QmlWaveformDisplay::setGroup(const QString& group) {
+ if (m_group == group) {
+ return;
+ }
+
+ m_group = group;
+ emit groupChanged(group);
+
+ // TODO m0dB unique_ptr ?
+ delete m_pWaveformDisplayRange;
+ m_pWaveformDisplayRange = new WaveformDisplayRange(m_group);
+ m_pWaveformDisplayRange->init();
+}
+
+const QString& QmlWaveformDisplay::getGroup() const {
+ return m_group;
+}
+
+void QmlWaveformDisplay::slotTrackLoaded(TrackPointer pTrack) {
+ // TODO: Investigate if it's a bug that this debug assertion fails when
+ // passing tracks on the command line
+ // DEBUG_ASSERT(m_pCurrentTrack == pTrack);
+ setCurrentTrack(pTrack);
+}
+
+void QmlWaveformDisplay::slotTrackLoading(TrackPointer pNewTrack, TrackPointer pOldTrack) {
+ Q_UNUSED(pOldTrack); // only used in DEBUG_ASSERT
+ DEBUG_ASSERT(m_pCurrentTrack == pOldTrack);
+ setCurrentTrack(pNewTrack);
+}
+
+void QmlWaveformDisplay::slotTrackUnloaded() {
+ setCurrentTrack(nullptr);
+}
+
+void QmlWaveformDisplay::setCurrentTrack(TrackPointer pTrack) {
+ // TODO: Check if this is actually possible
+ if (m_pCurrentTrack == pTrack) {
+ return;
+ }
+
+ if (m_pCurrentTrack != nullptr) {
+ disconnect(m_pCurrentTrack.get(), nullptr, this, nullptr);
+ }
+
+ m_pCurrentTrack = pTrack;
+ if (pTrack != nullptr) {
+ connect(pTrack.get(),
+ &Track::waveformSummaryUpdated,
+ this,
+ &QmlWaveformDisplay::slotWaveformUpdated);
+ }
+ slotWaveformUpdated();
+
+ if (m_pWaveformDisplayRange) {
+ m_pWaveformDisplayRange->setTrack(m_pCurrentTrack);
+ }
+}
+
+void QmlWaveformDisplay::slotWaveformUpdated() {
+ update();
+}
+
+} // namespace qml
+} // namespace mixxx
diff --git a/src/qml/qmlwaveformdisplay.h b/src/qml/qmlwaveformdisplay.h
new file mode 100644
index 000000000000..d0689edcfd37
--- /dev/null
+++ b/src/qml/qmlwaveformdisplay.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include "qml/qmlplayerproxy.h"
+#include "track/track.h"
+#include "util/performancetimer.h"
+#include "waveform/isynctimeprovider.h"
+
+// #include "rendergraph/opengl/graph.h"
+#include "rendergraph/opacitynode.h"
+
+class WaveformDisplayRange;
+
+namespace allshader {
+class WaveformRenderMark;
+class WaveformRenderMarkRange;
+} // namespace allshader
+
+namespace mixxx {
+namespace qml {
+
+class QmlPlayerProxy;
+
+class QmlWaveformDisplay : public QQuickItem, ISyncTimeProvider {
+ Q_OBJECT
+ Q_PROPERTY(QmlPlayerProxy* player READ getPlayer WRITE setPlayer
+ NOTIFY playerChanged REQUIRED)
+ Q_PROPERTY(QString group READ getGroup WRITE setGroup NOTIFY groupChanged REQUIRED)
+ QML_NAMED_ELEMENT(WaveformDisplay)
+
+ public:
+ QmlWaveformDisplay(QQuickItem* parent = nullptr);
+ ~QmlWaveformDisplay() override;
+
+ void setPlayer(QmlPlayerProxy* player);
+ QmlPlayerProxy* getPlayer() const;
+
+ void setGroup(const QString& group);
+ const QString& getGroup() const;
+
+ QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override;
+ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
+
+ int fromTimerToNextSyncMicros(const PerformanceTimer& timer) override;
+ int getSyncIntervalTimeMicros() const override {
+ return m_syncIntervalTimeMicros;
+ }
+ private slots:
+ void slotTrackLoaded(TrackPointer pLoadedTrack);
+ void slotTrackLoading(TrackPointer pNewTrack, TrackPointer pOldTrack);
+ void slotTrackUnloaded();
+ void slotWaveformUpdated();
+
+ void slotFrameSwapped();
+ void slotWindowChanged(QQuickWindow* window);
+ signals:
+ void playerChanged();
+ void groupChanged(const QString& group);
+
+ private:
+ void setCurrentTrack(TrackPointer pTrack);
+
+ QPointer m_pPlayer;
+ TrackPointer m_pCurrentTrack;
+
+ PerformanceTimer m_timer;
+
+ QString m_group;
+ WaveformDisplayRange* m_pWaveformDisplayRange{};
+ int m_syncIntervalTimeMicros{1000000 / 60}; // TODO don't hardcode
+
+ std::unique_ptr m_pGraph;
+ rendergraph::OpacityNode* m_pOpacityNode;
+ allshader::WaveformRenderMark* m_pWaveformRenderMark;
+ allshader::WaveformRenderMarkRange* m_pWaveformRenderMarkRange;
+};
+
+} // namespace qml
+} // namespace mixxx
diff --git a/src/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt
new file mode 100644
index 000000000000..91bcb99beb5f
--- /dev/null
+++ b/src/rendergraph/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(opengl)
+add_subdirectory(scenegraph)
+add_subdirectory(../../res/shaders/rendergraph shaders)
diff --git a/src/rendergraph/README b/src/rendergraph/README
new file mode 100644
index 000000000000..8110ef888f8e
--- /dev/null
+++ b/src/rendergraph/README
@@ -0,0 +1,29 @@
+rendergraph is an abstraction layer that can be compiled to be used with QtQuick scene graph classes
+or with QOpenGL classes. This abstraction layer follows the design of the QtQuick scene graph, with
+classes such as Material, Geometry, Node and GeometryNode, but it only gives access to a subset of
+its functionality.
+
+The rendergraph/scenegraph implementation calls the underlying scenegraph classes directly or almost
+directly. The opengl/layer implementation class implements classes that mimic the behaviour of the
+scenegraph classes.
+
+The objective is to be able to write code that can be used both within the context of a QWidgets
+applications and of a QML application. This includes using the same Vulkan-style GLSL shader code for
+both. (The QSGMaterialShader uses the qsb files, QOpenGLShader uses the OpenGL shader code generated
+by qsb)
+
+The common code is in library rendergraph, src/rendergraph
+
+The scene graph implementation is in library rendergraph_sg, src/rendergraph/scenegraph
+
+The OpenGL implementation is in library rendergraph_gl, src/rendergraph/opengl
+
+Example shader and nodes are in library rendergraph_examples, in src/rendergraph/examples.
+Note that this code is agnostic of the layer that it will be linked against.
+
+Example applications are in src/rendergraph/example/sg_example and src/rendergraph/examples/gl_example,
+which each link with rendergraph_examples and respectively with rendergraph_gl and rendergraph_sg.
+
+This is still very much work in progress!
+
+m0dB
diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp
new file mode 100644
index 000000000000..fd9e5f73ec72
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp
@@ -0,0 +1,39 @@
+#include "endoftrackmaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+EndOfTrackMaterial::EndOfTrackMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& EndOfTrackMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "gradient"});
+ return set;
+}
+
+/* static */ const UniformSet& EndOfTrackMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.color"});
+ return set;
+}
+
+MaterialType* EndOfTrackMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int EndOfTrackMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr EndOfTrackMaterial::createShader() const {
+ return std::make_unique(
+ "endoftrack.vert", "endoftrack.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h
new file mode 100644
index 000000000000..e66a825c1641
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class EndOfTrackMaterial;
+}
+
+class rendergraph::EndOfTrackMaterial : public rendergraph::Material {
+ public:
+ EndOfTrackMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.cpp b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp
new file mode 100644
index 000000000000..e16ca4213d77
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp
@@ -0,0 +1,39 @@
+#include "patternmaterial.h"
+
+#include
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+
+using namespace rendergraph;
+
+PatternMaterial::PatternMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& PatternMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "texcoord"});
+ return set;
+}
+
+/* static */ const UniformSet& PatternMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* PatternMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int PatternMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr PatternMaterial::createShader() const {
+ return std::make_unique(
+ "pattern.vert", "pattern.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/patternmaterial.h b/src/rendergraph/common/rendergraph/material/patternmaterial.h
new file mode 100644
index 000000000000..0df75c073714
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/patternmaterial.h
@@ -0,0 +1,34 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+#include "rendergraph/texture.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class PatternMaterial;
+}
+
+class rendergraph::PatternMaterial : public rendergraph::Material {
+ public:
+ PatternMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+
+ Texture* texture(int /*binding*/) const override {
+ return m_pTexture.get();
+ }
+
+ void setTexture(std::unique_ptr texture) {
+ m_pTexture = std::move(texture);
+ }
+
+ private:
+ std::unique_ptr m_pTexture;
+};
diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp
new file mode 100644
index 000000000000..c1d53d3f8bcc
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp
@@ -0,0 +1,39 @@
+#include "rgbamaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+RGBAMaterial::RGBAMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& RGBAMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "color"});
+ return set;
+}
+
+/* static */ const UniformSet& RGBAMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* RGBAMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int RGBAMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr RGBAMaterial::createShader() const {
+ return std::make_unique(
+ "rgba.vert", "rgba.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/rgbamaterial.h b/src/rendergraph/common/rendergraph/material/rgbamaterial.h
new file mode 100644
index 000000000000..b5b72a949c90
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class RGBAMaterial;
+}
+
+class rendergraph::RGBAMaterial : public rendergraph::Material {
+ public:
+ RGBAMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp
new file mode 100644
index 000000000000..3a2f014d07e4
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp
@@ -0,0 +1,39 @@
+#include "rgbmaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+RGBMaterial::RGBMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& RGBMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "color"});
+ return set;
+}
+
+/* static */ const UniformSet& RGBMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* RGBMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int RGBMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr RGBMaterial::createShader() const {
+ return std::make_unique(
+ "rgb.vert", "rgb.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/rgbmaterial.h b/src/rendergraph/common/rendergraph/material/rgbmaterial.h
new file mode 100644
index 000000000000..6d4d74b25145
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class RGBMaterial;
+}
+
+class rendergraph::RGBMaterial : public rendergraph::Material {
+ public:
+ RGBMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.cpp b/src/rendergraph/common/rendergraph/material/texturematerial.cpp
new file mode 100644
index 000000000000..090e6070d988
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/texturematerial.cpp
@@ -0,0 +1,39 @@
+#include "texturematerial.h"
+
+#include
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+
+using namespace rendergraph;
+
+TextureMaterial::TextureMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& TextureMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "texcoord"});
+ return set;
+}
+
+/* static */ const UniformSet& TextureMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* TextureMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int TextureMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr TextureMaterial::createShader() const {
+ return std::make_unique(
+ "texture.vert", "texture.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/texturematerial.h b/src/rendergraph/common/rendergraph/material/texturematerial.h
new file mode 100644
index 000000000000..4e0ac39b9891
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/texturematerial.h
@@ -0,0 +1,34 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+#include "rendergraph/texture.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class TextureMaterial;
+}
+
+class rendergraph::TextureMaterial : public rendergraph::Material {
+ public:
+ TextureMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+
+ Texture* texture(int /*binding*/) const override {
+ return m_pTexture.get();
+ }
+
+ void setTexture(std::unique_ptr texture) {
+ m_pTexture = std::move(texture);
+ }
+
+ private:
+ std::unique_ptr m_pTexture;
+};
diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp
new file mode 100644
index 000000000000..8e4bf12ed9c9
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp
@@ -0,0 +1,39 @@
+#include "unicolormaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+UniColorMaterial::UniColorMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& UniColorMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position"});
+ return set;
+}
+
+/* static */ const UniformSet& UniColorMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix", "ubuf.color"});
+ return set;
+}
+
+MaterialType* UniColorMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int UniColorMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr UniColorMaterial::createShader() const {
+ return std::make_unique(
+ "unicolor.vert", "unicolor.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/common/rendergraph/material/unicolormaterial.h b/src/rendergraph/common/rendergraph/material/unicolormaterial.h
new file mode 100644
index 000000000000..9516bb8c731b
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class UniColorMaterial;
+}
+
+class rendergraph::UniColorMaterial : public rendergraph::Material {
+ public:
+ UniColorMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/examples/CMakeLists.txt b/src/rendergraph/examples/CMakeLists.txt
new file mode 100644
index 000000000000..61ba086297f6
--- /dev/null
+++ b/src/rendergraph/examples/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.16)
+project(rendergraph LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
+
+qt_standard_project_setup()
+
+add_subdirectory(../ rendergraph)
+add_subdirectory(gl_example)
+add_subdirectory(sg_example)
diff --git a/src/rendergraph/examples/README b/src/rendergraph/examples/README
new file mode 100644
index 000000000000..6168efc09c47
--- /dev/null
+++ b/src/rendergraph/examples/README
@@ -0,0 +1,6 @@
+Build examples with
+
+$ cd ../../../
+$ mkdir build-rendergraph_examples
+$ cd build-rendergraph_examples
+$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples
diff --git a/src/rendergraph/examples/common/examplenodes.cpp b/src/rendergraph/examples/common/examplenodes.cpp
new file mode 100644
index 000000000000..420bbae40cac
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenodes.cpp
@@ -0,0 +1,60 @@
+#include "examplenodes.h"
+
+#include
+#include
+#include
+
+#include "rendergraph/geometry.h"
+#include "rendergraph/material/endoftrackmaterial.h"
+#include "rendergraph/material/texturematerial.h"
+
+using namespace rendergraph;
+
+ExampleNode1::ExampleNode1() {
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, horizontalGradientArray, 4);
+
+ QColor color("red");
+ material().setUniform(0,
+ QVector4D{color.redF(),
+ color.greenF(),
+ color.blueF(),
+ color.alphaF()});
+}
+
+ExampleNode2::ExampleNode2() {
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, horizontalGradientArray, 4);
+
+ QColor color("blue");
+ material().setUniform(0,
+ QVector4D{color.redF(),
+ color.greenF(),
+ color.blueF(),
+ color.alphaF()});
+}
+
+ExampleNode3::ExampleNode3() {
+ auto* m = new TextureMaterial;
+
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(TextureMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, texcoordArray, 4);
+
+ QMatrix4x4 matrix;
+
+ matrix.scale(0.3);
+ material().setUniform(0, matrix);
+}
+
+void ExampleNode3::setTexture(std::unique_ptr texture) {
+ dynamic_cast(material()).setTexture(std::move(texture));
+}
diff --git a/src/rendergraph/examples/common/examplenodes.h b/src/rendergraph/examples/common/examplenodes.h
new file mode 100644
index 000000000000..1ff3d01fb443
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenodes.h
@@ -0,0 +1,36 @@
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/texture.h"
+
+namespace rendergraph {
+class ExampleNode1;
+class ExampleNode2;
+class ExampleNode3;
+} // namespace rendergraph
+
+class rendergraph::ExampleNode1 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f};
+ static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f};
+ static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f};
+
+ ExampleNode1();
+};
+
+class rendergraph::ExampleNode2 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f};
+ static constexpr float verticalGradientArray[] = {1.f, 1.f, 0.f, 0.f};
+ static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f};
+
+ ExampleNode2();
+};
+
+class rendergraph::ExampleNode3 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f};
+ static constexpr float texcoordArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f};
+
+ ExampleNode3();
+
+ void setTexture(std::unique_ptr texture);
+};
diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt
new file mode 100644
index 000000000000..6a055ea5560e
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/CMakeLists.txt
@@ -0,0 +1,40 @@
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
+
+qt_add_executable(gl_example
+ window.cpp window.h
+ main.cpp
+ ../common/examplenodes.cpp
+)
+
+set_target_properties(gl_example PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(gl_example PRIVATE
+ rendergraph_gl
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+target_include_directories(gl_example PRIVATE ../common)
+
+qt_add_resources(gl_example "gl_example"
+ PREFIX
+ /example
+ FILES
+ images/test.png
+)
+
+install(TARGETS gl_example
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_app_script(
+ TARGET gl_example
+ OUTPUT_SCRIPT deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/src/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/examples/gl_example/images/test.png
new file mode 100644
index 000000000000..f9580346eb8b
Binary files /dev/null and b/src/rendergraph/examples/gl_example/images/test.png differ
diff --git a/src/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/examples/gl_example/main.cpp
new file mode 100644
index 000000000000..7b62238b429b
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/main.cpp
@@ -0,0 +1,12 @@
+#include
+
+#include "window.h"
+
+int main(int argc, char* argv[]) {
+ QGuiApplication app(argc, argv);
+
+ Window window;
+ window.show();
+
+ return app.exec();
+}
diff --git a/src/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/examples/gl_example/window.cpp
new file mode 100644
index 000000000000..a74fc3afab39
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/window.cpp
@@ -0,0 +1,48 @@
+#include "window.h"
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+#include "rendergraph/graph.h"
+
+Window::Window() {
+}
+
+Window::~Window() = default;
+
+void Window::closeEvent(QCloseEvent*) {
+ // since this is the only and last window, we need to cleanup before destruction,
+ // because at destruction the context can't be used anymore
+ m_rendergraph.reset();
+}
+
+void Window::initializeGL() {
+ auto node = std::make_unique();
+ node->appendChildNode(std::make_unique());
+ node->appendChildNode(std::make_unique());
+ node->appendChildNode(std::make_unique());
+
+ {
+ QImage img(":/example/images/test.png");
+ rendergraph::Context context;
+ static_cast(node->lastChild())
+ ->setTexture(
+ std::make_unique(context, img));
+ }
+
+ m_rendergraph = std::make_unique(std::move(node));
+ m_rendergraph->initialize();
+}
+
+void Window::resizeGL(int, int) {
+}
+
+void Window::paintGL() {
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_BLEND);
+ // qt scene graph uses premultiplied alpha color in the shader,
+ // so we need to do the same
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ m_rendergraph->render();
+}
diff --git a/src/rendergraph/examples/gl_example/window.h b/src/rendergraph/examples/gl_example/window.h
new file mode 100644
index 000000000000..6d7d5f697498
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/window.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include
+#include
+
+namespace rendergraph {
+class Graph;
+}
+
+class Window : public QOpenGLWindow {
+ public:
+ Window();
+ ~Window();
+
+ void initializeGL() override;
+ void resizeGL(int w, int h) override;
+ void paintGL() override;
+
+ void closeEvent(QCloseEvent* ev) override;
+
+ private:
+ std::unique_ptr m_rendergraph;
+};
diff --git a/src/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/examples/sg_example/CMakeLists.txt
new file mode 100644
index 000000000000..378e062fcf22
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/CMakeLists.txt
@@ -0,0 +1,36 @@
+qt_add_executable(sg_example WIN32 MACOSX_BUNDLE
+ customitem.cpp
+ customitem.h
+ main.cpp
+ ../common/examplenodes.cpp
+)
+
+target_link_libraries(sg_example PRIVATE
+ rendergraph_sg
+)
+target_include_directories(sg_example PRIVATE ../common)
+
+qt_add_qml_module(sg_example
+ URI RenderGraph
+ QML_FILES
+ qml/main.qml
+ RESOURCES
+ images/test.png
+ RESOURCE_PREFIX /example
+ NO_RESOURCE_TARGET_PATH
+)
+
+install(TARGETS sg_example
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET sg_example
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/src/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/examples/sg_example/customitem.cpp
new file mode 100644
index 000000000000..bf87f780f179
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/customitem.cpp
@@ -0,0 +1,59 @@
+#include "customitem.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+#include "rendergraph/scenegraph.h"
+
+CustomItem::CustomItem(QQuickItem* parent)
+ : QQuickItem(parent) {
+ setFlag(ItemHasContents, true);
+}
+
+CustomItem::~CustomItem() = default;
+
+void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
+ m_geometryChanged = true;
+ update();
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+}
+
+QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) {
+ QSGRectangleNode* bgNode;
+ if (!node) {
+ bgNode = window()->createRectangleNode();
+ bgNode->setColor(QColor(0, 0, 0, 255));
+ bgNode->setRect(boundingRect());
+
+ m_node = std::make_unique();
+ m_node->appendChildNode(std::make_unique());
+ m_node->appendChildNode(std::make_unique());
+ m_node->appendChildNode(std::make_unique());
+
+ {
+ QImage img(":/example/images/test.png");
+ auto context = rendergraph::createSgContext(window());
+ static_cast(m_node->lastChild())
+ ->setTexture(std::make_unique(
+ *context, img));
+ }
+
+ bgNode->appendChildNode(rendergraph::sgNode(m_node.get()));
+
+ node = bgNode;
+ } else {
+ bgNode = static_cast(node);
+ }
+
+ if (m_geometryChanged) {
+ bgNode->setRect(boundingRect());
+ m_geometryChanged = false;
+ }
+
+ return node;
+}
diff --git a/src/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/examples/sg_example/customitem.h
new file mode 100644
index 000000000000..304fb5c8f5ec
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/customitem.h
@@ -0,0 +1,26 @@
+#ifndef CUSTOMITEM_H
+#define CUSTOMITEM_H
+
+#include
+
+namespace rendergraph {
+class Node;
+}
+
+class CustomItem : public QQuickItem {
+ Q_OBJECT
+ QML_ELEMENT
+
+ public:
+ explicit CustomItem(QQuickItem* parent = nullptr);
+ ~CustomItem();
+
+ protected:
+ QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override;
+ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
+
+ bool m_geometryChanged{};
+ std::unique_ptr m_node;
+};
+
+#endif // CUSTOMITEM_H
diff --git a/src/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/examples/sg_example/images/test.png
new file mode 100644
index 000000000000..f9580346eb8b
Binary files /dev/null and b/src/rendergraph/examples/sg_example/images/test.png differ
diff --git a/src/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/examples/sg_example/main.cpp
new file mode 100644
index 000000000000..ab2e5b34072d
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/main.cpp
@@ -0,0 +1,16 @@
+#include
+#include
+#include
+
+#include "customitem.h"
+
+int main(int argc, char** argv) {
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///example/qml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/src/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/examples/sg_example/qml/main.qml
new file mode 100644
index 000000000000..2410ad1a5604
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/qml/main.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import RenderGraph
+
+Item {
+ id: root
+
+ width: 640
+ height: 480
+
+ CustomItem {
+ anchors.fill: parent
+ }
+}
diff --git a/src/rendergraph/opengl/CMakeLists.txt b/src/rendergraph/opengl/CMakeLists.txt
new file mode 100644
index 000000000000..5b8c9f819698
--- /dev/null
+++ b/src/rendergraph/opengl/CMakeLists.txt
@@ -0,0 +1,54 @@
+add_library(rendergraph_gl
+ rendergraph/attribute.h
+ rendergraph/attributeset.cpp
+ rendergraph/attributeset.h
+ rendergraph/context.cpp
+ rendergraph/context.h
+ rendergraph/geometry.cpp
+ rendergraph/geometry.h
+ rendergraph/geometrynode.cpp
+ rendergraph/geometrynode.h
+ rendergraph/material.cpp
+ rendergraph/material.h
+ rendergraph/materialshader.cpp
+ rendergraph/materialshader.h
+ rendergraph/materialtype.cpp
+ rendergraph/materialtype.h
+ rendergraph/node.cpp
+ rendergraph/node.h
+ rendergraph/opacitynode.cpp
+ rendergraph/opacitynode.h
+ rendergraph/texture.cpp
+ rendergraph/texture.h
+ rendergraph/types.cpp
+ rendergraph/types.h
+ rendergraph/uniform.h
+ rendergraph/uniformscache.cpp
+ rendergraph/uniformscache.h
+ rendergraph/uniformset.cpp
+ rendergraph/uniformset.h
+ ../common/rendergraph/material/endoftrackmaterial.cpp
+ ../common/rendergraph/material/endoftrackmaterial.h
+ ../common/rendergraph/material/patternmaterial.cpp
+ ../common/rendergraph/material/patternmaterial.h
+ ../common/rendergraph/material/texturematerial.cpp
+ ../common/rendergraph/material/texturematerial.h
+ ../common/rendergraph/material/rgbamaterial.cpp
+ ../common/rendergraph/material/rgbamaterial.h
+ ../common/rendergraph/material/rgbmaterial.cpp
+ ../common/rendergraph/material/rgbmaterial.h
+ ../common/rendergraph/material/unicolormaterial.cpp
+ ../common/rendergraph/material/unicolormaterial.h
+ rendergraph/graph.cpp
+ rendergraph/graph.h
+ rendergraph/openglnode.cpp
+ rendergraph/openglnode.h
+)
+
+target_link_libraries(rendergraph_gl PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+
+target_include_directories(rendergraph_gl PUBLIC . ../common)
diff --git a/src/rendergraph/opengl/rendergraph/attribute.h b/src/rendergraph/opengl/rendergraph/attribute.h
new file mode 100644
index 000000000000..5fae9e3df544
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/attribute.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+
+#include "rendergraph/types.h"
+
+namespace rendergraph {
+struct Attribute;
+}
+
+struct rendergraph::Attribute {
+ const int m_tupleSize;
+ const PrimitiveType m_primitiveType;
+ const QString m_name;
+
+ Attribute(int tupleSize, PrimitiveType primitiveType)
+ : m_tupleSize{tupleSize},
+ m_primitiveType{primitiveType} {
+ }
+
+ Attribute(int tupleSize, PrimitiveType primitiveType, QString name)
+ : m_tupleSize{tupleSize},
+ m_primitiveType{primitiveType},
+ m_name{std::move(name)} {
+ }
+
+ template
+ static Attribute create() {
+ return Attribute(tupleSizeOf(), primitiveTypeOf());
+ }
+};
diff --git a/src/rendergraph/opengl/rendergraph/attributeset.cpp b/src/rendergraph/opengl/rendergraph/attributeset.cpp
new file mode 100644
index 000000000000..6ca9d378983d
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/attributeset.cpp
@@ -0,0 +1,23 @@
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+AttributeSet::AttributeSet() = default;
+
+AttributeSet::AttributeSet(std::initializer_list list, const std::vector& names)
+ : AttributeSet() {
+ int i = 0;
+ for (auto item : list) {
+ add(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]});
+ }
+}
+
+AttributeSet::~AttributeSet() = default;
+
+void AttributeSet::add(const Attribute& attribute) {
+ m_attributes.push_back(attribute);
+}
+
+const std::vector& AttributeSet::attributes() const {
+ return m_attributes;
+}
diff --git a/src/rendergraph/opengl/rendergraph/attributeset.h b/src/rendergraph/opengl/rendergraph/attributeset.h
new file mode 100644
index 000000000000..8d21921ee5e5
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/attributeset.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+
+#include "rendergraph/attribute.h"
+
+namespace rendergraph {
+class AttributeSet;
+}
+
+class rendergraph::AttributeSet {
+ public:
+ AttributeSet();
+ AttributeSet(std::initializer_list list, const std::vector& names);
+
+ ~AttributeSet();
+
+ const std::vector& attributes() const;
+
+ private:
+ void add(const Attribute& attribute);
+
+ std::vector m_attributes;
+};
+
+namespace rendergraph {
+template
+AttributeSet makeAttributeSet(const std::vector& names) {
+ return AttributeSet({(Attribute::create())...}, names);
+}
+} // namespace rendergraph
diff --git a/src/rendergraph/opengl/rendergraph/context.cpp b/src/rendergraph/opengl/rendergraph/context.cpp
new file mode 100644
index 000000000000..c795cd2b00b0
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/context.cpp
@@ -0,0 +1,6 @@
+#include "rendergraph/context.h"
+
+using namespace rendergraph;
+
+Context::Context() = default;
+Context::~Context() = default;
diff --git a/src/rendergraph/opengl/rendergraph/context.h b/src/rendergraph/opengl/rendergraph/context.h
new file mode 100644
index 000000000000..99e7f8e0ec73
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/context.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace rendergraph {
+class Context;
+}
+
+class rendergraph::Context {
+ public:
+ Context();
+ ~Context();
+};
diff --git a/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt
new file mode 100644
index 000000000000..bd918d2f9ad3
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.16)
+project(rendergraph LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
+
+qt_standard_project_setup()
+
+add_subdirectory(../ rendergraph)
+add_subdirectory(gl_example)
+add_subdirectory(sg_example)
+add_subdirectory(common)
diff --git a/src/rendergraph/opengl/rendergraph/examples/README b/src/rendergraph/opengl/rendergraph/examples/README
new file mode 100644
index 000000000000..6168efc09c47
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/README
@@ -0,0 +1,6 @@
+Build examples with
+
+$ cd ../../../
+$ mkdir build-rendergraph_examples
+$ cd build-rendergraph_examples
+$ cmake -DCMAKE_PREFIX_PATH=~/Qt/6.7.2/macos/ ../src/rendergraph/examples
diff --git a/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt
new file mode 100644
index 000000000000..e7ee6c0160e1
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/common/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library(rendergraph_examples
+ examplenodes.cpp
+ examplenodes.h
+)
+
+target_link_libraries(rendergraph_examples PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+
+target_include_directories(rendergraph_examples PUBLIC ../../../ .)
diff --git a/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp
new file mode 100644
index 000000000000..420bbae40cac
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.cpp
@@ -0,0 +1,60 @@
+#include "examplenodes.h"
+
+#include
+#include
+#include
+
+#include "rendergraph/geometry.h"
+#include "rendergraph/material/endoftrackmaterial.h"
+#include "rendergraph/material/texturematerial.h"
+
+using namespace rendergraph;
+
+ExampleNode1::ExampleNode1() {
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, horizontalGradientArray, 4);
+
+ QColor color("red");
+ material().setUniform(0,
+ QVector4D{color.redF(),
+ color.greenF(),
+ color.blueF(),
+ color.alphaF()});
+}
+
+ExampleNode2::ExampleNode2() {
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(EndOfTrackMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, horizontalGradientArray, 4);
+
+ QColor color("blue");
+ material().setUniform(0,
+ QVector4D{color.redF(),
+ color.greenF(),
+ color.blueF(),
+ color.alphaF()});
+}
+
+ExampleNode3::ExampleNode3() {
+ auto* m = new TextureMaterial;
+
+ setMaterial(std::make_unique());
+ setGeometry(std::make_unique(TextureMaterial::attributes(), 4));
+
+ geometry().setAttributeValues(0, positionArray, 4);
+ geometry().setAttributeValues(1, texcoordArray, 4);
+
+ QMatrix4x4 matrix;
+
+ matrix.scale(0.3);
+ material().setUniform(0, matrix);
+}
+
+void ExampleNode3::setTexture(std::unique_ptr texture) {
+ dynamic_cast(material()).setTexture(std::move(texture));
+}
diff --git a/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h
new file mode 100644
index 000000000000..1ff3d01fb443
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/common/examplenodes.h
@@ -0,0 +1,36 @@
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/texture.h"
+
+namespace rendergraph {
+class ExampleNode1;
+class ExampleNode2;
+class ExampleNode3;
+} // namespace rendergraph
+
+class rendergraph::ExampleNode1 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f};
+ static constexpr float verticalGradientArray[] = {1.f, 1.f, -1.f, -1.f};
+ static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f};
+
+ ExampleNode1();
+};
+
+class rendergraph::ExampleNode2 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f};
+ static constexpr float verticalGradientArray[] = {1.f, 1.f, 0.f, 0.f};
+ static constexpr float horizontalGradientArray[] = {-1.f, 1.f, -1.f, 1.f};
+
+ ExampleNode2();
+};
+
+class rendergraph::ExampleNode3 : public rendergraph::GeometryNode {
+ public:
+ static constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f};
+ static constexpr float texcoordArray[] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f};
+
+ ExampleNode3();
+
+ void setTexture(std::unique_ptr texture);
+};
diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt
new file mode 100644
index 000000000000..30f6a547b88c
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/CMakeLists.txt
@@ -0,0 +1,39 @@
+find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)
+
+qt_add_executable(gl_example
+ window.cpp window.h
+ main.cpp
+)
+
+set_target_properties(gl_example PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(gl_example PRIVATE
+ rendergraph_gl
+ rendergraph_examples
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+
+qt_add_resources(gl_example "gl_example"
+ PREFIX
+ /example
+ FILES
+ images/test.png
+)
+
+install(TARGETS gl_example
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_app_script(
+ TARGET gl_example
+ OUTPUT_SCRIPT deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png b/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png
new file mode 100644
index 000000000000..f9580346eb8b
Binary files /dev/null and b/src/rendergraph/opengl/rendergraph/examples/gl_example/images/test.png differ
diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp b/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp
new file mode 100644
index 000000000000..7b62238b429b
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/main.cpp
@@ -0,0 +1,12 @@
+#include
+
+#include "window.h"
+
+int main(int argc, char* argv[]) {
+ QGuiApplication app(argc, argv);
+
+ Window window;
+ window.show();
+
+ return app.exec();
+}
diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp
new file mode 100644
index 000000000000..81bc0db439b1
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.cpp
@@ -0,0 +1,48 @@
+#include "window.h"
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+#include "rendergraph/opengl/graph.h"
+
+Window::Window() {
+}
+
+Window::~Window() = default;
+
+void Window::closeEvent(QCloseEvent*) {
+ // since this is the only and last window, we need to cleanup before destruction,
+ // because at destruction the context can't be used anymore
+ m_rendergraph.reset();
+}
+
+void Window::initializeGL() {
+ auto node = std::make_unique();
+ node->appendChildNode(std::make_unique());
+ node->appendChildNode(std::make_unique());
+ node->appendChildNode(std::make_unique());
+
+ {
+ QImage img(":/example/images/test.png");
+ rendergraph::Context context;
+ static_cast(node->lastChild())
+ ->setTexture(
+ std::make_unique(context, img));
+ }
+
+ m_rendergraph = std::make_unique(std::move(node));
+ m_rendergraph->initialize();
+}
+
+void Window::resizeGL(int, int) {
+}
+
+void Window::paintGL() {
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_BLEND);
+ // qt scene graph uses premultiplied alpha color in the shader,
+ // so we need to do the same
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ m_rendergraph->render();
+}
diff --git a/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h
new file mode 100644
index 000000000000..6d7d5f697498
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/gl_example/window.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include
+#include
+
+namespace rendergraph {
+class Graph;
+}
+
+class Window : public QOpenGLWindow {
+ public:
+ Window();
+ ~Window();
+
+ void initializeGL() override;
+ void resizeGL(int w, int h) override;
+ void paintGL() override;
+
+ void closeEvent(QCloseEvent* ev) override;
+
+ private:
+ std::unique_ptr m_rendergraph;
+};
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt b/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt
new file mode 100644
index 000000000000..096e131e47e9
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/CMakeLists.txt
@@ -0,0 +1,35 @@
+qt_add_executable(sg_example WIN32 MACOSX_BUNDLE
+ customitem.cpp
+ customitem.h
+ main.cpp
+)
+
+target_link_libraries(sg_example PRIVATE
+ rendergraph_sg
+ rendergraph_examples
+)
+
+qt_add_qml_module(sg_example
+ URI RenderGraph
+ QML_FILES
+ qml/main.qml
+ RESOURCES
+ images/test.png
+ RESOURCE_PREFIX /example
+ NO_RESOURCE_TARGET_PATH
+)
+
+install(TARGETS sg_example
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET sg_example
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
+)
+install(SCRIPT ${deploy_script})
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp
new file mode 100644
index 000000000000..fcf1f0feb079
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.cpp
@@ -0,0 +1,59 @@
+#include "customitem.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+#include "rendergraph/scenegraph/scenegraph.h"
+
+CustomItem::CustomItem(QQuickItem* parent)
+ : QQuickItem(parent) {
+ setFlag(ItemHasContents, true);
+}
+
+CustomItem::~CustomItem() = default;
+
+void CustomItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
+ m_geometryChanged = true;
+ update();
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+}
+
+QSGNode* CustomItem::updatePaintNode(QSGNode* node, UpdatePaintNodeData*) {
+ QSGRectangleNode* bgNode;
+ if (!node) {
+ bgNode = window()->createRectangleNode();
+ bgNode->setColor(QColor(0, 0, 0, 255));
+ bgNode->setRect(boundingRect());
+
+ m_node = std::make_unique();
+ m_node->appendChildNode(std::make_unique());
+ m_node->appendChildNode(std::make_unique());
+ m_node->appendChildNode(std::make_unique());
+
+ {
+ QImage img(":/example/images/test.png");
+ auto context = rendergraph::createSgContext(window());
+ static_cast(m_node->lastChild())
+ ->setTexture(std::make_unique(
+ *context, img));
+ }
+
+ bgNode->appendChildNode(rendergraph::sgNode(m_node.get()));
+
+ node = bgNode;
+ } else {
+ bgNode = static_cast(node);
+ }
+
+ if (m_geometryChanged) {
+ bgNode->setRect(boundingRect());
+ m_geometryChanged = false;
+ }
+
+ return node;
+}
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h
new file mode 100644
index 000000000000..304fb5c8f5ec
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/customitem.h
@@ -0,0 +1,26 @@
+#ifndef CUSTOMITEM_H
+#define CUSTOMITEM_H
+
+#include
+
+namespace rendergraph {
+class Node;
+}
+
+class CustomItem : public QQuickItem {
+ Q_OBJECT
+ QML_ELEMENT
+
+ public:
+ explicit CustomItem(QQuickItem* parent = nullptr);
+ ~CustomItem();
+
+ protected:
+ QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override;
+ void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
+
+ bool m_geometryChanged{};
+ std::unique_ptr m_node;
+};
+
+#endif // CUSTOMITEM_H
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png b/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png
new file mode 100644
index 000000000000..f9580346eb8b
Binary files /dev/null and b/src/rendergraph/opengl/rendergraph/examples/sg_example/images/test.png differ
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp b/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp
new file mode 100644
index 000000000000..ab2e5b34072d
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/main.cpp
@@ -0,0 +1,16 @@
+#include
+#include
+#include
+
+#include "customitem.h"
+
+int main(int argc, char** argv) {
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///example/qml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml b/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml
new file mode 100644
index 000000000000..2410ad1a5604
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/examples/sg_example/qml/main.qml
@@ -0,0 +1,13 @@
+import QtQuick
+import RenderGraph
+
+Item {
+ id: root
+
+ width: 640
+ height: 480
+
+ CustomItem {
+ anchors.fill: parent
+ }
+}
diff --git a/src/rendergraph/opengl/rendergraph/geometry.cpp b/src/rendergraph/opengl/rendergraph/geometry.cpp
new file mode 100644
index 000000000000..c098f476e11d
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/geometry.cpp
@@ -0,0 +1,94 @@
+#include "rendergraph/geometry.h"
+
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+Geometry::Geometry(const AttributeSet& attributeSet, int vertexCount)
+ : m_drawingMode(Geometry::DrawingMode::TriangleStrip) // to mimic sg default
+ ,
+ m_vertexCount(vertexCount) {
+ int offset = 0;
+ for (const auto& attribute : attributeSet.attributes()) {
+ m_offsets.push_back(offset);
+ offset += attribute.m_tupleSize;
+ m_tupleSizes.push_back(attribute.m_tupleSize);
+ }
+ m_stride = offset * sizeof(float);
+ m_data.resize(offset * vertexCount);
+}
+
+Geometry::~Geometry() = default;
+
+void Geometry::setAttributeValues(int attributePosition, const float* from, int numTuples) {
+ const int offset = m_offsets[attributePosition];
+ const int tupleSize = m_tupleSizes[attributePosition];
+ const int skip = m_stride / sizeof(float) - tupleSize;
+
+ float* to = m_data.data();
+ to += offset;
+ while (numTuples--) {
+ int k = tupleSize;
+ while (k--) {
+ *to++ = *from++;
+ }
+ to += skip;
+ }
+}
+
+float* Geometry::vertexData() {
+ return m_data.data();
+}
+
+template
+T* Geometry::vertexDataAs() {
+ return reinterpret_cast(vertexData());
+}
+
+template Geometry::Point2D* Geometry::vertexDataAs();
+
+template Geometry::TexturedPoint2D* Geometry::vertexDataAs();
+
+template Geometry::RGBColoredPoint2D* Geometry::vertexDataAs();
+
+template Geometry::RGBAColoredPoint2D* Geometry::vertexDataAs();
+
+void Geometry::allocate(int vertexCount) {
+ m_vertexCount = vertexCount;
+ m_data.resize((m_stride / sizeof(float)) * m_vertexCount);
+}
+
+void Geometry::setDrawingMode(Geometry::DrawingMode mode) {
+ m_drawingMode = mode;
+}
+
+Geometry::DrawingMode Geometry::drawingMode() const {
+ return m_drawingMode;
+}
+
+int Geometry::attributeCount() const {
+ return m_tupleSizes.size();
+}
+
+int Geometry::vertexCount() const {
+ return m_vertexCount;
+}
+
+int Geometry::offset(int attributeIndex) const {
+ return m_offsets[attributeIndex];
+}
+
+int Geometry::tupleSize(int attributeIndex) const {
+ return m_tupleSizes[attributeIndex];
+}
+
+int Geometry::stride() const {
+ return m_stride;
+}
+
+Geometry::DrawingMode m_drawingMode;
+int m_vertexCount;
+std::vector m_tupleSizes;
+std::vector m_offsets;
+int m_stride;
+std::vector m_data;
diff --git a/src/rendergraph/opengl/rendergraph/geometry.h b/src/rendergraph/opengl/rendergraph/geometry.h
new file mode 100644
index 000000000000..a4fdf2fa90cb
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/geometry.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include
+
+#include "rendergraph/types.h"
+
+namespace rendergraph {
+class Geometry;
+class AttributeSet;
+} // namespace rendergraph
+
+class rendergraph::Geometry {
+ public:
+ struct Point2D {
+ QVector2D position2D;
+ Point2D(float x, float y)
+ : position2D{x, y} {
+ }
+ };
+
+ struct TexturedPoint2D {
+ QVector2D position2D;
+ QVector2D texcoord2D;
+ TexturedPoint2D(float x, float y, float tx, float ty)
+ : position2D{x, y},
+ texcoord2D{tx, ty} {
+ }
+ };
+
+ struct RGBColoredPoint2D {
+ QVector2D position2D;
+ QVector3D color3D;
+ RGBColoredPoint2D(float x, float y, float r, float g, float b)
+ : position2D{x, y},
+ color3D{r, g, b} {
+ }
+ };
+
+ struct RGBAColoredPoint2D {
+ QVector2D position2D;
+ QVector4D color4D;
+ RGBAColoredPoint2D(float x, float y, float r, float g, float b, float a)
+ : position2D{x, y},
+ color4D{r, g, b, a} {
+ }
+ };
+
+ enum class DrawingMode {
+ Triangles,
+ TriangleStrip
+ };
+
+ Geometry(const AttributeSet& attributeSet, int vertexCount);
+ ~Geometry();
+
+ void allocate(int vertexCount);
+
+ void setAttributeValues(int attributePosition, const float* data, int numTuples);
+
+ float* vertexData();
+
+ template
+ T* vertexDataAs();
+
+ DrawingMode drawingMode() const;
+ void setDrawingMode(DrawingMode mode);
+
+ int attributeCount() const;
+ int vertexCount() const;
+ int offset(int attributeIndex) const;
+ int tupleSize(int attributeIndex) const;
+ int stride() const;
+
+ private:
+ DrawingMode m_drawingMode;
+ int m_vertexCount;
+ std::vector m_tupleSizes;
+ std::vector m_offsets;
+ int m_stride;
+ std::vector m_data;
+};
diff --git a/src/rendergraph/opengl/rendergraph/geometrynode.cpp b/src/rendergraph/opengl/rendergraph/geometrynode.cpp
new file mode 100644
index 000000000000..67df96e4d114
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/geometrynode.cpp
@@ -0,0 +1,120 @@
+#include "rendergraph/geometrynode.h"
+
+#include
+
+#include "rendergraph/shadercache.h"
+#include "rendergraph/texture.h"
+
+using namespace rendergraph;
+
+GeometryNode::GeometryNode() = default;
+
+GeometryNode::~GeometryNode() = default;
+
+void GeometryNode::setGeometry(std::unique_ptr pGeometry) {
+ m_pGeometry = std::move(pGeometry);
+}
+
+void GeometryNode::setMaterial(std::unique_ptr pMaterial) {
+ m_pMaterial = std::move(pMaterial);
+}
+
+Geometry& GeometryNode::geometry() const {
+ return *m_pGeometry;
+}
+
+Material& GeometryNode::material() const {
+ return *m_pMaterial;
+}
+
+void GeometryNode::initialize() {
+ initializeOpenGLFunctions();
+ m_pMaterial->setShader(ShaderCache::getShaderForMaterial(m_pMaterial.get()));
+ Node::initialize();
+}
+
+namespace {
+GLenum toGlDrawingMode(Geometry::DrawingMode mode) {
+ switch (mode) {
+ case Geometry::DrawingMode::Triangles:
+ return GL_TRIANGLES;
+ case Geometry::DrawingMode::TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ }
+}
+} // namespace
+
+void GeometryNode::render() {
+ Geometry& geometry = *m_pGeometry;
+ Material& material = *m_pMaterial;
+
+ if (geometry.vertexCount() == 0) {
+ return;
+ }
+
+ glEnable(GL_BLEND);
+ // qt scene graph uses premultiplied alpha color in the shader,
+ // so we need to do the same
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ QOpenGLShaderProgram& shader = material.shader();
+ shader.bind();
+
+ if (m_pMaterial->clearUniformsCacheDirty() || !material.isLastModifierOfShader()) {
+ material.modifyShader();
+ const UniformsCache& cache = m_pMaterial->uniformsCache();
+ for (int i = 0; i < cache.count(); i++) {
+ int location = material.uniformLocation(i);
+ switch (cache.type(i)) {
+ case Type::UInt:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ case Type::Float:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ case Type::Vector2D:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ case Type::Vector3D:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ case Type::Vector4D:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ case Type::Matrix4x4:
+ shader.setUniformValue(location, cache.get(i));
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < geometry.attributeCount(); i++) {
+ int location = material.attributeLocation(i);
+ shader.enableAttributeArray(location);
+ shader.setAttributeArray(location,
+ geometry.vertexData() + geometry.offset(i),
+ geometry.tupleSize(i),
+ geometry.stride());
+ }
+
+ // TODO multiple textures
+ auto pTexture = m_pMaterial->texture(1);
+ if (pTexture) {
+ pTexture->glTexture()->bind();
+ }
+
+ glDrawArrays(toGlDrawingMode(geometry.drawingMode()), 0, geometry.vertexCount());
+
+ if (pTexture) {
+ pTexture->glTexture()->release();
+ }
+
+ for (int i = 0; i < geometry.attributeCount(); i++) {
+ int location = material.attributeLocation(i);
+ shader.disableAttributeArray(location);
+ }
+
+ shader.release();
+
+ Node::render();
+}
diff --git a/src/rendergraph/opengl/rendergraph/geometrynode.h b/src/rendergraph/opengl/rendergraph/geometrynode.h
new file mode 100644
index 000000000000..4804f123d93f
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/geometrynode.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+#include "rendergraph/geometry.h"
+#include "rendergraph/material.h"
+#include "rendergraph/node.h"
+
+namespace rendergraph {
+class GeometryNode;
+class Material;
+} // namespace rendergraph
+
+class rendergraph::GeometryNode : public rendergraph::Node, public QOpenGLFunctions {
+ public:
+ GeometryNode();
+ ~GeometryNode();
+
+ template
+ void initForRectangles(int numRectangles) {
+ const int verticesPerRectangle = 6; // 2 rectangles
+ setGeometry(std::make_unique(T_Material::attributes(),
+ numRectangles * verticesPerRectangle));
+ setMaterial(std::make_unique());
+ geometry().setDrawingMode(Geometry::DrawingMode::Triangles);
+ }
+
+ void initialize() override;
+ void render() override;
+
+ void setMaterial(std::unique_ptr material);
+ void setGeometry(std::unique_ptr geometry);
+ Geometry& geometry() const;
+ Material& material() const;
+
+ private:
+ std::unique_ptr m_pMaterial;
+ std::unique_ptr m_pGeometry;
+};
diff --git a/src/rendergraph/opengl/rendergraph/graph.cpp b/src/rendergraph/opengl/rendergraph/graph.cpp
new file mode 100644
index 000000000000..cbb61e9e45ac
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/graph.cpp
@@ -0,0 +1,81 @@
+#include "rendergraph/graph.h"
+
+#include
+
+#include "rendergraph/node.h"
+
+using namespace rendergraph;
+
+Graph::Graph(std::unique_ptr node)
+ : m_pTopNode(std::move(node)) {
+ if (m_pTopNode->graph() != this) {
+ addToGraph(m_pTopNode.get());
+ }
+}
+
+Graph::~Graph() = default;
+
+void Graph::initialize() {
+ for (auto pNode : m_pInitializeNodes) {
+ pNode->initialize();
+ }
+ m_pInitializeNodes.clear();
+}
+
+void Graph::render() {
+ if (!m_pInitializeNodes.empty()) {
+ initialize();
+ }
+ if (!m_pTopNode->isSubtreeBlocked()) {
+ render(m_pTopNode.get());
+ }
+}
+
+void Graph::resize(int w, int h) {
+ resize(m_pTopNode.get(), w, h);
+}
+
+void Graph::preprocess() {
+ for (auto pNode : m_pPreprocessNodes) {
+ if (!pNode->isSubtreeBlocked()) {
+ pNode->preprocess();
+ }
+ }
+}
+
+void Graph::render(Node* pNode) {
+ pNode->render();
+ pNode = pNode->firstChild();
+ while (pNode) {
+ if (!pNode->isSubtreeBlocked()) {
+ render(pNode);
+ }
+ pNode = pNode->nextSibling();
+ }
+}
+
+void Graph::resize(Node* pNode, int w, int h) {
+ pNode->resize(w, h);
+ pNode = pNode->firstChild();
+ while (pNode) {
+ resize(pNode, w, h);
+ pNode = pNode->nextSibling();
+ }
+}
+
+void Graph::addToGraph(Node* pNode) {
+ assert(pNode->graph() == nullptr);
+
+ pNode->setGraph(this);
+ m_pInitializeNodes.push_back(pNode);
+ if (pNode->usePreprocess()) {
+ m_pPreprocessNodes.push_back(pNode);
+ }
+ pNode = pNode->firstChild();
+ while (pNode) {
+ if (pNode->graph() != this) {
+ addToGraph(pNode);
+ }
+ pNode = pNode->nextSibling();
+ }
+}
diff --git a/src/rendergraph/opengl/rendergraph/graph.h b/src/rendergraph/opengl/rendergraph/graph.h
new file mode 100644
index 000000000000..e5179fdde786
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/graph.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+#include
+
+namespace rendergraph {
+class Graph;
+class Node;
+} // namespace rendergraph
+
+class rendergraph::Graph {
+ public:
+ Graph(std::unique_ptr node);
+ ~Graph();
+ void initialize();
+ void render();
+ void resize(int w, int h);
+ void preprocess();
+ void addToGraph(Node* pNode);
+
+ private:
+ void render(Node* pNode);
+ void resize(Node* pNode, int, int h);
+
+ const std::unique_ptr m_pTopNode;
+ std::vector m_pPreprocessNodes;
+ std::vector m_pInitializeNodes;
+};
diff --git a/src/rendergraph/opengl/rendergraph/material.cpp b/src/rendergraph/opengl/rendergraph/material.cpp
new file mode 100644
index 000000000000..6c525b1ebf5a
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material.cpp
@@ -0,0 +1,36 @@
+#include "rendergraph/material.h"
+
+#include "rendergraph/shadercache.h"
+#include "rendergraph/texture.h"
+
+using namespace rendergraph;
+
+Material::Material(const UniformSet& uniformSet)
+ : m_uniformsCache(uniformSet) {
+}
+
+Material::~Material() = default;
+
+void Material::setShader(std::shared_ptr pShader) {
+ m_pShader = pShader;
+}
+
+MaterialShader& Material::shader() const {
+ return *m_pShader;
+}
+
+int Material::uniformLocation(int uniformIndex) const {
+ return m_pShader->uniformLocation(uniformIndex);
+}
+
+int Material::attributeLocation(int attributeIndex) const {
+ return m_pShader->attributeLocation(attributeIndex);
+}
+
+void Material::modifyShader() {
+ m_pShader->setLastModifiedByMaterial(this);
+}
+
+bool Material::isLastModifierOfShader() const {
+ return this == m_pShader->lastModifiedByMaterial();
+}
diff --git a/src/rendergraph/opengl/rendergraph/material.h b/src/rendergraph/opengl/rendergraph/material.h
new file mode 100644
index 000000000000..8c54be251e20
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+
+#include "rendergraph/uniformscache.h"
+
+namespace rendergraph {
+class UniformSet;
+class Material;
+class MaterialShader;
+class MaterialType;
+class Texture;
+class GeometryNode;
+} // namespace rendergraph
+
+class rendergraph::Material {
+ public:
+ Material(const UniformSet& uniformSet);
+ virtual ~Material();
+ virtual int compare(const Material* other) const = 0;
+ virtual std::unique_ptr createShader() const = 0;
+ virtual MaterialType* type() const = 0;
+
+ template
+ void setUniform(int uniformIndex, const T& value) {
+ m_uniformsCache.set(uniformIndex, value);
+ m_uniformsCacheDirty = true;
+ }
+
+ void setShader(std::shared_ptr pShader);
+
+ MaterialShader& shader() const;
+
+ int uniformLocation(int uniformIndex) const;
+ int attributeLocation(int attributeIndex) const;
+
+ private:
+ friend MaterialShader;
+ friend GeometryNode;
+
+ void modifyShader();
+ bool isLastModifierOfShader() const;
+
+ const UniformsCache& uniformsCache() const {
+ return m_uniformsCache;
+ }
+
+ bool clearUniformsCacheDirty() {
+ if (m_uniformsCacheDirty) {
+ m_uniformsCacheDirty = false;
+ return true;
+ }
+ return false;
+ }
+
+ virtual Texture* texture(int /*binding*/) const {
+ return nullptr;
+ }
+
+ std::shared_ptr m_pShader;
+ UniformsCache m_uniformsCache;
+ bool m_uniformsCacheDirty{};
+};
diff --git a/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp
new file mode 100644
index 000000000000..fd9e5f73ec72
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.cpp
@@ -0,0 +1,39 @@
+#include "endoftrackmaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+EndOfTrackMaterial::EndOfTrackMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& EndOfTrackMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "gradient"});
+ return set;
+}
+
+/* static */ const UniformSet& EndOfTrackMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.color"});
+ return set;
+}
+
+MaterialType* EndOfTrackMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int EndOfTrackMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr EndOfTrackMaterial::createShader() const {
+ return std::make_unique(
+ "endoftrack.vert", "endoftrack.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h
new file mode 100644
index 000000000000..e66a825c1641
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/endoftrackmaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class EndOfTrackMaterial;
+}
+
+class rendergraph::EndOfTrackMaterial : public rendergraph::Material {
+ public:
+ EndOfTrackMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp
new file mode 100644
index 000000000000..e16ca4213d77
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/patternmaterial.cpp
@@ -0,0 +1,39 @@
+#include "patternmaterial.h"
+
+#include
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+
+using namespace rendergraph;
+
+PatternMaterial::PatternMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& PatternMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "texcoord"});
+ return set;
+}
+
+/* static */ const UniformSet& PatternMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* PatternMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int PatternMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr PatternMaterial::createShader() const {
+ return std::make_unique(
+ "pattern.vert", "pattern.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/opengl/rendergraph/material/patternmaterial.h b/src/rendergraph/opengl/rendergraph/material/patternmaterial.h
new file mode 100644
index 000000000000..0df75c073714
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/patternmaterial.h
@@ -0,0 +1,34 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+#include "rendergraph/texture.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class PatternMaterial;
+}
+
+class rendergraph::PatternMaterial : public rendergraph::Material {
+ public:
+ PatternMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+
+ Texture* texture(int /*binding*/) const override {
+ return m_pTexture.get();
+ }
+
+ void setTexture(std::unique_ptr texture) {
+ m_pTexture = std::move(texture);
+ }
+
+ private:
+ std::unique_ptr m_pTexture;
+};
diff --git a/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp
new file mode 100644
index 000000000000..c1d53d3f8bcc
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.cpp
@@ -0,0 +1,39 @@
+#include "rgbamaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+RGBAMaterial::RGBAMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& RGBAMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "color"});
+ return set;
+}
+
+/* static */ const UniformSet& RGBAMaterial::uniforms() {
+ static UniformSet set = makeUniformSet({"ubuf.matrix"});
+ return set;
+}
+
+MaterialType* RGBAMaterial::type() const {
+ static MaterialType type;
+ return &type;
+}
+
+int RGBAMaterial::compare(const Material* other) const {
+ Q_ASSERT(other && type() == other->type());
+ const auto* otherCasted = static_cast(other);
+ return otherCasted == this ? 0 : 1;
+}
+
+std::unique_ptr RGBAMaterial::createShader() const {
+ return std::make_unique(
+ "rgba.vert", "rgba.frag", uniforms(), attributes());
+}
diff --git a/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h
new file mode 100644
index 000000000000..b5b72a949c90
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/rgbamaterial.h
@@ -0,0 +1,21 @@
+#include "rendergraph/attributeset.h"
+#include "rendergraph/material.h"
+
+namespace rendergraph {
+class RGBAMaterial;
+}
+
+class rendergraph::RGBAMaterial : public rendergraph::Material {
+ public:
+ RGBAMaterial();
+
+ static const AttributeSet& attributes();
+
+ static const UniformSet& uniforms();
+
+ MaterialType* type() const override;
+
+ int compare(const Material* other) const override;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp
new file mode 100644
index 000000000000..3a2f014d07e4
--- /dev/null
+++ b/src/rendergraph/opengl/rendergraph/material/rgbmaterial.cpp
@@ -0,0 +1,39 @@
+#include "rgbmaterial.h"
+
+#include
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+RGBMaterial::RGBMaterial()
+ : Material(uniforms()) {
+}
+
+/* static */ const AttributeSet& RGBMaterial::attributes() {
+ static AttributeSet set = makeAttributeSet({"position", "color"});
+ return set;
+}
+
+/* static */ const UniformSet& RGBMaterial::uniforms() {
+ static UniformSet set = makeUniformSet