diff --git a/CMakeLists.txt b/CMakeLists.txt
index 134c27e3c51b..39b784079cf9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3809,6 +3809,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/shaders/rendergraph/CMakeLists.txt b/res/shaders/rendergraph/CMakeLists.txt
new file mode 100644
index 000000000000..dfcecc593a46
--- /dev/null
+++ b/res/shaders/rendergraph/CMakeLists.txt
@@ -0,0 +1,47 @@
+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-qsb"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /shaders/rendergraph
+ FILES
+ ${shaders}
+)
+
+if(USE_QSHADER_FOR_GL)
+ message(STATUS "Adding qsb shaders to rendergraph_gl")
+ qt6_add_shaders(rendergraph_gl "shaders-qsb"
+ BATCHABLE
+ PRECOMPILE
+ OPTIMIZED
+ PREFIX
+ /shaders/rendergraph
+ FILES
+ ${shaders}
+ )
+else()
+ message(STATUS "Adding gl shaders to rendergraph_gl")
+ include(generated_shaders_gl.cmake)
+
+ qt_add_resources(rendergraph_gl "shaders-gl"
+ PREFIX
+ /shaders/rendergraph
+ FILES
+ ${generated_shaders_gl}
+ )
+endif()
diff --git a/res/shaders/rendergraph/README b/res/shaders/rendergraph/README
new file mode 100644
index 000000000000..644c2ce76477
--- /dev/null
+++ b/res/shaders/rendergraph/README
@@ -0,0 +1,10 @@
+The CMakeLists.txt in this folder generates qsb shader bundles from the spirv shaders.
+
+The qsb files can be used by QML / Qt scene graph QSGShader. Since Qt >= 6.6 we can load the qsb files and extract the glsl shaders
+programmatically with QShader and use the with QOpenGLShader. For Qt < 6.6 the files have to generated manually with the script:
+
+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
+)
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..7099bb8f3dd5
--- /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); // premultiply 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/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt
new file mode 100644
index 000000000000..059bbdaa0504
--- /dev/null
+++ b/src/rendergraph/CMakeLists.txt
@@ -0,0 +1,10 @@
+find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
+message(STATUS "Qt version ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}")
+
+if (QT_VERSION_MINOR GREATER_EQUAL 6)
+set(USE_QSHADER_FOR_GL ON)
+endif()
+
+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..8b1720e22c8c
--- /dev/null
+++ b/src/rendergraph/README
@@ -0,0 +1,63 @@
+rendergraph is an abstraction layer that can be compiled with one of two backends:
+
+- scenegraph (QtQuick scene graph classes)
+- opengl (custom classes using QOpenGL)
+
+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 and only uses a subset of its functionality.
+
+The scenegraph backend class the underlying QtQuick scene graph classes directly or almost directly. The opengl layer implements classes that mimic the behaviour of the
+Qt scene graph classes.
+
+The objective of rendergraph is to be able to write code that can be used verbatim within the context of both QWidgets applications and QML applications. 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)
+
+Primarily, rendering with rendergraph uses a graph of Node's, typically of type GeometryNode. A GeometryNode uses a Geometry, which contains the vertex data of what the node renders (positional, texture coordinates, color information...) and a Material, which is the interface to the underlying shader. Uniform data is set through the Material. The QtQuick scene graph documentation is a good started point to understand the design.
+
+The code is organized as follows:
+
+common/rendergraph
+ Public header files. This is the primary API, identical for both backends, and the only files that should be included to write code compatible with both backends.
+
+common
+ Implementation of the functionality declared in the common/rendergraph headers that is identical for both backends.
+ The general guideline is "code duplicated between the backends should be in the common layer".
+
+common/rendergraph/material
+ Several basic 'materials' for common rendering operations (e.g. uniform or variying color, texture)
+
+scenegraph
+ Implementation of the functionality declared in the common/rendergraph headers that is specific to call the scenegraph backend
+
+scenegraph/rendergraph
+ Public header files specific for the opengl backend. This is the layer between QtQuick application code and the common rendergraph API.
+
+scenegraph/backend
+ The scenegraph backend implementation, the layer between the common/rendergraph API and the underlying QtQuick scene graph classes.
+
+opengl
+ Implementation of the functionality declared in the common/rendergraph headers that is specific to call the opengl backend
+
+opengl/rendergraph
+ Public header files specific for the opengl backend. This is the layer between QWidgets/QOpenGL application code and the common rendergraph API.
+
+opengl/backend
+ The opengl backend implementation, the layer between the common/rendergraph API and custom implemented classes that mimic the behaviour QtQuick scene graph classes.
+
+Furthermore, some simple examples:
+
+examples/sg_example
+ An example QtQuick application that uses the scenegraph backend
+
+examples/gl_example
+ An example QOpenGLWindow based application that uses the opengl backend
+
+examples/common
+ The graphics code that is identical for both example
+
+../../res/shaders/rendergraph/
+ The qsb and (generated) gl shader files
+ See ../../res/shaders/rendergraph/README for more info
+
+More advanced use of rendergraph can be found in the waveform/renderers/allshader classes, which have partially been ported the rendergraph.
+
+
+m0dB
diff --git a/src/rendergraph/common/attributeset.cpp b/src/rendergraph/common/attributeset.cpp
new file mode 100644
index 000000000000..a2fef348d9c0
--- /dev/null
+++ b/src/rendergraph/common/attributeset.cpp
@@ -0,0 +1,12 @@
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+AttributeSet::AttributeSet(std::initializer_list list,
+ const std::vector& names)
+ : BaseAttributeSet(list, names) {
+}
+
+const std::vector& AttributeSet::attributes() const {
+ return m_attributes;
+}
diff --git a/src/rendergraph/common/node.cpp b/src/rendergraph/common/node.cpp
new file mode 100644
index 000000000000..24f5d29d3eeb
--- /dev/null
+++ b/src/rendergraph/common/node.cpp
@@ -0,0 +1,7 @@
+#include "rendergraph/node.h"
+
+using namespace rendergraph;
+
+Node::Node()
+ : TreeNode(this) {
+}
diff --git a/src/rendergraph/common/opacitynode.cpp b/src/rendergraph/common/opacitynode.cpp
new file mode 100644
index 000000000000..caa6526626f6
--- /dev/null
+++ b/src/rendergraph/common/opacitynode.cpp
@@ -0,0 +1,7 @@
+#include "rendergraph/opacitynode.h"
+
+using namespace rendergraph;
+
+OpacityNode::OpacityNode()
+ : TreeNode(this) {
+}
diff --git a/src/rendergraph/common/rendergraph/assert.h b/src/rendergraph/common/rendergraph/assert.h
new file mode 100644
index 000000000000..431ddbd12d75
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/assert.h
@@ -0,0 +1 @@
+#include "../../../util/assert.h"
diff --git a/src/rendergraph/common/rendergraph/attribute.h b/src/rendergraph/common/rendergraph/attribute.h
new file mode 100644
index 000000000000..150ad00c3aaf
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/attribute.h
@@ -0,0 +1,26 @@
+#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, 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/common/rendergraph/attributeset.h b/src/rendergraph/common/rendergraph/attributeset.h
new file mode 100644
index 000000000000..589a6e51bfc3
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/attributeset.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "backend/baseattributeset.h"
+
+namespace rendergraph {
+class AttributeSet;
+}
+
+class rendergraph::AttributeSet : public rendergraph::BaseAttributeSet {
+ public:
+ AttributeSet(std::initializer_list list, const std::vector& names);
+ const std::vector& attributes() const;
+};
+
+namespace rendergraph {
+template
+AttributeSet makeAttributeSet(const std::vector& names) {
+ return AttributeSet({(Attribute::create())...}, names);
+}
+} // namespace rendergraph
diff --git a/src/rendergraph/common/rendergraph/geometry.h b/src/rendergraph/common/rendergraph/geometry.h
new file mode 100644
index 000000000000..c44572c3b9a2
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/geometry.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include
+#include
+
+#include "backend/basegeometry.h"
+#include "rendergraph/attributeset.h"
+
+namespace rendergraph {
+class Geometry;
+} // namespace rendergraph
+
+class rendergraph::Geometry : public rendergraph::BaseGeometry {
+ 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 rendergraph::AttributeSet& attributeSet, int vertexCount);
+ ~Geometry();
+
+ void allocate(int vertexCount);
+
+ void setAttributeValues(int attributePosition, const float* data, int numTuples);
+
+ float* vertexData();
+
+ template
+ T* vertexDataAs() {
+ return reinterpret_cast(vertexData());
+ }
+
+ DrawingMode drawingMode() const;
+ void setDrawingMode(DrawingMode mode);
+};
diff --git a/src/rendergraph/common/rendergraph/geometrynode.h b/src/rendergraph/common/rendergraph/geometrynode.h
new file mode 100644
index 000000000000..44384b370bf5
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/geometrynode.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "backend/basegeometrynode.h"
+#include "rendergraph/geometry.h"
+#include "rendergraph/material.h"
+#include "rendergraph/treenode.h"
+
+namespace rendergraph {
+class GeometryNode;
+} // namespace rendergraph
+
+class rendergraph::GeometryNode : public rendergraph::BaseGeometryNode,
+ public rendergraph::TreeNode {
+ public:
+ 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 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/common/rendergraph/material.h b/src/rendergraph/common/rendergraph/material.h
new file mode 100644
index 000000000000..e15ef5c0f1ce
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include
+
+#include "backend/basematerial.h"
+#include "rendergraph/assert.h"
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+#include "rendergraph/texture.h"
+#include "rendergraph/uniformscache.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class Material;
+} // namespace rendergraph
+
+class rendergraph::Material : public rendergraph::BaseMaterial {
+ public:
+ Material(const UniformSet& uniformSet);
+ virtual ~Material();
+
+ // see QSGMaterial::compare.
+ // TODO decide if this should be virtual. QSGMaterial::compare is virtual
+ // to concrete Material can implement a compare function, but in rendergraph
+ // we can compare the uniforms cache and texture already here, which seems
+ // sufficient.
+ int compare(const Material* pOther) const {
+ DEBUG_ASSERT(type() == pOther->type());
+ int cacheCompareResult = std::memcmp(m_uniformsCache.data(),
+ pOther->m_uniformsCache.data(),
+ m_uniformsCache.size());
+ if (cacheCompareResult != 0) {
+ return cacheCompareResult;
+ }
+ // TODO multiple textures
+ if (!texture(0) || !pOther->texture(0)) {
+ return texture(0) ? 1 : -1;
+ }
+
+ const qint64 diff = texture(0)->comparisonKey() - pOther->texture(0)->comparisonKey();
+ return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
+ }
+
+ virtual std::unique_ptr createShader() const = 0;
+
+ template
+ void setUniform(int uniformIndex, const T& value) {
+ m_uniformsCache.set(uniformIndex, value);
+ m_uniformsCacheDirty = true;
+ }
+
+ const UniformsCache& uniformsCache() const {
+ return m_uniformsCache;
+ }
+
+ bool clearUniformsCacheDirty() {
+ if (m_uniformsCacheDirty) {
+ m_uniformsCacheDirty = false;
+ return true;
+ }
+ return false;
+ }
+
+ virtual Texture* texture(int) const {
+ return nullptr;
+ }
+
+ private:
+ UniformsCache m_uniformsCache;
+ bool m_uniformsCacheDirty{};
+};
diff --git a/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp
new file mode 100644
index 000000000000..ee35b465934b
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.cpp
@@ -0,0 +1,33 @@
+#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;
+}
+
+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..94c80693799e
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/endoftrackmaterial.h
@@ -0,0 +1,19 @@
+#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;
+
+ 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..864f4a60adf9
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/patternmaterial.cpp
@@ -0,0 +1,33 @@
+#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;
+}
+
+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..661246abd056
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/patternmaterial.h
@@ -0,0 +1,32 @@
+#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;
+
+ std::unique_ptr createShader() const override;
+
+ Texture* texture(int) 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..e81382bf9ce9
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.cpp
@@ -0,0 +1,34 @@
+#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;
+}
+
+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..0921ebb4faef
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbamaterial.h
@@ -0,0 +1,19 @@
+#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;
+
+ 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..455555d1e97a
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.cpp
@@ -0,0 +1,33 @@
+#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;
+}
+
+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..cfe1fde7782d
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/rgbmaterial.h
@@ -0,0 +1,19 @@
+#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;
+
+ 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..b3f61767626c
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/texturematerial.cpp
@@ -0,0 +1,33 @@
+#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;
+}
+
+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..2fa5a2310cca
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/texturematerial.h
@@ -0,0 +1,32 @@
+#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;
+
+ 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..d67fd16320c9
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.cpp
@@ -0,0 +1,33 @@
+#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;
+}
+
+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..26225f4d3c68
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/material/unicolormaterial.h
@@ -0,0 +1,19 @@
+#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;
+
+ std::unique_ptr createShader() const override;
+};
diff --git a/src/rendergraph/common/rendergraph/materialshader.h b/src/rendergraph/common/rendergraph/materialshader.h
new file mode 100644
index 000000000000..43c811ebebd7
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/materialshader.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "backend/basematerialshader.h"
+#include "rendergraph/attributeset.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class MaterialShader;
+} // namespace rendergraph
+
+class rendergraph::MaterialShader : public rendergraph::BaseMaterialShader {
+ public:
+ MaterialShader(const char* vertexShaderFile,
+ const char* fragmentShaderFile,
+ const UniformSet& uniforms,
+ const AttributeSet& attributeSet);
+};
diff --git a/src/rendergraph/common/rendergraph/materialtype.h b/src/rendergraph/common/rendergraph/materialtype.h
new file mode 100644
index 000000000000..67da71e91f34
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/materialtype.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "backend/basematerialtype.h"
+
+namespace rendergraph {
+class MaterialType;
+}
+
+class rendergraph::MaterialType : public rendergraph::BaseMaterialType {
+};
diff --git a/src/rendergraph/common/rendergraph/node.h b/src/rendergraph/common/rendergraph/node.h
new file mode 100644
index 000000000000..7ce62dd08cc8
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/node.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "backend/basenode.h"
+#include "rendergraph/treenode.h"
+
+namespace rendergraph {
+class Node;
+} // namespace rendergraph
+
+class rendergraph::Node : public rendergraph::BaseNode, public rendergraph::TreeNode {
+ public:
+ Node();
+};
diff --git a/src/rendergraph/common/rendergraph/opacitynode.h b/src/rendergraph/common/rendergraph/opacitynode.h
new file mode 100644
index 000000000000..ac64340520a9
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/opacitynode.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "backend/baseopacitynode.h"
+#include "rendergraph/treenode.h"
+
+namespace rendergraph {
+class OpacityNode;
+} // namespace rendergraph
+
+class rendergraph::OpacityNode : public rendergraph::BaseOpacityNode,
+ public rendergraph::TreeNode {
+ public:
+ OpacityNode();
+};
diff --git a/src/rendergraph/common/rendergraph/texture.h b/src/rendergraph/common/rendergraph/texture.h
new file mode 100644
index 000000000000..8e095ca31209
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/texture.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include
+#include
+
+#include "backend/basetexture.h"
+#include "rendergraph/context.h"
+
+namespace rendergraph {
+class Texture;
+} // namespace rendergraph
+
+class rendergraph::Texture {
+ public:
+ Texture(Context& context, const QImage& image);
+
+ BaseTexture* backendTexture() const {
+ return m_pTexture.get();
+ }
+
+ // used by Material::compare
+ qint64 comparisonKey() const;
+
+ private:
+ const std::unique_ptr m_pTexture{};
+};
diff --git a/src/rendergraph/common/rendergraph/treenode.h b/src/rendergraph/common/rendergraph/treenode.h
new file mode 100644
index 000000000000..2cd943ce838c
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/treenode.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class TreeNode;
+} // namespace rendergraph
+
+class rendergraph::TreeNode {
+ public:
+ TreeNode(rendergraph::BaseNode* pBackendNode)
+ : m_pBackendNode(pBackendNode) {
+ }
+ virtual ~TreeNode() = default;
+
+ void appendChildNode(std::unique_ptr&& pChild);
+
+ // remove all child nodes. returns the list of child nodes
+ // by returning the node in the list. the caller can keep
+ // the pointer, thus transferring ownership. otherwise the
+ // nodes will be destroyed
+ std::unique_ptr removeAllChildNodes();
+
+ // remove a single child node for the list of child nodes.
+ // the caller can keep the pointer, thus transferring ownership.
+ // otherwise the node will be destroyed
+ std::unique_ptr removeChildNode(TreeNode* pChild);
+
+ TreeNode* parent() const {
+ return m_pParent;
+ }
+ TreeNode* firstChild() const {
+ return m_pFirstChild.get();
+ }
+ TreeNode* lastChild() const {
+ return m_pLastChild;
+ }
+ TreeNode* nextSibling() const {
+ return m_pNextSibling.get();
+ }
+ TreeNode* previousSibling() const {
+ return m_pPreviousSibling;
+ }
+
+ void setUsePreprocess(bool value);
+
+ rendergraph::BaseNode* backendNode() {
+ return m_pBackendNode;
+ }
+
+ virtual void initialize() {
+ }
+ virtual void resize(int, int) {
+ }
+
+ private:
+ void onAppendChildNode(TreeNode* pChild);
+ void onRemoveAllChildNodes();
+ void onRemoveChildNode(TreeNode* pChild);
+
+ rendergraph::BaseNode* m_pBackendNode;
+ TreeNode* m_pParent{};
+ std::unique_ptr m_pFirstChild;
+ TreeNode* m_pLastChild{};
+ std::unique_ptr m_pNextSibling;
+ TreeNode* m_pPreviousSibling{};
+};
diff --git a/src/rendergraph/common/rendergraph/types.h b/src/rendergraph/common/rendergraph/types.h
new file mode 100644
index 000000000000..768738964fd6
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/types.h
@@ -0,0 +1,31 @@
+#pragma once
+
+namespace rendergraph {
+
+enum class PrimitiveType {
+ UInt,
+ Float,
+};
+
+enum class Type {
+ UInt,
+ Float,
+ Vector2D,
+ Vector3D,
+ Vector4D,
+ Matrix4x4
+};
+
+int sizeOf(Type type);
+int sizeOf(PrimitiveType primitiveType);
+
+template
+Type typeOf();
+
+template
+PrimitiveType primitiveTypeOf();
+
+template
+int tupleSizeOf();
+
+} // namespace rendergraph
diff --git a/src/rendergraph/common/rendergraph/uniform.h b/src/rendergraph/common/rendergraph/uniform.h
new file mode 100644
index 000000000000..19a6a9c74a35
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniform.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+
+#include "rendergraph/types.h"
+
+namespace rendergraph {
+struct Uniform;
+}
+
+struct rendergraph::Uniform {
+ const Type m_type;
+ const QString m_name;
+
+ Uniform(Type type)
+ : m_type{type} {
+ }
+
+ Uniform(Type type, QString name)
+ : m_type{type},
+ m_name{std::move(name)} {
+ }
+
+ template
+ static Uniform create() {
+ return Uniform(typeOf());
+ }
+};
diff --git a/src/rendergraph/common/rendergraph/uniformscache.cpp b/src/rendergraph/common/rendergraph/uniformscache.cpp
new file mode 100644
index 000000000000..c94539631d76
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniformscache.cpp
@@ -0,0 +1,27 @@
+#include "rendergraph/uniformscache.h"
+
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+UniformsCache::UniformsCache(const UniformSet& uniformSet) {
+ int offset = 0;
+ m_infos.reserve(uniformSet.uniforms().size());
+ for (const auto& uniform : uniformSet.uniforms()) {
+ const int size = sizeOf(uniform.m_type);
+ m_infos.push_back({uniform.m_type, offset});
+ offset += size;
+ }
+ m_byteArray.resize(offset);
+ m_byteArray.fill('\0');
+}
+
+UniformsCache::~UniformsCache() = default;
+
+void UniformsCache::set(int uniformIndex, const void* ptr, int size) {
+ memcpy(m_byteArray.data() + m_infos[uniformIndex].m_offset, ptr, size);
+}
+
+void UniformsCache::get(int uniformIndex, void* ptr, int size) const {
+ memcpy(ptr, m_byteArray.data() + m_infos[uniformIndex].m_offset, size);
+}
diff --git a/src/rendergraph/common/rendergraph/uniformscache.h b/src/rendergraph/common/rendergraph/uniformscache.h
new file mode 100644
index 000000000000..045887f0c37e
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniformscache.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include "rendergraph/assert.h"
+#include "rendergraph/types.h"
+#include "rendergraph/uniformset.h"
+
+namespace rendergraph {
+class UniformsCache;
+} // namespace rendergraph
+
+class rendergraph::UniformsCache {
+ public:
+ UniformsCache(const UniformSet& uniformSet);
+ ~UniformsCache();
+
+ template
+ void set(int uniformIndex, const T& value) {
+ DEBUG_ASSERT(type(uniformIndex) == typeOf());
+ DEBUG_ASSERT(std::is_trivially_copyable());
+ set(uniformIndex, static_cast(&value), sizeOf(typeOf()));
+ }
+
+ template
+ T get(int uniformIndex) const {
+ DEBUG_ASSERT(type(uniformIndex) == typeOf());
+ DEBUG_ASSERT(std::is_trivially_copyable());
+ T value;
+ get(uniformIndex, static_cast(&value), sizeof(T));
+ return value;
+ }
+ Type type(int uniformIndex) const {
+ return m_infos[uniformIndex].m_type;
+ }
+
+ const char* data() const {
+ return m_byteArray.data();
+ }
+ qsizetype size() const {
+ return m_byteArray.size();
+ }
+ int count() const {
+ return static_cast(m_infos.size());
+ }
+
+ private:
+ void set(int uniformIndex, const void* ptr, int size);
+ void get(int uniformIndex, void* ptr, int size) const;
+
+ struct Info {
+ const Type m_type;
+ const int m_offset;
+ };
+
+ std::vector m_infos;
+ QByteArray m_byteArray;
+};
+
+template<>
+inline void rendergraph::UniformsCache::set(int uniformIndex, const QColor& color) {
+ set(uniformIndex, QVector4D{color.redF(), color.greenF(), color.blueF(), color.alphaF()});
+}
+
+template<>
+inline void rendergraph::UniformsCache::set(
+ int uniformIndex, const QMatrix4x4& matrix) {
+ DEBUG_ASSERT(type(uniformIndex) == typeOf());
+ set(uniformIndex, matrix.constData(), sizeOf(typeOf()));
+}
diff --git a/src/rendergraph/common/rendergraph/uniformset.cpp b/src/rendergraph/common/rendergraph/uniformset.cpp
new file mode 100644
index 000000000000..68e077613bd8
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniformset.cpp
@@ -0,0 +1,20 @@
+#include "rendergraph/uniformset.h"
+
+using namespace rendergraph;
+
+UniformSet::UniformSet(std::initializer_list list, const std::vector& names) {
+ int i = 0;
+ for (auto item : list) {
+ add(Uniform{item.m_type, names[i++]});
+ }
+}
+
+UniformSet::~UniformSet() = default;
+
+void UniformSet::add(const Uniform& uniform) {
+ m_uniforms.push_back(uniform);
+}
+
+const std::vector& UniformSet::uniforms() const {
+ return m_uniforms;
+}
diff --git a/src/rendergraph/common/rendergraph/uniformset.h b/src/rendergraph/common/rendergraph/uniformset.h
new file mode 100644
index 000000000000..34af87e5c94c
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/uniformset.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "rendergraph/uniform.h"
+
+namespace rendergraph {
+class UniformSet;
+}
+
+class rendergraph::UniformSet {
+ public:
+ UniformSet(std::initializer_list list, const std::vector& names);
+
+ ~UniformSet();
+
+ const std::vector& uniforms() const;
+
+ private:
+ void add(const Uniform& uniform);
+ std::vector m_uniforms;
+};
+
+namespace rendergraph {
+template
+UniformSet makeUniformSet(const std::vector& names) {
+ return UniformSet({(Uniform::create())...}, names);
+}
+} // namespace rendergraph
diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h
new file mode 100644
index 000000000000..2f2a46dc2ebe
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbavertexupdater.h
@@ -0,0 +1,137 @@
+#pragma once
+
+#include "rendergraph/geometry.h"
+
+namespace rendergraph {
+class RGBAVertexUpdater;
+}
+
+class rendergraph::RGBAVertexUpdater {
+ public:
+ RGBAVertexUpdater(Geometry::RGBAColoredPoint2D* pData)
+ : m_pData(pData),
+ m_pWrite(pData) {
+ }
+
+ void addRectangle(
+ QVector2D lt,
+ QVector2D rb,
+ QVector4D rgba) {
+ addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgba.x(), rgba.y(), rgba.z(), rgba.w());
+ }
+ void addRectangleVGradient(
+ QVector2D lt,
+ QVector2D rb,
+ QVector4D rgbat,
+ QVector4D rgbab) {
+ addRectangleVGradient(lt.x(),
+ lt.y(),
+ rb.x(),
+ rb.y(),
+ rgbat.x(),
+ rgbat.y(),
+ rgbat.z(),
+ rgbat.w(),
+ rgbab.x(),
+ rgbab.y(),
+ rgbab.z(),
+ rgbab.w());
+ }
+ void addRectangleHGradient(
+ QVector2D lt,
+ QVector2D rb,
+ QVector4D rgbal,
+ QVector4D rgbar) {
+ addRectangleHGradient(lt.x(),
+ lt.y(),
+ rb.x(),
+ rb.y(),
+ rgbal.x(),
+ rgbal.y(),
+ rgbal.z(),
+ rgbal.w(),
+ rgbar.x(),
+ rgbar.y(),
+ rgbar.z(),
+ rgbar.w());
+ }
+ void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b, float a) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r, g, b, a);
+ addTriangle(x1, y2, x2, y2, x2, y1, r, g, b, a);
+ }
+ void addRectangleVGradient(
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float r1,
+ float g1,
+ float b1,
+ float a1,
+ float r2,
+ float g2,
+ float b2,
+ float a2) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r1, g1, b1, a1, r2, g2, b2, a2);
+ addTriangle(x1, y2, x2, y2, x2, y1, r2, g2, b2, a2, r2, g2, b2, a2, r1, g1, b1, a1);
+ }
+ void addRectangleHGradient(
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float r1,
+ float g1,
+ float b1,
+ float a1,
+ float r2,
+ float g2,
+ float b2,
+ float a2) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, a1, r2, g2, b2, a2, r1, g1, b1, a1);
+ addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, a1, r2, g2, b2, a2, r2, g2, b2, a2);
+ }
+ void addTriangle(float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3,
+ float r,
+ float g,
+ float b,
+ float a) {
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r, g, b, a};
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r, g, b, a};
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r, g, b, a};
+ }
+ void addTriangle(float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3,
+ float r1,
+ float g1,
+ float b1,
+ float a1,
+ float r2,
+ float g2,
+ float b2,
+ float a2,
+ float r3,
+ float g3,
+ float b3,
+ float a3) {
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x1, y1, r1, g1, b1, a1};
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x2, y2, r2, g2, b2, a2};
+ *m_pWrite++ = Geometry::RGBAColoredPoint2D{x3, y3, r3, g3, b3, a3};
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ Geometry::RGBAColoredPoint2D* const m_pData;
+ Geometry::RGBAColoredPoint2D* m_pWrite;
+};
diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h
new file mode 100644
index 000000000000..e85f525a5edf
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/rgbvertexupdater.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "rendergraph/geometry.h"
+
+namespace rendergraph {
+class RGBVertexUpdater;
+}
+
+class rendergraph::RGBVertexUpdater {
+ public:
+ RGBVertexUpdater(Geometry::RGBColoredPoint2D* pData)
+ : m_pData(pData),
+ m_pWrite(pData) {
+ }
+
+ void addRectangle(
+ QVector2D lt,
+ QVector2D rb,
+ QVector3D rgb) {
+ addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), rgb.x(), rgb.y(), rgb.z());
+ }
+ void addRectangleVGradient(
+ QVector2D lt,
+ QVector2D rb,
+ QVector3D rgbt,
+ QVector3D rgbb) {
+ addRectangleVGradient(lt.x(),
+ lt.y(),
+ rb.x(),
+ rb.y(),
+ rgbt.x(),
+ rgbt.y(),
+ rgbt.z(),
+ rgbb.x(),
+ rgbb.y(),
+ rgbb.z());
+ }
+ void addRectangleHGradient(
+ QVector2D lt,
+ QVector2D rb,
+ QVector3D rgbl,
+ QVector3D rgbr) {
+ addRectangleHGradient(lt.x(),
+ lt.y(),
+ rb.x(),
+ rb.y(),
+ rgbl.x(),
+ rgbl.y(),
+ rgbl.z(),
+ rgbr.x(),
+ rgbr.y(),
+ rgbr.z());
+ }
+ void addRectangle(float x1, float y1, float x2, float y2, float r, float g, float b) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r, g, b);
+ addTriangle(x1, y2, x2, y2, x2, y1, r, g, b);
+ }
+ void addRectangleVGradient(
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float r1,
+ float g1,
+ float b1,
+ float r2,
+ float g2,
+ float b2) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1);
+ addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2);
+ }
+ void addRectangleHGradient(
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float r1,
+ float g1,
+ float b1,
+ float r2,
+ float g2,
+ float b2) {
+ addTriangle(x1, y1, x2, y1, x1, y2, r1, g1, b1, r2, g2, b2, r1, g1, b1);
+ addTriangle(x1, y2, x2, y2, x2, y1, r1, g1, b1, r2, g2, b2, r2, g2, b2);
+ }
+ void addTriangle(float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3,
+ float r,
+ float g,
+ float b) {
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r, g, b};
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r, g, b};
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r, g, b};
+ }
+ void addTriangle(float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3,
+ float r1,
+ float g1,
+ float b1,
+
+ float r2,
+ float g2,
+ float b2,
+
+ float r3,
+ float g3,
+ float b3) {
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x1, y1, r1, g1, b1};
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x2, y2, r2, g2, b2};
+ *m_pWrite++ = Geometry::RGBColoredPoint2D{x3, y3, r3, g3, b3};
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ Geometry::RGBColoredPoint2D* const m_pData;
+ Geometry::RGBColoredPoint2D* m_pWrite;
+};
diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h
new file mode 100644
index 000000000000..e754fb509a0c
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/texturedvertexupdater.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "rendergraph/geometry.h"
+
+namespace rendergraph {
+class TexturedVertexUpdater;
+}
+
+class rendergraph::TexturedVertexUpdater {
+ public:
+ TexturedVertexUpdater(Geometry::TexturedPoint2D* pData)
+ : m_pData(pData),
+ m_pWrite(pData) {
+ }
+ void addRectangle(
+ QVector2D lt, QVector2D rb, QVector2D tlr, QVector2D trb) {
+ addRectangle(lt.x(), lt.y(), rb.x(), rb.y(), tlr.x(), tlr.y(), trb.x(), trb.y());
+ }
+ void addRectangle(
+ float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) {
+ addTriangle(x1, y1, x2, y1, x1, y2, tx1, ty1, tx2, ty1, tx1, ty2);
+ addTriangle(x1, y2, x2, y2, x2, y1, tx1, ty2, tx2, ty2, tx2, ty1);
+ }
+ void addTriangle(float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3,
+ float tx1,
+ float ty1,
+ float tx2,
+ float ty2,
+ float tx3,
+ float ty3) {
+ *m_pWrite++ = Geometry::TexturedPoint2D{x1, y1, tx1, ty1};
+ *m_pWrite++ = Geometry::TexturedPoint2D{x2, y2, tx2, ty2};
+ *m_pWrite++ = Geometry::TexturedPoint2D{x3, y3, tx3, ty3};
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ Geometry::TexturedPoint2D* const m_pData;
+ Geometry::TexturedPoint2D* m_pWrite;
+};
diff --git a/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h
new file mode 100644
index 000000000000..d98d40f8c195
--- /dev/null
+++ b/src/rendergraph/common/rendergraph/vertexupdaters/vertexupdater.h
@@ -0,0 +1,37 @@
+#pragma once
+
+namespace rendergraph {
+class VertexUpdater;
+}
+
+class rendergraph::VertexUpdater {
+ public:
+ VertexUpdater(Geometry::Point2D* pData)
+ : m_pData(pData),
+ m_pWrite(pData) {
+ }
+ void addRectangle(
+ QVector2D lt, QVector2D rb) {
+ addRectangle(lt.x(), lt.y(), rb.x(), rb.y());
+ }
+ void addRectangle(
+ float x1,
+ float y1,
+ float x2,
+ float y2) {
+ addTriangle(x1, y1, x2, y1, x1, y2);
+ addTriangle(x1, y2, x2, y2, x2, y1);
+ }
+ void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3) {
+ *m_pWrite++ = Geometry::Point2D{x1, y1};
+ *m_pWrite++ = Geometry::Point2D{x2, y2};
+ *m_pWrite++ = Geometry::Point2D{x3, y3};
+ }
+ int index() const {
+ return static_cast(m_pWrite - m_pData);
+ }
+
+ private:
+ Geometry::Point2D* const m_pData;
+ Geometry::Point2D* m_pWrite;
+};
diff --git a/src/rendergraph/common/treenode.cpp b/src/rendergraph/common/treenode.cpp
new file mode 100644
index 000000000000..63fe5667ffc7
--- /dev/null
+++ b/src/rendergraph/common/treenode.cpp
@@ -0,0 +1,50 @@
+#include "rendergraph/treenode.h"
+
+#include "backend/basenode.h"
+
+using namespace rendergraph;
+
+void TreeNode::appendChildNode(std::unique_ptr&& pChild) {
+ auto pChildRawPtr = pChild.get();
+ if (m_pLastChild) {
+ pChild->m_pPreviousSibling = m_pLastChild;
+ m_pLastChild->m_pNextSibling = std::move(pChild);
+ } else {
+ m_pFirstChild = std::move(pChild);
+ }
+ m_pLastChild = pChildRawPtr;
+ m_pLastChild->m_pParent = this;
+
+ onAppendChildNode(pChildRawPtr);
+}
+
+std::unique_ptr TreeNode::removeAllChildNodes() {
+ onRemoveAllChildNodes();
+
+ m_pLastChild = nullptr;
+ TreeNode* pChild = m_pFirstChild.get();
+ while (pChild) {
+ pChild->m_pParent = nullptr;
+ pChild = pChild->m_pNextSibling.get();
+ }
+ return std::move(m_pFirstChild);
+}
+
+std::unique_ptr TreeNode::removeChildNode(TreeNode* pChild) {
+ onRemoveChildNode(pChild);
+
+ std::unique_ptr pRemoved;
+ if (pChild == m_pFirstChild.get()) {
+ pRemoved = std::move(m_pFirstChild);
+ m_pFirstChild = std::move(pChild->m_pNextSibling);
+ } else {
+ pRemoved = std::move(pChild->m_pPreviousSibling->m_pNextSibling);
+ pChild->m_pPreviousSibling->m_pNextSibling = std::move(pChild->m_pNextSibling);
+ pChild->m_pPreviousSibling = nullptr;
+ }
+ if (pChild == m_pLastChild) {
+ m_pLastChild = nullptr;
+ }
+ pChild->m_pParent = nullptr;
+ return pRemoved;
+}
diff --git a/src/rendergraph/common/types.cpp b/src/rendergraph/common/types.cpp
new file mode 100644
index 000000000000..211538e5af47
--- /dev/null
+++ b/src/rendergraph/common/types.cpp
@@ -0,0 +1,111 @@
+#include "rendergraph/types.h"
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace rendergraph;
+
+int rendergraph::sizeOf(PrimitiveType type) {
+ switch (type) {
+ case PrimitiveType::UInt:
+ return sizeof(uint32_t);
+ case PrimitiveType::Float:
+ return sizeof(float);
+ }
+ return 0;
+}
+
+int rendergraph::sizeOf(Type type) {
+ switch (type) {
+ case Type::UInt:
+ return sizeof(uint32_t);
+ case Type::Float:
+ return sizeof(float);
+ case Type::Vector2D:
+ return sizeof(float) * 2;
+ case Type::Vector3D:
+ return sizeof(float) * 3;
+ case Type::Vector4D:
+ return sizeof(float) * 4;
+ case Type::Matrix4x4:
+ return sizeof(float) * 4 * 4;
+ }
+ return 0;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::UInt;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::Float;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::Vector2D;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::Vector3D;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::Vector4D;
+}
+
+template<>
+Type rendergraph::typeOf() {
+ return Type::Matrix4x4;
+}
+
+template<>
+PrimitiveType rendergraph::primitiveTypeOf() {
+ return PrimitiveType::Float;
+}
+
+template<>
+PrimitiveType rendergraph::primitiveTypeOf() {
+ return PrimitiveType::UInt;
+}
+
+template<>
+PrimitiveType rendergraph::primitiveTypeOf() {
+ return PrimitiveType::Float;
+}
+template<>
+PrimitiveType rendergraph::primitiveTypeOf() {
+ return PrimitiveType::Float;
+}
+template<>
+PrimitiveType rendergraph::primitiveTypeOf() {
+ return PrimitiveType::Float;
+}
+
+template<>
+int rendergraph::tupleSizeOf() {
+ return 1;
+}
+template<>
+int rendergraph::tupleSizeOf() {
+ return 1;
+}
+template<>
+int rendergraph::tupleSizeOf() {
+ return 2;
+}
+template<>
+int rendergraph::tupleSizeOf() {
+ return 3;
+}
+template<>
+int rendergraph::tupleSizeOf() {
+ return 4;
+}
diff --git a/src/rendergraph/examples/CMakeLists.txt b/src/rendergraph/examples/CMakeLists.txt
new file mode 100644
index 000000000000..b826c663aa45
--- /dev/null
+++ b/src/rendergraph/examples/CMakeLists.txt
@@ -0,0 +1,18 @@
+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)
+
+target_compile_definitions(rendergraph_gl PUBLIC
+ $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED>
+)
+target_compile_definitions(rendergraph_sg PUBLIC
+ $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED>
+)
+
+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..588b5a43ef46
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenodes.cpp
@@ -0,0 +1,73 @@
+#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));
+}
+
+ExampleTopNode::ExampleTopNode(rendergraph::Context& context) {
+ TreeNode::appendChildNode(std::make_unique());
+ TreeNode::appendChildNode(std::make_unique());
+ TreeNode::appendChildNode(std::make_unique());
+
+ {
+ QImage img(":/example/images/test.png");
+ static_cast(TreeNode::lastChild())
+ ->setTexture(
+ std::make_unique(context, img));
+ }
+}
diff --git a/src/rendergraph/examples/common/examplenodes.h b/src/rendergraph/examples/common/examplenodes.h
new file mode 100644
index 000000000000..ca0efe732234
--- /dev/null
+++ b/src/rendergraph/examples/common/examplenodes.h
@@ -0,0 +1,43 @@
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/node.h"
+#include "rendergraph/texture.h"
+
+namespace rendergraph {
+class ExampleNode1;
+class ExampleNode2;
+class ExampleNode3;
+class ExampleTopNode;
+} // 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);
+};
+
+class rendergraph::ExampleTopNode : public rendergraph::Node {
+ public:
+ ExampleTopNode(rendergraph::Context& context);
+};
diff --git a/src/rendergraph/examples/gl_example/CMakeLists.txt b/src/rendergraph/examples/gl_example/CMakeLists.txt
new file mode 100644
index 000000000000..0d28d7a7d7d1
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/CMakeLists.txt
@@ -0,0 +1,35 @@
+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
+)
+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..a5e5085d2570
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/window.cpp
@@ -0,0 +1,36 @@
+#include "window.h"
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+
+Window::Window() {
+}
+
+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_pEngine.reset();
+}
+
+void Window::initializeGL() {
+ rendergraph::Context context;
+
+ auto node = std::make_unique(context);
+
+ m_pEngine = std::make_unique(std::move(node));
+ m_pEngine->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_pEngine->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..8c2a5646bef0
--- /dev/null
+++ b/src/rendergraph/examples/gl_example/window.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include
+#include
+
+#include "rendergraph/engine.h"
+
+namespace rendergraph {
+class Graph;
+}
+
+class Window : public QOpenGLWindow {
+ public:
+ Window();
+
+ void initializeGL() override;
+ void resizeGL(int w, int h) override;
+ void paintGL() override;
+
+ void closeEvent(QCloseEvent* ev) override;
+
+ private:
+ std::unique_ptr m_pEngine;
+};
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..8651791c0a08
--- /dev/null
+++ b/src/rendergraph/examples/sg_example/customitem.cpp
@@ -0,0 +1,48 @@
+#include "customitem.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "examplenodes.h"
+#include "rendergraph/context.h"
+#include "rendergraph/node.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());
+
+ rendergraph::Context context(window());
+ m_node = std::make_unique(context);
+ bgNode->appendChildNode(m_node->backendNode());
+
+ 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..968b690e3daf
--- /dev/null
+++ b/src/rendergraph/opengl/CMakeLists.txt
@@ -0,0 +1,78 @@
+add_library(rendergraph_gl
+../common/attributeset.cpp
+../common/treenode.cpp
+../common/node.cpp
+../common/opacitynode.cpp
+../common/rendergraph/attribute.h
+../common/rendergraph/attributeset.h
+../common/rendergraph/treenode.h
+../common/rendergraph/geometry.h
+../common/rendergraph/geometrynode.h
+../common/rendergraph/material.h
+../common/rendergraph/material/endoftrackmaterial.cpp
+../common/rendergraph/material/endoftrackmaterial.h
+../common/rendergraph/material/patternmaterial.cpp
+../common/rendergraph/material/patternmaterial.h
+../common/rendergraph/material/rgbamaterial.cpp
+../common/rendergraph/material/rgbamaterial.h
+../common/rendergraph/material/rgbmaterial.cpp
+../common/rendergraph/material/rgbmaterial.h
+../common/rendergraph/material/texturematerial.cpp
+../common/rendergraph/material/texturematerial.h
+../common/rendergraph/material/unicolormaterial.cpp
+../common/rendergraph/material/unicolormaterial.h
+../common/rendergraph/materialshader.h
+../common/rendergraph/materialtype.h
+../common/rendergraph/node.h
+../common/rendergraph/opacitynode.h
+../common/rendergraph/texture.h
+../common/rendergraph/types.h
+../common/rendergraph/uniform.h
+../common/rendergraph/uniformscache.cpp
+../common/rendergraph/uniformscache.h
+../common/rendergraph/uniformset.cpp
+../common/rendergraph/uniformset.h
+../common/types.cpp
+backend/baseattributeset.cpp
+backend/baseattributeset.h
+backend/basegeometry.cpp
+backend/basegeometry.h
+backend/basegeometrynode.cpp
+backend/basegeometrynode.h
+backend/basematerial.cpp
+backend/basematerial.h
+backend/basematerialshader.cpp
+backend/basematerialtype.h
+backend/basenode.h
+backend/baseopacitynode.h
+backend/baseopenglnode.cpp
+backend/baseopenglnode.h
+backend/shadercache.h
+backend/basetexture.h
+treenode.cpp
+engine.cpp
+geometry.cpp
+geometrynode.cpp
+material.cpp
+materialshader.cpp
+openglnode.cpp
+rendergraph/context.h
+rendergraph/engine.h
+rendergraph/openglnode.h
+texture.cpp
+)
+
+target_link_libraries(rendergraph_gl PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::OpenGL
+)
+
+# USE_QSHADER_FOR_GL is set in rendergraph/CMakeLists.txt
+if(USE_QSHADER_FOR_GL)
+ message(STATUS "Using QShader to load qsb shaders for opengl")
+ target_link_libraries(rendergraph_gl PUBLIC Qt6::GuiPrivate)
+ target_compile_definitions(rendergraph_gl PRIVATE USE_QSHADER_FOR_GL)
+endif()
+
+target_include_directories(rendergraph_gl PUBLIC . ../common)
diff --git a/src/rendergraph/opengl/backend/baseattributeset.cpp b/src/rendergraph/opengl/backend/baseattributeset.cpp
new file mode 100644
index 000000000000..9ddec2306765
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseattributeset.cpp
@@ -0,0 +1,14 @@
+#include "rendergraph/assert.h"
+#include "rendergraph/attributeset.h"
+
+using namespace rendergraph;
+
+BaseAttributeSet::BaseAttributeSet(std::initializer_list list,
+ const std::vector& names) {
+ DEBUG_ASSERT(list.size() == names.size());
+ int i = 0;
+ m_attributes.reserve(list.size());
+ for (auto item : list) {
+ m_attributes.push_back(Attribute{item.m_tupleSize, item.m_primitiveType, names[i++]});
+ }
+}
diff --git a/src/rendergraph/opengl/backend/baseattributeset.h b/src/rendergraph/opengl/backend/baseattributeset.h
new file mode 100644
index 000000000000..57b7fcf76ec3
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseattributeset.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include
+#include
+
+#include "rendergraph/attribute.h"
+
+namespace rendergraph {
+class BaseAttributeSet;
+}
+
+class rendergraph::BaseAttributeSet {
+ protected:
+ BaseAttributeSet(std::initializer_list list, const std::vector& names);
+ std::vector m_attributes;
+};
diff --git a/src/rendergraph/opengl/backend/basegeometry.cpp b/src/rendergraph/opengl/backend/basegeometry.cpp
new file mode 100644
index 000000000000..03ffd7ac9882
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometry.cpp
@@ -0,0 +1,41 @@
+#include "backend/basegeometry.h"
+
+#include "rendergraph/geometry.h"
+
+using namespace rendergraph;
+
+BaseGeometry::BaseGeometry(
+ const AttributeSet& attributeSet, int vertexCount)
+ : m_drawingMode(static_cast(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);
+}
+
+int BaseGeometry::attributeCount() const {
+ return m_tupleSizes.size();
+}
+
+int BaseGeometry::vertexCount() const {
+ return m_vertexCount;
+}
+
+int BaseGeometry::offset(int attributeIndex) const {
+ return m_offsets[attributeIndex];
+}
+
+int BaseGeometry::tupleSize(int attributeIndex) const {
+ return m_tupleSizes[attributeIndex];
+}
+
+int BaseGeometry::stride() const {
+ return m_stride;
+}
diff --git a/src/rendergraph/opengl/backend/basegeometry.h b/src/rendergraph/opengl/backend/basegeometry.h
new file mode 100644
index 000000000000..60281f6cb4b6
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometry.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include
+
+#include "rendergraph/attributeset.h"
+
+namespace rendergraph {
+class BaseGeometry;
+}
+
+class rendergraph::BaseGeometry {
+ protected:
+ BaseGeometry(const AttributeSet& attributeSet, int vertexCount);
+
+ public:
+ int attributeCount() const;
+ int vertexCount() const;
+ int offset(int attributeIndex) const;
+ int tupleSize(int attributeIndex) const;
+ int stride() const;
+
+ protected:
+ int 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/backend/basegeometrynode.cpp b/src/rendergraph/opengl/backend/basegeometrynode.cpp
new file mode 100644
index 000000000000..889b654deca1
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometrynode.cpp
@@ -0,0 +1,100 @@
+#include "backend/basegeometrynode.h"
+
+#include
+
+#include "backend/shadercache.h"
+#include "rendergraph/geometrynode.h"
+#include "rendergraph/texture.h"
+
+using namespace rendergraph;
+
+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 BaseGeometryNode::initializeBackend() {
+ initializeOpenGLFunctions();
+ GeometryNode* pThis = static_cast(this);
+ pThis->material().setShader(ShaderCache::getShaderForMaterial(&pThis->material()));
+}
+
+void BaseGeometryNode::renderBackend() {
+ GeometryNode* pThis = static_cast(this);
+ Geometry& geometry = pThis->geometry();
+ Material& material = pThis->material();
+
+ 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 (material.clearUniformsCacheDirty() || !material.isLastModifierOfShader()) {
+ material.modifyShader();
+ const UniformsCache& cache = material.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 = material.texture(1);
+ if (pTexture) {
+ pTexture->backendTexture()->bind();
+ }
+
+ glDrawArrays(toGlDrawingMode(geometry.drawingMode()), 0, geometry.vertexCount());
+
+ if (pTexture) {
+ pTexture->backendTexture()->release();
+ }
+
+ for (int i = 0; i < geometry.attributeCount(); i++) {
+ int location = material.attributeLocation(i);
+ shader.disableAttributeArray(location);
+ }
+
+ shader.release();
+}
diff --git a/src/rendergraph/opengl/backend/basegeometrynode.h b/src/rendergraph/opengl/backend/basegeometrynode.h
new file mode 100644
index 000000000000..b215fa5dda34
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basegeometrynode.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseGeometryNode;
+}
+
+class rendergraph::BaseGeometryNode : public rendergraph::BaseNode,
+ public QOpenGLFunctions {
+ protected:
+ BaseGeometryNode() = default;
+
+ public:
+ void initializeBackend() override;
+ void renderBackend() override;
+};
diff --git a/src/rendergraph/opengl/backend/basematerial.cpp b/src/rendergraph/opengl/backend/basematerial.cpp
new file mode 100644
index 000000000000..ae6817ce878e
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerial.cpp
@@ -0,0 +1,34 @@
+#include "backend/basematerial.h"
+
+#include "rendergraph/material.h"
+
+using namespace rendergraph;
+
+void BaseMaterial::setShader(std::shared_ptr pShader) {
+ m_pShader = pShader;
+}
+
+MaterialShader& BaseMaterial::shader() const {
+ return *m_pShader;
+}
+
+int BaseMaterial::uniformLocation(int uniformIndex) const {
+ return m_pShader->uniformLocation(uniformIndex);
+}
+
+int BaseMaterial::attributeLocation(int attributeIndex) const {
+ return m_pShader->attributeLocation(attributeIndex);
+}
+
+void BaseMaterial::modifyShader() {
+ m_pShader->setLastModifiedByMaterial(this);
+}
+
+bool BaseMaterial::isLastModifierOfShader() const {
+ return this == m_pShader->lastModifiedByMaterial();
+}
+
+int BaseMaterial::compare(const BaseMaterial* other) const {
+ auto pThis = static_cast(this);
+ return pThis->compare(static_cast(other));
+}
diff --git a/src/rendergraph/opengl/backend/basematerial.h b/src/rendergraph/opengl/backend/basematerial.h
new file mode 100644
index 000000000000..c08b60daab27
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerial.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "rendergraph/materialshader.h"
+#include "rendergraph/materialtype.h"
+
+namespace rendergraph {
+class BaseMaterial;
+}
+
+class rendergraph::BaseMaterial {
+ protected:
+ BaseMaterial() = default;
+
+ public:
+ virtual MaterialType* type() const = 0;
+
+ // For parity with QSGMaterial, not used yet
+ int compare(const BaseMaterial* other) const;
+
+ void setShader(std::shared_ptr pShader);
+
+ MaterialShader& shader() const;
+
+ int uniformLocation(int uniformIndex) const;
+ int attributeLocation(int attributeIndex) const;
+
+ void modifyShader();
+ bool isLastModifierOfShader() const;
+ std::shared_ptr m_pShader;
+};
diff --git a/src/rendergraph/opengl/backend/basematerialshader.cpp b/src/rendergraph/opengl/backend/basematerialshader.cpp
new file mode 100644
index 000000000000..d4b245e482a0
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerialshader.cpp
@@ -0,0 +1,18 @@
+#include "backend/basematerialshader.h"
+
+using namespace rendergraph;
+
+int BaseMaterialShader::attributeLocation(int attributeIndex) const {
+ return m_attributeLocations[attributeIndex];
+}
+
+int BaseMaterialShader::uniformLocation(int uniformIndex) const {
+ return m_uniformLocations[uniformIndex];
+}
+
+BaseMaterial* BaseMaterialShader::lastModifiedByMaterial() const {
+ return m_pLastModifiedByMaterial;
+}
+void BaseMaterialShader::setLastModifiedByMaterial(BaseMaterial* pMaterial) {
+ m_pLastModifiedByMaterial = pMaterial;
+}
diff --git a/src/rendergraph/opengl/backend/basematerialshader.h b/src/rendergraph/opengl/backend/basematerialshader.h
new file mode 100644
index 000000000000..f7082c15ef01
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerialshader.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include
+#include
+
+namespace rendergraph {
+class BaseMaterial; // fwd decl to avoid circular dependency
+class BaseMaterialShader;
+} // namespace rendergraph
+
+class rendergraph::BaseMaterialShader : public QOpenGLShaderProgram {
+ protected:
+ BaseMaterialShader() = default;
+
+ public:
+ int attributeLocation(int attributeIndex) const;
+ int uniformLocation(int uniformIndex) const;
+
+ BaseMaterial* lastModifiedByMaterial() const;
+ void setLastModifiedByMaterial(BaseMaterial* pMaterial);
+
+ std::vector m_attributeLocations;
+ std::vector m_uniformLocations;
+ BaseMaterial* m_pLastModifiedByMaterial{};
+};
diff --git a/src/rendergraph/opengl/backend/basematerialtype.h b/src/rendergraph/opengl/backend/basematerialtype.h
new file mode 100644
index 000000000000..50b925d0bf4f
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basematerialtype.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace rendergraph {
+class BaseMaterialType {
+};
+} // namespace rendergraph
diff --git a/src/rendergraph/opengl/backend/basenode.h b/src/rendergraph/opengl/backend/basenode.h
new file mode 100644
index 000000000000..d224e4cc8b3c
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basenode.h
@@ -0,0 +1,44 @@
+#pragma once
+
+namespace rendergraph {
+class Engine; // fwd decl to avoid circular dependency
+}
+
+namespace rendergraph {
+class BaseNode;
+}
+
+class rendergraph::BaseNode {
+ protected:
+ BaseNode() = default;
+
+ public:
+ void setUsePreprocessFlag(bool value) {
+ m_usePreprocess = value;
+ }
+ bool usePreprocess() const {
+ return m_usePreprocess;
+ }
+ virtual bool isSubtreeBlocked() const {
+ return false;
+ }
+ virtual void preprocess() {
+ }
+ virtual void renderBackend() {
+ }
+ virtual void initializeBackend() {
+ }
+ virtual void resizeBackend(int, int) {
+ }
+
+ void setEngine(Engine* pEngine) {
+ m_pEngine = pEngine;
+ }
+ Engine* engine() const {
+ return m_pEngine;
+ }
+
+ private:
+ bool m_usePreprocess{};
+ Engine* m_pEngine{};
+};
diff --git a/src/rendergraph/opengl/backend/baseopacitynode.h b/src/rendergraph/opengl/backend/baseopacitynode.h
new file mode 100644
index 000000000000..e07f7d141fbe
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopacitynode.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseOpacityNode;
+}
+
+class rendergraph::BaseOpacityNode : public rendergraph::BaseNode {
+ protected:
+ BaseOpacityNode() = default;
+
+ public:
+ void setOpacity(float opacity) {
+ m_opacity = opacity;
+ }
+ bool isSubtreeBlocked() const override {
+ return m_opacity == 0.f;
+ }
+
+ private:
+ float m_opacity{1.f};
+};
diff --git a/src/rendergraph/opengl/backend/baseopenglnode.cpp b/src/rendergraph/opengl/backend/baseopenglnode.cpp
new file mode 100644
index 000000000000..1ba415696607
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopenglnode.cpp
@@ -0,0 +1,21 @@
+#include "backend/baseopenglnode.h"
+
+#include "rendergraph/openglnode.h"
+
+using namespace rendergraph;
+
+void BaseOpenGLNode::initializeBackend() {
+ initializeOpenGLFunctions();
+ OpenGLNode* pThis = static_cast(this);
+ pThis->initializeGL();
+}
+
+void BaseOpenGLNode::renderBackend() {
+ OpenGLNode* pThis = static_cast(this);
+ pThis->paintGL();
+}
+
+void BaseOpenGLNode::resizeBackend(int w, int h) {
+ OpenGLNode* pThis = static_cast(this);
+ pThis->resizeGL(w, h);
+}
diff --git a/src/rendergraph/opengl/backend/baseopenglnode.h b/src/rendergraph/opengl/backend/baseopenglnode.h
new file mode 100644
index 000000000000..70c33642413b
--- /dev/null
+++ b/src/rendergraph/opengl/backend/baseopenglnode.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+
+#include "backend/basenode.h"
+
+namespace rendergraph {
+class BaseOpenGLNode;
+}
+
+class rendergraph::BaseOpenGLNode : public rendergraph::BaseNode,
+ public QOpenGLFunctions {
+ protected:
+ BaseOpenGLNode() = default;
+
+ public:
+ void initializeBackend() override;
+ void renderBackend() override;
+ void resizeBackend(int w, int h) override;
+};
diff --git a/src/rendergraph/opengl/backend/basetexture.h b/src/rendergraph/opengl/backend/basetexture.h
new file mode 100644
index 000000000000..f260dc91ebbe
--- /dev/null
+++ b/src/rendergraph/opengl/backend/basetexture.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include
+
+namespace rendergraph {
+using BaseTexture = QOpenGLTexture;
+}
diff --git a/src/rendergraph/opengl/backend/shadercache.h b/src/rendergraph/opengl/backend/shadercache.h
new file mode 100644
index 000000000000..9c4653e493c7
--- /dev/null
+++ b/src/rendergraph/opengl/backend/shadercache.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include